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

example yield.hpp possibly destroys locked mutex #99

Closed
thadeuluiz opened this issue Nov 23, 2016 · 8 comments
Closed

example yield.hpp possibly destroys locked mutex #99

thadeuluiz opened this issue Nov 23, 2016 · 8 comments

Comments

@thadeuluiz
Copy link

I posted this on the boost bugtracker however it wasnt noticed by anyone, so ill post it here.

In the examples/asio/detail/yield.hpp file, the sleeping fiber owns the mutex(for when async_result::get returns, the asio call returns and the yield_completion is destroyed).
Consider Fiber A, the callee of the async operation, and Fiber B, the fiber that calls async_handler.
Fiber B possibly runs in another thread.

Fiber B locks the mutex, sets fiber A as ready and goes to sleep without releasing the lock. Fiber A goes through the return process and destroys the mutex.

The solution is very simple. Simply reaquire the lock after the suspend call returns.

@olk
Copy link
Member

olk commented Nov 23, 2016

Because you did not mention the code lines I assume you refer to the implementation of yield_completion::wait().

The fiber calling this function locks the mutex (that protects variable completed_) and the suspends if the work was not completed yet.
Please note how the fiber is suspended: fibers::context::active()->suspend( lk);
The lock is released after this fiber has been suspended.

@olk olk closed this as completed Nov 23, 2016
@thadeuluiz
Copy link
Author

exactly, however, when suspend returns, it does not reaquire the lock, which is needed for safety

@olk
Copy link
Member

olk commented Nov 23, 2016

Why should it acquire the lock again - the variable isn't required to protected again.

@thadeuluiz
Copy link
Author

thadeuluiz commented Nov 23, 2016

I'll try to make it as clear as I can.

The thread that calls fibers::context::active()->set_ready( ctx_) does so while holding the lock.

The thread that created the lock then resumes. Note that the notifying thread might still hold the lock. It might have been suspended from execution by the OS or whatever.
The resumed thread that created the lock continues and destroys the mutex.

The notifying thread never releases the lock, yet it is destroyed.

I assumed that on return of fibers::context::active()->suspend( lk), lk would own the mutex, but it is not the case.

this might explain what im saying.
https://gist.github.com/thadeuluiz/d1d15d25d7875f1eab2089fedd707580

@olk olk reopened this Jan 12, 2017
@MacoChris
Copy link

Can confirm this is an issue in yield_handler_base::operator()(error_code) when a different thread resumes the fibre than the one waiting on it (ASAN complains about a bad read when releasing lk, since the resumed fibre may have already destroyed the lock).

I assumed that on return of fibers::context::active()->suspend( lk), lk would own the mutex, but it is not the case.

I also assumed this, and am surprised it's not the case (especially when this function is not documented). The current fix we're trying over the weekend is to add lk.lock(); at detail/yield.hpp:50.

@olk
Copy link
Member

olk commented Feb 17, 2017

fibers::context is not part of public the public API, it should not be used by the users.
keep in mind that the asio binding is an example - especially some support is required by the asio library (see #102).

I'm focused on other parts of the library - so I've no time to take care for the asio binding.

@MacoChris
Copy link

Yeah, we're running asio and the fibers in different threads to avoid the main-loop annoyance / integrate better with existing code.

It seems weird that yield is example code, but relies on internal APIs. Would we be better off writing something analogous to the 'use_future' in asio, returning a fiber future if we want less risk of interface breakage?

@olk
Copy link
Member

olk commented Apr 30, 2017

The problem is not related to the lock - the problem is that yield_completion is shared between async_result and yield_handler. It should go out of scope if the last reference (yield_handler or async_result) goes out of scope. Using an intrusive poitner should fix the issue.

olk added a commit that referenced this issue Apr 30, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
@olk olk closed this as completed Apr 30, 2017
olk added a commit that referenced this issue May 1, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
olk added a commit that referenced this issue May 2, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
olk added a commit that referenced this issue May 2, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
olk added a commit that referenced this issue May 2, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
olk added a commit that referenced this issue May 2, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
olk added a commit that referenced this issue May 2, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
olk added a commit that referenced this issue May 2, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
olk added a commit that referenced this issue May 3, 2017
- in context of #99: example yield.hpp possibly destroys locked mutex
Romain-Geissler-1A pushed a commit to Romain-Geissler-1A/fiber that referenced this issue Apr 3, 2020
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

No branches or pull requests

3 participants