Skip to content

Commit e734e17

Browse files
authored
Merge pull request #1 from natefaubion/impl
Initial implementation
2 parents 0ebef5e + 62e39b8 commit e734e17

File tree

7 files changed

+662
-0
lines changed

7 files changed

+662
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ node_modules
66
# Generated files
77
.psci
88
output
9+
10+
.psc-ide-port

.travis.yml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
language: node_js
2+
dist: trusty
3+
sudo: required
4+
node_js: stable
5+
install:
6+
- npm install -g bower
7+
- npm install
8+
script:
9+
- bower install --production
10+
- npm run -s build
11+
- bower install
12+
- npm -s test
13+
after_success:
14+
- >-
15+
test $TRAVIS_TAG &&
16+
echo $GITHUB_TOKEN | pulp login &&
17+
echo y | pulp publish --no-push

bower.json

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "purescript-avar",
3+
"homepage": "https://github.com/slamdata/purescript-avar",
4+
"authors": [
5+
"Nathan Faubion <[email protected]>"
6+
],
7+
"description": "Low-level interface for asynchronous variables",
8+
"main": "",
9+
"keywords": [
10+
"PureScript",
11+
"AVar"
12+
],
13+
"license": "Apache-2.0",
14+
"ignore": [
15+
"**/.*",
16+
"node_modules",
17+
"bower_components",
18+
"test",
19+
"tests"
20+
],
21+
"dependencies": {
22+
"purescript-eff": "^3.1.0",
23+
"purescript-maybe": "^3.0.0",
24+
"purescript-either": "^3.1.0",
25+
"purescript-functions": "^3.0.0",
26+
"purescript-exceptions": "^3.0.0"
27+
},
28+
"devDependencies": {
29+
"purescript-assert": "^3.0.0",
30+
"purescript-console": "^3.0.0",
31+
"purescript-transformers": "^3.4.0",
32+
"purescript-refs": "^3.0.0"
33+
}
34+
}

package.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"private": true,
3+
"scripts": {
4+
"clean": "rimraf output && rimraf .pulp-cache",
5+
"build": "jshint src --verbose && pulp build -- --censor-lib --strict",
6+
"test": "pulp test"
7+
},
8+
"devDependencies": {
9+
"jshint": "^2.9.5",
10+
"pulp": "^11.0.0",
11+
"purescript-psa": "^0.5.1",
12+
"purescript": "^0.11.5",
13+
"rimraf": "^2.6.1"
14+
}
15+
}

src/Control/Monad/Eff/AVar.js

