[fpc-pascal] Semaphore problems

Vinzent Höfler JeLlyFish.software at gmx.net
Mon Jul 24 20:33:23 CEST 2006


Burkhard Carstens wrote:
> Am Montag, 24. Juli 2006 19:22 schrieb Vinzent Höfler:
>> Burkhard Carstens wrote:
>>> Am Montag, 24. Juli 2006 17:27 schrieb Vinzent Hoefler:
>>>>> - the rtlEvent works even with timeout (with my patch posted on
>>>>> mantis), but this one clears the signaled state, when wait is
>>>>> called.
>>>> *oh* Before the wait? That's not good. This cries for race
>>>> conditions and subtle deadlock situations.
>>> thread synchronization uses this and takes care of it by calling
>>> RTLeventstartwait which locks the associated mutex.
>> As far as I just read code and comments, it does it for Windows to
>> get Unix behaviour, although I don't see the point here, because this
>> just forces the race condition the POSIX-Threads implementation
>> actually tries to circumvent with this associated mutex.
 >
> I don't think so.

Well, in that case either I am totally stupid (won't be news, anyway) or 
I must have different source code:

 From "rtl/unix/cthreads.inc":

|procedure intRTLEventStartWait(AEvent: PRTLEvent);
|var p:pintrtlevent;
|
|begin
|  p:=pintrtlevent(aevent);
|  pthread_mutex_lock(@p^.mutex);
|end;
|
|procedure intRTLEventWaitFor(AEvent: PRTLEvent);
|var p:pintrtlevent;
|
|begin
|  p:=pintrtlevent(aevent);
|  pthread_cond_wait(@p^.condvar, @p^.mutex);
|  pthread_mutex_unlock(@p^.mutex);
|end;

Which is pretty much what you described and if pthread_cond_wait() works 
as described, there's no race condition here.

But now "rtl/win/systhrd.inc":

|procedure intRTLEventStartWait(AEvent: PRTLEvent);
|begin
|  { this is to get at least some common behaviour on unix and win32:
|    events before startwait are lost on unix, so reset the event on
|    win32 as well }
|  ResetEvent(THANDLE(AEvent));
|end;
|
|procedure intRTLEventWaitFor(AEvent: PRTLEvent);
|const
|  INFINITE=dword(-1);
|begin
|  WaitForSingleObject(THANDLE(AEvent), INFINITE);
|end;

I don't see any locking in intRTLEventStartWait(), but I see the signal 
being cleared in an unprotected way. So the signal can still get set 
between the calls to intRTLStartWait() and intRTLEventWaitFor() which 
makes the clearing quite useless.

> In unix, the mutex is locked, which ensures, there is 
> not setevent between startwait and actual call to wait, because the 
> call to wait seems to reset the cond, then releases the mutex and 
> starts waiting.

Yes, and now I seem to understand why. Unix only implements transient 
signals. So if there's noone waiting on the signal, it gets lost. 
Windows OTOH seems to implement persistent signals. That's all the 
difference.

 > In windows, this doesn't matter (resetting the event on
> startwait), because it preserves the signaled state of the event.

It preserves the signalled state? By resetting it? I don't think so.

AFAICS, it resets the signal before actually starting to wait for it. So 
it still can sneak in if the timing is right. That's what I'd call a 
race condition.

 > The wait call just returns immediately, if the event is allready 
signaled.
> To me, this is fine. As long as rtlevent is used with this in mind, it 
> should give a consistent behaviour on windows and linux. (see 
> classes.inc thread synchronization)

You mean code like:

|if timeout>0 then
|  begin
|    RtlEventStartWait(SynchronizeTimeoutEvent);
|    RtlEventWaitFor(SynchronizeTimeoutEvent,timeout);
|  end
|  else
|    RtlEventResetEvent(SynchronizeTimeoutEvent);

So where's the code that makes sure that the thread signalling the event 
is (only) resumed between the two calls?

>> I'm not even sure, if Unix actually clears the event. The man-pages
>> don't seem to indicate so.
> 
> It does, at least with the timedwait. I tested that extensively.

Yes, and now I think we both know why. I seem to repeat myself, but it 
might be important: The difference is all between implementing transient 
and persistent signals.


Vinzent.



More information about the fpc-pascal mailing list