Skip to content

Commit f24c546

Browse files
authored
Add a simple mutex example to the overview (WebAssembly#50)
1 parent 536fd6b commit f24c546

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

proposals/threads/Overview.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,107 @@ This proposal adds a new shared linear memory type and some new operations for
77
atomic memory access. The responsibility of creating and joining threads is
88
deferred to the embedder.
99

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+
10111
## Agents and Agent Clusters
11112

12113
An *agent* is the execution context for a WebAssembly module. For the web

0 commit comments

Comments
 (0)