Skip to content

Commit 9903dd2

Browse files
committed
[IMPL] useLock hook
1 parent 2a5d535 commit 9903dd2

4 files changed

Lines changed: 205 additions & 1 deletion

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useCallback, useEffect, useState } from "react"
2+
import { useLock } from "../../../../../../packages/react-tools/src";
3+
4+
/**
5+
The component uses _useLock_ hook to simulate a buffer write by a producer and read from a consumer.
6+
*/
7+
export const UseLock = () => {
8+
const [buffer, setBuffer] = useState<number[]>([]);
9+
const [lock, setLock] = useState<{ held: string[], pending: string[] }>({ held: [], pending: [] });
10+
const [messages, setMessages] = useState<{ consumer: string[], producer: string[] }>({
11+
consumer: [],
12+
producer: []
13+
});
14+
const do_something = useCallback((mode: "read" | "write") => {
15+
return new Promise<void>((res) => {
16+
setTimeout(() => {
17+
if (mode === "read") {
18+
let el: number | undefined;
19+
setBuffer(b => b.filter((_, index, arr) => {
20+
if (index !== arr.length - 1) {
21+
return true;
22+
} else {
23+
el = _;
24+
return false;
25+
}
26+
}))
27+
setMessages(m => ({...m, consumer: [...m.consumer, el !== undefined ? "Consumer has read " + el : "Consumer has read nothing"]}))
28+
} else {
29+
const n = Math.floor(Math.random() * 11);
30+
setBuffer(b => [...b, n]);
31+
setMessages(m => ({ ...m, producer: [...m.producer, "Producer has written " + n] }));
32+
}
33+
res();
34+
}, mode === "read" ? 1600 : 2400);
35+
});
36+
}, []);
37+
38+
const [createExclusiveLock, query] = useLock("resource", async () => {
39+
await do_something("write");
40+
});
41+
42+
const [createSharedLock] = useLock("resource", async () => {
43+
await do_something("read");
44+
}, { mode: "shared" });
45+
46+
useEffect(() => {
47+
const interval = setInterval(async () => {
48+
const n = Math.floor(Math.random() * 11);
49+
n > 6 ? createExclusiveLock() : createSharedLock();
50+
}, 700);
51+
return () => clearInterval(interval);
52+
}, [createExclusiveLock, createSharedLock]);
53+
54+
useEffect(() => {
55+
const interval = setInterval(async () => {
56+
const result = await query();
57+
const held = (result.held || []).map(el => el.name + " - " + el.mode);
58+
const pending = (result.pending || []).map(el => el.name + " - " + el.mode);
59+
setLock({ held, pending });
60+
}, 1000)
61+
return () => {
62+
clearInterval(interval)
63+
}
64+
}, [query]);
65+
66+
return <div>
67+
<div style={{ marginTop: 30, display: "grid", gridTemplateColumns: "auto auto auto auto auto", justifyContent: "center", gap: 50, overflow: 'auto', maxHeight: 400 }}>
68+
<div style={{ overflow: 'auto', maxHeight: 400 }}>
69+
<h3>Producer</h3>
70+
{
71+
messages.producer.map((m, index) => <p key={index}>{m}</p>)
72+
}
73+
</div>
74+
<div>
75+
<h3>Buffer</h3>
76+
<p>{JSON.stringify(buffer, null, 6)}</p>
77+
</div>
78+
<div style={{ display: "grid", gridTemplateColumns: "auto", justifyContent: "center", gap: 50, overflow: 'auto', maxHeight: 400 }}>
79+
<div>
80+
<h3>Consumer </h3>
81+
{
82+
messages.consumer.map((m, index) => <p key={index}>{m}</p>)
83+
}
84+
</div>
85+
</div>
86+
<div style={{ overflow: 'auto', maxHeight: 400 }}>
87+
<h4>Held Lock</h4>
88+
{
89+
lock.held.map((el, index) => <p key={index}>{el}</p>)
90+
}
91+
</div>
92+
<div style={{ overflow: 'auto', maxHeight: 400 }}>
93+
<h4>Pending Lock</h4>
94+
{
95+
lock.pending.map((el, index) => <p key={index}>{el}</p>)
96+
}
97+
</div>
98+
</div>
99+
</div>
100+
}

apps/react-tools-demo/src/components/hooks/useLock/useLock.md

Whitespace-only changes.

apps/react-tools-demo/src/markdown/useLock.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,110 @@
11
# useLock
22
Hook to use [Web Locks API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API).
33

