Closed elivdahan closed 5 years ago
For others reading this via google, the main point is considering lws uses the single-threaded event loop style, the recommended answer is one context for the user code. That context can handle as many serving vhosts and discrete client connections as you like. None of the scenarios in the examples require multiple contexts... even multiple serving threads is done with one context.
I saw on #961 that you mentioned that one of the points is the old version of OpenSSL initialization and global variables, Is that problem still relevant? For which versions of OpenSSL?
You have the code... see for yourself
https://libwebsockets.org/git/libwebsockets/tree/lib/tls/openssl/ssl.c#n389
AFAIK openssl is the only point that may actually break it. Everything else in lws is dereferenced through the context cleanly.
For now (latest table version) there are more problematic points to work with multiple lws contexts from different threads?
It's going to be bloated... as an optimization on *nix where process fds are ordinals starting from 0, the context allocates some tables at creation for all possible fds the process may meet... you can find out how much with INFO logging. Since these are processwide assets, not just thread-specific, each context must allocate the whole thing. If your process - the whole process - actually only needs a few dozen fds max, you can artificially restrict this either with ulimit or in the context creation struct
https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-context-vhost.h#n358-361
Otherwise... this is FOSS. "Are there ever going to be problems" doesn't have an answer if you're choosing to do your own thing. The situation is as described, if you want to do it nobody will stop you. But if you find problems you may need to get your hands dirty.
Something else with this context-per-thread scheme is there no longer any global serialization from a single event loop, depending on what you're doing this may introduce a big burden for locking where none was needed before. But that's a trade-off you can figure out.
I was using a context-per-thread for a while on the client side but switched to a single context and service thread at the end. The only issue I have seen if calling lws_context_destroy(context) sometimes causes a double free crash especially when it failed to connect server.
On Tue, Mar 12, 2019 at 11:14 AM Andy Green notifications@github.com wrote:
For others reading this via google, the main point is considering lws uses the single-threaded event loop style, the recommended answer is one context for the user code. That context can handle as many serving vhosts and discrete client connections as you like. None of the scenarios in the examples require multiple contexts... even multiple serving threads is done with one context.
I saw on #961 https://github.com/warmcat/libwebsockets/issues/961 that you mentioned that one of the points is the old version of OpenSSL initialization and global variables, Is that problem still relevant? For which versions of OpenSSL?
You have the code... see for yourself
https://libwebsockets.org/git/libwebsockets/tree/lib/tls/openssl/ssl.c#n389
AFAIK openssl is the only point that may actually break it. Everything else in lws is dereferenced through the context cleanly.
For now (latest table version) there are more problematic points to work with multiple lws contexts from different threads?
It's going to be bloated... as an optimization on *nix where process fds are ordinals starting from 0, the context allocates some tables at creation for all possible fds the process may meet... you can find out how much with INFO logging. Since these are processwide assets, not just thread-specific, each context must allocate the whole thing. If your process - the whole process - actually only needs a few dozen fds max, you can artificially restrict this either with ulimit or in the context creation struct
https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-context-vhost.h#n358-361
Otherwise... this is FOSS. "Are there ever going to be problems" doesn't have an answer if you're choosing to do your own thing. The situation is as described, if you want to do it nobody will stop you. But if you find problems you may need to get your hands dirty.
Something else with this context-per-thread scheme is there no longer any global serialization from a single event loop, depending on what you're doing this may introduce a big burden for locking where none was needed before. But that's a trade-off you can figure out.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/warmcat/libwebsockets/issues/1516#issuecomment-472066545, or mute the thread https://github.com/notifications/unsubscribe-auth/ABxPC9HDOJ2JeD67qbI5Y5OqmnqDEXCQks5vV9JfgaJpZM4bqtgQ .
@kzhdev a bit late to debug it now but this sounds like the openssl init stuff. Modern openssl reference-counts init / destroy apis and doesn't make this kind of confusion. Older openssl blows up even if two different libs in the same process init it.
For others reading this via google, the main point is considering lws uses the single-threaded event loop style, the recommended answer is one context for the user code. That context can handle as many serving vhosts and discrete client connections as you like. None of the scenarios in the examples require multiple contexts... even multiple serving threads is done with one context.
I saw on #961 that you mentioned that one of the points is the old version of OpenSSL initialization and global variables, Is that problem still relevant? For which versions of OpenSSL?
You have the code... see for yourself
https://libwebsockets.org/git/libwebsockets/tree/lib/tls/openssl/ssl.c#n389
AFAIK openssl is the only point that may actually break it. Everything else in lws is dereferenced through the context cleanly.
For now (latest table version) there are more problematic points to work with multiple lws contexts from different threads?
It's going to be bloated... as an optimization on *nix where process fds are ordinals starting from 0, the context allocates some tables at creation for all possible fds the process may meet... you can find out how much with INFO logging. Since these are processwide assets, not just thread-specific, each context must allocate the whole thing. If your process - the whole process - actually only needs a few dozen fds max, you can artificially restrict this either with ulimit or in the context creation struct
https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-context-vhost.h#n358-361
Otherwise... this is FOSS. "Are there ever going to be problems" doesn't have an answer if you're choosing to do your own thing. The situation is as described, if you want to do it nobody will stop you. But if you find problems you may need to get your hands dirty.
Something else with this context-per-thread scheme is there no longer any global serialization from a single event loop, depending on what you're doing this may introduce a big burden for locking where none was needed before. But that's a trade-off you can figure out.
Thanks for fast reply!, Maybe I got something wrong, but as I see it, using single lws context on one thread means that this thread should manage a buffer pool for outgoing requests. In that case, each thread that wish to send a message needs to allocate a buffer and send it to the lws thread using queue/ring buffer. Then, the lws thread should release it. For incoming requests the challenge might be even bigger since I need to: 1) Multiplex the message for the relevant thread which leads to manage some "requests" table 2) Trust my user (thread that is not lws) to release the allocated buffer
I just thought that it might be easier let any thread to operate on its own context with its own lws_context and service loop. Maybe I missed some API/library-ability that meant just to meet these goals?
Thanks again for your time.
"manage a buffer pool for outgoing requests"
Lws is very flexible, but whether a buffer pool is useful or not depends on what you're doing with it. There are several minimal examples that show how to use the lws ringbuffer apis if that's what you need. But it's not part of the core function of lws to do that.
On a server, there's commonly a pretence that you will never run out of memory, so this kind of idea that user code should be insulated from any regulation about that is common. If you want to send a big file, that approach for maximum laziness at the highest level code is dump it all into heap and some magic will send it and deallocate heap as it goes.
Lws approach is to not produce output for send until it can be sent, so even considering kernel buffers, there is a minimum of in-flight data around. That's why lws works fine on really constrained targets like ESP32. Following that scheme properly impacts the design of the top level code, when you produce output, how you manage state, and how concurrency is handled - ie, if threads are actually bringing anything to the party. Another knock-on from that design principle is the buffer you ask to send doesn't have to persist beyond the call to the send api, eg it can be on the stack, so there's nothing for user code to track and deallocate. You have to deal with state in your user code, but it's possible to make very tight and clean implementations this way.
Hi Andy, First of all, thank you for the great libwebsocket!. I was trying to understand from GitHub issues/mailing lists of lws if it’s safe to use multiple lws contexts on the same process inside different threads, where each thread is 100% independent and has its own service loop:
From here (2014) I’ve understand that it is safe: https://libwebsockets.org/pipermail/libwebsockets/2014-September/001340.html
and from here (2017) I’ve understand that it is NOT safe: https://github.com/warmcat/libwebsockets/issues/961
So, 1) For now (latest table version) is it safe to work with multiple lws contexts from different threads ? 2) I saw on https://github.com/warmcat/libwebsockets/issues/961 that you mentioned that one of the points is the old version of OpenSSL initialization and global variables, Is that problem still relevant? For which versions of OpenSSL? 3) For now (latest table version) there are more problematic points to work with multiple lws contexts from different threads?
BTW, I’ve tried to create some multithreaded example (below) that with 10 threads, each has lws context with its own service loop, each thread act like WS client calling 100 times to WS echo server and it worked just fine with no crashes.
Thanks in advance!
code:
// Simple LibWebSockets test client //////////////////////////////////////////////////////////////////////////
include
include
include
include
include
include "libwebsockets.h"
//////////////////////////////////////////////////////////////////////////
static int bExit; static int bDenyDeflate = 1; static int request_counter = 0;
static int callback_test(struct lws wsi, enum lws_callback_reasons reason, void user, void* in, size_t len);
//////////////////////////////////////////////////////////////////////////
// Escape the loop when a SIGINT signal is received static void onSigInt(int sig) { bExit = 1; }
//////////////////////////////////////////////////////////////////////////
// The registered protocols static struct lws_protocols protocols[] = { { "test-protocol", // Protocol name callback_test, // Protocol callback 0, // Data size per session (can be left empty) 512, // Receive buffer size (can be left empty)
};
// The extensions LWS supports, without them some requests may not be able to work static const struct lws_extension extensions[] = { { "permessage-deflate", lws_extension_callback_pm_deflate, "permessage-deflate; client_max_window_bits" }, { "deflate-frame", lws_extension_callback_pm_deflate, "deflate_frame" }, { NULL, NULL, NULL } // Always needed at the end };
// List to identify the indices of the protocols by name enum protocolList { PROTOCOL_TEST,
};
//////////////////////////////////////////////////////////////////////////
// Callback for the test protocol static int callback_test(struct lws wsi, enum lws_callback_reasons reason, void user, void* in, size_t len) { // The message we send back to the echo server const char msg[128] = "Simple webserver echo test!";
}
//////////////////////////////////////////////////////////////////////////
// Main application entry void main_tr(void dataptr) { lws_set_log_level(LLL_ERR | LLL_WARN, lwsl_emit_syslog); // We don't need to see the notice messages
}
define NUM_THREADS 10
int main() {
/ this variable is our reference to the second thread / pthread_t inc_x_thread[NUM_THREADS]; / create a second thread which executes inc_x(&x) / for (int i = 0; i < NUM_THREADS; i++) { if(pthread_create(&inc_x_thread[i], NULL,main_tr, NULL)) {
}
/ wait for the second thread to finish / for (int i = 0; i < NUM_THREADS; i++) { if(pthread_join(inc_x_thread[i], NULL)) {
}
return 0;
}