Skip to content

Commit 4be694d

Browse files
committed
feat(fiber): fiber now can switch between multiple contexts based on instruction count
1 parent 3ac6dd3 commit 4be694d

File tree

5 files changed

+100
-47
lines changed

5 files changed

+100
-47
lines changed

src/internals/FiberContext.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ export class FiberContext<E, A> implements ICancellable, IFiber<E, A> {
5151
*/
5252
public static evaluateWith<E, A>(
5353
io: IO<E, A>,
54-
scheduler: IScheduler
54+
scheduler: IScheduler,
55+
maxInstructionCount: number = Number.MAX_SAFE_INTEGER
5556
): FiberContext<E, A> {
56-
return new FiberContext(io.asInstruction, scheduler)
57+
return new FiberContext(io.asInstruction, scheduler, maxInstructionCount)
5758
}
5859

5960
private static dispatchResult<E, A>(
@@ -74,7 +75,7 @@ export class FiberContext<E, A> implements ICancellable, IFiber<E, A> {
7475
private constructor(
7576
instruction: Instruction,
7677
private readonly scheduler: IScheduler,
77-
private readonly maxInstructionCount: number = Number.MAX_SAFE_INTEGER
78+
private readonly maxInstructionCount: number
7879
) {
7980
this.stackA.push(instruction)
8081
this.init()
@@ -196,7 +197,7 @@ export class FiberContext<E, A> implements ICancellable, IFiber<E, A> {
196197
// A new context is created so that computation from that instruction can happen separately.
197198
// and then join back into the current context.
198199
// Using the same stack will corrupt it completely.
199-
const nContext = new FiberContext(j.i0, this.scheduler)
200+
const nContext = new FiberContext(j.i0, this.scheduler, j.i1)
200201
this.cancellationList.push(nContext)
201202
data = nContext
202203
break

src/main/FIO.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,11 @@ export class FIO<E1 = unknown, A1 = unknown, R1 = NoEnv> {
280280
/**
281281
* Creates a new [[Fiber]] to run the given [[IO]].
282282
*/
283-
public static fork<E1, A1>(io: IO<E1, A1>): UIO<IFiber<E1, A1>> {
284-
return new FIO(Tag.Fork, io)
283+
public static fork<E1, A1>(
284+
io: IO<E1, A1>,
285+
maxInstructionCount: number = Number.MAX_SAFE_INTEGER
286+
): UIO<IFiber<E1, A1>> {
287+
return new FIO(Tag.Fork, io, maxInstructionCount)
285288
}
286289

287290
/**

src/main/Instructions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export interface INever {
6969
}
7070
export interface IFork {
7171
i0: Instruction
72+
i1: number // TODO: Instead of number pass runtime.
7273
tag: Tag.Fork
7374
}
7475
export interface IAccess<X = unknown, Y = unknown> {

src/main/Queue.ts

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import {LinkedListNode, List} from 'standard-data-structures'
2-
3-
import {PureMutableList} from '../internals/PureMutableList'
1+
import {DoublyLinkedList, List, Option} from 'standard-data-structures'
42

53
import {Await} from './Await'
64
import {FIO, NoEnv, UIO} from './FIO'
@@ -14,41 +12,44 @@ export class Queue<A = never> {
1412
* Returns the Queue as an array
1513
*/
1614
public get asArray(): UIO<A[]> {
17-
return this.Q.asArray
15+
return UIO(() => this.Q.asArray)
1816
}
1917

2018
/**
2119
* Returns the number of elements in the queue
2220
*/
2321
public get length(): UIO<number> {
24-
return this.Q.length
22+
return UIO(() => this.Q.length)
2523
}
2624

2725
/**
2826
* Pulls an item from the queue
2927
*/
3028
public get take(): UIO<A> {
31-
return this.Q.shift.chain(sz =>
32-
sz
33-
.map(FIO.of)
34-
.getOrElse(
35-
FIO.flatten(
36-
Await.of<never, A>().chain(
37-
FIO.encase(await => this.T.add(await).and(await.get))
38-
)
39-
)
29+
return FIO.flattenM(() => {
30+
const sz = this.Q.shift()
31+
32+
if (Option.isSome(sz)) {
33+
return FIO.of(sz.value)
34+
}
35+
36+
return FIO.flatten(
37+
Await.of<never, A>().chain(
38+
FIO.encase(await => {
39+
this.T.add(await)
40+
41+
return await.get
42+
})
4043
)
41-
)
44+
)
45+
})
4246
}
4347

4448
/**
4549
* Creates a new bounded Queue
4650
*/
4751
public static bounded<A>(capacity: number): UIO<Queue<A>> {
48-
return PureMutableList.of<A>().zipWith(
49-
PureMutableList.of<Await<never, A>>(),
50-
(Q, T) => new Queue(capacity, Q, T)
51-
)
52+
return UIO(() => new Queue(capacity))
5253
}
5354

5455
/**
@@ -58,24 +59,40 @@ export class Queue<A = never> {
5859
return Queue.bounded(Number.MAX_SAFE_INTEGER)
5960
}
6061

61-
private constructor(
62-
public readonly capacity: number,
63-
private readonly Q: PureMutableList<A>,
64-
private readonly T: PureMutableList<Await<never, A>>
65-
) {}
62+
private readonly Q = DoublyLinkedList.of<A>()
63+
private readonly T = DoublyLinkedList.of<Await<never, A>>()
64+
private constructor(public readonly capacity: number) {}
6665

6766
/**
6867
* Inserts an item into the queue
6968
*/
70-
public offer(a: A): UIO<LinkedListNode<A>> {
71-
return this.Q.add(a).tap(_ => this.setAwaited(_.value))
69+
public offer(a: A): UIO<void> {
70+
return FIO.flattenM(
71+
(): UIO<void> => {
72+
if (this.T.length === 0) {
73+
this.Q.add(a)
74+
75+
return FIO.void()
76+
}
77+
78+
const io = new Array<UIO<boolean>>()
79+
while (this.T.length !== 0) {
80+
const item = this.T.shift()
81+
if (Option.isSome(item)) {
82+
io.push(item.value.set(FIO.of(a)))
83+
}
84+
}
85+
86+
return FIO.seq(io).void
87+
}
88+
)
7289
}
7390

7491
/**
7592
* Adds all the provided items into the queue
7693
*/
77-
public offerAll(...a: A[]): UIO<Array<LinkedListNode<A>>> {
78-
return FIO.seq(a.map(_ => this.offer(_)))
94+
public offerAll(...a: A[]): UIO<void> {
95+
return FIO.seq(a.map(_ => this.offer(_))).void
7996
}
8097

8198
/**
@@ -92,19 +109,6 @@ export class Queue<A = never> {
92109
return itar(0, List.empty<A>()).map(_ => _.asArray)
93110
}
94111

95-
private setAwaited(value: A): UIO<boolean[]> {
96-
const itar = (list: List<UIO<boolean>>): UIO<List<UIO<boolean>>> =>
97-
this.T.shift.chain(_ =>
98-
_.map(AWT => itar(list.prepend(AWT.set(FIO.of(value))))).getOrElse(
99-
FIO.of(list)
100-
)
101-
)
102-
103-
return itar(List.empty<UIO<boolean>>())
104-
.tap(_ => (!_.isEmpty ? this.Q.shift : FIO.void()))
105-
.chain(_ => FIO.seq(_.asArray))
106-
}
107-
108112
/**
109113
* Converts a queue into a [[FStream]]
110114
*/

test/FiberContext.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {testScheduler} from 'ts-scheduler/test'
44

55
import {FiberContext} from '../src/internals/FiberContext'
66
import {FIO} from '../src/main/FIO'
7+
import {FStream} from '../src/main/FStream'
78
import {testRuntime} from '../src/runtimes/TestRuntime'
89

910
import {Counter} from './internals/Counter'
@@ -196,4 +197,47 @@ describe('FiberContext', () => {
196197
it.skip('should return none')
197198
})
198199
})
200+
201+
context('instruction count is reduced', () => {
202+
it.skip('should cooperatively merge two streams', () => {
203+
const list = new Array<number>()
204+
const insert = FIO.encase((_: number) => void list.push(_))
205+
206+
const scheduler = testScheduler()
207+
FiberContext.evaluateWith(
208+
FStream.range(101, 103)
209+
.merge(FStream.range(901, 903))
210+
.mapM(insert).drain,
211+
scheduler,
212+
5
213+
)
214+
215+
scheduler.run()
216+
217+
const expected = [901, 101, 102, 103, 902, 903]
218+
assert.deepStrictEqual(list, expected)
219+
})
220+
221+
it('should switch between multiple contexts', () => {
222+
const MAX_INSTRUCTION_COUNT = 5
223+
const scheduler = testScheduler()
224+
const actual = new Array<number>()
225+
const insert = FIO.encase((_: number) => void actual.push(_))
226+
const longIO = FIO.of(1)
227+
.and(FIO.of(2))
228+
.and(FIO.of(3))
229+
.and(FIO.of(4))
230+
.and(FIO.of(5))
231+
.chain(insert)
232+
const shortIO = FIO.of(1000).chain(insert)
233+
234+
FiberContext.evaluateWith(longIO, scheduler, MAX_INSTRUCTION_COUNT)
235+
FiberContext.evaluateWith(shortIO, scheduler, 5)
236+
237+
scheduler.run()
238+
239+
const expected = [1000, 5]
240+
assert.deepStrictEqual(actual, expected)
241+
})
242+
})
199243
})

0 commit comments

Comments
 (0)