4
4
5
5
import { Backend } from "./backend"
6
6
import { Observer } from "./helpers/observer"
7
- import { PublicKeys , Result , Error , Status } from "./interfaces"
7
+ import { PublicKeys , Result , Error , ErrorType , Status } from "./interfaces"
8
8
9
9
interface PublicKeysResult extends Result {
10
10
keys : PublicKeys
@@ -19,6 +19,29 @@ export interface Cache<T, G> {
19
19
get ( ...args : any [ ] ) : Promise < T >
20
20
}
21
21
22
+ type Task = [ string , Date , number ]
23
+
24
+ function timeout ( ms : number ) {
25
+ return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) )
26
+ }
27
+
28
+ export function locked < T , G extends Actor > (
29
+ f : ( this : G , ...args : any [ ] ) => Promise < T | Error >
30
+ ) : ( this : G , ...args : any [ ] ) => Promise < T | Error > {
31
+ return async function ( this : G , ...args : any [ ] ) : Promise < T | Error > {
32
+ // we lock the task
33
+ if ( ( await this . lock ( f . name ) ) === false )
34
+ return { status : Status . Failed } // we can't obtain a lock
35
+
36
+ const result = await f . apply ( this , args )
37
+
38
+ // we unlock the task
39
+ this . unlock ( f . name )
40
+
41
+ return result
42
+ }
43
+ }
44
+
22
45
export function cached < T , G extends Actor > (
23
46
f : ( this : G , ...args : any [ ] ) => Promise < T > ,
24
47
name : string
@@ -48,32 +71,43 @@ export function cached<T, G extends Actor>(
48
71
}
49
72
50
73
async function getKeys ( this : Actor ) : Promise < PublicKeysResult | Error > {
51
- const result = await this . backend . appointments . getKeys ( )
74
+ const response = await this . backend . appointments . getKeys ( )
52
75
53
- if ( "code" in result )
76
+ if ( "code" in response )
54
77
return {
55
78
status : Status . Failed ,
56
- error : result ,
79
+ error : {
80
+ type : ErrorType . RPC ,
81
+ data : response ,
82
+ } ,
57
83
}
58
84
59
85
return {
60
86
status : Status . Succeeded ,
61
- keys : result ,
87
+ keys : response ,
62
88
}
63
89
}
64
90
65
91
export class Actor extends Observer {
66
- public keys = cached ( getKeys , "keys" )
92
+ public keys = cached ( locked ( getKeys ) , "keys" )
67
93
public backend : Backend
68
94
public actor : string
69
95
public id : string
70
96
97
+ private _taskId : number
98
+ private _tasks : Task [ ]
99
+ private _locked : boolean
100
+
71
101
constructor ( actor : string , id : string , backend : Backend ) {
72
102
// the ID will be used to address local storage so that e.g. we can
73
103
// manage multiple providers, users etc. if necessary...
74
104
75
105
super ( )
76
106
107
+ this . _taskId = 0
108
+ this . _tasks = [ ]
109
+ this . _locked = false
110
+
77
111
this . actor = actor
78
112
this . id = id
79
113
this . backend = backend
@@ -97,9 +131,37 @@ export class Actor extends Observer {
97
131
this . backend . temporary . set ( `${ this . actor } ::${ this . id } ::${ key } ` , value )
98
132
}
99
133
100
- public unlock ( key : string ) { }
134
+ unlock ( task : string ) {
135
+ if ( this . _tasks . length === 0 ) return false // should never happen
136
+ if ( this . _tasks [ 0 ] [ 0 ] !== task ) return false // wrong task order (should not happen)
137
+ this . _tasks = this . _tasks . slice ( 1 )
138
+ return true
139
+ }
101
140
102
- public clearLocks ( ) { }
141
+ async lock ( task : string ) {
142
+ if ( this . _tasks . find ( ( t : Task ) => t [ 0 ] === task ) !== undefined ) {
143
+ console . warn (
144
+ `Task ${ this . actor } ::${ this . id } ::${ task } is already in queue, aborting...`
145
+ )
146
+ return false
147
+ }
103
148
104
- public lock ( key : string ) { }
149
+ const taskId = this . _taskId ++
150
+ this . _tasks . push ( [ task , new Date ( ) , taskId ] )
151
+
152
+ while ( true ) {
153
+ if ( this . _tasks . length === 0 ) return false // should not happen
154
+ const [ t , dt , id ] = this . _tasks [ 0 ]
155
+ if ( id === taskId ) break // it's our turn
156
+ if ( new Date ( ) . getTime ( ) - dt . getTime ( ) > 1000 * 10 )
157
+ // tasks time out after 10 seconds
158
+ this . _tasks = this . _tasks . slice ( 1 )
159
+ await timeout ( 10 )
160
+ }
161
+ return true
162
+ }
163
+
164
+ clearLocks ( ) {
165
+ this . _tasks = [ ]
166
+ }
105
167
}
0 commit comments