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

Problem: poller_t invalid behavior on moved sockets #208

Merged
merged 2 commits into from
Apr 19, 2018

Conversation

kurdybacha
Copy link
Contributor

@kurdybacha kurdybacha commented Apr 18, 2018

On adding invalid socket (e.g. after move) there was exception thrown
but leaving modified and unconsistent internal state.
Besides that there was no possibility to remove a socket that was moved
into.

Solutions: changed internal unordered_map "handlers" to operator on zmq internal pointers and rollback when zmq_poller_add fails.

Added two test cases covering the issues.

On adding invalid socket (e.g. after move) there was exception thrown
but leaving modified and unconsistent internal state.
Besides that there was no possibility to remove a socket that was moved
into.

Solutions: check for socket validity (added operator bool) and changed
internal unordered_map "handlers" to operator on zmq internal pointers.

Added two test cases covering the issues.
@coveralls
Copy link

Pull Request Test Coverage Report for Build 38

  • 0 of 0 (NaN%) changed or added relevant lines in 0 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage remained the same at 56.25%

Files with Coverage Reduction New Missed Lines %
zmq.hpp 2 64.38%
Totals Coverage Status
Change from base Build 37: 0.0%
Covered Lines: 36
Relevant Lines: 64

💛 - Coveralls

1 similar comment
@coveralls
Copy link

Pull Request Test Coverage Report for Build 38

  • 0 of 0 (NaN%) changed or added relevant lines in 0 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage remained the same at 56.25%

Files with Coverage Reduction New Missed Lines %
zmq.hpp 2 64.38%
Totals Coverage Status
Change from base Build 37: 0.0%
Covered Lines: 36
Relevant Lines: 64

💛 - Coveralls

@coveralls
Copy link

coveralls commented Apr 18, 2018

Pull Request Test Coverage Report for Build 39

  • 0 of 0 (NaN%) changed or added relevant lines in 0 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage remained the same at 56.25%

Files with Coverage Reduction New Missed Lines %
zmq.hpp 2 64.38%
Totals Coverage Status
Change from base Build 37: 0.0%
Covered Lines: 36
Relevant Lines: 64

💛 - Coveralls

zmq.hpp Outdated
@@ -1053,10 +1058,12 @@ namespace zmq

void add (zmq::socket_t &socket, short events, handler_t handler)
{
if (!socket)
throw error_t ();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errno must be set to EINVAL for consistency, or error_t must be created with an explicit error code of EINVAL. An appropriate ctor of error_t must be added.

Copy link
Contributor Author

@kurdybacha kurdybacha Apr 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for review. I don't think that setting EINVAL in cpp wrapper is a good idea, we have exceptions already for carrying information and should not interfere with libzmq errno but I get the point that someone might rely on errno after throw and pattern is we throw after zmq_ call. Updated a review to throw only after zmq call fails so passing handling of nullptr socket to zmq in this case. Thanks.

Copy link
Member

@sigiesec sigiesec Apr 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ctor of error_t reads errno. It is so far only intended to be thrown when there was a libzmq error.
As it is now, the exception will contain an unpredictable errno, whatever value errno had before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you seen updated pull request?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now saw it... Sorry I did not read your comment properly earlier. Your change solves the problem I mentioned, but I think it has another problem.

auto inserted = false;
if (handler)
std::tie(it, inserted) = handlers.emplace (socket.ptr, std::move (handler));
if (0 == zmq_poller_add (poller_ptr, socket.ptr, inserted ? &(it->second) : nullptr, events)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a sensible behaviour, if inserted == false. This would mean that add had already been called for the same socket, which is not a valid use. There probably should be a test for this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can be addressed later.

Copy link
Contributor Author

@kurdybacha kurdybacha Apr 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is a valid use case and you are right that that if inserted is false it means socket already added but it will behave properly: rollback would not be triggered, only exception with errno specific to "already present" scenario. And this case is already covered by test add_handler_twice_throws.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fine then :) Sorry I missed that a test for this already exists.

if (0 == zmq_poller_add (poller_ptr, socket.ptr, handler_ptr, events)) {
auto it = std::end (handlers);
auto inserted = false;
if (handler)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment was removed, but it is still applicable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, should have explained that. This comment was only confusing as it is valid to pass nullptr user data to zmq_poller_add. libzmq does not care what user passes there, it only passively handles it back on wait. Similarly to actors in czmq

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is valid for libzmq/zmq_poller_add, but it is not sensible in the way cppzmq uses it. In wait, a no-op results if handler was nullptr here.

Copy link
Contributor Author

@kurdybacha kurdybacha Apr 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you are referring to if we should allow empty handler at all then this is a separate discussion.
You should not create //todo comments for that because that might indicate that something was agreed (where?) and waiting to be done. If you think if something can be improved why not create an issue open for discussion, or provide pull request..
In my opinion we should allow empty handlers, it is a valid use case where user is interested in wait signalling only by returning and no-op but I am open for discussion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original comment was indeed mistaken. When I wrote it, I thought that it would lead to some error later on, but this is not the case. So I am fine to remove it.

If you used a poller with a single socket and an empty handler, it might make sense. But it is somewhat misaligned with the design of poller_t, which manages and calls back into the handlers. I would still tend to disallow is, but I am also fine to leave it as it is. It would be good to have a test that uses it this way (i.e. not only for adding an empty handler, but also polling afterwards).

@sigiesec sigiesec merged commit 85a9805 into zeromq:master Apr 19, 2018
@kurdybacha kurdybacha deleted the poller-fix branch April 19, 2018 20:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants