@@ -7,6 +7,107 @@ This proposal adds a new shared linear memory type and some new operations for
7
7
atomic memory access. The responsibility of creating and joining threads is
8
8
deferred to the embedder.
9
9
10
+ ## Example
11
+
12
+ Here is an example of a naive mutex implemented in WebAssembly. It uses one
13
+ memory location (address 0) to store the state of the lock. If its value is
14
+ 0, the mutex is unlocked. If its value is 1, the mutex is locked.
15
+
16
+ ```
17
+ (module
18
+ ;; Import 1 page (64Kib) of shared memory.
19
+ (import "env" "memory" (memory 1 1 shared))
20
+
21
+ ;; Lock a mutex at address 0. 0 => unlocked, 1 => locked.
22
+ (func (export "lockMutex")
23
+ (block $done
24
+ (loop $retry
25
+ ;; Attempt to grab the mutex. The cmpxchg operation atomically
26
+ ;; does the following:
27
+ ;; - Loads the value at address 0.
28
+ ;; - If it is 0 (unlocked), set it to 1 (locked).
29
+ ;; - Return the originally loaded value.
30
+ (i32.atomic.rmw.cmpxchg
31
+ (i32.const 0) ;; lock address
32
+ (i32.const 0) ;; expected value (0 => unlocked)
33
+ (i32.const 1)) ;; replacement value (1 => locked)
34
+
35
+ ;; The top of the stack is the originally loaded value.
36
+ ;; If it is 0, this means we acquired the mutex and can exit
37
+ ;; the loop.
38
+ (i32.eqz)
39
+ (br_if $done)
40
+ (drop)
41
+
42
+ ;; Wait for the other agent to finish with mutex.
43
+ (i32.wait
44
+ (i32.const 0) ;; lock address
45
+ (i32.const 1) ;; expected value ( 1=> locked)
46
+ (f64.const inf)) ;; infinite timeout
47
+
48
+ ;; Try to acquire the lock again.
49
+ (br $retry)
50
+ )
51
+ )
52
+ )
53
+
54
+ ;; Unlock a mutex at address 0.
55
+ (func (export "unlockMutex")
56
+ ;; Unlock the mutex.
57
+ (i32.atomic.store
58
+ (i32.const 0) ;; lock address
59
+ (i32.const 0)) ;; 0 => unlocked
60
+
61
+ ;; Wake one agent that is waiting on this lock.
62
+ (wake
63
+ (i32.const 0) ;; lock address
64
+ (i32.const 1)) ;; wake 1 waiter
65
+ )
66
+
67
+ (func (export "tryLockMutex") ... )
68
+ )
69
+ ```
70
+
71
+ Here is an example of using this module in a JavaScript host.
72
+
73
+ ``` JavaScript
74
+ // / main.js ///
75
+ let moduleBytes = ... ; // An ArrayBuffer containing the WebAssembly module above.
76
+ let memory = new WebAssembly.Memory ({initial: 1 , maximum: 1 , shared: true });
77
+ let worker = new Worker (' worker.js' );
78
+
79
+ // Send the shared memory to the worker.
80
+ worker .postMessage (memory);
81
+
82
+ let imports = {env: {memory: memory}};
83
+ let module = WebAssembly .instantiate (moduleBytes, imports).then (
84
+ ({instance}) => {
85
+ // Blocking on the main thread is not allowed, so we can't
86
+ // call lockMutex.
87
+ if (instance .exports .tryLockMutex ()) {
88
+ ...
89
+ instance .exports .unlockMutex ();
90
+ }
91
+ });
92
+
93
+
94
+ // / worker.js ///
95
+ let moduleBytes = ... ; // An ArrayBuffer containing the WebAssembly module above.
96
+
97
+ // Listen for messages from the main thread.
98
+ onmessage = function (e ) {
99
+ let memory = e .data ;
100
+ let imports = {env: {memory: memory}};
101
+ let module = WebAssembly .instantiate (moduleBytes, imports).then (
102
+ ({instance}) => {
103
+ // Blocking on a Worker thread is allowed.
104
+ instance .exports .lockMutex ();
105
+ ...
106
+ instance .exports .unlockMutex ();
107
+ });
108
+ };
109
+ ```
110
+
10
111
## Agents and Agent Clusters
11
112
12
113
An * agent* is the execution context for a WebAssembly module. For the web
0 commit comments