Opened 10 years ago
Closed 9 years ago
#7461 closed Bugs (fixed)
detail::win32::ReleaseSemaphore may be called with count_to_release equal to 0
Reported by: | Owned by: | viboes | |
---|---|---|---|
Milestone: | Boost 1.55.0 | Component: | thread |
Version: | Boost 1.51.0 | Severity: | Problem |
Keywords: | Cc: |
Description
ReleaseSemaphore is documented by Microsoft as: "... lReleaseCount [in] The amount by which the semaphore object's current count is to be increased. The value must be greater than zero" http://msdn.microsoft.com/en-us/library/windows/desktop/ms685071(v=vs.85).aspx
When I run boundschecker on boost threads, I get a error because count_to_release is 0 in the following:
void release(unsigned count_to_release) {
notified=true; detail::win32::ReleaseSemaphore(semaphore,count_to_release,0);
}
(this is line 71 in boost_1_51_0/boost/thread/win32/condition_variable.hpp)
Change History (18)
comment:1 by , 10 years ago
Summary: | detail::win32::ReleaseSemaphore may be called with count_to_release euql to 0 → detail::win32::ReleaseSemaphore may be called with count_to_release equal to 0 |
---|
comment:2 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
Version: | Boost 1.52.0 → Boost 1.51.0 |
comment:3 by , 10 years ago
comment:4 by , 10 years ago
I apologize for the terseness. Here are a few detais. When I notice a zero count, the stack is:
c4test.exe!boost::detail::basic_cv_list_entry::release(unsigned int count_to_release=0) Line 68 c4test.exe!boost::detail::basic_cv_list_entry::release_waiters() Line 73
c4test.exe!boost::detail::basic_condition_variable::notify_all() Line 285 + 0x1a bytes
My conditional breakpoint (condition: "count_to_release == 0) is on the following line (before we modify notified)
void release(unsigned count_to_release) { --break-- notified=true;
When I hit this, code I have the following state of the local variables. Note that notified = true at this point in time,
it {px=0x0000000001dde7b0 } [ptr] 0x0000000001dd75b8 {px=0x0000000001dde7b0 } px 0x0000000001dde7b0 semaphore {handle_to_manage=0x0000000000000100 } wake_sem {handle_to_manage=0x0000000000000184 } waiters 0 notified true references 1 this 0x00000000003dee28 internal_mutex {...} boost::mutex total_count 0 long active_generation_count 0 unsigned int generations [2]({px=0x0000000001dde630 },{px=0x0000000001dde7b0 }) wake_sem {handle_to_manage=0x0000000000000198 }
comment:5 by , 10 years ago
Thanks for the details. Please, could you attach the example? Could you see when waiters become 0?
comment:6 by , 10 years ago
Hi again,
I think I have an hint. Could you check if defining ~entry_manager as follows avoid the problem?
~entry_manager() { if(! entry->is_notified()) { entry->remove_waiter(); } }
follow-up: 8 comment:7 by , 10 years ago
The proposed solution does solve the issue, meaning that with this change I no longer observe a call to basic_cv_list_entry::release with count_to_release equal to 0.
comment:8 by , 10 years ago
Replying to jsbache@…:
The proposed solution does solve the issue, meaning that with this change I no longer observe a call to basic_cv_list_entry::release with count_to_release equal to 0.
Thanks for checking. I will apply the modification and run the test on windows as soon as i have access to a windows machine.
comment:9 by , 10 years ago
Milestone: | To Be Determined → Boost 1.52.0 |
---|
comment:10 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Committed revision [81024].
comment:11 by , 10 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Hi,
it seems that the patch introduce a severe restriction, see #7657. I will rollback the change and take the time to analyze your issue.
comment:12 by , 10 years ago
Milestone: | Boost 1.52.0 → To Be Determined |
---|
comment:13 by , 9 years ago
Status: | reopened → new |
---|
comment:14 by , 9 years ago
Status: | new → assigned |
---|
comment:15 by , 9 years ago
Hi,
I don't reach to understand how your issue can arrive.
Could you add a small example reproduce the issue?
comment:16 by , 9 years ago
I suspect that the following sequence is possible.
- thread A call wait and it stands in [1] below. The value for waiters is 1.
- thread B notify_all and after wake the waiters in [2] the scheduler choose the ready thread A.
- thread A ends the call to do_wait and the entry_manager destructor calls. The value for waiters is 0. in [3] the scheduler choose the ready thread B.
- thread B calls release_waiters() in [4] which calls to release(0).
Current Code
template<typename lock_type> bool do_wait(lock_type& lock,timeout abs_time) { relocker<lock_type> locker(lock); entry_manager entry(get_wait_entry()); locker.unlock(); bool woken=false; while(!woken) { if(!entry->wait(abs_time)) // [1] { return false; } woken=entry->woken(); } return woken; } // [3] void notify_all() BOOST_NOEXCEPT { if(detail::interlocked_read_acquire(&total_count)) { boost::lock_guard<boost::mutex> internal_lock(internal_mutex); if(!total_count) { return; } wake_waiters(total_count); // [2] for(generation_list::iterator it=generations.begin(), end=generations.end(); it!=end;++it) { (*it)->release_waiters(); [4] } generations.clear(); wake_sem=detail::win32::handle(0); } }
I suspect that the call to entry->remove_waiter() inside entry_manager() must be protected
Could some one check if the following patch for boost/thread/win32/condition_variable.hpp works
=================================================================== --- condition_variable.hpp (revision 85663) +++ condition_variable.hpp (working copy) @@ -191,18 +191,17 @@ struct entry_manager { entry_ptr const entry; + boost::mutex& internal_mutex; BOOST_THREAD_NO_COPYABLE(entry_manager) - entry_manager(entry_ptr const& entry_): - entry(entry_) + entry_manager(entry_ptr const& entry_, boost::mutex& mutex_): + entry(entry_), internal_mutex(mutex_) {} ~entry_manager() { - //if(! entry->is_notified()) // several regression #7657 - { + boost::lock_guard<boost::mutex> internal_lock(internal_mutex); entry->remove_waiter(); - } } list_entry* operator->() @@ -218,7 +217,7 @@ { relocker<lock_type> locker(lock); - entry_manager entry(get_wait_entry()); + entry_manager entry(get_wait_entry(), internal_mutex); locker.unlock();
comment:18 by , 9 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Committed revision [85815].
Hi,
I don't know how the tool you are using has deduced such an issue.
The count_to_release parameter seems to be always > 0.
Please let me know if I'm missing something.
Either it is called with 1
or with the contents of waiters.
This variable is initialized as follows
and updated by
But remove_waiter is always called after add_waiter
Any deep explanation of on which circumstances this issue appear is welcome.