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