<html>
<head>
<style><!--
.hmmessage P
{
margin:0px;
padding:0px
}
body.hmmessage
{
font-size: 12pt;
font-family:Calibri
}
--></style></head>
<body class='hmmessage'><div dir='ltr'><br>It is on the basis of efficiency that I do things this way.<br><br><br>It is true in a race condition, we do extra work.<br><br><br>But the idea is to less work under any lock, to reduce contention<br>and increase scaling.<br><br><br>Keep in mind that the contention is on unrelated work,<br>while the race condition is on related work.<br><br><br>So the contention would occur more than the race.<br><br><br>i.e. we have n mutexes being entered on m threads, for the first time.<br>Let's say m > n.<br>We allow m calls to pthread_mutex_new to proceed perhaps in parallel.<br>To the extent that it can. Maybe not at all. Maybe significantly.<br>And then we throw out m - n of them.<br><br><br>The alternative would be n serialized calls to pthread_mutex_new.<br><br><br>This is a common pattern. I didn't make it up.<br>Someone changed my code from your way to this way in the past<br>for this reason. The pattern there was a little different, in that<br>in the lost race case, the caller had to retry, because it didn't get<br>a correct/coherent result. But this occasional retry is still preferable<br>for scaling.<br><br><br>In truth, the call to malloc will be somewhat serialized, depending on underlying platform.<br>And, analogs on Windows, InitializeCriticalSection is internally a bit serialized,<br>and InitializeSRWLock is not at all.<br>malloc often has some thread-local small free list to reduce contention.<br><br> - Jay<br><br><br><br><div>> Date: Thu, 1 Sep 2016 10:03:43 -0500<br>> From: rodney_bates@lcwb.coop<br>> To: hosking@purdue.edu; rodney.m.bates@acm.org<br>> CC: jay.krell@cornell.edu; m3devel@elegosoft.com<br>> Subject: Re: [M3devel] idioms for tracking initialization state and raising errors?<br>> <br>> OK, on that basis, I withdraw the suggestion about allocating the PCRITICAL_SECTION<br>> while locked.  In any case, the wasted allocate/delete would probably be rare.<br>> <br>> On 08/31/2016 11:18 PM, Hosking, Antony L wrote:<br>> > I may have been suspicious about internal locking within pthread_mutex_new, but don’t recall precisely.  It might even have been a deadlock bug that I encountered.  In general I don’t like library calls inside the mutex blocks − just the state changes I am really trying to protect.<br>> ><br>> >> On 1 Sep 2016, at 11:40 AM, Rodney M. Bates <rodney_bates@lcwb.coop> wrote:<br>> >><br>> >><br>> >><br>> >> On 08/31/2016 12:58 PM, Rodney M. Bates wrote:<br>> >>><br>> >>><br>> >>> On 07/31/2016 04:07 AM, Jay K wrote:<br>> >>>> Is this a correct idiom?<br>> >>>> If so, I think it is fairly reasonable.<br>> >>>><br>> >>>><br>> >>>> That is:<br>> >>>>   be very willing to "early return"<br>> >>>>   put all cleanup in finally<br>> >>>>   raise errors in finally<br>> >>>><br>> >>>><br>> >>>> In particular, I don't want to repeat the cleanup.<br>> >>>> I don't want to raise before leaving critical sections.<br>> >>>><br>> >>>><br>> >>>> I don't intend this to raise within a raise, but only<br>> >>>> from the "normal" exit of the finally block.<br>> >>>><br>> >>>><br>> >>>> I also don't want extra local booleans, i.e. raise: boolean to indicate failure.<br>> >>>> Just the one to indicate success.<br>> >>>><br>> >>>><br>> >>>> PROCEDURE InitMutex (mutex: Mutex) =<br>> >>>>    VAR<br>> >>>>      lock: PCRITICAL_SECTION := NIL;<br>> >>>>      locked: PCRITICAL_SECTION := NIL;<br>> >>>><br>> >>>>    BEGIN<br>> >>>>      IF mutex.initialized THEN RETURN END;<br>> >>>><br>> >>>>      TRY<br>> >>>><br>> >>>>        lock := NewCriticalSection();<br>> >>>>        IF lock = NIL THEN RETURN; END;<br>> >>>><br>> >>><br>> >>>           ^I suggest moving the two statements above inside the critical<br>> >>>            section on initLock, after the inner check on mutex.initialized.<br>> >>>            That way, if we lose a race, it avoids executing creation then<br>> >>>            deletion of an unneeded CriticalSection, and further eliminates<br>> >>>            the cleanup action in the code.<br>> >>><br>> >><br>> >> Tony, is there some reason why, in ThreadPThread.m3, pthread_mutex_new can't be<br>> >> done while holding initMu?  In initMutex, it is doing it the way Jay proposed,<br>> >> i.e., allocate before getting initMu, which (the allocation) could turn out unnecessary<br>> >> if we lose a race, then slightly later, freeing it, never used, if a lost race happened.<br>> >><br>> >><br>> >>> BTW, I am assuming initLock is a single global CriticalSection?<br>> >>><br>> >>><br>> >>>>        EnterCriticalSection(ADR(initLock));<br>> >>>>        locked := ADR(initLock);<br>> >>>><br>> >>>>        IF mutex.initialized THEN RETURN END;<br>> >>>><br>> >>>>        (* We won the race. *)<br>> >>>>        RTHeapRep.RegisterFinalCleanup (mutex, CleanMutex);<br>> >>>>        mutex.lock := lock;<br>> >>>>        lock := NIL;<br>> >>>>        mutex.initialized := TRUE;<br>> >>>><br>> >>>>      FINALLY<br>> >>>>        IF locked # NIL THEN LeaveCriticalSection(locked); END;<br>> >>>>        DelCriticalSection(lock);<br>> >>>>        IF NOT mutex.initialized THEN (* Raise after leaving critical section. *)<br>> >>>>          RuntimeError.Raise (RuntimeError.T.OutOfMemory);<br>> >>>>        END;<br>> >>>>      END;<br>> >>>><br>> >>>>    END InitMutex;<br>> >>>><br>> >>>><br>> >>>> Thank you,<br>> >>>>   - Jay<br>> >>>><br>> >>>><br>> >>>> _______________________________________________<br>> >>>> M3devel mailing list<br>> >>>> M3devel@elegosoft.com<br>> >>>> https://m3lists.elegosoft.com/mailman/listinfo/m3devel<br>> >>>><br>> >>><br>> >><br>> >> --<br>> >> Rodney Bates<br>> >> rodney.m.bates@acm.org<br>> >> _______________________________________________<br>> >> M3devel mailing list<br>> >> M3devel@elegosoft.com<br>> >> https://m3lists.elegosoft.com/mailman/listinfo/m3devel<br>> ><br>> <br>> -- <br>> Rodney Bates<br>> rodney.m.bates@acm.org<br></div>                                      </div></body>
</html>