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)