Skip to content

Commit 44d6d98

Browse files
committed
Initial implementation
1 parent 0ebef5e commit 44d6d98

File tree

4 files changed

+343
-0
lines changed

4 files changed

+343
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
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

bower.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
}

src/Control/Monad/Eff/AVar.js

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
"use strict";
2+
3+
var EMPTY = {};
4+
var NO_EFFECT = function () {};
5+
6+
function MutableQueue () {
7+
this.head = null;
8+
this.last = null;
9+
this.size = 0;
10+
}
11+
12+
function MutableCell (queue, value) {
13+
this.queue = queue;
14+
this.value = value;
15+
this.next = null;
16+
this.prev = null;
17+
}
18+
19+
function putQueue (queue, value) {
20+
var cell = new MutableCell(queue, value);
21+
cell.prev = queue.tail;
22+
queue.last.next = cell;
23+
queue.last = cell;
24+
queue.size++;
25+
return cell;
26+
}
27+
28+
function insertQueue (queue, value) {
29+
var cell = new MutableCell(queue, value);
30+
cell.next = queue.head;
31+
queue.head.prev = cell;
32+
queue.head = cell;
33+
queue.size++;
34+
return cell;
35+
}
36+
37+
function takeQueue (queue) {
38+
if (queue.size === 0) {
39+
return null;
40+
}
41+
var cell = queue.head;
42+
queue.head = cell.next;
43+
queue.head.prev = null;
44+
queue.size--;
45+
cell.next = null;
46+
cell.queue = null;
47+
return cell.value;
48+
}
49+
50+
function deleteCell (cell) {
51+
if (cell.queue) {
52+
if (cell.prev) {
53+
cell.prev.next = cell.next;
54+
}
55+
if (cell.next) {
56+
cell.next.prev = cell.prev;
57+
}
58+
cell.queue = null;
59+
cell.value = null;
60+
cell.next = null;
61+
cell.prev = null;
62+
}
63+
}
64+
65+
function AVar () {
66+
this.draining = false;
67+
this.error = null;
68+
this.value = EMPTY;
69+
this.consumers = new MutableQueue();
70+
this.producers = new MutableQueue();
71+
}
72+
73+
exports.makeEmptyVar = function () {
74+
return new AVar();
75+
};
76+
77+
exports.makeVar = function (value) {
78+
return function () {
79+
var avar = new AVar();
80+
avar.value = value;
81+
return avar;
82+
};
83+
};
84+
85+
exports._killVar = function (left, right, error, avar) {
86+
return function () {
87+
if (avar.error === null) {
88+
avar.error = error;
89+
drainVar(left, right, avar);
90+
}
91+
};
92+
};
93+
94+
exports._putVar = function (left, right, value, avar, cb) {
95+
return function () {
96+
if (avar.error !== null) {
97+
runEff(cb(left(avar.error)));
98+
return NO_EFFECT;
99+
}
100+
var cell = putQueue(avar.producers, { cb: cb, value: value });
101+
drainVar(left, right, avar);
102+
return function () {
103+
deleteCell(cell);
104+
};
105+
};
106+
};
107+
108+
exports._takeVar = function (left, right, avar, cb) {
109+
return function () {
110+
if (avar.error !== null) {
111+
runEff(cb(left(avar.error)));
112+
return NO_EFFECT;
113+
}
114+
var cell = putQueue(avar.consumers, { cb: cb, peek: false, value: value });
115+
drainVar(left, right, avar);
116+
return function () {
117+
deleteCell(cell);
118+
};
119+
};
120+
};
121+
122+
exports._readVar = function (left, right, avar, cb) {
123+
return function () {
124+
if (avar.error !== null) {
125+
runEff(cb(left(avar.error)));
126+
return NO_EFFECT;
127+
}
128+
var cell = insertQueue(avar.consumers, { cb: cb, peek: true, value: value });
129+
drainVar(left, right, avar);
130+
return function () {
131+
deleteCell(cell);
132+
};
133+
}
134+
};
135+
136+
exports._tryPutVar = function (left, right, value, avar) {
137+
return function () {
138+
if (avar.value === EMPTY && value.error === null) {
139+
putQueue(avar.queue, value);
140+
drainVar(left, right, avar);
141+
return true;
142+
} else {
143+
return false;
144+
}
145+
};
146+
};
147+
148+
exports._tryTakeVar = function (left, right, nothing, just, avar) {
149+
return function () {
150+
var value = avar.value;
151+
if (value === EMPTY || value.error !== null) {
152+
return nothing;
153+
} else {
154+
avar.value = EMPTY;
155+
drainVar(left, right, avar);
156+
return just(value);
157+
}
158+
};
159+
};
160+
161+
exports._tryReadVar = function (nothing, just, avar) {
162+
if (avar.value === EMPTY || value.error !== null) {
163+
return nothing;
164+
} else {
165+
return just(avar.value);
166+
}
167+
};
168+
169+
exports.isEmptyVar = function (avar) {
170+
return function () {
171+
return avar.value === EMPTY || avar.error !== null;
172+
};
173+
};
174+
175+
function drainVar (left, right, avar) {
176+
if (avar.draining) {
177+
return;
178+
}
179+
180+
var ps = avar.producers;
181+
var cs = avar.consumers;
182+
var value = avar.value;
183+
var p, c;
184+
185+
avar.draining = true;
186+
187+
if (avar.error === null) {
188+
while (1) {
189+
p = null;
190+
c = null;
191+
192+
if (cs.size === 0 || ps.size === 0) {
193+
break;
194+
}
195+
196+
if (value === EMPTY && (p = takeQueue(ps))) {
197+
value = avar.value = p.value;
198+
}
199+
200+
if (value !== EMPTY) {
201+
value = right(value);
202+
while (c = takeQueue(cs)) {
203+
runEff(c.cb(value));
204+
if (!c.peek) {
205+
break;
206+
}
207+
}
208+
value = EMPTY;
209+
}
210+
211+
if (p !== null) {
212+
runEff(p.cb(right(void 0)));
213+
}
214+
}
215+
}
216+
217+
if (avar.error !== null) {
218+
value = left(avar.error);
219+
while (1) {
220+
if (ps.size === 0 && cs.size === 0) {
221+
break;
222+
}
223+
if (p = takeQueue(ps)) {
224+
runEff(p.cb(value));
225+
}
226+
while (c = takeQueue(cs)) {
227+
runEff(c.cb(value));
228+
if (!c.peek) {
229+
break;
230+
}
231+
}
232+
}
233+
break;
234+
}
235+
236+
avar.draining = false;
237+
}
238+
239+
function runEff(eff) {
240+
try {
241+
eff();
242+
} catch (error) {
243+
setTimeout(function () {
244+
throw error;
245+
}, 0);
246+
}
247+
}

