-
Notifications
You must be signed in to change notification settings - Fork 1
Massive cleanup #4
base: master
Are you sure you want to change the base?
Conversation
Add support for multithreaded request processing Enable streaming multipart downloads for reduced memory usage Cleaned up the architecture a little by deleting a lots of code! Upstream mongoose (and upstream libyuarel) are now submodules in vendor/ Removed support for WebSockets - for now.
| @@ -1,16 +1,24 @@ | |||
| The MIT License | |||
|
|
|||
| Copyright (c) 2007-2019 Dinesh Manajipet <dinesh.manajipet@proemion.com> | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Has been the license changed even on the upstream mongoose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh there are 2 licenses. One is the license of upstream mongoose C library - which is GPL something and commercial something.
Then there is the license of the mongoose-cpp fork we started off with. That was a "fork" of mongoose C library - but the license is MIT. With this pull request - the upstream mongoose is just a git submodule inside vendor/ , and we rewrote so much of the mongoose-cpp library - thought to update the license.
|
|
||
| void Request::setIsValid(bool value) | ||
| { | ||
| mIsValid = value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if two threads are calling this function? Is it possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is possible:
Line 265 in a1d9149
| server->mCurrentRequests[c]->setIsValid(false); |
The Server.cpp mostly uses this variable to notify any threads related to this Request when a connection closes, that this request is no longer valid.
Good catch. I had the Response::mIsValid an std::atomic_bool but forgot to make this one the same. Fixed.
|
|
||
| void Request::setMultipartEntities(const std::vector<Request::MultipartEntity> &entities) | ||
| { | ||
| mMultipartEntities = entities; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if two threads are calling this function? Is it possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically it is possible. So maybe we should ask the users to not do that.
This is mostly done after we finished getting a multipart request, before we call Controller::process (which is where a user can spawn new threads if they need).
A one time call really.
| addRoute("GET", "/hello", MyController, hello); | ||
| } | ||
| public: | ||
| bool hello(const std::shared_ptr<Request>& req, const std::shared_ptr<Response>& res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that two handlers like this one gets the same response object? I mean is it possible that two threads are working on the same Response *? If so there's a problem because i saw response methods are not thread safe. If not why passing a shared pointer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see any owning transfer or sharing here. Why not just Response&?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked the rest of the code. Looks like shared pointers are everywhere. But I can't see what to share here. Looks like it should be unique_ptr or just rvalue. So we can transfer the ownership of request and response to other thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rferrazz nope. before "handling" any request, the Server.cpp creates a request/response pair.
@asergunov the shared pointer is mostly there to support "end users" of this library to do multithreaded things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the magic happens here:
https://github.com/Proemion/mongoose-cpp/blob/multithreaded-server/lib/Server.cpp#L43
That's where we create and destroy a request/response pair for each incoming connection. Then we simply use the controller to dispatch the request to the appropriate handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do have to test the multi threaded server a lot though. Thinking of writing unit tests for it later today.
asergunov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's totally unclear how ownership passes for arguments and class members. I don't think smart pointers is right tool here.
| addRoute("GET", "/hello", MyController, hello); | ||
| } | ||
| public: | ||
| bool hello(const std::shared_ptr<Request>& req, const std::shared_ptr<Response>& res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see any owning transfer or sharing here. Why not just Response&?
| addRoute("GET", "/hello", MyController, hello); | ||
| } | ||
| public: | ||
| bool hello(const std::shared_ptr<Request>& req, const std::shared_ptr<Response>& res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked the rest of the code. Looks like shared pointers are everywhere. But I can't see what to share here. Looks like it should be unique_ptr or just rvalue. So we can transfer the ownership of request and response to other thread.
|
P.S I wish we were reviewing this repo, https://github.com/saidinesh5/mongoose-cpp/issues .. because that reflects the correct "relationship" between us and the upstream mongoose C library now. The plan was to delete this repo and fork from my clone |
|
@saidinesh5 For threading it will be nice to use non-copyable objects to make sure they are using only from one thread at once |
|
@asergunov but the threads are sharing the Request/Response objects because of the shared |
|
@saidinesh5 it will be nice to split server and client side API like that done in |
|
@asergunov but responses are also marked as invalid in the "client" too.. Responses are marked invalid
The Send operation can happen in the main thread/some subthread created by the user.. That's how I couldn't use promises. Plus we don't have (easy) control over the event loop run by mongoose library. So we can't just wait for a promise to finish in the main thread that runs the event loop. |
|
@saidinesh5 I'm not about using using future/promise there. I'm just about pattern shared private part pattern. It will be much cleaner if controller will take ownership of request/response objects. It could take it by rvalue or by unique_ptr. So controller could start new worker thread and pass ownership of request/response there. When response is filled up can just destroy it. Server still could have control over request/response validity in case connection was closed but using shared private part [EDIT] |
|
I don't like (don't understand) the reason to have shared pointers there. When you share something you have to make sure it's thread safe. But that's really bad pattern. I personally prefer RAII pattern. So you have object wrapping connection. So you can transfer it but can't copy.
To be able to process it in threads it should be something like |
Add support for multithreaded request processing
Enable streaming multipart downloads for reduced memory usage
Cleaned up the architecture a little by deleting a lots of code!
Upstream mongoose (and upstream libyuarel) are now submodules in vendor/
Removed support for WebSockets - for now.