Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

poll interface #28

Open
slimshader opened this issue Jul 16, 2014 · 10 comments
Open

poll interface #28

slimshader opened this issue Jul 16, 2014 · 10 comments

Comments

@slimshader
Copy link

Why is there no poll interface as with mongoose. I mean equivalent of mg_poll_server()?

I understand that current design might be more convenient to some but original one was much more flexible because threading model was up to the user.

In my case I am using civetweb in game engine and I had to really work-around current design to get back to original poll version.

@bel2125
Copy link
Member

bel2125 commented Jul 16, 2014

I think there has never been a mg_poll_server interface in Civetweb. Civetweb was forked from Mongoose in summer 2013 - since then they are two different webservers with different developers who have different ideas and priorities. Probably mg_poll_server has been added later?

What is this function actually supposed to do?

@slimshader
Copy link
Author

That is mongoose "hello world":

int main(void) {
struct mg_server *server = mg_create_server(NULL, NULL);
mg_set_option(server, "document_root", "."); // Serve current directory
mg_set_option(server, "listening_port", "8080"); // Open port 8080

for (;;) {
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
}
mg_destroy_server(&server);

return 0;
}

And from the docs:

"void mg_poll_server(struct mg_server *server, int milliseconds);
Performs one iteration of IO loop by iterating over all active connections, performing select() syscall on all sockets with a timeout of milliseconds. When select() returns, Mongoose does an IO for each socket that has data to be sent or received. Application code must call mg_poll_server() in a loop."

So basically its api allows to handle server requests in the thread of users choosing - for example from the main thread. Civetweb does not currently allow it. Poll api could be added to Civetweb quite easily imho: when number of threads passed to mq_start() is 0 (zero) then user has to explicitly call mg_poll_server() to process all pending requests in that thread.

It would be very useful for applications that already run in a loop constantly - like a game in my example.

@bel2125
Copy link
Member

bel2125 commented Jul 16, 2014

I once planned to provide a way to run the server in a single threaded environment - with the focus to small embedded devices, not game engines, but it would basically work for you as well. It is also useful for debugging, but I don't thing it is really simple. We can not take any code from mongoose, since it has an incompatible license. I did not do this yet, because I always considered it "nice to have", but not as essentially necessary - up to now I could afford the extra threads and I managed to debug it also as a multi-threaded application.
Why do you think you can not use the multi-threaded version?

@slimshader
Copy link
Author

It is problematic when you want to embed it in already functional application. Because server request handlers are always called from new thread, all the objects that will be accessed from it suddenly have to be thread safe. This bad idea for several reasons. Also, current design is also not very efficient as it should use tasking and not thread-per-request and that could also be fixed by moving threading model to the user.

Civetweb must have client processing loop implemented already, why not expose it to the user? Why would Mongoose code be needed?

I will survive as I already made my C++ version of HttpServer poll-based. Requests from handler thread are added to internal queue, then thread blocks, waits for a call to poll() from main thread, poll() fullfills queued requests and wakes handler thread to resume client processing.

@bel2125
Copy link
Member

bel2125 commented Jul 17, 2014

I recently added functions to protect objects that are not thread safe:
https://github.com/bel2125/civetweb/blob/master/include/civetweb.h#L341

I agree, the current "thread-per-connection" design is not very efficient. We discussed that since October 2013: https://groups.google.com/forum/#!topic/civetweb/sFHF75IDSH0
The initial discussion was to do this for V1.6, but later I decided that V1.6 should be mainly about Lua redesign. V1.6 is released since about 3 weeks (and I took some holidays afterwards). So we can continue the discussion for V1.7 right now.

The is some more threading discussion here:
https://groups.google.com/forum/#!topic/civetweb/jQMC-okP2xk

@slimshader
Copy link
Author

mg_(un)lock_context(struct mg_context* ctx) seem ok to sync all worker threads but what about main application thread (and others)?

Imagine you have a GUI application, user interacts with it the usual way, clicks widgets, edits with keyboard etc, lets say it is an spreadsheet app. In the background there is civetweb server running handling a request to print webpage based on the current state of the spreadsheet, now the spreadsheet has to locked every time it is accessed by civetweb or the user. But if there was poll interface, civetweb could be polled in the GUI thread explicitly or in some kind of Timer object that also triggers code in man/UI thread making everything just work correctly and integration of a server a breeze.

@bel2125
Copy link
Member

bel2125 commented Jul 25, 2014

Actually it can be done with the present lock mechanisms in the API as well.
However, I agree that when we rewrite the threading mechanism, we should also support that civetweb does not create any thread at all - so you would have to use a poll interface like the one you need.

Changing the threading mechanism is not something that can be done quickly over the weekend, so it might take some weeks until I find time to look at this more closely.

@bel2125
Copy link
Member

bel2125 commented Apr 21, 2015

This issue is now open for a while, so I want to update the status:

The issue is a request for an interface to use the web server in a way it does not create any thread, but only works as a callback called from an external thread.
It was reported when we planned to rewrite the threading model used within the server. Civetweb uses one thread per open connection, which in theory appears to be a bad idea since it uses a lot of threads, and threads are in theory expensive. Now, meanwhile we tested >32000 clients with one Civetweb instance by just creating 32000 threads, and in practice did not have any problems on a 5 year old standard desktop computer (see #50). It was not even a special server hardware but something you might even get second hand for <100$ nowadays.
So the original reason to restructure the threading model is no longer true (it was never true, but now there is an experimental evidence).

Thus, I think I will not restructure the threading model any time soon.

Still, I do understand the problem and probably make a short example how you could use the multi-threaded server in an otherwise single-threaded application without taking care about locks there.
Internally it will be some kind of lock or message queue, but not a really single threaded server.
Also I will start with this example not before the next release.

@slimshader
Copy link
Author

It is not just a matter of performance, threading model should not be part of library or should be configurable at very least (whether task scheduler is single threaded or a pool of threads or just blocking etc.). For example boost.asio which is de-facto standard high performance networking / asynchronous request handling does not create threads for handling messages but lets the user decide how it should be handled. This way if user wants to create thread-per-connection it is perfectly fine (and often the choice) but still allows to customize things enough to be able to handle requests from very specific thread (like UI thread). If it wont happen for civetweb that's fine, I am syncing it manually now anyway but configurability is a good thing.

@bel2125
Copy link
Member

bel2125 commented Apr 23, 2015

I technically agree.
But boost is a C++ library and civetweb is and should remain pure C.
Changing the threading model and stabilizing the server again is a lot of work, and I can currently not take this effort.
I just wanted to state the original reason to restructure the threading model, the expected limit for a high number of connections, turned out to be not really a problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants