Opened 10 years ago

Closed 10 years ago

Last modified 10 years ago

#7668 closed Bugs (fixed)

thread_group::join_all() should check whether its threads are joinable

Reported by: boost.lists@… Owned by: viboes
Milestone: Boost 1.53.0 Component: thread
Version: Boost 1.52.0 Severity: Problem
Keywords: thread; thread_group Cc:

Description (last modified by viboes)

If a thread in a thread_group is not joinable and BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED is defined, thread_group::join_all() throws invalid_argument. Since it's impossible to enumerate the threads in a thread_group, it should ensure on its own that every thread is joinable().

Change History (13)

comment:1 by viboes, 10 years ago

Description: modified (diff)
Owner: changed from Anthony Williams to viboes
Status: newassigned

comment:2 by viboes, 10 years ago

Milestone: To Be DeterminedBoost 1.53.0

Committed in trunk revision [81270].

comment:3 by anonymous, 10 years ago

What are users suppose to do in the meantime?

in reply to:  3 comment:4 by viboes, 10 years ago

Replying to anonymous:

What are users suppose to do in the meantime?

What do you mean?

comment:5 by viboes, 10 years ago

Resolution: fixed
Status: assignedclosed

Committed revision [81667].

comment:6 by mendola@…, 10 years ago

The fix doesn't solve the problem indeed the thread can detach itself so between the call joinable() and join() the precondition would not be valid anymore.

in reply to:  6 comment:7 by viboes, 10 years ago

Replying to mendola@…:

The fix doesn't solve the problem indeed the thread can detach itself so between the call joinable() and join() the precondition would not be valid anymore.

From my point of view a thread in a thread_group is owned by the thread group. Any direct operation on a thread belonging to the thread group falls in undefined behavior. From the documentation

Member function create_thread()

template<typename F>
thread* create_thread(F threadfunc);

Effects:

    Create a new boost::thread object as-if by new thread(threadfunc) and add it to the group. 
Postcondition:

    this->size() is increased by one, the new thread is running. 
Returns:

    A pointer to the new boost::thread object. 


Member function add_thread()

void add_thread(thread* thrd);

Precondition:

    The expression delete thrd is well-formed and will not result in undefined behaviour. 
Effects:

    Take ownership of the boost::thread object pointed to by thrd and add it to the group. 
Postcondition:

    this->size() is increased by one. 


comment:8 by Andrew Molyneux <andrew.molyneux@…>, 10 years ago

I don't think there does have to be a direct operation on the thread - the thread can just end normally (by returning from the function), which unless I'm very much mistaken makes it non-joinable. This could happen at any time, so I believe the code committed in revision 81270 has a race condition.

in reply to:  8 comment:9 by viboes, 10 years ago

Replying to Andrew Molyneux <andrew.molyneux@…>:

I don't think there does have to be a direct operation on the thread - the thread can just end normally (by returning from the function), which unless I'm very much mistaken makes it non-joinable. This could happen at any time, so I believe the code committed in revision 81270 has a race condition.

No. I thread that finish normally is still joinable. From where are you deducing this?

comment:10 by Andrew Molyneux <andrew.molyneux@…>, 10 years ago

No. I thread that finish normally is still joinable. From where are you deducing this?

I thought I'd observed it, but on further investigation the situation is a bit more complex. My code is actually using thread::timed_join() periodically, to check if the thread is still running. I've distilled it down to the following code, which triggers the behaviour that surprised me:

void threadFunction()
{
  boost::this_thread::sleep(boost::posix_time::millisec(100));
}

int main(int argc, char* argv[])
{
  boost::thread theThread(threadFunction);
  // Wait long enough to be fairly certain that the thread has finished
  boost::this_thread::sleep(boost::posix_time::millisec(200));
  if (!theThread.timed_join(boost::posix_time::millisec(0)))
  {
    cout << "Thread still running???" << endl;
    return 1;
  }
  theThread.join();
}

When I run that code, I get an exception on the call to theThread.join(), complaining that the thread is not joinable. It seems that after theThread.timed_join() succeeds, the thread is no longer joinable. Is this the expected behaviour?

By the way, my environment is Windows 7 64-bit, but 32-bit Boost, with Visual Studio 2005 and Boost 1.52.0).

comment:11 by anonymous, 10 years ago

Yes it is indeed.

in reply to:  10 ; comment:12 by viboes, 10 years ago

Replying to Andrew Molyneux <andrew.molyneux@…>:

No. I thread that finish normally is still joinable. From where are you deducing this?

I thought I'd observed it, but on further investigation the situation is a bit more complex. My code is actually using thread::timed_join() periodically, to check if the thread is still running. I've distilled it down to the following code, which triggers the behaviour that surprised me:

void threadFunction()
{
  boost::this_thread::sleep(boost::posix_time::millisec(100));
}

int main(int argc, char* argv[])
{
  boost::thread theThread(threadFunction);
  // Wait long enough to be fairly certain that the thread has finished
  boost::this_thread::sleep(boost::posix_time::millisec(200));
  if (!theThread.timed_join(boost::posix_time::millisec(0)))
  {
    cout << "Thread still running???" << endl;
    return 1;
  }
  theThread.join();
}

When I run that code, I get an exception on the call to theThread.join(), complaining that the thread is not joinable. It seems that after theThread.timed_join() succeeds, the thread is no longer joinable. Is this the expected behaviour?

Yes, I confirm.

in reply to:  12 comment:13 by Andrew Molyneux <andrew.molyneux@…>, 10 years ago

Replying to viboes:

Replying to Andrew Molyneux <andrew.molyneux@…>:

[snip]

When I run that code, I get an exception on the call to theThread.join(), complaining that the thread is not joinable. It seems that after theThread.timed_join() succeeds, the thread is no longer joinable. Is this the expected behaviour?

Yes, I confirm.

Excellent! Sorry for wasting your time with my ignorance. On reading the documentation again, it is clear that is the case. I've fixed my code now :-)

Note: See TracTickets for help on using tickets.