RecursionLock.st
branchjv
changeset 23084 0ffb59b273ff
parent 23083 c8dcd89b9cf6
child 23085 9effc2bcf8a6
--- a/RecursionLock.st	Tue Aug 29 10:05:32 2017 +0100
+++ b/RecursionLock.st	Wed Aug 30 12:38:37 2017 +0100
@@ -20,6 +20,19 @@
 	category:'Kernel-Processes'
 !
 
+!RecursionLock primitiveDefinitions!
+%{
+#define THINLOCKING
+#ifdef THINLOCKING
+# include <thinlocks.h>
+static inline unsigned INT* stxGetLockwordPtr(OBJ o) {
+    return (unsigned INT*)(&__OINST(o, process));
+}
+
+#endif
+%}
+! !
+
 !RecursionLock class methodsFor:'documentation'!
 
 copyright
@@ -99,17 +112,163 @@
     ^ self new
 ! !
 
+!RecursionLock methodsFor:'accessing'!
+
+count
+    ^ self processAndCount at: 2.
+
+!
+
+owner
+    ^ self processAndCount at: 1.
+
+! !
+
 !RecursionLock methodsFor:'acquire & release'!
 
+acquireWithTimeoutMs: timeout
+    "
+    Acquire the lock:
+
+       * If the lock is not owned by any process, lock it and return immediately.
+       * If the lock is already owned by the calling process, return immediately.
+       * Otherwise, wait until owning process release it (by means of #release)
+         at most `timeout` milliseconds. If `timeout` is nil, wait forever.
+
+    Return `true` if the lock has been acquired or `false` if bot (e.g. wait
+    timed out)
+    "
+%{  /* NOCONTEXT */
+#ifdef THINLOCKING
+    if ( stxThinLock( stxGetLockwordPtr(self) ) == StxThinlockSuccess ) {
+        return (true);
+    }
+#endif
+%}.
+    "/ Inflate the lock if it's not yet inflated.
+    "/
+    "/ Note, that #inflate method checks again if it's inflated or not,
+    "/ it may haopen some other thread inflated the lock in between the check
+    "/ here and code in #inflate.
+    process class == SmallInteger ifTrue:[ self inflate ].
+    ^ super acquireWithTimeoutMs: timeout
+!
+
 release
     "
-    Release the lock.
+    Release the lock. Return true of lock has been released, `false` if
+    not (because calling process does not own it).
     "
-    super release ifFalse:[ 
+%{  /* NOCONTEXT */
+#ifdef THINLOCKING
+    if ( stxThinUnlock( stxGetLockwordPtr(self) ) == StxThinlockSuccess ) {
+        return (true);
+    }
+#endif
+%}.
+    "/ Inflate the lock if it's not yet inflated.
+    "/
+    "/ Note that #inflate method checks again if it's inflated or not,
+    "/ it may haopen some other thread inflated the lock in between the check
+    "/ here and code in #inflate
+    "/
+    "/ Note that `someobject class == SmallInteger` is handled as a special
+    "/ case in stc and compiled as `__isSmallInteger(someobject)` and thus
+    "/ very fast - just bitwise and + non-zero test. Don't change!
+    process class == SmallInteger ifTrue:[ self inflate ].
+    super release ifFalse:[
         self error: ('Calling process does not own the lock (caller: %1, owner: %2)' bindWith: Processor activeProcess id with: (process isNil ifTrue:['<no owner>'] ifFalse:[process id])).
     ].
+! !
 
-    "Created: / 03-10-2017 / 13:06:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!RecursionLock methodsFor:'initialization'!
+
+initialize
+    super initialize.
+    process := 0.
+
+    "Modified: / 25-01-1997 / 00:19:15 / cg"
+    "Modified: / 29-08-2017 / 09:53:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!RecursionLock methodsFor:'private'!
+
+inflate
+    "Inflates (thin) lock (into fat lock). If the lock is already a fat lock,
+     #inflate is no-op.
+
+    Called by:
+
+       * #acquire* in case of contention or if maximum nesting count
+         is exceeded (unlikely)
+       * #release in case of contention
+
+
+    "
+
+    | processAndCount wasBlocked |
+
+
+    processAndCount := Array new: 2.
+    wasBlocked := OperatingSystem blockInterrupts.
+    "/ Note that `someobject class == SmallInteger` is handled as a special
+    "/ case in stc and compiled as `__isSmallInteger(someobject)` and thus
+    "/ very fast - just bitwise and + non-zero test. Don't change!
+    process class == SmallInteger ifTrue:[
+        self processAndCountInto: processAndCount.
+        process := processAndCount at: 1.
+        count   := processAndCount at: 2.
+        sema setCount: 0.
+    ].
+    wasBlocked ifFalse:[ OperatingSystem unblockInterrupts ].
+
+!
+
+processAndCount
+    | processAndCount |
+
+    processAndCount := Array new: 2.
+    self processAndCountInto: processAndCount.
+    ^ processAndCount
+
+!
+
+processAndCountInto: anArray
+    "Fills in `anArray` with owning process and nesting count.
+
+     Note that by the time this method returns, the data in given array may
+     be already obsolete.
+    "
+    | pid cnt proc |
+
+    "/ Note that `someobject class == SmallInteger` is handled as a special
+    "/ case in stc and compiled as `__isSmallInteger(someobject)` and thus
+    "/ very fast - just bitwise and + non-zero test. Don't change!
+    process class == SmallInteger ifTrue:[
+        %{
+#ifdef THINLOCKING
+        unsigned INT _pid = stxLockwordGetPid( *stxGetLockwordPtr(self) );
+        unsigned INT _cnt = stxLockwordGetCnt( *stxGetLockwordPtr(self) );
+
+        if (_pid == INV_PROCESS_ID) {
+            pid = nil;
+            cnt = __MKINT(0);
+        } else {
+            pid = __MKINT(_pid);
+            cnt = __MKINT(_cnt);
+        }
+#endif
+        %}.
+        pid notNil ifTrue:[
+            proc := ObjectMemory processesKnownInVM detect:[:p|p id == pid] ifNone:[nil].
+        ].
+    ] ifFalse:[
+        proc := process.
+        cnt := count.
+    ].
+    anArray at: 1 put: proc.
+    anArray at: 2 put: cnt.
+
 ! !
 
 !RecursionLock methodsFor:'printing & storing'!
@@ -150,7 +309,7 @@
 
     |p|
 
-    ^ (p := process) notNil and:[Processor activeProcess ~~ p and:[p isDead not]]
+    ^ (p := self owner) notNil and:[Processor activeProcess ~~ p and:[p isDead not]]
 ! !
 
 !RecursionLock methodsFor:'signaling'!