Boost logo

Boost :

Subject: Re: [boost] [threadpool] new version v12
From: Anthony Williams (anthony.ajw_at_[hidden])
Date: 2008-11-03 04:25:58


"vicente.botet" <vicente.botet_at_[hidden]> writes:

> ----- Original Message -----
> From: "Anthony Williams" <anthony.ajw_at_[hidden]>
> To: <boost_at_[hidden]>
> Sent: Monday, November 03, 2008 9:05 AM
> Subject: Re: [boost] [threadpool] new version v12
>
>
>>>> One thing you can do with fibers that you can't easily do with a
>>>> single stack is switch back to the parent task when the nested task
>>>> blocks. Doing so allows you to run *other* tasks from the pool if a
>>>> thread blocks and the task it is waiting for is already running
>>>> elsewhere. You can also migrate tasks between threads.
>
>>>> Doing either of these requires that the task is prepared for it.
>>>
>>> in which sense (sorry I'm not aware of)
>>
>> Fibers are still tied to a particular thread, so thread-local
>> variables and boost::this_thread::get_id() still return the same value
>> for a nested task. This means that a task that calls future::get()
>> might find that its thread-local variables have been overwritten by a
>> nested task when it resumes. It also means that any data structures
>> keyed by the thread::id may have been altered. Finally, the nested
>> task inherits all the locks of the parent, so it may deadlock if it
>> tries to lock the same mutexes (rather than just block if it is
>> running on a separate thread).
>
> You are right Anthony, task behavior shouldn't depend on thread
> specifics, either thread id, locks or thread-locals data because other
> sub-tasks can migrate to this thread while waiting for the sub-task
> completion. The same occurs for programs that run well sequentially
> and crash when multi-threading takes place. This do not have as
> consequence that we can not use threads neither global variables but
> that we need to implement thread-safe functions and some times use for
> that some kind of thread synchronization, other use threads specific
> variables instead of global variables.
>
> For task the same applies; there are some entities(functions, classes,
> ..) that are task-safe and others that need some task specific
> synchronization tools to ensure task safety.

Yes. Unfortunately in some cases the set of functions that are not
"task safe" includes particular uses of the standard C library and use
of particular C++ constructs in particular ways on some platforms.

For example, with MSVC the CRT makes use of thread-local storage for
many things, from errno to the buffer for gmtime to the current
exception during exception handling. If fiber-local storage is
available (Vista), it uses that if you have a sufficiently recent
version of MSVC (I forget whether you need VS2005 or VS2008), but on
XP or older versions of MSVC it uses plain TLS.

You need to be aware that these things will be replaced if the task is
suspended and a new fiber scheduled. Doubly so if the task is migrated
to another physical thread. This makes it a really bad idea to wait
for a future in a catch block, for example.

> I think the fork/join framework work well for task-safe entities while
> will have unexpected behavior otherwise.
> * The first question is whether we can use such a framework knowing
> that we need to take care of the task safety or discard it because it
> is dangerous when the entities are not task-safe.

Of course we can. However, we need to be sure to publicise this fact.

> * The second question if we use this kind of framework is how can we
> make task-unsafe entities task-safe using some kind of synchronization
> or specific context at the task level.

In the general case, I don't think we can. However, we can do things
to mitigate the problem. For example if the thread pool collaborated
with boost::thread, it could switch the thread data so you had a
different thread::id value, and thread_specific_ptr values were local
to the task. This doesn't help with locks or thread-specific data
taken from outside of boost (e.g. in the CRT), but it does help
somewhat.

Also, it might be useful to have a "don't nest tasks" flag, like
boost::disable_interruption:

void foo()
{
    boost::tp::disable_task_nesting dtn;
    some_future.get(); // won't schedule another task on this thread
} // task nesting enable again

Anthony

-- 
Anthony Williams
Author of C++ Concurrency in Action | http://www.manning.com/williams
Custom Software Development | http://www.justsoftwaresolutions.co.uk
Just Software Solutions Ltd, Registered in England, Company Number 5478976.
Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk