From the 1.48 documentation
"Member function timed_join()
bool timed_join(const system_time& wait_until);
template<typename TimeDuration>
bool timed_join(TimeDuration const& rel_time);
Preconditions:
this->get_id()!=boost::this_thread::get_id()
Postconditions:
If *this refers to a thread of execution on entry, and timed_join
returns true, that thread of execution has completed, and *this no longer
refers to any thread of execution. If this call to timed_join returns false,
*this is unchanged.
"
Your second call doesn't satisfy the pre-conditions, so that the outcome of
this second call is undefined.
Best,
Vicente
--
View this message in context: http://boost.2283326.n4.nabble.com/thread-1-48-Multiple-interrupt-timed-join-leads-to-deadlock-tp4639403p4639476.html
Sent from the Boost - Dev mailing list archive at Nabble.com.
That precondition tests that your are not interrupting yourself doesn't
say anything about thread safety. Am I missing something ?
It depends on whether you want to submit an independent library. This has
the advantage that you don't
have to be backward compatible.
> I have named it Thread_Group (capitalized) to not conflict
> with old thread_group implementation.
>
> 1) Thread group is now thread safe, it can be used concurrently by
> multiple threads
Why a thread group should be inherently thread-safe? It seems to me that
having a thread container is already useful.
> 2) thread_group now maintains a list of handlers with the responsibility
> to:
> -) Avoid join and interrupts to be called concurrently on a thread
> -) Avoid to call join on a joined thread
> -) Avoid to call interrupt on a joined/interrupt thread
IMO, all the threads in a thread_group are owned by the group, and use move
semantics, no need to use pointer to threads. As a consequence there is no
need for the handler/wrapper.
> 3) Added join_any with the semantic to wait of any of the threads to
> terminate.
I don't like the implementation polling on all the threads to see if one of
them has ended.
Maybe the use of at_thread_exit could help. If all the threads of a thread
group are created with the create_thread function, an alternative is to use
a decorator of the user function that can signal when the thread has been
finished. This behavior is quite close to futures.
> 3) create_thread now returns a thread::id
> *this is a break change*, what we can do now is to leave the
> create_thread as it is (returning a thread pointer) but returning
> not a real thread but a class inherited by thread allowing only
> the get_id method on it (other methods should be implemented with
> throw("method not callable").
Do you really need to remove threads?
I will even go for don't returning anything?
I will remove also the add_thread function.
> 4) Due the fact mutex are not fair a thread issuing an interrupt_all
> most likely will go in starvation if a thread is issuing a join_all
> (especialy if the group contains a single thread). I can work at
> it.
Could you clarify your concern?
Why you have needed to change the implementation of join_all? It seems more
complex now.
As I said I'm for deprecating thread_group in Boost.Thread. The
implementation you are proposing has not changed my mind.
I would prefer an approach where data structures (containers) and algorithms
(join, interrupt, ...) are separated, thread-safety is not mandatory, ...
Best,
Vicente
--
View this message in context: http://boost.2283326.n4.nabble.com/thread-1-48-Multiple-interrupt-timed-join-leads-to-deadlock-tp4639403p4639866.html
Sent from the Boost - Dev mailing list archive at Nabble.com.
_______________________________________________
It can manage without pestering the developers the fact that one entity
spawns a batch of threads and then wait for the completion waiting on
the join() while another entity (an user interface as example) can stop
the whole process if it's taking too much time. Otherwise as soon
someone performs a boost::thread_group::join then nothing can be done
from outside to stop the process. It seems a natural use to me.
>> 2) thread_group now maintains a list of handlers with the responsibility
>> to:
>> -) Avoid join and interrupts to be called concurrently on a thread
>> -) Avoid to call join on a joined thread
>> -) Avoid to call interrupt on a joined/interrupt thread
>
> IMO, all the threads in a thread_group are owned by the group, and use move
> semantics, no need to use pointer to threads. As a consequence there is no
> need for the handler/wrapper.
This is true if the thread_group does not permits to be used by multiple
threads interrupting/joining.
>> 3) Added join_any with the semantic to wait of any of the threads to
>> terminate.
>
> I don't like the implementation polling on all the threads to see if one of
> them has ended.
> Maybe the use of at_thread_exit could help. If all the threads of a thread
> group are created with the create_thread function, an alternative is to use
> a decorator of the user function that can signal when the thread has been
> finished. This behavior is quite close to futures.
I agree with it.
>> 3) create_thread now returns a thread::id
>> *this is a break change*, what we can do now is to leave the
>> create_thread as it is (returning a thread pointer) but returning
>> not a real thread but a class inherited by thread allowing only
>> the get_id method on it (other methods should be implemented with
>> throw("method not callable").
>
> Do you really need to remove threads?
> I will even go for don't returning anything?
>
> I will remove also the add_thread function.
I agree as well.
>> 4) Due the fact mutex are not fair a thread issuing an interrupt_all
>> most likely will go in starvation if a thread is issuing a join_all
>> (especialy if the group contains a single thread). I can work at
>> it.
>
> Could you clarify your concern?
Sure, if a thread performs a closed loop:
a) lock_mutex;
b) timed_join
c) unlock_mutex
d) goto a
and another thread is doing:
a) lock_mutex;
b) interrupt
c) unlock_mutex
then it goes in starvationm we have observed this (even in a
deterministic way), then I had to make the two interrupt/timed_join on
the thread handler fair each other.
Our platform is a linux platform with a 3.2.0 kernel.
> Why you have needed to change the implementation of join_all? It seems more
> complex now.
Because if you want to issue an interrupt while someone is joining a
thread you need to have a join timed in order to periodically release
the mutex protecting the thread.
> As I said I'm for deprecating thread_group in Boost.Thread. The
> implementation you are proposing has not changed my mind.
Fine with me, leaving it as it is now is worst than
> I would prefer an approach where data structures (containers) and algorithms
> (join, interrupt, ...) are separated, thread-safety is not mandatory, ...
I agreed with you about the fact if not explicitly written a library is
not meant to be thread safe, this rule doesn't fit a thread library.
I suggest to explicitly write, even in bold, the fact that thread_group
and thread classes are not thread safe.
Regards
Gaetano Mendola
_______________________________________________
Note that this is a specific behavior that can not be added to the
thread_group class. I will be for the addition of an algorithm/free
function that try to join the threads on a container/range during a
given duration or until an expiration time (removing the joined threads).
>>> 2) thread_group now maintains a list of handlers with the
>>> responsibility
>>> to:
>>> -) Avoid join and interrupts to be called concurrently on a
>>> thread
>>> -) Avoid to call join on a joined thread
>>> -) Avoid to call interrupt on a joined/interrupt thread
>>
>> IMO, all the threads in a thread_group are owned by the group, and
>> use move
>> semantics, no need to use pointer to threads. As a consequence there
>> is no
>> need for the handler/wrapper.
>
> This is true if the thread_group does not permits to be used by multiple
> threads interrupting/joining.
I understand now why you did this way. But I will not do that.
>
>>> 4) Due the fact mutex are not fair a thread issuing an interrupt_all
>>> most likely will go in starvation if a thread is issuing a
>>> join_all
>>> (especialy if the group contains a single thread). I can work at
>>> it.
>>
>> Could you clarify your concern?
>
> Sure, if a thread performs a closed loop:
>
> a) lock_mutex;
> b) timed_join
> c) unlock_mutex
> d) goto a
>
> and another thread is doing:
>
> a) lock_mutex;
> b) interrupt
> c) unlock_mutex
>
> then it goes in starvationm we have observed this (even in a
> deterministic way), then I had to make the two interrupt/timed_join on
> the thread handler fair each other.
> Our platform is a linux platform with a 3.2.0 kernel.
>
IMO, only one thread should join/interrupt all the threads. This avoid
all these issues.
>> Why you have needed to change the implementation of join_all? It
>> seems more
>> complex now.
>
> Because if you want to issue an interrupt while someone is joining a
> thread you need to have a join timed in order to periodically release
> the mutex protecting the thread.
I see why now.
>
>> As I said I'm for deprecating thread_group in Boost.Thread. The
>> implementation you are proposing has not changed my mind.
>
> Fine with me, leaving it as it is now is worst than
The class thread_group is not a big one. You are free to propose to
Boost whatever you consider is good for the Boost community.
>
>> I would prefer an approach where data structures (containers) and
>> algorithms
>> (join, interrupt, ...) are separated, thread-safety is not mandatory,
>> ...
>
> I agreed with you about the fact if not explicitly written a library is
> not meant to be thread safe, this rule doesn't fit a thread library.
This is your opinion and I respect it even if I don't share it.
> I suggest to explicitly write, even in bold, the fact that thread_group
> and thread classes are not thread safe.
I believed that we agreed that the thread group was thread-safe! Of
course the thread class is not thread-safe. I could add something like
all the functions in this library are not thread-safe until stated
explicitly, as I did it for Boost.Chrono since the beginning.
Best,
Vicente
I don't know who the boost thread maintainer is and how/who decides
if a design is good to be implemented or it work the other way around?
>>>> 2) thread_group now maintains a list of handlers with the
>>>> responsibility
>>>> to:
>>>> -) Avoid join and interrupts to be called concurrently on a
>>>> thread
>>>> -) Avoid to call join on a joined thread
>>>> -) Avoid to call interrupt on a joined/interrupt thread
>>>
>>> IMO, all the threads in a thread_group are owned by the group, and
>>> use move
>>> semantics, no need to use pointer to threads. As a consequence there
>>> is no
>>> need for the handler/wrapper.
>>
>> This is true if the thread_group does not permits to be used by multiple
>> threads interrupting/joining.
> I understand now why you did this way. But I will not do that.
Then the maintainer is you?
>>>> 4) Due the fact mutex are not fair a thread issuing an interrupt_all
>>>> most likely will go in starvation if a thread is issuing a
>>>> join_all
>>>> (especialy if the group contains a single thread). I can work at
>>>> it.
>>>
>>> Could you clarify your concern?
>>
>> Sure, if a thread performs a closed loop:
>>
>> a) lock_mutex;
>> b) timed_join
>> c) unlock_mutex
>> d) goto a
>>
>> and another thread is doing:
>>
>> a) lock_mutex;
>> b) interrupt
>> c) unlock_mutex
>>
>> then it goes in starvationm we have observed this (even in a
>> deterministic way), then I had to make the two interrupt/timed_join on
>> the thread handler fair each other.
>> Our platform is a linux platform with a 3.2.0 kernel.
>>
> IMO, only one thread should join/interrupt all the threads. This avoid
> all these issues.
Avoid the issues at thread group level, but those issues will be present
into an upper layer.
>>> As I said I'm for deprecating thread_group in Boost.Thread. The
>>> implementation you are proposing has not changed my mind.
>>
>> Fine with me, leaving it as it is now is worst than
> The class thread_group is not a big one. You are free to propose to
> Boost whatever you consider is good for the Boost community.
I'm proposing to decide what thread_group has to do because as it is
now it have design flaws.
>>> I would prefer an approach where data structures (containers) and
>>> algorithms
>>> (join, interrupt, ...) are separated, thread-safety is not mandatory,
>>> ...
>>
>> I agreed with you about the fact if not explicitly written a library is
>> not meant to be thread safe, this rule doesn't fit a thread library.
> This is your opinion and I respect it even if I don't share it.
>> I suggest to explicitly write, even in bold, the fact that thread_group
>> and thread classes are not thread safe.
> I believed that we agreed that the thread group was thread-safe! Of
> course the thread class is not thread-safe. I could add something like
> all the functions in this library are not thread-safe until stated
> explicitly, as I did it for Boost.Chrono since the beginning.
From an academic pure point of view the thread group is thread safe
because it protects his internal list so it can be used by multiple
threads, but to the other side if used (as it is now) from multiple
threads then the best you can get is "everyone guess" joining a batch of
already joined threads or for the matter to interrupt an already
joined group.
It seems it was coded when it was not a problem for a thread to be
joined even if joined/interrupted.
Regards
Gaetano Mendola
Best,
Vicente
Of course not if thread_group is treated as not thread safe indeed you
avoid all those issues at once.
>> It seems it was coded when it was not a problem for a thread to be
>> joined even if joined/interrupted.
> You are right, the last change in thread had some some undesirable
> impacts on thread_group. IMO, the two fixes I reported in this thread
> resolve the issues.
> Please let me know if this is not the case.
It solves the issue yes. A consideration I can do is to completely
remove the mutex protection inside the thread_group because it's
useless not being a class meant to be used by multiple threads, also
due the fact it is an only header class the inspection of
implementation it gives false expectations.
Regards
Gaetano Mendola