@@ -12,31 +12,32 @@ export const INTERNAL_MUTEX_CONTROLLER = Symbol(
1212 "Thread.InternalMutexController" ,
1313) ;
1414
15- // Defines the capabilities hidden from the user but available to the Guard and Condvar
15+ const INDEX = 0 ;
16+ const LOCKED = 1 ;
17+ const UNLOCKED = 0 ;
18+
1619export interface MutexController {
1720 unlock ( ) : void ;
1821 blockingLock ( ) : void ;
1922 lock ( ) : Promise < void > ;
2023}
21- const LOCKED = 1 ;
22- const UNLOCKED = 0 ;
2324
2425export class MutexGuard < T extends SharedMemoryView | void >
2526 implements Disposable {
2627 #data: T ;
28+ #mutex: MutexController ;
2729 #released = false ;
28- [ INTERNAL_MUTEX_CONTROLLER ] ! : MutexController ;
2930
30- constructor ( data : T , controller : MutexController ) {
31+ constructor ( data : T , mutex : MutexController ) {
3132 this . #data = data ;
32- this . #released = false ;
33-
34- Object . defineProperty ( this , INTERNAL_MUTEX_CONTROLLER , {
35- value : controller ,
36- writable : false ,
37- enumerable : false ,
38- configurable : false ,
39- } ) ;
33+ this . #mutex = mutex ;
34+ }
35+
36+ /**
37+ * Internal accessor for Condvar support
38+ */
39+ get [ INTERNAL_MUTEX_CONTROLLER ] ( ) : MutexController {
40+ return this . #mutex ;
4041 }
4142
4243 get value ( ) : T {
@@ -46,14 +47,13 @@ export class MutexGuard<T extends SharedMemoryView | void>
4647
4748 [ Symbol . dispose ] ( ) {
4849 if ( ! this . #released) {
49- const controller = this [ INTERNAL_MUTEX_CONTROLLER ] ;
50- controller . unlock ( ) ;
5150 this . #released = true ;
51+ this . #mutex. unlock ( ) ;
5252 }
5353 }
5454
5555 dispose ( ) {
56- return this [ Symbol . dispose ] ( ) ;
56+ this [ Symbol . dispose ] ( ) ;
5757 }
5858}
5959
@@ -63,72 +63,73 @@ export class Mutex<T extends SharedMemoryView | void = void>
6363 register ( 0 , this ) ;
6464 }
6565
66- // Strict private fields
67- #lockState: Int32Array < SharedArrayBuffer > ;
6866 #data: T ;
67+ #lockState: Int32Array < SharedArrayBuffer > ;
68+ #controller: MutexController ;
6969
7070 constructor ( data ?: T , _existingLockBuffer ?: SharedArrayBuffer ) {
7171 super ( ) ;
7272 this . #data = data as T ;
73-
74- if ( _existingLockBuffer ) {
75- this . #lockState = new Int32Array ( _existingLockBuffer ) ;
76- } else {
77- this . #lockState = new Int32Array ( new SharedArrayBuffer ( 4 ) ) ;
78- }
73+ this . #lockState = _existingLockBuffer
74+ ? new Int32Array ( _existingLockBuffer )
75+ : new Int32Array ( new SharedArrayBuffer ( 4 ) ) ;
76+ this . #controller = {
77+ unlock : ( ) => this . #unlock( ) ,
78+ blockingLock : ( ) => this . #performBlockingLock( ) ,
79+ lock : ( ) => this . #performAsyncLock( ) ,
80+ } ;
7981 }
8082
8183 #tryLock( ) : boolean {
8284 return (
83- Atomics . compareExchange ( this . #lockState, 0 , UNLOCKED , LOCKED ) === UNLOCKED
85+ Atomics . compareExchange ( this . #lockState, INDEX , UNLOCKED , LOCKED ) ===
86+ UNLOCKED
8487 ) ;
8588 }
8689
87- #blockingLock( ) : void {
90+ #unlock( ) : void {
91+ if (
92+ Atomics . compareExchange ( this . #lockState, INDEX , LOCKED , UNLOCKED ) !==
93+ LOCKED
94+ ) {
95+ throw new Error ( "Mutex was not locked or locked by another thread" ) ;
96+ }
97+ Atomics . notify ( this . #lockState, INDEX , 1 ) ;
98+ }
99+
100+ /**
101+ * Shared logic for blocking lock.
102+ * Used by both public blockingLock() and the Controller (for Condvar)
103+ */
104+ #performBlockingLock( ) : void {
88105 while ( true ) {
89106 if ( this . #tryLock( ) ) return ;
90- Atomics . wait ( this . #lockState, 0 , LOCKED ) ;
107+ Atomics . wait ( this . #lockState, INDEX , LOCKED ) ;
91108 }
92109 }
93110
94- async #lock( ) : Promise < void > {
111+ /**
112+ * Shared logic for async lock.
113+ * Used by both public lock() and the Controller (for Condvar)
114+ */
115+ async #performAsyncLock( ) : Promise < void > {
95116 while ( true ) {
96117 if ( this . #tryLock( ) ) return ;
97- const result = Atomics . waitAsync ( this . #lockState, 0 , LOCKED ) ;
118+ const result = Atomics . waitAsync ( this . #lockState, INDEX , LOCKED ) ;
98119 if ( result . async ) {
99120 await result . value ;
100121 }
101122 }
102123 }
103124
104- #unlock( ) : void {
105- if (
106- Atomics . compareExchange ( this . #lockState, 0 , LOCKED , UNLOCKED ) !== LOCKED
107- ) {
108- throw new Error ( "Mutex was not locked or locked by another thread" ) ;
109- }
110- Atomics . notify ( this . #lockState, 0 , 1 ) ;
111- }
112-
113- /**
114- * Creates the closure that allows the Guard to control this Mutex.
115- */
116- #createController( ) : MutexController {
117- return {
118- unlock : ( ) => this . #unlock( ) ,
119- blockingLock : ( ) => this . #blockingLock( ) ,
120- lock : ( ) => this . #lock( ) ,
121- } ;
122- }
123-
124125 public blockingLock ( ) : MutexGuard < T > {
125- this . #blockingLock ( ) ;
126- return new MutexGuard ( this . #data, this . #createController ( ) ) ;
126+ this . #performBlockingLock ( ) ;
127+ return new MutexGuard ( this . #data, this . #controller ) ;
127128 }
128129
129130 public async lock ( ) : Promise < MutexGuard < T > > {
130- await this . #lock ( ) ;
131- return new MutexGuard ( this . #data, this . #createController ( ) ) ;
131+ await this . #performAsyncLock ( ) ;
132+ return new MutexGuard ( this . #data, this . #controller ) ;
132133 }
133134
134135 [ toSerialized ] ( ) {
@@ -153,12 +154,7 @@ export class Mutex<T extends SharedMemoryView | void = void>
153154 static override [ toDeserialized ] (
154155 obj : ReturnType < Mutex < any > [ typeof toSerialized ] > [ "value" ] ,
155156 ) {
156- let data ;
157-
158- if ( obj . data !== undefined ) {
159- data = deserialize ( obj . data ) ;
160- }
161-
157+ const data = obj . data !== undefined ? deserialize ( obj . data ) : undefined ;
162158 return new Mutex ( data , obj . lockBuffer ) ;
163159 }
164160}
0 commit comments