Simpler treatment of Refs

This commit is contained in:
Tony Garnock-Jones 2021-02-23 15:53:42 +01:00
parent ec5146a8a7
commit 77fbab89dd
2 changed files with 26 additions and 54 deletions

View File

@ -5,51 +5,20 @@ export type Handle = number;
export type ExitReason = null | { ok: true } | { ok: false, err: Error };
export type LocalAction = (t: Turn) => void;
export const assert = Symbol('assert');
export const retract = Symbol('retract');
export const message = Symbol('message');
export const sync = Symbol('sync');
export interface Entity {
[assert]?(turn: Turn, assertion: Assertion, handle: Handle): void;
[retract]?(turn: Turn, handle: Handle): void;
[message]?(turn: Turn, body: Assertion): void;
[sync]?(turn: Turn, peer: Entity): void;
assert?(turn: Turn, assertion: Assertion, handle: Handle): void;
retract?(turn: Turn, handle: Handle): void;
message?(turn: Turn, body: Assertion): void;
sync?(turn: Turn, peer: Ref): void;
}
export class Ref implements Entity {
readonly relay: Actor;
readonly target: Entity;
constructor(relay: Actor, target: Entity) {
this.relay = relay;
this.target = target;
}
[assert](turn: Turn, assertion: Assertion, handle: Handle): void {
this.target[assert]?.(turn, assertion, handle);
}
[retract](turn: Turn, handle: Handle): void {
this.target[retract]?.(turn, handle);
}
[message](turn: Turn, body: Assertion): void {
this.target[message]?.(turn, body);
}
[sync](turn: Turn, peer: Ref) {
this.target[sync] ? this.target[sync]!(turn, peer) : turn.message(peer, true);
}
}
type OutboundMap = Map<Handle, Ref>;
export interface Ref { readonly relay: Actor, readonly target: Entity };
export class Actor {
readonly outbound: OutboundMap;
readonly outbound: Map<Handle, Ref>;
exitReason: ExitReason = null;
constructor(initialAssertions: OutboundMap = new Map()) {
constructor(initialAssertions = new Map<Handle, Ref>()) {
this.outbound = initialAssertions;
}
@ -76,7 +45,7 @@ let nextHandle = 0;
export class Turn {
readonly actor: Actor;
readonly queues: Map<Actor, LocalAction[]> = new Map();
readonly queues = new Map<Actor, LocalAction[]>();
static for(actor: Actor, f: LocalAction): void {
const t = new Turn(actor);
@ -89,12 +58,12 @@ export class Turn {
}
ref(e: Entity): Ref {
return (e instanceof Ref) ? e : new Ref(this.actor, e);
return { relay: this.actor, target: e };
}
spawn(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): void {
this.enqueue(this.actor, () => {
const newOutbound: OutboundMap = new Map();
const newOutbound = new Map<Handle, Ref>();
initialAssertions.forEach(key => {
newOutbound.set(key, this.actor.outbound.get(key)!); // we trust initialAssertions
this.actor.outbound.delete(key);
@ -112,7 +81,7 @@ export class Turn {
const h = nextHandle++;
this.enqueue(ref.relay, t => {
this.actor.outbound.set(h, ref);
ref[assert]?.(t, assertion, h);
ref.target.assert?.(t, assertion, h);
});
return h;
}
@ -130,17 +99,20 @@ export class Turn {
_retract(ref: Ref, handle: Handle): void {
this.enqueue(ref.relay, t => {
this.actor.outbound.delete(handle);
ref[retract]?.(t, handle);
ref.target.retract?.(t, handle);
});
}
sync(ref: Ref): Promise<Turn> {
return new Promise(resolve =>
this.enqueue(ref.relay, t => ref[sync]?.(t, this.ref({ [message]: resolve }))));
return new Promise(resolve => {
const k = this.ref({ message: resolve });
this.enqueue(ref.relay, t =>
ref.target.sync ? ref.target.sync!(t, k) : t.message(k, true));
});
}
message(ref: Ref, assertion: Assertion): void {
this.enqueue(ref.relay, t => ref[message]?.(t, assertion));
this.enqueue(ref.relay, t => ref.target.message?.(t, assertion));
}
enqueue(relay: Actor, a: LocalAction): void {

16
main.ts
View File

@ -1,4 +1,4 @@
import { Actor, Assertion, Entity, Handle, Ref, Turn, assert, message, retract } from './actor.js';
import { Actor, Assertion, Entity, Handle, Ref, Turn } from './actor.js';
import { Dictionary, IdentityMap, is, Record } from 'preserves';
import { Bag, ChangeDescription } from './bag';
@ -9,7 +9,7 @@ class Dataspace implements Entity {
readonly assertions = new Bag<Ref>();
readonly subscriptions: Dictionary<Map<Ref, Dictionary<Handle>>> = new Dictionary();
[assert](turn: Turn, rec: Assertion, handle: Handle): void {
assert(turn: Turn, rec: Assertion, handle: Handle): void {
if (!Record.isRecord<Ref>(rec)) return;
this.handleMap.set(handle, rec);
if (this.assertions.change(rec, +1) !== ChangeDescription.ABSENT_TO_PRESENT) return;
@ -27,7 +27,7 @@ class Dataspace implements Entity {
seen.has(rec) || seen.set(rec, turn.assert(peer, rec)));
}
[retract](turn: Turn, upstreamHandle: Handle): void {
retract(turn: Turn, upstreamHandle: Handle): void {
const rec = this.handleMap.get(upstreamHandle);
if (rec === void 0) return;
this.handleMap.delete(upstreamHandle);
@ -46,7 +46,7 @@ class Dataspace implements Entity {
}
}
[message](turn: Turn, rec: Assertion): void {
message(turn: Turn, rec: Assertion): void {
if (!Record.isRecord<Ref>(rec)) return;
this.subscriptions.get(rec.label)?.forEach((_seen, peer) => turn.message(peer, rec));
}
@ -70,7 +70,7 @@ Turn.for(new Actor(), async (t: Turn) => {
}
setValue(t, 0);
t.assert(ds, Observe(SetBox.constructorInfo.label, t.ref({
[message](t: Turn, [newValue]: [number]): void {
message(t: Turn, [newValue]: [number]): void {
// console.log(`Box: got ${newValue}`);
if (newValue % 25000 === 0) {
const endTime = Date.now();
@ -91,7 +91,7 @@ Turn.for(new Actor(), async (t: Turn) => {
console.log('Spawning Client');
let count = 0;
t.assert(ds, Observe(BoxState.constructorInfo.label, t.ref({
[assert](t: Turn, [currentValue]: [number]): void {
assert(t: Turn, [currentValue]: [number]): void {
// console.log(`Client: got ${currentValue}`);
if (currentValue === LIMIT) {
console.log(`Client: quitting at limit`);
@ -102,8 +102,8 @@ Turn.for(new Actor(), async (t: Turn) => {
}
})));
t.assert(ds, Observe(BoxState.constructorInfo.label, t.ref({
[assert](_t: Turn, _assertion: Assertion): void { count++; },
[retract](t: Turn, _handle: Handle) {
assert(_t: Turn, _assertion: Assertion): void { count++; },
retract(t: Turn, _handle: Handle) {
if (--count === 0) {
console.log('Client: detected box termination');
t.quit();