Re: [Nagios-devel] Re: Re: FreeBSD thread issues
Posted: Wed Aug 24, 2005 3:21 am
Christophe Yayon wrote:
> Hi all,
>
> here is the answer of FreeBSD-hackers list :
>
>
> This posting demonstrates a fundamental confusion between thread-safe and
> async-safe. That is the root of the problem in the communication.
> Thread-safe functions are a dime a dozen and relatively easy to write.
> async-safe functions are very rare and much harder to do useful things
> with. I've tried to explain the difference below using fgets() as an
> example of the difficulties.
>
Umm... Feels like either me or those guys are missing a third distinction.
* thread-safe; function is guaranteed not to modify global state which
other threads depend on (fgets(), f.e.)
* async-safe (async-IO-safe, really); function is guaranteed not to
block or mess up in IO due to other threads (read(), write() et al.
fgets isn't in here when I come to think of it, because if you fgets()
in two separate threads on the same FILE* pointer you'll end up with
undefined behaviour).
* async-signal-safe; function is guaranteed to only modify parameters it
has been passed and may be re-entered any number of times at any point
of execution.
Given that fork() itself is listed in the table of async-signal-safe
functions I interpret this as thread-safe functions being enough in the
child, provided that it won't read or write to FILE* pointers that were
open prior to the fork() call (or any other such thing that might occur).
If these distinctions are wrong, I need to have this clarified before I
can budge.
>
>>fgets() must also be async-safe, since it's passed its storage-buffer
>>from the calling function. It can contain races if several threads (or
>>programs for that matter) tries to read FIFO's at the same time or are
>>trying to store things to the same piece of memory, but that's neither
>>new, strange or in any way non-obvious. Obviously, fgets() relies on
>>lower-level IO code which must be thread-safe (read() in this case) on
>>account of them being syscalls inside multitasking kernels.
>
>
> fgets need not be async-safe, but it does need to be thread-safe.
> When one fork after pthread_create, one may only call async-safe
> functions. The weaker requirements of thread safety can be shown to
> not necessarily be async safe. If two different threads call fgets(),
> mutexes will keep one thread from running if the other is in the
> middle of changing the FILE * internal state. However, if that thread
> is interrupted by the scheduler with the mutex held, and fork() is
> called, then the new copy of the address space will still have that
> mutex held.
So the child can't use the FILE* pointers opened (and used) in the
parent. Nothing new under the sun (although without threads the only
issue is races). If the child fopen()'s a file of its own there will be
no lock contention and everyone will be happy.
> Any attempt by this new process, with its own address
> space, to acquire the lock is doomed to failure. Since the parent and
> child execute in different address spaces, there is no way for a
> thread that does not exist in the child to unlock the locked mutex.
>
>
> Normally this happens like so:
>
> Thread A Thread B
>
> fgets(fp, b1, 10);
> lock fp's mutex
> copy 5 available bytes into b1
>
> fgets(fp, b2, 10)
> try lock fp's mutex
>
> unlock fp's mutex
> return
>
> attempt to lock finishes
> b2 can be updated
> unlock mutex.
>
> However, in the fork case:
>
> Thread A Thread B
>
> fgets(fp, b1, 10);
> lock fp's mutex
> copy 5 available bytes into b1
>
>
...[email truncated]...
This post was automatically imported from historical nagios-devel mailing list archives
Original poster: [email protected]
> Hi all,
>
> here is the answer of FreeBSD-hackers list :
>
>
> This posting demonstrates a fundamental confusion between thread-safe and
> async-safe. That is the root of the problem in the communication.
> Thread-safe functions are a dime a dozen and relatively easy to write.
> async-safe functions are very rare and much harder to do useful things
> with. I've tried to explain the difference below using fgets() as an
> example of the difficulties.
>
Umm... Feels like either me or those guys are missing a third distinction.
* thread-safe; function is guaranteed not to modify global state which
other threads depend on (fgets(), f.e.)
* async-safe (async-IO-safe, really); function is guaranteed not to
block or mess up in IO due to other threads (read(), write() et al.
fgets isn't in here when I come to think of it, because if you fgets()
in two separate threads on the same FILE* pointer you'll end up with
undefined behaviour).
* async-signal-safe; function is guaranteed to only modify parameters it
has been passed and may be re-entered any number of times at any point
of execution.
Given that fork() itself is listed in the table of async-signal-safe
functions I interpret this as thread-safe functions being enough in the
child, provided that it won't read or write to FILE* pointers that were
open prior to the fork() call (or any other such thing that might occur).
If these distinctions are wrong, I need to have this clarified before I
can budge.
>
>>fgets() must also be async-safe, since it's passed its storage-buffer
>>from the calling function. It can contain races if several threads (or
>>programs for that matter) tries to read FIFO's at the same time or are
>>trying to store things to the same piece of memory, but that's neither
>>new, strange or in any way non-obvious. Obviously, fgets() relies on
>>lower-level IO code which must be thread-safe (read() in this case) on
>>account of them being syscalls inside multitasking kernels.
>
>
> fgets need not be async-safe, but it does need to be thread-safe.
> When one fork after pthread_create, one may only call async-safe
> functions. The weaker requirements of thread safety can be shown to
> not necessarily be async safe. If two different threads call fgets(),
> mutexes will keep one thread from running if the other is in the
> middle of changing the FILE * internal state. However, if that thread
> is interrupted by the scheduler with the mutex held, and fork() is
> called, then the new copy of the address space will still have that
> mutex held.
So the child can't use the FILE* pointers opened (and used) in the
parent. Nothing new under the sun (although without threads the only
issue is races). If the child fopen()'s a file of its own there will be
no lock contention and everyone will be happy.
> Any attempt by this new process, with its own address
> space, to acquire the lock is doomed to failure. Since the parent and
> child execute in different address spaces, there is no way for a
> thread that does not exist in the child to unlock the locked mutex.
>
>
> Normally this happens like so:
>
> Thread A Thread B
>
> fgets(fp, b1, 10);
> lock fp's mutex
> copy 5 available bytes into b1
>
> fgets(fp, b2, 10)
> try lock fp's mutex
>
> unlock fp's mutex
> return
>
> attempt to lock finishes
> b2 can be updated
> unlock mutex.
>
> However, in the fork case:
>
> Thread A Thread B
>
> fgets(fp, b1, 10);
> lock fp's mutex
> copy 5 available bytes into b1
>
>
...[email truncated]...
This post was automatically imported from historical nagios-devel mailing list archives
Original poster: [email protected]