Opened 10 years ago

Closed 10 years ago

#7669 closed Bugs (fixed)

thread_group::join_all() should catch resource_deadlock_would_occur

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

Description

If a thread in thread_group is this_thread, join_all() throws resource_deadlock_would_occur. This violates the function post-condition: "Every thread in the group has terminated." Because of the above, and because it's impossible to enumerate the threads is a thread_group, it should catch resource_deadlock_would_occur and proceed with the joining loop. (The exception can be re-thrown when the loop ends.)

Change History (9)

comment:1 by viboes, 10 years ago

I think that we should change the pre-conditions of thread_group::join_all as the join() precondition should not be forcedly checked.

Requires: for each thread th in the thread group satisfy this_thread::get_id()!=th.get_id().

comment:2 by Igor R. <boost.lists@…>, 10 years ago

This would make thread_group unusable in the cases where the above precondition cannot be enforced. The question is why it's better than my proposal.

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

Replying to Igor R. <boost.lists@…>:

This would make thread_group unusable in the cases where the above precondition cannot be enforced. The question is why it's better than my proposal.

You are right, the user can not check this pre-condition as it has no access to the threads in the group other than duplicating. But calling to join_all and joining all but one of the threads don't respect either the post-conditions. I don't think it is a good design to to request to join on itself.

I planned to deprecate thread_group so I don't want to invest too much on it.

I guess there are two options:

  • the implementation check always if the this_thread is in the thread group and throw a specific exception, or
  • the library provides a function that states if this_thread is in the thread group, so that the user that is in a context that the current thread could be one of the group could check it. The join_all function has the Require clause described above.

What do you think? What do you think?

in reply to:  3 ; comment:4 by Igor R. <boost.lists@…>, 10 years ago

Replying to viboes:

First of all, the question is whether we want to improve thread_group and make it more usable - or not. If you really intend to deprecate it, then perhaps changing the formal pre-condition is enough. On the other hand, if the intent is to improve the usability, I'am afraid none of the 2 options would really help.

You are right, the user can not check this pre-condition as it has no access to the threads in the group other than duplicating. But calling to join_all and joining all but one of the threads don't respect either the post-conditions.

Right, the post-condition should be updated, if you decide to handle this_thread case. But please note that thread::join() post-condition is also incorrect with regard to this issue. IIUC, this story began when thread::join() just deadlocked if it was this_thread, and thread & thread_group behaved consistently at this point; then the deadlock issue was solved by throwing resource_deadlock_would_occur exception, but neither formal post-condition of thread::join() nor thread_group behavior were updated.

I don't think it is a good design to to request to join on itself.

Usually it's not a deliberate decision to join on itself, but design constraints, when trying to avoid this situation might unnecessarily complicate design. But "bad design" argument is even more applicable to thread::join() itself - nevertheless its behavior is reasonable and doesn't produce UB.

  • the implementation check always if the this_thread is in the thread group and throw a specific exception

What should the user do at this point? IIUC, it's impossible to recover from this situation (as opposed to thread::join() case!).

  • the library provides a function that states if this_thread is in the thread group, so that the user that is in a context that the current thread could be one of the group could check it. The join_all function has the Require clause described above.

Actually, this's equivalent to the above option, i.e the user will get information but won't be able to recover.

P.S. please note that this ticket doesn't differ much from #7668.

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

Replying to Igor R. <boost.lists@…>:

Replying to viboes:

First of all, the question is whether we want to improve thread_group and make it more usable - or not. If you really intend to deprecate it, then perhaps changing the formal pre-condition is enough.

Ye sthis is my intention, but I need movable containers more portables. There are yet some compilers in the regression tests that don't support Boost.Move,/Container, ...

On the other hand, if the intent is to improve the usability, I'am afraid none of the 2 options would really help.

You are right, the user can not check this pre-condition as it has no access to the threads in the group other than duplicating. But calling to join_all and joining all but one of the threads don't respect either the post-conditions.

Right, the post-condition should be updated, if you decide to handle this_thread case. But please note that thread::join() post-condition is also incorrect with regard to this issue.

Please, could you say explicitly what is incorrect?

IIUC, this story began when thread::join() just deadlocked if it was this_thread, and thread & thread_group behaved consistently at this point; then the deadlock issue was solved by throwing resource_deadlock_would_occur exception, but neither formal post-condition of thread::join() nor thread_group behavior were updated.

You know more than me.

I don't think it is a good design to to request to join on itself.

Usually it's not a deliberate decision to join on itself, but design constraints, when trying to avoid this situation might unnecessarily complicate design. But "bad design" argument is even more applicable to thread::join() itself - nevertheless its behavior is reasonable and doesn't produce UB.

Could you give an example where the user could want to call join_all from a thread inside the thread_group. I don't see none. In addition, it is difficult to provide a consistent behavior if this case could be a valid one.

  • the implementation check always if the this_thread is in the thread group and throw a specific exception

What should the user do at this point? IIUC, it's impossible to recover from this situation (as opposed to thread::join() case!).

You are right. this is not a solution.

  • the library provides a function that states if this_thread is in the thread group, so that the user that is in a context that the current thread could be one of the group could check it. The join_all function has the Require clause described above.

Actually, this's equivalent to the above option, i.e the user will get information but won't be able to recover.

Not really. Note that this is an exception throw to signal a pre-condition not satisfied situation? The user needs of course change his design.

P.S. please note that this ticket doesn't differ much from #7668.

Yes, and join() has as pre-condition that a thread can not join itself. I was just extrapolating the same argument on the second solution I gave.

comment:6 by viboes, 10 years ago

Milestone: To Be DeterminedBoost 1.53.0

Committed in trunk revision [81289].

comment:7 by anonymous, 10 years ago

Well my code did work in 1.51 but now it SIGABRTS because of this. How do I fix it now, before 1.53?

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

Replying to anonymous:

Well my code did work in 1.51 but now it SIGABRTS because of this. How do I fix it now, before 1.53?

Please, could you post the code that SIGABORTS?

comment:9 by viboes, 10 years ago

Resolution: fixed
Status: newclosed

Committed revision [81667].

Note: See TracTickets for help on using tickets.