effect 3.19.3 → 3.19.5
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/PartitionedSemaphore/package.json +6 -0
- package/dist/cjs/PartitionedSemaphore.js +147 -0
- package/dist/cjs/PartitionedSemaphore.js.map +1 -0
- package/dist/cjs/index.js +4 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/fiberRuntime.js +14 -4
- package/dist/cjs/internal/fiberRuntime.js.map +1 -1
- package/dist/cjs/internal/version.js +1 -1
- package/dist/dts/PartitionedSemaphore.d.ts +76 -0
- package/dist/dts/PartitionedSemaphore.d.ts.map +1 -0
- package/dist/dts/Types.d.ts +1 -1
- package/dist/dts/Types.d.ts.map +1 -1
- package/dist/dts/index.d.ts +5 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/fiberRuntime.d.ts.map +1 -1
- package/dist/esm/PartitionedSemaphore.js +137 -0
- package/dist/esm/PartitionedSemaphore.js.map +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/fiberRuntime.js +14 -4
- package/dist/esm/internal/fiberRuntime.js.map +1 -1
- package/dist/esm/internal/version.js +1 -1
- package/package.json +9 -1
- package/src/PartitionedSemaphore.ts +200 -0
- package/src/Types.ts +1 -1
- package/src/index.ts +6 -0
- package/src/internal/fiberRuntime.ts +14 -4
- package/src/internal/version.ts +1 -1
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 3.19.4
|
|
3
|
+
* @experimental
|
|
4
|
+
*/
|
|
5
|
+
import * as Effect from "./Effect.js"
|
|
6
|
+
import * as Iterable from "./Iterable.js"
|
|
7
|
+
import * as MutableHashMap from "./MutableHashMap.js"
|
|
8
|
+
import * as Option from "./Option.js"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @since 3.19.4
|
|
12
|
+
* @category Models
|
|
13
|
+
* @experimental
|
|
14
|
+
*/
|
|
15
|
+
export const TypeId: TypeId = "~effect/PartitionedSemaphore"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @since 3.19.4
|
|
19
|
+
* @category Models
|
|
20
|
+
* @experimental
|
|
21
|
+
*/
|
|
22
|
+
export type TypeId = "~effect/PartitionedSemaphore"
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A `PartitionedSemaphore` is a concurrency primitive that can be used to
|
|
26
|
+
* control concurrent access to a resource across multiple partitions identified
|
|
27
|
+
* by keys.
|
|
28
|
+
*
|
|
29
|
+
* The total number of permits is shared across all partitions, with waiting
|
|
30
|
+
* permits equally distributed among partitions using a round-robin strategy.
|
|
31
|
+
*
|
|
32
|
+
* This is useful when you want to limit the total number of concurrent accesses
|
|
33
|
+
* to a resource, while still allowing for fair distribution of access across
|
|
34
|
+
* different partitions.
|
|
35
|
+
*
|
|
36
|
+
* @since 3.19.4
|
|
37
|
+
* @category Models
|
|
38
|
+
* @experimental
|
|
39
|
+
*/
|
|
40
|
+
export interface PartitionedSemaphore<in K> {
|
|
41
|
+
readonly [TypeId]: TypeId
|
|
42
|
+
|
|
43
|
+
readonly withPermits: (
|
|
44
|
+
key: K,
|
|
45
|
+
permits: number
|
|
46
|
+
) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A `PartitionedSemaphore` is a concurrency primitive that can be used to
|
|
51
|
+
* control concurrent access to a resource across multiple partitions identified
|
|
52
|
+
* by keys.
|
|
53
|
+
*
|
|
54
|
+
* The total number of permits is shared across all partitions, with waiting
|
|
55
|
+
* permits equally distributed among partitions using a round-robin strategy.
|
|
56
|
+
*
|
|
57
|
+
* This is useful when you want to limit the total number of concurrent accesses
|
|
58
|
+
* to a resource, while still allowing for fair distribution of access across
|
|
59
|
+
* different partitions.
|
|
60
|
+
*
|
|
61
|
+
* @since 3.19.4
|
|
62
|
+
* @category Constructors
|
|
63
|
+
* @experimental
|
|
64
|
+
*/
|
|
65
|
+
export const makeUnsafe = <K = unknown>(options: {
|
|
66
|
+
readonly permits: number
|
|
67
|
+
}): PartitionedSemaphore<K> => {
|
|
68
|
+
const maxPermits = Math.max(0, options.permits)
|
|
69
|
+
|
|
70
|
+
if (!Number.isFinite(maxPermits)) {
|
|
71
|
+
return {
|
|
72
|
+
[TypeId]: TypeId,
|
|
73
|
+
withPermits: () => (effect) => effect
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let totalPermits = maxPermits
|
|
78
|
+
let waitingPermits = 0
|
|
79
|
+
|
|
80
|
+
type Waiter = {
|
|
81
|
+
permits: number
|
|
82
|
+
readonly resume: () => void
|
|
83
|
+
}
|
|
84
|
+
const partitions = MutableHashMap.empty<K, Set<Waiter>>()
|
|
85
|
+
|
|
86
|
+
const take = (key: K, permits: number) =>
|
|
87
|
+
Effect.async<void>((resume) => {
|
|
88
|
+
if (maxPermits < permits) {
|
|
89
|
+
return resume(Effect.never)
|
|
90
|
+
} else if (totalPermits >= permits) {
|
|
91
|
+
totalPermits -= permits
|
|
92
|
+
return resume(Effect.void)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const needed = permits - totalPermits
|
|
96
|
+
const taken = permits - needed
|
|
97
|
+
if (totalPermits > 0) {
|
|
98
|
+
totalPermits = 0
|
|
99
|
+
}
|
|
100
|
+
waitingPermits += needed
|
|
101
|
+
|
|
102
|
+
const waiters = Option.getOrElse(
|
|
103
|
+
MutableHashMap.get(partitions, key),
|
|
104
|
+
() => {
|
|
105
|
+
const set = new Set<Waiter>()
|
|
106
|
+
MutableHashMap.set(partitions, key, set)
|
|
107
|
+
return set
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
const entry: Waiter = {
|
|
112
|
+
permits: needed,
|
|
113
|
+
resume() {
|
|
114
|
+
cleanup()
|
|
115
|
+
resume(Effect.void)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function cleanup() {
|
|
119
|
+
waiters.delete(entry)
|
|
120
|
+
if (waiters.size === 0) {
|
|
121
|
+
MutableHashMap.remove(partitions, key)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
waiters.add(entry)
|
|
125
|
+
return Effect.sync(() => {
|
|
126
|
+
cleanup()
|
|
127
|
+
waitingPermits -= entry.permits
|
|
128
|
+
if (taken > 0) {
|
|
129
|
+
releaseUnsafe(taken)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
let iterator = partitions[Symbol.iterator]()
|
|
135
|
+
const releaseUnsafe = (permits: number) => {
|
|
136
|
+
while (permits > 0) {
|
|
137
|
+
if (waitingPermits === 0) {
|
|
138
|
+
totalPermits += permits
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let state = iterator.next()
|
|
143
|
+
if (state.done) {
|
|
144
|
+
iterator = partitions[Symbol.iterator]()
|
|
145
|
+
state = iterator.next()
|
|
146
|
+
if (state.done) return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const entry = Iterable.unsafeHead(state.value[1])
|
|
150
|
+
entry.permits--
|
|
151
|
+
waitingPermits--
|
|
152
|
+
if (entry.permits === 0) entry.resume()
|
|
153
|
+
permits--
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
[TypeId]: TypeId,
|
|
159
|
+
withPermits: (key, permits) => {
|
|
160
|
+
const takePermits = take(key, permits)
|
|
161
|
+
const release: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> = Effect.matchCauseEffect({
|
|
162
|
+
onFailure(cause) {
|
|
163
|
+
releaseUnsafe(permits)
|
|
164
|
+
return Effect.failCause(cause)
|
|
165
|
+
},
|
|
166
|
+
onSuccess(value) {
|
|
167
|
+
releaseUnsafe(permits)
|
|
168
|
+
return Effect.succeed(value)
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
return (effect) =>
|
|
172
|
+
Effect.uninterruptibleMask((restore) =>
|
|
173
|
+
Effect.flatMap(
|
|
174
|
+
restore(takePermits),
|
|
175
|
+
() => release(restore(effect))
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A `PartitionedSemaphore` is a concurrency primitive that can be used to
|
|
184
|
+
* control concurrent access to a resource across multiple partitions identified
|
|
185
|
+
* by keys.
|
|
186
|
+
*
|
|
187
|
+
* The total number of permits is shared across all partitions, with waiting
|
|
188
|
+
* permits equally distributed among partitions using a round-robin strategy.
|
|
189
|
+
*
|
|
190
|
+
* This is useful when you want to limit the total number of concurrent accesses
|
|
191
|
+
* to a resource, while still allowing for fair distribution of access across
|
|
192
|
+
* different partitions.
|
|
193
|
+
*
|
|
194
|
+
* @since 3.19.4
|
|
195
|
+
* @category Constructors
|
|
196
|
+
* @experimental
|
|
197
|
+
*/
|
|
198
|
+
export const make = <K = unknown>(options: {
|
|
199
|
+
readonly permits: number
|
|
200
|
+
}): Effect.Effect<PartitionedSemaphore<K>> => Effect.sync(() => makeUnsafe<K>(options))
|
package/src/Types.ts
CHANGED
|
@@ -261,7 +261,7 @@ export type Mutable<T> = {
|
|
|
261
261
|
*/
|
|
262
262
|
export type DeepMutable<T> = T extends ReadonlyMap<infer K, infer V> ? Map<DeepMutable<K>, DeepMutable<V>>
|
|
263
263
|
: T extends ReadonlySet<infer V> ? Set<DeepMutable<V>>
|
|
264
|
-
: T extends string | number | boolean | bigint | symbol ? T
|
|
264
|
+
: T extends string | number | boolean | bigint | symbol | Function ? T
|
|
265
265
|
: { -readonly [K in keyof T]: DeepMutable<T[K]> }
|
|
266
266
|
|
|
267
267
|
/**
|
package/src/index.ts
CHANGED
|
@@ -1090,6 +1090,12 @@ export * as Ordering from "./Ordering.js"
|
|
|
1090
1090
|
*/
|
|
1091
1091
|
export * as ParseResult from "./ParseResult.js"
|
|
1092
1092
|
|
|
1093
|
+
/**
|
|
1094
|
+
* @since 3.19.4
|
|
1095
|
+
* @experimental
|
|
1096
|
+
*/
|
|
1097
|
+
export * as PartitionedSemaphore from "./PartitionedSemaphore.js"
|
|
1098
|
+
|
|
1093
1099
|
/**
|
|
1094
1100
|
* @since 2.0.0
|
|
1095
1101
|
*/
|
|
@@ -194,10 +194,20 @@ const contOpSuccess = {
|
|
|
194
194
|
cont: core.FromIterator,
|
|
195
195
|
value: unknown
|
|
196
196
|
) => {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
while (true) {
|
|
198
|
+
const state = internalCall(() => cont.effect_instruction_i0.next(value))
|
|
199
|
+
if (state.done) {
|
|
200
|
+
return core.exitSucceed(state.value)
|
|
201
|
+
}
|
|
202
|
+
const primitive = yieldWrapGet(state.value)
|
|
203
|
+
if (!core.exitIsExit(primitive)) {
|
|
204
|
+
self.pushStack(cont)
|
|
205
|
+
return primitive
|
|
206
|
+
} else if (primitive._tag === "Failure") {
|
|
207
|
+
return primitive
|
|
208
|
+
}
|
|
209
|
+
value = primitive.value
|
|
210
|
+
}
|
|
201
211
|
}
|
|
202
212
|
}
|
|
203
213
|
|
package/src/internal/version.ts
CHANGED