I have bunch of questions that I separately discussed with @Andrew Brown , @Luke Wagner and @Till Schneidereit. Adding @Conrad Watt
I realize before WASIp3 it's probably too early to know what the interaction with threads would look like. I don't expect authoritative answers at this point, just discussion.
When we introduce real multi-cpu parallelism, I think we will become distributed system. Even if the individual component is single threaded. Because now events could happen in random order in different components. The mutation of the component state in the linear memory could happen in random order relative to events in other components.
Distributed system
Could we have mix of MT and ST components in the same dependency graph ?
Scheduler
Async interaction
Is WASI resource handle thread local ?
Is there re-used identity of the caller thread on component entry ?
Perhaps some of this needs to become part of components specification later.
Thanks!
some very quick hot takes to seed the conversation
- what is the cross component memory/consistency/coherency model ?
Since the component model doesn't know how each component is implemented, I think it's hard to make fine-grained guarantees at the level of the component model spec. Different underlying runtimes will give different consistency guarantees. Coarse-grained CM-level guarantees like "no concurrent reentry" might be possible though.
- should crossing the component boundary introduce read or write memory fence on the component state/memory ?
Again, I think this has to be a decision made in the semantics of the underlying runtime of each component - the guarantees provided by fences are very sensitive to other properties of the runtime (e.g. their compilation schemes for atomics). The CM level has no visibility on whether the component is "really" concurrent, faking it with green threads, rolling a virtual dice to pretend it's exhibiting relaxed caching behaviours etc...
- should we start thinking about state coherency it in terms "transaction isolation level" ?
- should individual methods specify in WIT how they join a "transaction" ?
This is one possible abstraction the CM could provide - I'd have to defer to @Luke Wagner on whether it's preferred over alternatives
- should wasmtime implement distributed deadlock prevention ?
- maybe we could provide advice on how to lock WIT resources across multiple calls to the same component
I'd expect the "guest" runtime (written in Wasm) to have this responsibility rather than the "host" Wasm runtime, but maybe I'm misunderstanding what it means to "lock" WIT resources
- is component marked as STA or MTA like in COM+ ?
- are calls to STA components serialized ?
This sounds like discussions I've had with @Luke Wagner and @Andrew Brown about the role of shared
(related - some mechanism for reentrancy prevention) in the CM
- is there fairness to the host and guest scheduler ? Which kind of fairness ?
- component priorities ? Or thread priorities ?
- host thread pool: inversion of priorities, starvation by blocking ?
- quota on green threads ? quota on physical threads ?
No specific opinions. One guiding idea - we've generally thought of the internal use of threads by a component as not being explicitly visible to the CM-level semantics. Resource exhaustion of threads by one greedy component could be thought of as similar to existing concerns about resource exhaustion of memory etc.
I don't currently have informed opinions about remaining Qs, but interested in discussion.
I should also add WRT my first answer that the (mostly) shared-nothing nature of the component model protects against a lot of cross-component weirdness from relaxed memory concurrency
canonopt
s of the canon lift
and canon lower
definitions that each component uses to lift/lower its exports/imports, specifically by the presence/absence of the async
(defined by wasip3) and shared
(defined later) options (where shared
implies async
). The key will be continuing to ensure, as we've done in wasip3, that all lift/lower combinations compose with reasonable behavior. Also, to wit, with cross-component recursion disallowed by default (with some TBD recursive
attribute on the function type to opt-in to it), components are more like Application STA in COM, except that the reentrancy rules apply to both STA and MTA.Lots of other good questions that I expect we'll need to discuss in more detail as we get into integrating shared-everything-threads into the component model.
Conrad Watt said:
I'm misunderstanding what it means to "lock" WIT resources
Yeah, it's not WIT resource related. I got that confused.
I was thinking about locking primitives in traditional OS kernel sense. Which may or may not be related to WIT resource. In guest memory "lock" like mutex would be represented by some state of the memory. But there is no OS kernel object, linked to host thread. No aborted threads, no abandoned mutexes, right ? When anything traps, all threads trap. And all caller components trap ?
I was also thinking about deadlocks, as seen by the host. When 2 different "client" instances call into same long lived "server" instance and start locking stuff inside in A->B and B->A order.
Conrad Watt said:
I should also add WRT my first answer that the (mostly) shared-nothing nature of the component model protects against a lot of cross-component weirdness from relaxed memory concurrency
There is internal state of long lived component instance.
The shared linear memory of the guest is enough to break that "shared nothing" assumption, from consistency perspective, no ?
I'm talking about multiple component instances (each running different physical thread/core) talking to each other and the non-coherent internal states it creates between them.
Well known transaction coordinator WIT API is probably good solution.
Another good solution could be coherent monotonic clock/timestamp on which other transaction protocols could be created.
Last updated: Dec 23 2024 at 13:07 UTC