-
-
Notifications
You must be signed in to change notification settings - Fork 361
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
added send_binary_blocking
function
#355
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: Farook Al-Sammarraie <farook@the-e-dev.com>
buffer.emplace_back(boost::asio::buffer(header)); | ||
buffer.emplace_back(boost::asio::buffer(msg)); | ||
|
||
boost::asio::io_service service; |
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.
Would it be possible to use adaptor_.get_io_service()
instead of creating a new io_service
?
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 tried to use it, however, it did not work properly for me. I think there might have been some timing issues where e.g. the deadline_timer is being canceled after write but the async_wait of the timer has not been started yet as the adaptor's IO was too busy?
buffer.emplace_back(boost::asio::buffer(msg)); | ||
|
||
boost::asio::io_service service; | ||
boost::asio::deadline_timer deadline_timer(service); |
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.
Since Crow already has task_timer
, would that be more appropriate?
if(!err) | ||
{ | ||
ec = boost::system::errc::make_error_code(boost::system::errc::timed_out); | ||
adaptor_.shutdown_write(); |
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.
since the adaptor is being shut down, shouldn't check_destroy()
or delete this
be used as well?
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.
my thought was that the caller decides whether he wants to close the connection in an error case or not. That is also the reason why &ec has to be provided.
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.
well if we're shutting down the adaptor I'm not sure there's anything left to do with the connection object, even for the http connection deleting the object was the course of action taken when an error occurs, the shutdown
parts were added to it later to make the process cleaner.
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, that is correct but I had a different approach. I am saving all connections in a map. If I call automatically check_destroy() which internally deletes itself (delete this) then I would run into a segmentation fault next time I tried to use it. In this case I can check the &ec and decide to remove that entry from my map. But does some other user know that he has to do that as well only if a write ran into a timeout?
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.
A use case like yours would probably be best solved with the close handler, maybe we can call the close method and have that delete the connection, and have the close handler deal with removing the connection from the map.
The approach I'm trying my best to take with Crow is to make getting something up and running as simple as can be while providing the as close as full access to the protocol internals for the convoluted edge cases that end up occurring.
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 tried to implement that approach but unfortunatelly I was not able to make it run in my asynchronous state machine.
It looks somehow like this now:
The Map is being locked everytime it is being used.
Every now and then something wants to send data -> map is being locked -> send.
During that send we have the case that it somehow hangs -> deadline timer -> check_destroy() -> onclose()-handler.
The onclose wants to lock the map as it wants to remove the connection but this is not possible as the map is still being locked by the send (deadlock).
Ok so now I outsource the onclose-task in a separate thread that waits for the send to finish and then locks the map to remove the connection.
Now "delete this" is being called as a final step of check_destroy() in the send task.
send-task returns and in an ideal world my outsourced thread will now recognize that it can lock the map to remove the deleted connection.
However, my asynchronous state machine decides to send another data and is faster than the outsourced thread.
Send() locks the map again and runs into a segfault as the connection had been deleted by itself.
Don't get me wrong. I am fine with your idea to have a very simple library and if you want to have check_destroy() being called in such a case then go for it, however, it did not work for me.
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.
Maybe returning a bool or enum or something could be applicable.
For your state machine, maybe you could check using an atomic bool flag (or using a similar approach), after sending data, if something has to be removed from the map.
E. g.: Send -> fails and results in onclose() -> sets flag and queues a deletion in a new thread
But before sending anything new, it checks that flag and resets it.
@@ -157,6 +158,36 @@ namespace crow | |||
do_write(); | |||
}); | |||
} | |||
|
|||
/// Send a binary encoded message in blocking mode. | |||
void send_binary_blocking(const std::string& msg, boost::system::error_code& ec) override |
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.
it would make sense to add a send_text_blocking()
. Also is it necessary to put ec
there?
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.
see my answer above regarding check_destroy()
|
||
boost::asio::io_service service; | ||
boost::asio::deadline_timer deadline_timer(service); | ||
deadline_timer.expires_from_now(boost::posix_time::seconds(3)); |
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.
This should probably be the same as timeout_
in app.h
.
send_binary_blocking
function
Regarding my opened bug "connection.send_binary() out of memory #340" I created a blocking send function with a local buffer and a deadline_timer which will cancel the asio::write after 3 seconds if it got stuck.