src/Control/Monad/Eff/AVar.purs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module Control.Monad.Eff.AVar
2+
( AVar
3+
, AVAR
4+
, AVarEff
5+
, AVarCallback
6+
, makeVar
7+
, makeEmptyVar
8+
, isEmptyVar
9+
, takeVar
10+
, tryTakeVar
11+
, putVar
12+
, tryPutVar
13+
, readVar
14+
, tryReadVar
15+
, killVar
16+
) where
17+
18+
import Prelude
19+
import Control.Monad.Eff (kind Effect, Eff)
20+
import Control.Monad.Eff.Exception (Error)
21+
import Data.Either (Either(..))
22+
import Data.Function.Uncurried as Fn
23+
import Data.Maybe (Maybe(..))
24+
25+
type AVarEff eff = Eff (avar AVAR | eff)
26+
27+
type AVarCallback eff a = ((Either Error a AVarEff eff Unit) AVarEff eff Unit)
28+
29+
foreign import data AVarType Type
30+
31+
foreign import data AVAREffect
32+
33+
foreign import makeEmptyVar eff a. AVarEff eff (AVar a)
34+
35+
foreign import makeVar eff a. a AVarEff eff (AVar a)
36+
37+
foreign import isEmptyVar eff a. AVar a AVarEff eff Boolean
38+
39+
killVar eff a. Error AVar a AVarEff eff Unit
40+
killVar err avar = Fn.runFn4 _killVar Left Right err avar
41+
42+
putVar eff a. a AVar a AVarCallback eff Unit AVarEff eff Unit
43+
putVar value avar cb = Fn.runFn5 _putVar Left Right value avar cb
44+
45+
tryPutVar eff a. a AVar a AVarEff eff Boolean
46+
tryPutVar value avar = Fn.runFn4 _tryPutVar Left Right value avar
47+
48+
takeVar eff a. AVar a AVarCallback eff a AVarEff eff Unit
49+
takeVar avar cb = Fn.runFn4 _takeVar Left Right avar cb
50+
51+
tryTakeVar eff a. AVar a AVarEff eff (Maybe a)
52+
tryTakeVar avar = Fn.runFn5 _tryTakeVar Left Right Nothing Just avar
53+
54+
readVar eff a. AVar a AVarCallback eff a AVarEff eff Unit
55+
readVar avar cb = Fn.runFn4 _readVar Left Right avar cb
56+
57+
tryReadVar eff a. AVar a AVarEff eff (Maybe a)
58+
tryReadVar avar = Fn.runFn3 _tryReadVar Nothing Just avar
59+
60+
foreign import _killVar eff a. Fn.Fn4 (Error Either Error a) (a Either Error a) Error (AVar a) (AVarEff eff Unit)
61+
foreign import _putVar eff a. Fn.Fn5 (Error Either Error a) (a Either Error a) a (AVar a) (AVarCallback eff Unit) (AVarEff eff Unit)
62+
foreign import _tryPutVar eff a. Fn.Fn4 (Error Either Error a) (a Either Error a) a (AVar a) (AVarEff eff Boolean)
63+
foreign import _takeVar eff a. Fn.Fn4 (Error Either Error a) (a Either Error a) (AVar a) (AVarCallback eff a) (AVarEff eff Unit)
64+
foreign import _tryTakeVar eff a. Fn.Fn5 (Error Either Error a) (a Either Error a) (Maybe a) (a Maybe a) (AVar a) (AVarEff eff (Maybe a))
65+
foreign import _readVar eff a. Fn.Fn4 (Error Either Error a) (a Either Error a) (AVar a) (AVarCallback eff a) (AVarEff eff Unit)
66+
foreign import _tryReadVar eff a. Fn.Fn3 (Maybe a) (a Maybe a) (AVar a) (AVarEff eff (Maybe a))

0 commit comments

Comments
 (0)