Skip to content

Commit c6bfc29

Browse files
committed
fix timing errors and other weirdness
1 parent eff535b commit c6bfc29

File tree

2 files changed

+193
-98
lines changed

2 files changed

+193
-98
lines changed

bench.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,36 @@
11
const createFile = require('./')
22

33
const st = createFile('benchmark.txt')
4-
st.open(benchWrite)
4+
st.open(tinyWrites)
5+
6+
function tinyWrites () {
7+
var offset = 0
8+
const buf = Buffer.alloc(1)
9+
console.time('10000 tiny writes')
10+
st.write(0, buf, function onwrite (err) {
11+
if (err) throw err
12+
offset++
13+
if (offset === 10000) {
14+
console.timeEnd('10000 tiny writes')
15+
return tinyReads()
16+
}
17+
st.write(offset, buf, onwrite)
18+
})
19+
}
20+
21+
function tinyReads () {
22+
var offset = 0
23+
console.time('10000 tiny reads')
24+
st.read(0, 1, function onread (err) {
25+
if (err) throw err
26+
offset++
27+
if (offset === 10000) {
28+
console.timeEnd('10000 tiny reads')
29+
return benchWrite()
30+
}
31+
st.read(offset, 1, onread)
32+
})
33+
}
534

635
function benchRead () {
736
var offset = 0

index.js

Lines changed: 163 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -25,118 +25,31 @@ function createFile (name, opts) {
2525
if (!opts) opts = {}
2626

2727
const maxSize = opts.maxSize || createFile.DEFAULT_MAX_SIZE
28-
const waiting = []
29-
const writers = []
30-
const readers = []
28+
const mutex = new Mutex()
3129

3230
var fs = null
3331
var file = null
3432
var entry = null
35-
var truncate = null
36-
var totalWriters = 0
33+
var readers = []
34+
var writers = []
3735

3836
return ras({read, write, open, stat, close})
3937

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-
10838
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+
}
11142

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)
11546
}
11647

11748
function close (req) {
118-
entry = file = fs = null
49+
readers = writers = entry = file = fs = null
11950
req.callback(null)
12051
}
12152

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-
14053
function stat (req) {
14154
req.callback(null, file)
14255
}
@@ -162,3 +75,156 @@ function createFile (name, opts) {
16275
}
16376
}
16477
}
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

Comments
 (0)