wasmmutex.inc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. {%MainUnit system.pp}
  2. // In nanoseconds
  3. Type
  4. // We use an alias here.
  5. TWasmMutex = TRTLCriticalSection;
  6. TMutexKind = (mkNormal,mkRecursive);
  7. TLockMutexResult = (lmrNone,lmrOK,lmrNotOwner,lmrError,lmrTimeout);
  8. Function MutexKind(const M : TWasmMutex) : TMutexKind;
  9. begin
  10. Result:=TMutexKind(M.Kind);
  11. end;
  12. procedure InitMutex(var M : TWasmMutex; aKind : TMutexKind = mkNormal; aOwner : TThreadID = Nil);
  13. begin
  14. FillChar(M,SizeOf(TWasmMutex),0);
  15. if aOwner=Nil then
  16. aOwner:=GetSelfThread;
  17. M.Creator:=aOwner;
  18. M.Kind:=Ord(aKind);
  19. fpc_wasm32_i32_atomic_store(@M.Owner,0);
  20. fpc_wasm32_i32_atomic_store(@M.Locked,0);
  21. end;
  22. procedure DoneMutex(var M : TWasmMutex);
  23. Var
  24. a : LongInt;
  25. begin
  26. if (fpc_wasm32_i32_atomic_load(@M.Locked)<>0) and (M.Creator=GetSelfThread) then
  27. begin
  28. M.Destroying:=True;
  29. a:=fpc_wasm32_memory_atomic_notify(@M.Locked,MaxThreadSignal);
  30. end;
  31. end;
  32. Function TryLockMutex(var M : TWasmMutex) : Boolean;
  33. Var
  34. Res : Boolean;
  35. begin
  36. // We already have the lock ?
  37. Res:=(fpc_wasm32_i32_atomic_load(@M.Locked)=1) and (TThreadID(fpc_wasm32_i32_atomic_load(@M.Owner))=GetSelfThread);
  38. if Not Res then
  39. Res:=fpc_wasm32_i32_atomic_rmw_cmpxchg_u(@M.Locked,0,1)=0
  40. else
  41. begin
  42. // TryLockMutex is called in a loop. Be VERY careful when adding this log.
  43. // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TryLockMutex : we ('+IntToStr(PtrUint(GetSelfThread))+') own the lock.');{$ENDIF}
  44. end;
  45. if Res then
  46. begin
  47. if (MutexKind(M)=mkRecursive) or (M.Count=0) then
  48. InterLockedIncrement(M.Count);
  49. // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TryLockMutex : setting owner to '+IntToStr(PtrUint(GetSelfThread))+'.');{$ENDIF}
  50. fpc_wasm32_i32_atomic_store(@M.Owner,LongWord(GetSelfThread));
  51. end;
  52. TryLockMutex:=Res;
  53. end;
  54. // aTimeOutNS is in nanoseconds. <0 (e.g. -1) is infinite
  55. Function LockMutexTimeoutNoWait(var m : TWasmMutex; aTimeOutNS : Int64) : TLockMutexResult;
  56. Var
  57. Res : TLockMutexResult;
  58. EndTime: TOSTime;
  59. begin
  60. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('LockMutexTimeoutNoWait('+IntToStr(m.locked)+','+intToStr(aTimeOutNS)+')');{$ENDIF}
  61. Res:=lmrNone;
  62. if aTimeOutNS>=0 then
  63. EndTime:=GetClockTime+aTimeOutNS
  64. else
  65. EndTime:=0;
  66. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('LockMutexTimeoutNoWait: entering loop');{$ENDIF}
  67. Repeat
  68. if TryLockMutex(M) then
  69. Res:=lmrOK
  70. else if (aTimeOutNS>=0) and (GetClockTime>EndTime) then
  71. Res:=lmrTimeOut;
  72. Until (res<>lmrNone);
  73. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('LockMutexTimeoutNoWait: done loop');{$ENDIF}
  74. LockMutexTimeoutNoWait:=Res;
  75. end;
  76. Function LockMutexTimeoutWait(var m : TWasmMutex; aTimeOutNS : Int64) : TLockMutexResult;
  77. Var
  78. Res : TLockMutexResult;
  79. EndTime: TOSTime;
  80. RemainingTime: Int64;
  81. begin
  82. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('LockMutexTimeoutWait('+IntToStr(m.locked)+','+intToStr(aTimeOutNS)+')');{$ENDIF}
  83. Res:=lmrNone;
  84. if aTimeOutNS>=0 then
  85. EndTime:=GetClockTime+aTimeOutNS
  86. else
  87. begin
  88. EndTime:=0;
  89. RemainingTime:=-1;
  90. end;
  91. InterLockedIncrement(M.Waiters);
  92. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('LockMutexTimeoutWait: entering loop');{$ENDIF}
  93. Repeat
  94. if TryLockMutex(m) then
  95. Res:=lmrOk
  96. else
  97. begin
  98. if aTimeOutNS>=0 then
  99. begin
  100. RemainingTime:=EndTime-GetClockTime;
  101. if RemainingTime<0 then
  102. Res:=lmrTimeOut;;
  103. end;
  104. if Res<>lmrNone then
  105. Case fpc_wasm32_memory_atomic_wait32(@M.Locked,1,RemainingTime) of
  106. 0, 1:
  107. if M.Destroying then
  108. Res:=lmrError;
  109. 2:
  110. if M.Destroying then
  111. Res:=lmrError
  112. else
  113. Res:=lmrTimeOut;
  114. end;
  115. end;
  116. Until Res<>lmrNone;
  117. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('LockMutexTimeoutWait: done loop');{$ENDIF}
  118. InterLockedDecrement(M.Waiters);
  119. LockMutexTimeoutWait:=Res;
  120. end;
  121. Function LockMutexTimeout(var m : TWasmMutex; aTimeOutNS : Int64) : TLockMutexResult;
  122. begin
  123. if TryLockMutex(M) then
  124. Result:=lmrOK
  125. else if isWaitAllowed then
  126. Result:=LockMutexTimeoutWait(m,aTimeOutNS)
  127. else
  128. Result:=LockMutexTimeoutNoWait(m,aTimeOutNS)
  129. end;
  130. Function LockMutex(var m : TRTLCriticalSection) : TLockMutexResult;
  131. begin
  132. LockMutexTimeout(M,-1);
  133. end;
  134. function UnLockMutex(var m : TRTLCriticalSection) : TLockMutexResult;
  135. var
  136. Res : TLockMutexResult;
  137. MyThread : TThreadID;
  138. EndTime: TOSTime;
  139. a : LongInt;
  140. begin
  141. Res:=lmrNone;
  142. MyThread:=GetSelfThread;
  143. if MyThread<>TThreadID(fpc_wasm32_i32_atomic_load(@M.Owner)) then
  144. Res:=lmrNotOwner
  145. else if M.Count=0 then
  146. Res:=lmrError
  147. else
  148. begin
  149. res:=lmrOK;
  150. if (MutexKind(M)=mkRecursive) or (M.Count=1) then
  151. InterLockedDecrement(M.Count);
  152. if (M.Count=0) then
  153. begin
  154. fpc_wasm32_i32_atomic_store(@M.Owner,0);
  155. fpc_wasm32_i32_atomic_store(@M.Locked,0);
  156. a:=fpc_wasm32_memory_atomic_notify(@M.Locked,1);
  157. end;
  158. end;
  159. end;