dirlock.bend
cswap-bend-cli@0.1.0.0 · 101 lines · 18 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/fs.bend as Fs
3import cswap-bend-cli@0.1.0.0/act.bend as Act
4import cswap-bend-cli@0.1.0.0/clock.bend as Clock
5import cswap-bend-cli@0.1.0.0/fs/write.bend as FsW
6
7
8
9
10
11
12
13
14def ActM(-A: Type) -> Type:
15 Act.of(A)
16
17def ActM.pure(-A: Type, x: A) -> Act.of(A):
18 Act.pure(A, x)
19
20def ActM.bind(-A: Type, -B: Type, m: Act.of(A), f: A -> Act.of(B)) -> Act.of(B):
21 Act.bind(A, B, m, f)
22
23
24def Dirlock.mtime(+path: String) -> IO(Result<&1, &1, U32 & String, U32>):
25 import "./effs/mtime.c"
26 import "./effs/mtime.js"
27
28def poll_ms() -> U32:
29 250
30
31def mkdir_ok(r: Fs.Res(String)) -> Fs.Res(Bool):
32 match r:
33 case Fail{e}:
34 Done{False{}}
35 case Done{out}:
36 Done{True{}}
37
38
39def take_once(+dir: String) -> Act.of(Bool):
40 IO.bind(Fs.Res(String), Fs.Res(Bool), Fs.tool("/bin/mkdir", [dir]), r => IO.pure(Fs.Res(Bool), mkdir_ok(r)))
41
42def stale_of(m: Result<&1, &1, U32 & String, U32>, +now: U32, +staleness: U32) -> Bool:
43 match m:
44 case Fail{e}:
45 False{}
46 case Done{t}:
47 U32.is_gt(now, (t + staleness : U32))
48
49def is_stale(+dir: String, +staleness: U32) -> Act.of(Bool):
50 do ActM<Bool>:
51 now : U32 <- Act.lift(U32, Clock.now_s())
52 m : Result<&1, &1, U32 & String, U32> <- Act.lift(Result<&1, &1, U32 & String, U32>, Dirlock.mtime(dir))
53 return stale_of(m, now, staleness)
54
55def reclaim(+stale: Bool, +dir: String) -> Act.of(Unit):
56 Act.cond(Unit, stale, Fs.tool_unit("/bin/rmdir", [dir]), Act.pure(Unit, Unit{}))
57
58def basename(+p: String) -> String:
59 Maybe.default(&2, String, List.last(&2, String, String.split(p, '/')), p)
60
61def busy_msg(+dir: String) -> String:
62 "Could not acquire " ++ basename(dir) ++ " — Claude Code appears to be refreshing credentials. Retry in a few seconds."
63
64
65def retake(+dir: String, +staleness: U32) -> Act.of(Bool):
66 do ActM<Bool>:
67 st : Bool <- is_stale(dir, staleness)
68 reclaim(st, dir)
69 Act.lift(Unit, IO.sleep(poll_ms()))
70 take_once(dir)
71
72
73def wait(+fuel: Nat, +dir: String, +staleness: U32, +got: Bool) -> Act.of(Unit):
74 match fuel got:
75 case _ True{}:
76 Act.pure(Unit, Unit{})
77 case 0n False{}:
78 Act.fail(Unit, 4, busy_msg(dir))
79 case 1n++p False{}:
80 do ActM<Unit>:
81 again : Bool <- retake(dir, staleness)
82 wait(p, dir, staleness, again)
83
84def tries(+timeout_ms: U32) -> Nat:
85 U32.to_nat((timeout_ms / poll_ms() : U32))
86
87
88def take(+dir: String, +timeout_ms: U32, +staleness: U32) -> Act.of(Unit):
89 do ActM<Unit>:
90 FsW.mkdir_p(Fs.dirname(dir))
91 first : Bool <- take_once(dir)
92 wait(tries(timeout_ms), dir, staleness, first)
93
94def release(+dir: String) -> Act.of(Unit):
95 IO.bind(Fs.Res(Unit), Fs.Res(Unit), Fs.tool_unit("/bin/rmdir", [dir]), r => IO.pure(Fs.Res(Unit), Done{Unit{}}))
96
97
98def with_dirlock(-A: Type, +dir: String, +timeout_ms: U32, +staleness: U32, act: Act.of(A)) -> Act.of(A):
99 do ActM<A>:
100 take(dir, timeout_ms, staleness)
101 Act.finally(A, act, release(dir))