+296
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
/* globals exports, setTimeout */
2+
/* jshint -W097 */
3+
4+
"use strict";
5+
6+
var EMPTY = {};
7+
8+
function MutableQueue () {
9+
this.head = null;
10+
this.last = null;
11+
this.size = 0;
12+
}
13+
14+
function MutableCell (queue, value) {
15+
this.queue = queue;
16+
this.value = value;
17+
this.next = null;
18+
this.prev = null;
19+
}
20+
21+
function putLast (queue, value) {
22+
var cell = new MutableCell(queue, value);
23+
switch (queue.size) {
24+
case 0:
25+
queue.head = cell;
26+
break;
27+
case 1:
28+
cell.prev = queue.head;
29+
queue.head.next = cell;
30+
queue.last = cell;
31+
break;
32+
default:
33+
cell.prev = queue.last;
34+
queue.last.next = cell;
35+
queue.last = cell;
36+
}
37+
queue.size++;
38+
return cell;
39+
}
40+
41+
function takeLast (queue) {
42+
var cell;
43+
switch (queue.size) {
44+
case 0:
45+
return null;
46+
case 1:
47+
cell = queue.head;
48+
queue.head = null;
49+
break;
50+
case 2:
51+
cell = queue.last;
52+
queue.head.next = null;
53+
queue.last = null;
54+
break;
55+
default:
56+
cell = queue.last;
57+
queue.last = cell.prev;
58+
queue.last.next = null;
59+
}
60+
cell.prev = null;
61+
cell.queue = null;
62+
queue.size--;
63+
return cell.value;
64+
}
65+
66+
function takeHead (queue) {
67+
var cell;
68+
switch (queue.size) {
69+
case 0:
70+
return null;
71+
case 1:
72+
cell = queue.head;
73+
queue.head = null;
74+
break;
75+
case 2:
76+
cell = queue.head;
77+
queue.last.prev = null;
78+
queue.head = queue.last;
79+
queue.last = null;
80+
break;
81+
default:
82+
cell = queue.head;
83+
queue.head = cell.next;
84+
queue.head.prev = null;
85+
}
86+
cell.next = null;
87+
cell.queue = null;
88+
queue.size--;
89+
return cell.value;
90+
}
91+
92+
function deleteCell (cell) {
93+
if (cell.queue === null) {
94+
return;
95+
}
96+
if (cell.queue.last === cell) {
97+
takeLast(cell.queue);
98+
return;
99+
}
100+
if (cell.queue.head === cell) {
101+
takeHead(cell.queue);
102+
return;
103+
}
104+
if (cell.prev) {
105+
cell.prev.next = cell.next;
106+
}
107+
if (cell.next) {
108+
cell.next.prev = cell.prev;
109+
}
110+
cell.queue.size--;
111+
cell.queue = null;
112+
cell.value = null;
113+
cell.next = null;
114+
cell.prev = null;
115+
}
116+
117+
function AVar (value) {
118+
this.draining = false;
119+
this.error = null;
120+
this.value = value;
121+
this.takes = new MutableQueue();
122+
this.reads = new MutableQueue();
123+
this.puts = new MutableQueue();
124+
}
125+
126+
exports.makeEmptyVar = function () {
127+
return new AVar(EMPTY);
128+
};
129+
130+
exports.makeVar = function (value) {
131+
return function () {
132+
return new AVar(value);
133+
};
134+
};
135+
136+
exports._killVar = function (left, right, avar, error) {
137+
return function () {
138+
if (avar.error === null) {
139+
avar.error = error;
140+
avar.value = EMPTY;
141+
drainVar(left, right, avar);
142+
}
143+
};
144+
};
145+
146+
exports._putVar = function (left, right, avar, value, cb) {
147+
return function () {
148+
var cell = putLast(avar.puts, { cb: cb, value: value });
149+
drainVar(left, right, avar);
150+
return function () {
151+
deleteCell(cell);
152+
};
153+
};
154+
};
155+
156+
exports._takeVar = function (left, right, avar, cb) {
157+
return function () {
158+
var cell = putLast(avar.takes, cb);
159+
drainVar(left, right, avar);
160+
return function () {
161+
deleteCell(cell);
162+
};
163+
};
164+
};
165+
166+
exports._readVar = function (left, right, avar, cb) {
167+
return function () {
168+
var cell = putLast(avar.reads, cb);
169+
drainVar(left, right, avar);
170+
return function () {
171+
deleteCell(cell);
172+
};
173+
};
174+
};
175+
176+
exports._tryPutVar = function (left, right, avar, value) {
177+
return function () {
178+
if (avar.value === EMPTY && avar.error === null) {
179+
avar.value = value;
180+
drainVar(left, right, avar);
181+
return true;
182+
} else {
183+
return false;
184+
}
185+
};
186+
};
187+
188+
exports._tryTakeVar = function (left, right, nothing, just, avar) {
189+
return function () {
190+
var value = avar.value;
191+
if (value === EMPTY) {
192+
return nothing;
193+
} else {
194+
avar.value = EMPTY;
195+
drainVar(left, right, avar);
196+
return just(value);
197+
}
198+
};
199+
};
200+
201+
exports._tryReadVar = function (nothing, just, avar) {
202+
return function () {
203+
if (avar.value === EMPTY) {
204+
return nothing;
205+
} else {
206+
return just(avar.value);
207+
}
208+
};
209+
};
210+
211+
exports.isEmptyVar = function (avar) {
212+
return function () {
213+
return avar.value === EMPTY;
214+
};
215+
};
216+
217+
function drainVar (left, right, avar) {
218+
if (avar.draining) {
219+
return;
220+
}
221+
222+
var ps = avar.puts;
223+
var ts = avar.takes;
224+
var rs = avar.reads;
225+
var p, r, t, value, rsize;
226+
227+
avar.draining = true;
228+
229+
/* jshint -W084 */
230+
while (1) {
231+
p = null;
232+
r = null;
233+
t = null;
234+
value = avar.value;
235+
rsize = rs.size;
236+
237+
if (avar.error !== null) {
238+
value = left(avar.error);
239+
while (p = takeHead(ps)) {
240+
runEff(p.cb(value));
241+
}
242+
while (r = takeHead(rs)) {
243+
runEff(r(value));
244+
}
245+
while (t = takeHead(ts)) {
246+
runEff(t(value));
247+
}
248+
break;
249+
}
250+
251+
// Process the next put. We do not immediately invoke the callback
252+
// because we want to preserve ordering. If there are takes/reads
253+
// we want to run those first.
254+
if (value === EMPTY && (p = takeHead(ps))) {
255+
avar.value = value = p.value;
256+
}
257+
258+
if (value !== EMPTY) {
259+
// We go ahead and queue up the next take for the same reasons as
260+
// above. Invoking the read callbacks can affect the mutable queue.
261+
t = takeHead(ts);
262+
// We only want to process the reads queued up before running these
263+
// callbacks so we guard on rsize.
264+
while (rsize-- && (r = takeHead(rs))) {
265+
runEff(r(right(value)));
266+
}
267+
if (t !== null) {
268+
avar.value = EMPTY;
269+
runEff(t(right(value)));
270+
}
271+
}
272+
273+
if (p !== null) {
274+
runEff(p.cb(right(void 0)));
275+
}
276+
277+
// Callbacks could have queued up more items so we need to guard on the
278+
// actual mutable properties.
279+
if (avar.value === EMPTY && ps.size === 0 || avar.value !== EMPTY && ts.size === 0) {
280+
break;
281+
}
282+
}
283+
/* jshint +W084 */
284+
285+
avar.draining = false;
286+
}
287+
288+
function runEff(eff) {
289+
try {
290+
eff();
291+
} catch (error) {
292+
setTimeout(function () {
293+
throw error;
294+
}, 0);
295+
}
296+
}

0 commit comments

Comments
 (0)