@@ -25,118 +25,31 @@ function createFile (name, opts) {
25
25
if ( ! opts ) opts = { }
26
26
27
27
const maxSize = opts . maxSize || createFile . DEFAULT_MAX_SIZE
28
- const waiting = [ ]
29
- const writers = [ ]
30
- const readers = [ ]
28
+ const mutex = new Mutex ( )
31
29
32
30
var fs = null
33
31
var file = null
34
32
var entry = null
35
- var truncate = null
36
- var totalWriters = 0
33
+ var readers = [ ]
34
+ var writers = [ ]
37
35
38
36
return ras ( { read, write, open, stat, close} )
39
37
40
- function wait ( req ) {
41
- waiting . push ( req )
42
- }
43
-
44
- function allocWriter ( req ) {
45
- const io = { writer : null , req : null }
46
-
47
- entry . createWriter ( function ( writer ) {
48
- io . writer = writer
49
- io . writer . onerror = function ( err ) {
50
- onwrite ( err )
51
- }
52
- io . writer . onwriteend = function ( ) {
53
- onwrite ( null )
54
- }
55
-
56
- writers . push ( io )
57
- totalWriters ++
58
- write ( req )
59
- } , onallocerror )
60
-
61
- function onallocerror ( err ) {
62
- req . callback ( err )
63
- }
64
-
65
- function onwrite ( err ) {
66
- const req = io . req
67
- io . req = null
68
- writers . push ( io )
69
-
70
- if ( truncate ) {
71
- if ( io === truncate ) {
72
- truncate = null
73
- while ( waiting . length ) write ( waiting . pop ( ) )
74
- write ( req )
75
- return
76
- }
77
-
78
- if ( totalWriters - writers . length === 1 ) {
79
- truncate . writer . truncate ( truncate . req . offset )
80
- }
81
- }
82
-
83
- req . callback ( err , null )
84
- }
85
- }
86
-
87
- function allocReader ( ) {
88
- const io = { reader : null , req : null }
89
-
90
- io . reader = new FileReader ( )
91
- io . reader . onerror = function ( err ) {
92
- onread ( err , null )
93
- }
94
- io . reader . onload = function ( ) {
95
- onread ( null , Buffer . from ( this . result ) )
96
- }
97
-
98
- return io
99
-
100
- function onread ( err , buf ) {
101
- const req = io . req
102
- io . req = null
103
- readers . push ( io )
104
- req . callback ( err , buf )
105
- }
106
- }
107
-
108
38
function read ( req ) {
109
- const end = req . offset + req . size
110
- if ( end > file . size ) return req . callback ( new Error ( 'Could not satisfy length' ) )
39
+ const r = readers . pop ( ) || new ReadRequest ( readers , file , entry , mutex )
40
+ r . run ( req )
41
+ }
111
42
112
- const io = readers . pop ( ) || allocReader ( )
113
- io . req = req
114
- io . reader . readAsArrayBuffer ( file . slice ( req . offset , end ) )
43
+ function write ( req ) {
44
+ const w = writers . pop ( ) || new WriteRequest ( writers , file , entry , mutex )
45
+ w . run ( req )
115
46
}
116
47
117
48
function close ( req ) {
118
- entry = file = fs = null
49
+ readers = writers = entry = file = fs = null
119
50
req . callback ( null )
120
51
}
121
52
122
- function write ( req ) {
123
- if ( truncate ) return wait ( req )
124
-
125
- const io = writers . pop ( )
126
- if ( ! io ) return allocWriter ( req )
127
-
128
- io . req = req
129
-
130
- if ( req . offset > file . size ) {
131
- truncate = io
132
- if ( totalWriters - writers . length === 1 ) io . writer . truncate ( req . offset )
133
- return
134
- }
135
-
136
- io . writer . seek ( req . offset )
137
- io . writer . write ( new Blob ( [ req . data ] , TYPE ) )
138
- }
139
-
140
53
function stat ( req ) {
141
54
req . callback ( null , file )
142
55
}
@@ -162,3 +75,156 @@ function createFile (name, opts) {
162
75
}
163
76
}
164
77
}
78
+
79
+ function WriteRequest ( pool , file , entry , mutex ) {
80
+ this . writer = null
81
+ this . entry = entry
82
+ this . file = file
83
+ this . req = null
84
+ this . pool = pool
85
+ this . mutex = mutex
86
+ this . locked = false
87
+ this . truncating = false
88
+ }
89
+
90
+ WriteRequest . prototype . makeWriter = function ( ) {
91
+ const self = this
92
+ this . entry . createWriter ( function ( writer ) {
93
+ self . writer = writer
94
+
95
+ writer . onwriteend = function ( ) {
96
+ if ( self . writer !== writer ) throw new Error ( 'nah' )
97
+ self . onwrite ( null )
98
+ }
99
+
100
+ writer . onerror = function ( err ) {
101
+ if ( self . writer !== writer ) throw new Error ( 'nah' )
102
+ console . log ( 'ONERROR' , arguments )
103
+ self . onwrite ( err )
104
+ }
105
+
106
+ self . run ( self . req )
107
+ } )
108
+ }
109
+
110
+ WriteRequest . prototype . onwrite = function ( err ) {
111
+ const req = this . req
112
+ this . req = null
113
+
114
+ if ( this . locked ) {
115
+ this . locked = false
116
+ this . mutex . release ( )
117
+ }
118
+
119
+ if ( this . truncating ) {
120
+ this . truncating = false
121
+ if ( ! err ) return this . run ( req )
122
+ }
123
+
124
+ this . pool . push ( this )
125
+ if ( err ) console . log ( 'ERROR HERE' , err )
126
+ req . callback ( err , null )
127
+ }
128
+
129
+ WriteRequest . prototype . truncate = function ( ) {
130
+ this . truncating = true
131
+ this . writer . truncate ( this . req . offset )
132
+ }
133
+
134
+ WriteRequest . prototype . lock = function ( ) {
135
+ if ( this . locked ) return true
136
+ this . locked = this . mutex . lock ( this )
137
+ return this . locked
138
+ }
139
+
140
+ WriteRequest . prototype . run = function ( req ) {
141
+ this . req = req
142
+ if ( ! this . writer || this . writer . length !== this . file . size ) return this . makeWriter ( )
143
+
144
+ const end = req . offset + req . size
145
+ if ( end > this . file . size && ! this . lock ( ) ) return
146
+
147
+ if ( req . offset > this . writer . length ) {
148
+ if ( req . offset > this . file . size ) return this . truncate ( )
149
+ return this . makeWriter ( )
150
+ }
151
+
152
+ this . writer . seek ( req . offset )
153
+ this . writer . write ( new Blob ( [ req . data ] , TYPE ) )
154
+ }
155
+
156
+ function Mutex ( ) {
157
+ this . queued = null
158
+ }
159
+
160
+ Mutex . prototype . release = function ( ) {
161
+ const queued = this . queued
162
+ this . queued = null
163
+ for ( var i = 0 ; i < queued . length ; i ++ ) {
164
+ queued [ i ] . run ( queued [ i ] . req )
165
+ }
166
+ }
167
+
168
+ Mutex . prototype . lock = function ( req ) {
169
+ if ( this . queued ) {
170
+ this . queued . push ( req )
171
+ return false
172
+ }
173
+ this . queued = [ ]
174
+ return true
175
+ }
176
+
177
+ function ReadRequest ( pool , file , entry , mutex ) {
178
+ this . reader = new FileReader ( )
179
+ this . file = file
180
+ this . req = null
181
+ this . pool = pool
182
+ this . retry = true
183
+ this . mutex = mutex
184
+ this . locked = false
185
+
186
+ const self = this
187
+
188
+ this . reader . onerror = function ( ) {
189
+ self . onread ( this . error , null )
190
+ }
191
+
192
+ this . reader . onload = function ( ) {
193
+ const buf = Buffer . from ( this . result )
194
+ self . onread ( null , buf )
195
+ }
196
+ }
197
+
198
+ ReadRequest . prototype . lock = function ( ) {
199
+ if ( this . locked ) return true
200
+ this . locked = this . mutex . lock ( this )
201
+ return this . locked
202
+ }
203
+
204
+ ReadRequest . prototype . onread = function ( err , buf ) {
205
+ const req = this . req
206
+
207
+ if ( err && this . retry ) {
208
+ this . retry = false
209
+ if ( this . lock ( this ) ) this . run ( req )
210
+ return
211
+ }
212
+
213
+ this . req = null
214
+ this . pool . push ( this )
215
+ this . retry = true
216
+
217
+ if ( this . locked ) {
218
+ this . locked = false
219
+ this . mutex . release ( )
220
+ }
221
+
222
+ req . callback ( err , buf )
223
+ }
224
+
225
+ ReadRequest . prototype . run = function ( req ) {
226
+ const end = req . offset + req . size
227
+ this . req = req
228
+ if ( end > this . file . size ) return this . onread ( new Error ( 'Could not satisfy length' ) , null )
229
+ this . reader . readAsArrayBuffer ( this . file . slice ( req . offset , end ) )
230
+ }
0 commit comments