@fncts/io 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Channel/api.d.ts +27 -4
- package/Channel/internal/MergeDecision.d.ts +14 -0
- package/STM/api.d.ts +7 -0
- package/Sink/api.d.ts +455 -7
- package/TReentrantLock/api.d.ts +97 -0
- package/TReentrantLock/definition.d.ts +65 -0
- package/TReentrantLock.d.ts +2 -0
- package/_cjs/Cached/api.cjs +1 -1
- package/_cjs/Cached/api.cjs.map +1 -1
- package/_cjs/Channel/api/mapOutIOC.cjs +1 -1
- package/_cjs/Channel/api/mapOutIOC.cjs.map +1 -1
- package/_cjs/Channel/api/mergeAllWith.cjs +2 -2
- package/_cjs/Channel/api/mergeAllWith.cjs.map +1 -1
- package/_cjs/Channel/api/mergeWith.cjs +1 -1
- package/_cjs/Channel/api/mergeWith.cjs.map +1 -1
- package/_cjs/Channel/api.cjs +87 -32
- package/_cjs/Channel/api.cjs.map +1 -1
- package/_cjs/Channel/internal/MergeDecision.cjs +11 -2
- package/_cjs/Channel/internal/MergeDecision.cjs.map +1 -1
- package/_cjs/IO/api/foreachC.cjs +2 -2
- package/_cjs/IO/api/foreachC.cjs.map +1 -1
- package/_cjs/STM/api.cjs +15 -6
- package/_cjs/STM/api.cjs.map +1 -1
- package/_cjs/Sink/api.cjs +1180 -40
- package/_cjs/Sink/api.cjs.map +1 -1
- package/_cjs/Stream/api.cjs +28 -28
- package/_cjs/Stream/api.cjs.map +1 -1
- package/_cjs/TReentrantLock/api.cjs +297 -0
- package/_cjs/TReentrantLock/api.cjs.map +1 -0
- package/_cjs/TReentrantLock/definition.cjs +125 -0
- package/_cjs/TReentrantLock/definition.cjs.map +1 -0
- package/_cjs/TReentrantLock.cjs +32 -0
- package/_cjs/TReentrantLock.cjs.map +1 -0
- package/_cjs/collection/immutable/Conc/filterIO.cjs +35 -0
- package/_cjs/collection/immutable/Conc/filterIO.cjs.map +1 -0
- package/_cjs/collection/immutable/Conc.cjs +13 -0
- package/_cjs/collection/immutable/Conc.cjs.map +1 -1
- package/_mjs/Cached/api.mjs +1 -1
- package/_mjs/Cached/api.mjs.map +1 -1
- package/_mjs/Channel/api/mapOutIOC.mjs +1 -1
- package/_mjs/Channel/api/mapOutIOC.mjs.map +1 -1
- package/_mjs/Channel/api/mergeAllWith.mjs +2 -2
- package/_mjs/Channel/api/mergeAllWith.mjs.map +1 -1
- package/_mjs/Channel/api/mergeWith.mjs +1 -1
- package/_mjs/Channel/api/mergeWith.mjs.map +1 -1
- package/_mjs/Channel/api.mjs +75 -30
- package/_mjs/Channel/api.mjs.map +1 -1
- package/_mjs/Channel/internal/MergeDecision.mjs +7 -0
- package/_mjs/Channel/internal/MergeDecision.mjs.map +1 -1
- package/_mjs/IO/api/foreachC.mjs +2 -2
- package/_mjs/IO/api/foreachC.mjs.map +1 -1
- package/_mjs/STM/api.mjs +13 -6
- package/_mjs/STM/api.mjs.map +1 -1
- package/_mjs/Sink/api.mjs +996 -31
- package/_mjs/Sink/api.mjs.map +1 -1
- package/_mjs/Stream/api.mjs +28 -28
- package/_mjs/Stream/api.mjs.map +1 -1
- package/_mjs/TReentrantLock/api.mjs +243 -0
- package/_mjs/TReentrantLock/api.mjs.map +1 -0
- package/_mjs/TReentrantLock/definition.mjs +102 -0
- package/_mjs/TReentrantLock/definition.mjs.map +1 -0
- package/_mjs/TReentrantLock.mjs +4 -0
- package/_mjs/TReentrantLock.mjs.map +1 -0
- package/_mjs/collection/immutable/Conc/filterIO.mjs +22 -0
- package/_mjs/collection/immutable/Conc/filterIO.mjs.map +1 -0
- package/_mjs/collection/immutable/Conc.mjs +1 -0
- package/_mjs/collection/immutable/Conc.mjs.map +1 -1
- package/_src/Channel/api.ts +98 -11
- package/_src/Channel/internal/MergeDecision.ts +15 -0
- package/_src/IO/api.ts +1 -1
- package/_src/STM/api.ts +9 -0
- package/_src/Sink/api.ts +1350 -19
- package/_src/TFuture/definition.ts +1 -1
- package/_src/TReentrantLock/api.ts +193 -0
- package/_src/TReentrantLock/definition.ts +86 -0
- package/_src/TReentrantLock.ts +4 -0
- package/_src/collection/immutable/Conc/filterIO.ts +16 -0
- package/_src/collection/immutable/Conc.ts +1 -0
- package/collection/immutable/Conc/filterIO.d.ts +7 -0
- package/collection/immutable/Conc.d.ts +1 -0
- package/package.json +3 -3
@@ -0,0 +1,193 @@
|
|
1
|
+
import type { Lock, LockState } from "./definition.js";
|
2
|
+
|
3
|
+
import { RetryException } from "../STM.js";
|
4
|
+
import { ReadLock, TReentrantLock } from "./definition.js";
|
5
|
+
import { LockTag, WriteLock } from "./definition.js";
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @tsplus static fncts.io.TReentrantLock.ReadLockOps empty
|
9
|
+
*/
|
10
|
+
export const emptyReadLock: ReadLock = new ReadLock(HashMap.makeDefault());
|
11
|
+
|
12
|
+
/**
|
13
|
+
* @tsplus static fncts.io.TReentrantLock.ReadLockOps __call
|
14
|
+
*/
|
15
|
+
export function makeReadLock(fiberId: FiberId, count: number): ReadLock {
|
16
|
+
return count <= 0 ? ReadLock.empty : new ReadLock(HashMap.makeDefault<FiberId, number>().set(fiberId, count));
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* @tsplus static fncts.io.TReentrantLockOps make
|
21
|
+
* @tsplus static fncts.io.TReentrantLockOps __call
|
22
|
+
*/
|
23
|
+
export function make(): USTM<TReentrantLock> {
|
24
|
+
return TRef.make<LockState>(new ReadLock(HashMap.makeDefault())).map((ref) => new TReentrantLock(ref));
|
25
|
+
}
|
26
|
+
|
27
|
+
/**
|
28
|
+
* @tsplus getter fncts.io.TReentrantLock acquireRead
|
29
|
+
*/
|
30
|
+
export function acquireRead(self: TReentrantLock): USTM<number> {
|
31
|
+
return adjustRead(self, 1);
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* @tsplus getter fncts.io.TReentrantLock acquireWrite
|
36
|
+
*/
|
37
|
+
export function acquireWrite(self: TReentrantLock): USTM<number> {
|
38
|
+
return STM.Effect((journal, fiberId, _) => {
|
39
|
+
const lockState = self.data.unsafeGet(journal);
|
40
|
+
switch (lockState._tag) {
|
41
|
+
case LockTag.ReadLock: {
|
42
|
+
if (lockState.noOtherHolder(fiberId)) {
|
43
|
+
self.data.unsafeSet(journal, new WriteLock(1, lockState.readLocksHeld(fiberId), fiberId));
|
44
|
+
return 1;
|
45
|
+
}
|
46
|
+
throw new RetryException();
|
47
|
+
}
|
48
|
+
case LockTag.WriteLock: {
|
49
|
+
if (lockState.fiberId == fiberId) {
|
50
|
+
self.data.unsafeSet(journal, new WriteLock(lockState.writeLocks + 1, lockState.readLocks, fiberId));
|
51
|
+
return lockState.writeLocks + 1;
|
52
|
+
}
|
53
|
+
throw new RetryException();
|
54
|
+
}
|
55
|
+
}
|
56
|
+
});
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* @tsplus getter fncts.io.TReentrantLock releaseRead
|
61
|
+
*/
|
62
|
+
export function releaseRead(self: TReentrantLock): USTM<number> {
|
63
|
+
return adjustRead(self, -1);
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @tsplus getter fncts.io.TReentrantLock releaseWrite
|
68
|
+
*/
|
69
|
+
export function releaseWrite(self: TReentrantLock): USTM<number> {
|
70
|
+
return STM.Effect((journal, fiberId) => {
|
71
|
+
const lockState: LockState = self.data.unsafeGet(journal);
|
72
|
+
let res: LockState | undefined;
|
73
|
+
if (lockState._tag === LockTag.WriteLock && lockState.fiberId == fiberId) {
|
74
|
+
if (lockState.writeLocks === 1) {
|
75
|
+
res = new ReadLock(HashMap.makeDefault<FiberId, number>().set(fiberId, lockState.readLocks));
|
76
|
+
}
|
77
|
+
if (lockState.writeLocks > 1) {
|
78
|
+
res = new WriteLock(lockState.writeLocks - 1, lockState.readLocks, fiberId);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
if (!res) {
|
82
|
+
throw new Error(`Defect: Fiber ${fiberId.threadName} releasing write lock it does not hold`);
|
83
|
+
}
|
84
|
+
self.data.unsafeSet(journal, res);
|
85
|
+
return res.writeLocksHeld(fiberId);
|
86
|
+
});
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
* @tsplus fluent fncts.io.TReentrantLock withReadLock
|
91
|
+
*/
|
92
|
+
export function withReadLock<R, E, A>(self: TReentrantLock, io: IO<R, E, A>): IO<R, E, A> {
|
93
|
+
return IO.uninterruptibleMask(
|
94
|
+
({ restore }) => restore(self.acquireRead.commit) > restore(io).ensuring(self.releaseRead.commit),
|
95
|
+
);
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* @tsplus fluent fncts.io.TReentrantLock withWriteLock
|
100
|
+
*/
|
101
|
+
export function withWriteLock<R, E, A>(self: TReentrantLock, io: IO<R, E, A>): IO<R, E, A> {
|
102
|
+
return IO.uninterruptibleMask(
|
103
|
+
({ restore }) => restore(self.acquireWrite.commit) > restore(io).ensuring(self.releaseWrite.commit),
|
104
|
+
);
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* @tsplus getter fncts.io.TReentrantLock readLock
|
109
|
+
*/
|
110
|
+
export function readLock(self: TReentrantLock, __tsplusTrace?: string): IO<Has<Scope>, never, number> {
|
111
|
+
return self.acquireRead.commit.acquireRelease(() => self.releaseRead.commit);
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* @tsplus getter fncts.io.TReentrantLock writeLock
|
116
|
+
*/
|
117
|
+
export function writeLock(self: TReentrantLock, __tsplusTrace?: string): IO<Has<Scope>, never, number> {
|
118
|
+
return self.acquireWrite.commit.acquireRelease(() => self.releaseWrite.commit);
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* @tsplus getter fncts.io.TReentrantLock readLocked
|
123
|
+
*/
|
124
|
+
export function readLocked(self: TReentrantLock): USTM<boolean> {
|
125
|
+
return self.data.get.map((state) => state.readLocks > 0);
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* @tsplus getter fncts.io.TReentrantLock writeLocked
|
130
|
+
*/
|
131
|
+
export function writeLocked(self: TReentrantLock): USTM<boolean> {
|
132
|
+
return self.data.get.map((state) => state.writeLocks > 0);
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* @tsplus getter fncts.io.TReentrantLock locked
|
137
|
+
*/
|
138
|
+
export function locked(self: TReentrantLock): USTM<boolean> {
|
139
|
+
return self.readLocked.zipWith(self.writeLocked, (a, b) => a || b);
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* @tsplus getter fncts.io.TReentrantLock fiberReadLocks
|
144
|
+
*/
|
145
|
+
export function fiberReadLocks(self: TReentrantLock): USTM<number> {
|
146
|
+
return STM.Effect((journal, fiberId) => self.data.unsafeGet(journal).readLocksHeld(fiberId));
|
147
|
+
}
|
148
|
+
|
149
|
+
/**
|
150
|
+
* @tsplus getter fncts.io.TReentrantLock fiberWriteLocks
|
151
|
+
*/
|
152
|
+
export function fiberWriteLocks(self: TReentrantLock): USTM<number> {
|
153
|
+
return STM.Effect((journal, fiberId) => self.data.unsafeGet(journal).writeLocksHeld(fiberId));
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* @tsplus getter fncts.io.TReentrantLock writeLocks
|
158
|
+
*/
|
159
|
+
export function writeLocks(self: TReentrantLock): USTM<number> {
|
160
|
+
return self.data.get.map((state) => state.writeLocks);
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* @tsplus getter fncts.io.TReentrantLock readLocks
|
165
|
+
*/
|
166
|
+
export function readLocks(self: TReentrantLock): USTM<number> {
|
167
|
+
return self.data.get.map((state) => state.readLocks);
|
168
|
+
}
|
169
|
+
|
170
|
+
function adjustRead(self: TReentrantLock, delta: number): USTM<number> {
|
171
|
+
return STM.Effect((journal, fiberId, _) => {
|
172
|
+
const lockState = self.data.unsafeGet(journal);
|
173
|
+
switch (lockState._tag) {
|
174
|
+
case LockTag.ReadLock: {
|
175
|
+
const res = lockState.adjust(fiberId, delta);
|
176
|
+
self.data.unsafeSet(journal, res);
|
177
|
+
return res.readLocksHeld(fiberId);
|
178
|
+
}
|
179
|
+
case LockTag.WriteLock: {
|
180
|
+
if (!lockState.fiberId == fiberId) {
|
181
|
+
throw new RetryException();
|
182
|
+
}
|
183
|
+
const newTotal = lockState.writeLocks + delta;
|
184
|
+
if (newTotal < 0) {
|
185
|
+
throw new Error(`Defect: Fiber ${lockState.fiberId.threadName} releasing read lock it does not hold`);
|
186
|
+
} else {
|
187
|
+
self.data.unsafeSet(journal, new WriteLock(lockState.writeLocks, newTotal, lockState.fiberId));
|
188
|
+
}
|
189
|
+
return newTotal;
|
190
|
+
}
|
191
|
+
}
|
192
|
+
});
|
193
|
+
}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
/**
|
2
|
+
* A `TReentrantLock` is a reentrant read/write lock. Multiple readers may all
|
3
|
+
* concurrently acquire read locks. Only one writer is allowed to acquire a
|
4
|
+
* write lock at any given time. Read locks may be upgraded into write locks. A
|
5
|
+
* fiber that has a write lock may acquire other write locks or read locks.
|
6
|
+
*
|
7
|
+
* The two primary methods of this structure are `readLock`, which acquires a
|
8
|
+
* read lock in a scoped context, and `writeLock`, which acquires a write lock
|
9
|
+
* in a scoped context.
|
10
|
+
*
|
11
|
+
* Although located in the STM package, there is no need for locks within STM
|
12
|
+
* transactions. However, this lock can be quite useful in effectful code, to
|
13
|
+
* provide consistent read/write access to mutable state; and being in STM
|
14
|
+
* allows this structure to be composed into more complicated concurrent
|
15
|
+
* structures that are consumed from effectful code.
|
16
|
+
*
|
17
|
+
* @tsplus type fncts.io.TReentrantLock
|
18
|
+
* @tsplus companion fncts.io.TReentrantLockOps
|
19
|
+
*/
|
20
|
+
export class TReentrantLock {
|
21
|
+
constructor(readonly data: UTRef<LockState>) {}
|
22
|
+
}
|
23
|
+
|
24
|
+
export interface Lock {
|
25
|
+
readonly readLocks: number;
|
26
|
+
readonly readLocksHeld: (fiberId: FiberId) => number;
|
27
|
+
readonly writeLocks: number;
|
28
|
+
readonly writeLocksHeld: (fiberId: FiberId) => number;
|
29
|
+
}
|
30
|
+
|
31
|
+
export const enum LockTag {
|
32
|
+
WriteLock,
|
33
|
+
ReadLock,
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* @tsplus type fncts.io.TReentrantLock.WriteLock
|
38
|
+
* @tsplus companion fncts.io.TReentrantLock.WriteLockOps
|
39
|
+
*/
|
40
|
+
export class WriteLock implements Lock {
|
41
|
+
readonly _tag = LockTag.WriteLock;
|
42
|
+
constructor(readonly writeLocks: number, readonly readLocks: number, readonly fiberId: FiberId) {}
|
43
|
+
readLocksHeld(fiberId: FiberId): number {
|
44
|
+
return this.fiberId == fiberId ? this.readLocks : 0;
|
45
|
+
}
|
46
|
+
writeLocksHeld(fiberId: FiberId): number {
|
47
|
+
return this.fiberId == fiberId ? this.writeLocks : 0;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* @tsplus type fncts.io.TReentrantLock.ReadLock
|
53
|
+
* @tsplus companion fncts.io.TReentrantLock.ReadLockOps
|
54
|
+
*/
|
55
|
+
export class ReadLock implements Lock {
|
56
|
+
readonly _tag = LockTag.ReadLock;
|
57
|
+
constructor(readonly readers: HashMap<FiberId, number>) {}
|
58
|
+
get readLocks() {
|
59
|
+
return (this.readers.values as Iterable<number>).sum;
|
60
|
+
}
|
61
|
+
writeLocks = 0;
|
62
|
+
|
63
|
+
readLocksHeld(fiberId: FiberId) {
|
64
|
+
return this.readers.get(fiberId).getOrElse(0);
|
65
|
+
}
|
66
|
+
writeLocksHeld(_fiberId: FiberId) {
|
67
|
+
return 0;
|
68
|
+
}
|
69
|
+
|
70
|
+
noOtherHolder(fiberId: FiberId): boolean {
|
71
|
+
return this.readers.isEmpty || (this.readers.size === 1 && this.readers.has(fiberId));
|
72
|
+
}
|
73
|
+
adjust(fiberId: FiberId, adjust: number): ReadLock {
|
74
|
+
const total = this.readLocksHeld(fiberId);
|
75
|
+
const newTotal = total + adjust;
|
76
|
+
if (newTotal < 0) {
|
77
|
+
throw new Error(`Defect: Fiber ${fiberId.threadName} releasing read lock it does not hold`);
|
78
|
+
}
|
79
|
+
if (newTotal === 0) {
|
80
|
+
return new ReadLock(this.readers.remove(fiberId));
|
81
|
+
}
|
82
|
+
return new ReadLock(this.readers.set(fiberId, newTotal));
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
export type LockState = WriteLock | ReadLock;
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* @tsplus fluent fncts.Conc filterIO
|
3
|
+
*/
|
4
|
+
export function filterIO<A, R, E>(self: Conc<A>, p: (a: A) => IO<R, E, boolean>): IO<R, E, Conc<A>> {
|
5
|
+
return IO.defer(() => {
|
6
|
+
const builder = new ConcBuilder<A>();
|
7
|
+
let dest: IO<R, E, ConcBuilder<A>> = IO.succeedNow(builder);
|
8
|
+
for (const a of self) {
|
9
|
+
dest = dest.zipWith(p(a), (builder, res) => {
|
10
|
+
if (res) return builder.append(a);
|
11
|
+
else return builder;
|
12
|
+
});
|
13
|
+
}
|
14
|
+
return dest.map((builder) => builder.result());
|
15
|
+
});
|
16
|
+
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { Conc, ConcBuilder } from "@fncts/base/collection/immutable/Conc";
|
2
|
+
import { IO } from "@fncts/io/IO/definition";
|
3
|
+
/**
|
4
|
+
* @tsplus fluent fncts.Conc filterIO
|
5
|
+
* @tsplus location "@fncts/io/collection/immutable/Conc/filterIO"
|
6
|
+
*/
|
7
|
+
export declare function filterIO<A, R, E>(self: Conc<A>, p: (a: A) => IO<R, E, boolean>): IO<R, E, Conc<A>>;
|
package/package.json
CHANGED