1
1
import type { MutexInterface } from 'async-mutex' ;
2
2
import type { ResourceAcquire } from '@matrixai/resources' ;
3
- import { Mutex } from 'async-mutex' ;
3
+ import { Mutex , withTimeout } from 'async-mutex' ;
4
4
import { withF , withG } from '@matrixai/resources' ;
5
+ import { yieldMicro } from './utils' ;
6
+ import { ErrorAsyncLocksTimeout } from './errors' ;
5
7
6
8
/**
7
9
* Read-preferring read write lock
@@ -12,35 +14,62 @@ class RWLockReader {
12
14
protected lock : Mutex = new Mutex ( ) ;
13
15
protected release : MutexInterface . Releaser ;
14
16
15
- public acquireRead : ResourceAcquire < RWLockReader > = async ( ) => {
16
- const readerCount = ++ this . _readerCount ;
17
- // The first reader locks
18
- if ( readerCount === 1 ) {
19
- this . release = await this . lock . acquire ( ) ;
20
- }
21
- return [
22
- async ( ) => {
23
- const readerCount = -- this . _readerCount ;
24
- // The last reader unlocks
25
- if ( readerCount === 0 ) {
26
- this . release ( ) ;
17
+ public read ( timeout ?: number ) : ResourceAcquire < RWLockReader > {
18
+ return async ( ) => {
19
+ const readerCount = ++ this . _readerCount ;
20
+ // The first reader locks
21
+ if ( readerCount === 1 ) {
22
+ let lock : MutexInterface = this . lock ;
23
+ if ( timeout != null ) {
24
+ lock = withTimeout ( this . lock , timeout , new ErrorAsyncLocksTimeout ( ) ) ;
27
25
}
28
- } ,
29
- this ,
30
- ] ;
31
- } ;
26
+ try {
27
+ this . release = await lock . acquire ( ) ;
28
+ } catch ( e ) {
29
+ -- this . _readerCount ;
30
+ throw e ;
31
+ }
32
+ }
33
+ return [
34
+ async ( ) => {
35
+ const readerCount = -- this . _readerCount ;
36
+ // The last reader unlocks
37
+ if ( readerCount === 0 ) {
38
+ this . release ( ) ;
39
+ }
40
+ // Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54
41
+ await yieldMicro ( ) ;
42
+ } ,
43
+ this ,
44
+ ] ;
45
+ } ;
46
+ }
32
47
33
- public acquireWrite : ResourceAcquire < RWLockReader > = async ( ) => {
34
- ++ this . _writerCount ;
35
- this . release = await this . lock . acquire ( ) ;
36
- return [
37
- async ( ) => {
48
+ public write ( timeout ?: number ) : ResourceAcquire < RWLockReader > {
49
+ return async ( ) => {
50
+ ++ this . _writerCount ;
51
+ let lock : MutexInterface = this . lock ;
52
+ if ( timeout != null ) {
53
+ lock = withTimeout ( this . lock , timeout , new ErrorAsyncLocksTimeout ( ) ) ;
54
+ }
55
+ let release : MutexInterface . Releaser ;
56
+ try {
57
+ release = await lock . acquire ( ) ;
58
+ } catch ( e ) {
38
59
-- this . _writerCount ;
39
- this . release ( ) ;
40
- } ,
41
- this ,
42
- ] ;
43
- } ;
60
+ throw e ;
61
+ }
62
+ return [
63
+ async ( ) => {
64
+ release ( ) ;
65
+ -- this . _writerCount ;
66
+ // Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54
67
+ await yieldMicro ( ) ;
68
+ } ,
69
+ this ,
70
+ ] ;
71
+ } ;
72
+ }
44
73
45
74
public get readerCount ( ) : number {
46
75
return this . _readerCount ;
@@ -60,26 +89,30 @@ class RWLockReader {
60
89
61
90
public async withReadF < T > (
62
91
f : ( resources : [ RWLockReader ] ) => Promise < T > ,
92
+ timeout ?: number ,
63
93
) : Promise < T > {
64
- return withF ( [ this . acquireRead ] , f ) ;
94
+ return withF ( [ this . read ( timeout ) ] , f ) ;
65
95
}
66
96
67
97
public async withWriteF < T > (
68
98
f : ( resources : [ RWLockReader ] ) => Promise < T > ,
99
+ timeout ?: number ,
69
100
) : Promise < T > {
70
- return withF ( [ this . acquireWrite ] , f ) ;
101
+ return withF ( [ this . write ( timeout ) ] , f ) ;
71
102
}
72
103
73
104
public withReadG < T , TReturn , TNext > (
74
105
g : ( resources : [ RWLockReader ] ) => AsyncGenerator < T , TReturn , TNext > ,
106
+ timeout ?: number ,
75
107
) : AsyncGenerator < T , TReturn , TNext > {
76
- return withG ( [ this . acquireRead ] , g ) ;
108
+ return withG ( [ this . read ( timeout ) ] , g ) ;
77
109
}
78
110
79
111
public withWriteG < T , TReturn , TNext > (
80
112
g : ( resources : [ RWLockReader ] ) => AsyncGenerator < T , TReturn , TNext > ,
113
+ timeout ?: number ,
81
114
) : AsyncGenerator < T , TReturn , TNext > {
82
- return withG ( [ this . acquireWrite ] , g ) ;
115
+ return withG ( [ this . write ( timeout ) ] , g ) ;
83
116
}
84
117
}
85
118
0 commit comments