[M3devel] LockHeap
mika at async.caltech.edu
mika at async.caltech.edu
Wed Aug 13 03:47:58 CEST 2014
The code in LockHeap looks dubious to me:
VAR
holder: pthread_t;
inCritical := 0;
PROCEDURE LockHeap () =
VAR self := pthread_self();
BEGIN
WITH r = pthread_mutex_lock(heapMu) DO <*ASSERT r=0*> END;
IF inCritical = 0 THEN
holder := self;
ELSIF pthread_equal(holder, self) = 0 THEN
WITH r = pthread_cond_wait(heapCond, heapMu) DO <*ASSERT r=0*> END;
END;
INC(inCritical);
WITH r = pthread_mutex_unlock(heapMu) DO <*ASSERT r=0*> END;
END LockHeap;
My understanding is that this code is relatively simple in that there's
a single pthread mutex, heapMu, that protects holder and inCritical.
All it's trying to do is build a "reentrant" mutex (of the kind Dragisa
was asking for a while back). To annotate a bit:
PROCEDURE LockHeap () =
VAR self := pthread_self();
BEGIN
WITH r = pthread_mutex_lock(heapMu) DO <*ASSERT r=0*> END;
(* heapMu locked --- only a single thread can be here *)
IF inCritical = 0 THEN
holder := self;
ELSIF pthread_equal(holder, self) = 0 THEN
WITH r = pthread_cond_wait(heapCond, heapMu) DO <*ASSERT r=0*> END;
(* heapMu locked *)
END;
INC(inCritical);
WITH r = pthread_mutex_unlock(heapMu) DO <*ASSERT r=0*> END;
(* heapMu unlocked *)
END LockHeap;
Based on the assertions in UnlockHeap it seems that the postcondition of LockHeap
should be { holder = self AND inCritical > 0 }.
So we check if inCritical is 0 -- no one else wants the critical section. OK: mark
self as holder and return.
Next we check if we already have the critical section (pthread_equal() # 0). If we do,
ok, just return, we're done.
If neither of those succeeds, we wait for ... what, actually?
If neither succeeds, that means another thread is in the critical
section. We need to wait for it to release the critical section.
Doesn't that mean that we have to wait for the condition { inCritical =
0 OR pthread_equal(holder,self) # 0 }? Since we're a single thread here,
we actually need only wait for inCritical = 0....
In any case, my coding of this would be:
PROCEDURE LockHeap () =
VAR self := pthread_self();
BEGIN
WITH r = pthread_mutex_lock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
LOOP
IF inCritical = 0 THEN
holder := self;
EXIT
ELSIF pthread_equal(holder,self) # 0 THEN
EXIT
ELSE
WITH r = pthread_cond_wait(heapCond, heapMu, ThisLine()) DO <*ASSERT r=0*> END;
END
END;
INC(inCritical);
<*ASSERT pthread_equal(holder, self) # 0*>
WITH r = pthread_mutex_unlock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
END LockHeap;
(it makes more logical sense to have the pthread_equal test first, but it is probably much
faster to check inCritical so let's check that first).
I'm not sure how this could have gotten screwed up? It seems really basic.. unless I'm
misunderstanding the code.
I note that the documentation in RTOS.i3 also seems slightly incorrect:
PROCEDURE WaitHeap (VAR thread: RTHeapRep.ThreadState);
Blocks the caller until BroadcastHeap has been called and the allocator/collector critical section is released. The caller must be in the critical section.
Shouldn't it say "The calling thread must have entered the critical
section exactly once."? (Given that the "LockHeap" is reentrant...)
Why is there a reentrant mutex here, anyhow?
Mika
More information about the M3devel
mailing list