@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.
Files changed (81) hide show
  1. package/Channel/api.d.ts +27 -4
  2. package/Channel/internal/MergeDecision.d.ts +14 -0
  3. package/STM/api.d.ts +7 -0
  4. package/Sink/api.d.ts +455 -7
  5. package/TReentrantLock/api.d.ts +97 -0
  6. package/TReentrantLock/definition.d.ts +65 -0
  7. package/TReentrantLock.d.ts +2 -0
  8. package/_cjs/Cached/api.cjs +1 -1
  9. package/_cjs/Cached/api.cjs.map +1 -1
  10. package/_cjs/Channel/api/mapOutIOC.cjs +1 -1
  11. package/_cjs/Channel/api/mapOutIOC.cjs.map +1 -1
  12. package/_cjs/Channel/api/mergeAllWith.cjs +2 -2
  13. package/_cjs/Channel/api/mergeAllWith.cjs.map +1 -1
  14. package/_cjs/Channel/api/mergeWith.cjs +1 -1
  15. package/_cjs/Channel/api/mergeWith.cjs.map +1 -1
  16. package/_cjs/Channel/api.cjs +87 -32
  17. package/_cjs/Channel/api.cjs.map +1 -1
  18. package/_cjs/Channel/internal/MergeDecision.cjs +11 -2
  19. package/_cjs/Channel/internal/MergeDecision.cjs.map +1 -1
  20. package/_cjs/IO/api/foreachC.cjs +2 -2
  21. package/_cjs/IO/api/foreachC.cjs.map +1 -1
  22. package/_cjs/STM/api.cjs +15 -6
  23. package/_cjs/STM/api.cjs.map +1 -1
  24. package/_cjs/Sink/api.cjs +1180 -40
  25. package/_cjs/Sink/api.cjs.map +1 -1
  26. package/_cjs/Stream/api.cjs +28 -28
  27. package/_cjs/Stream/api.cjs.map +1 -1
  28. package/_cjs/TReentrantLock/api.cjs +297 -0
  29. package/_cjs/TReentrantLock/api.cjs.map +1 -0
  30. package/_cjs/TReentrantLock/definition.cjs +125 -0
  31. package/_cjs/TReentrantLock/definition.cjs.map +1 -0
  32. package/_cjs/TReentrantLock.cjs +32 -0
  33. package/_cjs/TReentrantLock.cjs.map +1 -0
  34. package/_cjs/collection/immutable/Conc/filterIO.cjs +35 -0
  35. package/_cjs/collection/immutable/Conc/filterIO.cjs.map +1 -0
  36. package/_cjs/collection/immutable/Conc.cjs +13 -0
  37. package/_cjs/collection/immutable/Conc.cjs.map +1 -1
  38. package/_mjs/Cached/api.mjs +1 -1
  39. package/_mjs/Cached/api.mjs.map +1 -1
  40. package/_mjs/Channel/api/mapOutIOC.mjs +1 -1
  41. package/_mjs/Channel/api/mapOutIOC.mjs.map +1 -1
  42. package/_mjs/Channel/api/mergeAllWith.mjs +2 -2
  43. package/_mjs/Channel/api/mergeAllWith.mjs.map +1 -1
  44. package/_mjs/Channel/api/mergeWith.mjs +1 -1
  45. package/_mjs/Channel/api/mergeWith.mjs.map +1 -1
  46. package/_mjs/Channel/api.mjs +75 -30
  47. package/_mjs/Channel/api.mjs.map +1 -1
  48. package/_mjs/Channel/internal/MergeDecision.mjs +7 -0
  49. package/_mjs/Channel/internal/MergeDecision.mjs.map +1 -1
  50. package/_mjs/IO/api/foreachC.mjs +2 -2
  51. package/_mjs/IO/api/foreachC.mjs.map +1 -1
  52. package/_mjs/STM/api.mjs +13 -6
  53. package/_mjs/STM/api.mjs.map +1 -1
  54. package/_mjs/Sink/api.mjs +996 -31
  55. package/_mjs/Sink/api.mjs.map +1 -1
  56. package/_mjs/Stream/api.mjs +28 -28
  57. package/_mjs/Stream/api.mjs.map +1 -1
  58. package/_mjs/TReentrantLock/api.mjs +243 -0
  59. package/_mjs/TReentrantLock/api.mjs.map +1 -0
  60. package/_mjs/TReentrantLock/definition.mjs +102 -0
  61. package/_mjs/TReentrantLock/definition.mjs.map +1 -0
  62. package/_mjs/TReentrantLock.mjs +4 -0
  63. package/_mjs/TReentrantLock.mjs.map +1 -0
  64. package/_mjs/collection/immutable/Conc/filterIO.mjs +22 -0
  65. package/_mjs/collection/immutable/Conc/filterIO.mjs.map +1 -0
  66. package/_mjs/collection/immutable/Conc.mjs +1 -0
  67. package/_mjs/collection/immutable/Conc.mjs.map +1 -1
  68. package/_src/Channel/api.ts +98 -11
  69. package/_src/Channel/internal/MergeDecision.ts +15 -0
  70. package/_src/IO/api.ts +1 -1
  71. package/_src/STM/api.ts +9 -0
  72. package/_src/Sink/api.ts +1350 -19
  73. package/_src/TFuture/definition.ts +1 -1
  74. package/_src/TReentrantLock/api.ts +193 -0
  75. package/_src/TReentrantLock/definition.ts +86 -0
  76. package/_src/TReentrantLock.ts +4 -0
  77. package/_src/collection/immutable/Conc/filterIO.ts +16 -0
  78. package/_src/collection/immutable/Conc.ts +1 -0
  79. package/collection/immutable/Conc/filterIO.d.ts +7 -0
  80. package/collection/immutable/Conc.d.ts +1 -0
  81. package/package.json +3 -3
@@ -20,4 +20,4 @@ export interface TFuture<E, A>
20
20
  */
21
21
  export interface TFutureOps extends NewtypeIso<TFutureN> {}
22
22
 
23
- export const TFuture: TFutureOps = Newtype<TFutureN>();
23
+ export const TFuture: TFutureOps = Newtype<TFutureN>();
@@ -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,4 @@
1
+ // codegen:start { preset: barrel, include: TReentrantLock/*.ts }
2
+ export * from "./TReentrantLock/api.js";
3
+ export * from "./TReentrantLock/definition.js";
4
+ // codegen:end
@@ -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
+ }
@@ -1,4 +1,5 @@
1
1
  // codegen:start { preset: barrel, include: ./Conc/*.ts }
2
+ export * from "./Conc/filterIO.js";
2
3
  export * from "./Conc/findIO.js";
3
4
  export * from "./Conc/mapIO.js";
4
5
  export * from "./Conc/takeWhileIO.js";
@@ -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>>;
@@ -1,3 +1,4 @@
1
+ export * from "./Conc/filterIO.js";
1
2
  export * from "./Conc/findIO.js";
2
3
  export * from "./Conc/mapIO.js";
3
4
  export * from "./Conc/takeWhileIO.js";
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@fncts/io",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "dependencies": {
5
- "@fncts/base": "0.0.9",
6
- "@fncts/typelevel": "0.0.7"
5
+ "@fncts/base": "0.0.10",
6
+ "@fncts/typelevel": "0.0.8"
7
7
  },
8
8
  "exports": {
9
9
  "./*": {