src/DbLock.bend
mylsm-lsm-store@0.5.0.0 · 118 lines · 0 definitions
1import Base
2import mylsm-lsm-store@0.5.0.0/src/DurableError.bend as DurableError
3import mylsm-lsm-store@0.5.0.0/src/DbLockPolicy.bend as Policy
4
5# Owns the open LOCK file until release.
6type Lock is Type:
7 Lock{file: File}
8
9# Builds the stable LOCK path for a database directory.
10def lock_path(path: String) -> String:
11 path ++ "/LOCK"
12
13def DbLock.try_lock(file: File) -> IO(File & Result<&1, &1, U32 & String, Unit>):
14 import "./effs/db_lock.c"
15 import "./effs/db_lock.js"
16
17def DbLock.try_unlock(file: File) -> IO(File & Result<&1, &1, U32 & String, Unit>):
18 import "./effs/db_unlock.c"
19 import "./effs/db_unlock.js"
20
21# Classifies a failure to open the LOCK file.
22def open_failed(error: U32 & String, operation: String, path: String) -> IO(Result<&1, &1, DurableError.Error, Lock>):
23 match error:
24 case (code, message):
25 IO.pure(Result<&1, &1, DurableError.Error, Lock>,
26 Fail{Policy.classify_lock_error(code, message, operation, path)})
27
28# Closes the file and classifies a failed lock attempt.
29def lock_failed(
30 file: File,
31 error: U32 & String,
32 operation: String,
33 path: String
34) -> IO(Result<&1, &1, DurableError.Error, Lock>):
35 match error:
36 case (code, message):
37 do IO<Result<&1, &1, DurableError.Error, Lock>>:
38 _closed : Unit <- File.close(file)
39 return Fail{Policy.classify_lock_error(code, message, operation, path)}
40
41# Returns an owned lock on success or closes the failed handle.
42def lock_result(
43 pair: File & Result<&1, &1, U32 & String, Unit>,
44 operation: String,
45 path: String
46) -> IO(Result<&1, &1, DurableError.Error, Lock>):
47 match pair:
48 case (file, Fail{error}):
49 lock_failed(file, error, operation, path)
50 case (file, Done{Unit{}}):
51 IO.pure(Result<&1, &1, DurableError.Error, Lock>, Done{Lock{file}})
52
53# Attempts the nonblocking OS lock on the open file.
54def lock_file(file: File, operation: String, path: String) -> IO(Result<&1, &1, DurableError.Error, Lock>):
55 do IO<Result<&1, &1, DurableError.Error, Lock>>:
56 pair : File & Result<&1, &1, U32 & String, Unit> <- DbLock.try_lock(file)
57 lock_result(pair, operation, path)
58
59# Locks the opened file or classifies its open error.
60def opened(
61 result: Result<&1, &1, U32 & String, File>,
62 operation: String,
63 path: String
64) -> IO(Result<&1, &1, DurableError.Error, Lock>):
65 match result:
66 case Fail{error}:
67 open_failed(error, operation, path)
68 case Done{file}:
69 lock_file(file, operation, path)
70
71# Opens the stable LOCK file and acquires its OS lock.
72def acquire(+path: String, operation: String) -> IO(Result<&1, &1, DurableError.Error, Lock>):
73 do IO<Result<&1, &1, DurableError.Error, Lock>>:
74 result : Result<&1, &1, U32 & String, File> <- File.open(lock_path(path), "a")
75 opened(result, operation, path)
76
77# Closes the file and preserves the unlock error.
78def release_failed(
79 file: File,
80 error: U32 & String,
81 operation: String,
82 path: String
83) -> IO(Result<&1, &1, DurableError.Error, Unit>):
84 match error:
85 case (code, message):
86 do IO<Result<&1, &1, DurableError.Error, Unit>>:
87 _closed : Unit <- File.close(file)
88 return Fail{DurableError.classify_open(DurableError.OpenHostFailure{code, message}, operation, path)}
89
90# Closes the file after a successful unlock.
91def release_succeeded(file: File) -> IO(Result<&1, &1, DurableError.Error, Unit>):
92 do IO<Result<&1, &1, DurableError.Error, Unit>>:
93 _closed : Unit <- File.close(file)
94 return Done{Unit{}}
95
96# Closes the file after either unlock result.
97def released(
98 pair: File & Result<&1, &1, U32 & String, Unit>,
99 operation: String,
100 path: String
101) -> IO(Result<&1, &1, DurableError.Error, Unit>):
102 match pair:
103 case (file, Fail{error}):
104 release_failed(file, error, operation, path)
105 case (file, Done{Unit{}}):
106 release_succeeded(file)
107
108# Unlocks the file and closes it after the result.
109def unlock_file(file: File, operation: String, path: String) -> IO(Result<&1, &1, DurableError.Error, Unit>):
110 do IO<Result<&1, &1, DurableError.Error, Unit>>:
111 pair : File & Result<&1, &1, U32 & String, Unit> <- DbLock.try_unlock(file)
112 released(pair, operation, path)
113
114# Consumes the lock and releases its file handle.
115def release(lock: Lock, operation: String, path: String) -> IO(Result<&1, &1, DurableError.Error, Unit>):
116 match lock:
117 case Lock{file}:
118 unlock_file(file, operation, path)