4+
## Usage
5+
6+
```tsx
7+
export const UseLock = () => {
8+
const [buffer, setBuffer] = useState<number[]>([]);
9+
const [lock, setLock] = useState<{ held: string[], pending: string[] }>({ held: [], pending: [] });
10+
const [messages, setMessages] = useState<{ reader: string[], writer: string[] }>({
11+
reader: [],
12+
writer: []
13+
});
14+
const do_something = useCallback((mode: "read" | "write") => {
15+
return new Promise<void>((res) => {
16+
setTimeout(() => {
17+
if (mode === "read") {
18+
let el: number | undefined;
19+
setBuffer(b => b.filter((_, index, arr) => {
20+
if (index !== arr.length - 1) {
21+
return true;
22+
} else {
23+
el = _;
24+
return false;
25+
}
26+
}))
27+
setMessages(m => ({...m, reader: [...m.reader, el !== undefined ? "Reader has read " + el : "Reader has read nothing"]}))
28+
} else {
29+
const n = Math.floor(Math.random() * 11);
30+
setBuffer(b => [...b, n]);
31+
setMessages(m => ({ ...m, writer: [...m.writer, "Writer has written " + n] }));
32+
}
33+
res();
34+
}, mode === "read" ? 1000 : 2500);
35+
});
36+
}, []);
37+
38+
const [createExclusiveLock, query] = useLock("resource", async () => {
39+
await do_something("write");
40+
});
41+
42+
const [createSharedLock] = useLock("resource", async () => {
43+
await do_something("read");
44+
}, { mode: "shared" });
45+
46+
useEffect(() => {
47+
const interval = setInterval(async () => {
48+
const n = Math.floor(Math.random() * 11);
49+
n % 2 === 0 ? createExclusiveLock() : createSharedLock();
50+
}, 500);
51+
return () => clearInterval(interval);
52+
}, [createExclusiveLock, createSharedLock]);
53+
54+
useEffect(() => {
55+
const interval = setInterval(async () => {
56+
const result = await query();
57+
const held = (result.held || []).map(el => el.name + " - " + el.mode);
58+
const pending = (result.pending || []).map(el => el.name + " - " + el.mode);
59+
setLock({ held, pending });
60+
})
61+
return () => {
62+
clearInterval(interval)
63+
}
64+
}, [query]);
65+
66+
return <div>
67+
<div style={{ marginTop: 30, display: "grid", gridTemplateColumns: "auto auto auto auto", justifyContent: "center", gap: 50, overflow: 'auto', maxHeight: 400 }}>
68+
<div style={{ overflow: 'auto', maxHeight: 400 }}>
69+
<h3>Writer</h3>
70+
{
71+
messages.writer.map((m, index) => <p key={index}>{m}</p>)
72+
}
73+
</div>
74+
<div>
75+
<h3>Buffer</h3>
76+
<p>{JSON.stringify(buffer, null, 6)}</p>
77+
</div>
78+
<div style={{ display: "grid", gridTemplateColumns: "auto", justifyContent: "center", gap: 50, overflow: 'auto', maxHeight: 400 }}>
79+
<div>
80+
<h3>Reader </h3>
81+
{
82+
messages.reader.map((m, index) => <p key={index}>{m}</p>)
83+
}
84+
</div>
85+
</div>
86+
<div style={{ display: "grid", gridTemplateRows: "auto auto", justifyContent: "center", gap: 50, overflow: 'auto', maxHeight: 400 }}>
87+
<div>
88+
<h4>Held Lock</h4>
89+
{
90+
lock.held.map((el, index) => <p key={index}>{el}</p>)
91+
}
92+
</div>
93+
<div>
94+
<h4>Pending Lock</h4>
95+
{
96+
lock.pending.map((el, index) => <p key={index}>{el}</p>)
97+
}
98+
</div>
99+
</div>
100+
</div>
101+
</div>
102+
}
103+
```
104+
105+
> The component uses _useLock_ hook to simulate a buffer write by a writer and read from a reader.
106+
107+
4108
## API
5109

6110
```tsx

packages/react-tools/src/hooks/useLock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const useLock = <T>(name?: string, cb?: LockGrantedCallback, opts?: LockO
1616
const callback = currCb ?? cb;
1717
const options = currOpts ?? opts;
1818
if (!n || !callback) {
19-
throw Error("useLock acquire function parameters missed.");
19+
return Promise.resolve() as Promise<T>;
2020
}
2121
if (options) {
2222
return navigator.locks.request(n, options, callback) as Promise<T>

0 commit comments

Comments
 (0)