tc39 / proposal-structs

JavaScript Structs: Fixed Layout Objects
http://tc39.es/proposal-structs/
624 stars 11 forks source link

Suggestion: simpler shared functions #56

Open dead-claudia opened 1 month ago

dead-claudia commented 1 month ago

Shared functions (and variables) could be the solution to the problem of methods, and if done correctly, could avoid most of the complexity of binding stuff to realms. They'd be heavily restricted, of course.

Things shared variables and shared struct properties can be set to:

When the shadow realms proposal comes around, those can also be added to this list, provided its methods employ the requisite locking to ensure it only runs on one calling agent at a time.

This should hopefully bring the flexibility needed. And with this API, I also propose the following modified API:

declare namespace Atomics {
    // same as before, including other new extensions...

    // New structs, all registered with internal names in reality
    export registered internal struct UnlockToken {
        constructor()
        get locked(): boolean
        unlock(): undefined
        [Symbol.dispose](): undefined
    }

    export registered internal struct Mutex {
        constructor()
        lock(token?: UnlockToken): UnlockToken
        lockIfAvailable(timeout: number, token?: UnlockToken): UnlockToken
    }

    export registered internal struct Condition {
        constructor()
        wait(token: UnlockToken): undefined
        waitFor(token: UnlockToken, timeout: number, predicate?: () => boolean): undefined
        notify(count?: number): undefined
    }
}

An example counter object might look like this:

shared struct Counter {
    value = 0
    inc() {
        let value = Atomics.load(this, "value")
        while (true) {
            let prev = Atomics.compareExchange(this, "value", value, value + 1)
            if (prev === value) return value + 1
            value = prev
        }
    }
}
StructRegistry.add(internalToken, Atomics.Mutex)

The code from https://github.com/tc39/proposal-structs/blob/main/ATTACHING-BEHAVIOR.md#coordinating-identity-continuity-among-workers could look like this instead:

// Inside worker A:
shared struct SharedThing {
  x;
  y;

  foo() {
    // Dynamically scoped to the caller's global, thus it always "just works"
    console.log("do a foo");
  }
};
let thing1 = new SharedThing;

workerB.postMessage(thing1);

// Inside worker B:
onmessage = (thing) => {
  // undefined is not a function, and `SharedThing` didn't even need to be defined!
  thing.foo();
};