bend-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,199 @@
1
+ import Base
2
+ import ./thread_state.bend as Thread
3
+
4
+ # Python effects
5
+
6
+ law detached_effect_rejected:
7
+ {Thread.step(Thread.Detached{}, Thread.PythonEffect{}) == None{} : Maybe<Thread.State>}
8
+
9
+ law accepted_effect_attached:
10
+ for state: Thread.State
11
+ for next: Thread.State
12
+ for accepted: {Thread.step(state, Thread.PythonEffect{}) == Some{next} : Maybe<Thread.State>}
13
+ {state == Thread.Attached{} : Thread.State}
14
+
15
+ # Inside native evaluation, no Python effect, save, restore, lease operation,
16
+ # or nested entry is accepted: the only way out is Leave.
17
+ law native_effect_rejected:
18
+ for state: Thread.State
19
+ for native: {Thread.is_native(state) == True{} : Bool}
20
+ {Thread.step(state, Thread.PythonEffect{}) == None{} : Maybe<Thread.State>}
21
+
22
+ law native_only_leaves:
23
+ for state: Thread.State
24
+ for operation: Thread.Operation
25
+ for native: {Thread.is_native(state) == True{} : Bool}
26
+ for staying: {Thread.is_leave(operation) == False{} : Bool}
27
+ {Thread.step(state, operation) == None{} : Maybe<Thread.State>}
28
+
29
+ # Attachment
30
+
31
+ law save_restore:
32
+ {Thread.run([Thread.Save{}, Thread.Restore{}], Thread.Attached{}) == Some{Thread.Attached{}} : Maybe<Thread.State>}
33
+
34
+ # Both detach policies and both outcomes (a native failure also leaves through
35
+ # Leave) return to the attached state.
36
+ law native_returns_attached:
37
+ for detach: Bool
38
+ for outcome: Thread.Outcome
39
+ {Thread.run(Thread.native_call(detach, outcome), Thread.Attached{}) == Some{Thread.Attached{}} : Maybe<Thread.State>}
40
+
41
+ law native_then_effect_accepted:
42
+ for detach: Bool
43
+ for outcome: Thread.Outcome
44
+ {Thread.run(List.append(&1, Thread.Operation, Thread.native_call(detach, outcome), [Thread.PythonEffect{}]), Thread.Attached{}) == Some{Thread.Attached{}} : Maybe<Thread.State>}
45
+
46
+ law release_before_restore_rejected:
47
+ for outcome: Thread.Outcome
48
+ {Thread.run([Thread.Save{}, Thread.Enter{}, Thread.Leave{outcome}, Thread.PythonEffect{}], Thread.Attached{}) == None{} : Maybe<Thread.State>}
49
+
50
+ law free_threaded_detaches:
51
+ for release_gil: Bool
52
+ {Thread.detaches(release_gil, True{}) == True{} : Bool}
53
+
54
+ # Leases
55
+
56
+ law acquire_requires_detached:
57
+ for state: Thread.State
58
+ for pool: List<&2, Thread.Context>
59
+ for slot: Nat
60
+ for attached: {state != Thread.Detached{} : Thread.State}
61
+ {Thread.step_session(Thread.Session{state, pool}, Thread.AcquireLease{slot}) == None{} : Maybe<Thread.Session>}
62
+
63
+ law return_requires_detached:
64
+ for state: Thread.State
65
+ for pool: List<&2, Thread.Context>
66
+ for slot: Nat
67
+ for outcome: Thread.Outcome
68
+ for small_heap: Bool
69
+ for cache_space: Bool
70
+ for attached: {state != Thread.Detached{} : Thread.State}
71
+ {Thread.step_session(Thread.Session{state, pool}, Thread.ReturnLease{slot, outcome, small_heap, cache_space}) == None{} : Maybe<Thread.Session>}
72
+
73
+ law reserved_unavailable:
74
+ for pool: List<&2, Thread.Context>
75
+ for slot: Nat
76
+ {Thread.available(Thread.reserve(pool, slot), slot) == False{} : Bool}
77
+
78
+ law reserved_acquisition_rejected:
79
+ for pool: List<&2, Thread.Context>
80
+ for slot: Nat
81
+ {Thread.acquire(Thread.reserve(pool, slot), slot) == None{} : Maybe<List<&2, Thread.Context>>}
82
+
83
+ law acquisition_requires_available:
84
+ for pool: List<&2, Thread.Context>
85
+ for slot: Nat
86
+ for next: List<&2, Thread.Context>
87
+ for acquired: {Thread.acquire(pool, slot) == Some{next} : Maybe<List<&2, Thread.Context>>}
88
+ {Thread.available(pool, slot) == True{} : Bool}
89
+
90
+ law reserved_returnable:
91
+ for pool: List<&2, Thread.Context>
92
+ for slot: Nat
93
+ for free: {Thread.available(pool, slot) == True{} : Bool}
94
+ {Thread.returnable(Thread.reserve(pool, slot), slot) == True{} : Bool}
95
+
96
+ law unleased_return_rejected:
97
+ for pool: List<&2, Thread.Context>
98
+ for slot: Nat
99
+ for outcome: Thread.Outcome
100
+ for small_heap: Bool
101
+ for cache_space: Bool
102
+ for unleased: {Thread.returnable(pool, slot) == False{} : Bool}
103
+ {Thread.release(pool, slot, outcome, small_heap, cache_space) == None{} : Maybe<List<&2, Thread.Context>>}
104
+
105
+ law double_return_rejected:
106
+ for pool: List<&2, Thread.Context>
107
+ for slot: Nat
108
+ for outcome: Thread.Outcome
109
+ for small_heap: Bool
110
+ for cache_space: Bool
111
+ {Thread.returnable(Thread.finish(pool, slot, outcome, small_heap, cache_space), slot) == False{} : Bool}
112
+
113
+ # Updating one slot leaves every other slot unchanged. In C, isolation comes
114
+ # from separate allocations and thread-local bp_current; this is the model's
115
+ # counterpart, a List.set/List.get lemma.
116
+ law context_update_isolated:
117
+ for pool: List<&2, Thread.Context>
118
+ for target: Nat
119
+ for other: Nat
120
+ for value: Thread.Context
121
+ for different: {Nat.is_eq(target, other) == False{} : Bool}
122
+ {List.get(&2, Thread.Context, List.set(&2, Thread.Context, pool, target, value), other) == List.get(&2, Thread.Context, pool, other) : Maybe<&2, Thread.Context>}
123
+
124
+ law failure_isolated:
125
+ for small_heap: Bool
126
+ for cache_space: Bool
127
+ for pool: List<&2, Thread.Context>
128
+ for target: Nat
129
+ for other: Nat
130
+ for different: {Nat.is_eq(target, other) == False{} : Bool}
131
+ {List.get(&2, Thread.Context, Thread.finish(pool, target, Thread.Failed{}, small_heap, cache_space), other) == List.get(&2, Thread.Context, pool, other) : Maybe<&2, Thread.Context>}
132
+
133
+ # Reuse
134
+
135
+ law failed_context_discarded:
136
+ for small_heap: Bool
137
+ for cache_space: Bool
138
+ {Thread.completion(Thread.Failed{}, small_heap, cache_space) == Thread.Discarded{} : Thread.Context}
139
+
140
+ law failed_context_unavailable:
141
+ for small_heap: Bool
142
+ for cache_space: Bool
143
+ for pool: List<&2, Thread.Context>
144
+ for slot: Nat
145
+ {Thread.available(Thread.finish(pool, slot, Thread.Failed{}, small_heap, cache_space), slot) == False{} : Bool}
146
+
147
+ law successful_context_reusable:
148
+ {Thread.completion(Thread.Succeeded{}, True{}, True{}) == Thread.Idle{} : Thread.Context}
149
+
150
+ law reusable_requires_success:
151
+ for outcome: Thread.Outcome
152
+ for small_heap: Bool
153
+ for cache_space: Bool
154
+ for reusable: {Thread.completion(outcome, small_heap, cache_space) == Thread.Idle{} : Thread.Context}
155
+ {outcome == Thread.Succeeded{} : Thread.Outcome}
156
+
157
+ law reusable_requires_small_heap:
158
+ for outcome: Thread.Outcome
159
+ for small_heap: Bool
160
+ for cache_space: Bool
161
+ for reusable: {Thread.completion(outcome, small_heap, cache_space) == Thread.Idle{} : Thread.Context}
162
+ {small_heap == True{} : Bool}
163
+
164
+ law reusable_requires_cache_space:
165
+ for outcome: Thread.Outcome
166
+ for small_heap: Bool
167
+ for cache_space: Bool
168
+ for reusable: {Thread.completion(outcome, small_heap, cache_space) == Thread.Idle{} : Thread.Context}
169
+ {cache_space == True{} : Bool}
170
+
171
+ # Whole invocations: the thread-state and lease models together.
172
+
173
+ # From any idle slot, an invocation with any number of rounds, detach policy,
174
+ # and native outcomes returns attached, leaving only its own slot updated.
175
+ law invocation_returns_attached:
176
+ for +pool: List<&2, Thread.Context>
177
+ for +slot: Nat
178
+ for +detach: Bool
179
+ for +outcomes: List<&2, Thread.Outcome>
180
+ for +small_heap: Bool
181
+ for +cache_space: Bool
182
+ for +free: {Thread.available(pool, slot) == True{} : Bool}
183
+ {Thread.run_session(Thread.invocation(slot, detach, outcomes, small_heap, cache_space), Thread.Session{Thread.Attached{}, pool}) == Some{Thread.Session{Thread.Attached{}, Thread.finish(pool, slot, Thread.health(outcomes), small_heap, cache_space)}} : Maybe<Thread.Session>}
184
+
185
+ law leased_invocation_rejected:
186
+ for pool: List<&2, Thread.Context>
187
+ for slot: Nat
188
+ for detach: Bool
189
+ for outcomes: List<&2, Thread.Outcome>
190
+ for small_heap: Bool
191
+ for cache_space: Bool
192
+ for busy: {Thread.available(pool, slot) == False{} : Bool}
193
+ {Thread.run_session(Thread.invocation(slot, detach, outcomes, small_heap, cache_space), Thread.Session{Thread.Attached{}, pool}) == None{} : Maybe<Thread.Session>}
194
+
195
+ # A native failure in any round poisons the lease, so it is never cached.
196
+ law failure_poisons_lease:
197
+ for before: List<&2, Thread.Outcome>
198
+ for after: List<&2, Thread.Outcome>
199
+ {Thread.health(List.append(&2, Thread.Outcome, before, Thread.Failed{} <> after)) == Thread.Failed{} : Thread.Outcome}
@@ -0,0 +1,362 @@
1
+ import Base
2
+ import ./THREAD_LAWS.bend as Laws
3
+ import ./thread_state.bend as Thread
4
+
5
+ # Discriminators
6
+
7
+ def none_discriminator(value: Maybe<Thread.State>) -> Type:
8
+ match value:
9
+ case None{}:
10
+ Unit
11
+ case Some{_}:
12
+ Empty
13
+
14
+ def none_not_some(-next: Thread.State, accepted: {None{} == Some{next} : Maybe<Thread.State>}) -> Empty:
15
+ %accepted : none_discriminator(_)
16
+ Unit{}
17
+
18
+ def true_discriminator(value: Bool) -> Type:
19
+ match value:
20
+ case True{}:
21
+ Unit
22
+ case False{}:
23
+ Empty
24
+
25
+ def true_not_false(different: {True{} == False{} : Bool}) -> Empty:
26
+ %different : true_discriminator(_)
27
+ Unit{}
28
+
29
+ def false_discriminator(value: Bool) -> Type:
30
+ match value:
31
+ case True{}:
32
+ Empty
33
+ case False{}:
34
+ Unit
35
+
36
+ def false_not_true(different: {False{} == True{} : Bool}) -> Empty:
37
+ %different : false_discriminator(_)
38
+ Unit{}
39
+
40
+ def not_true(-A: Type, different: {False{} == True{} : Bool}) -> A:
41
+ Empty.absurd(A, false_not_true(different))
42
+
43
+ def not_false(-A: Type, different: {True{} == False{} : Bool}) -> A:
44
+ Empty.absurd(A, true_not_false(different))
45
+
46
+ # Python effects
47
+
48
+ def Laws.detached_effect_rejected():
49
+ {==}
50
+
51
+ def Laws.accepted_effect_attached(state, next, accepted):
52
+ match state:
53
+ case Thread.Attached{}:
54
+ {==}
55
+ case Thread.Detached{}:
56
+ Empty.absurd({Thread.Detached{} == Thread.Attached{} : Thread.State}, none_not_some(next, accepted))
57
+ case Thread.AttachedNative{}:
58
+ Empty.absurd({Thread.AttachedNative{} == Thread.Attached{} : Thread.State}, none_not_some(next, accepted))
59
+ case Thread.DetachedNative{}:
60
+ Empty.absurd({Thread.DetachedNative{} == Thread.Attached{} : Thread.State}, none_not_some(next, accepted))
61
+
62
+ def Laws.native_only_leaves(state, operation, native, staying):
63
+ match state operation:
64
+ case Thread.Attached{} op:
65
+ not_true({Thread.step(Thread.Attached{}, op) == None{} : Maybe<Thread.State>}, native)
66
+ case Thread.Detached{} op:
67
+ not_true({Thread.step(Thread.Detached{}, op) == None{} : Maybe<Thread.State>}, native)
68
+ case Thread.AttachedNative{} Thread.Leave{outcome}:
69
+ not_false({Some{Thread.Attached{}} == None{} : Maybe<Thread.State>}, staying)
70
+ case Thread.DetachedNative{} Thread.Leave{outcome}:
71
+ not_false({Some{Thread.Detached{}} == None{} : Maybe<Thread.State>}, staying)
72
+ case Thread.AttachedNative{} Thread.Save{}:
73
+ {==}
74
+ case Thread.AttachedNative{} Thread.Restore{}:
75
+ {==}
76
+ case Thread.AttachedNative{} Thread.Enter{}:
77
+ {==}
78
+ case Thread.AttachedNative{} Thread.PythonEffect{}:
79
+ {==}
80
+ case Thread.AttachedNative{} Thread.AcquireLease{_}:
81
+ {==}
82
+ case Thread.AttachedNative{} Thread.ReturnLease{_, _, _, _}:
83
+ {==}
84
+ case Thread.DetachedNative{} Thread.Save{}:
85
+ {==}
86
+ case Thread.DetachedNative{} Thread.Restore{}:
87
+ {==}
88
+ case Thread.DetachedNative{} Thread.Enter{}:
89
+ {==}
90
+ case Thread.DetachedNative{} Thread.PythonEffect{}:
91
+ {==}
92
+ case Thread.DetachedNative{} Thread.AcquireLease{_}:
93
+ {==}
94
+ case Thread.DetachedNative{} Thread.ReturnLease{_, _, _, _}:
95
+ {==}
96
+
97
+ # A Python effect is not Leave, so this is a special case of native_only_leaves.
98
+ def Laws.native_effect_rejected(state, native):
99
+ Laws.native_only_leaves(state, Thread.PythonEffect{}, native, {==})
100
+
101
+ # Attachment
102
+
103
+ def Laws.save_restore():
104
+ {==}
105
+
106
+ def Laws.native_returns_attached(detach, outcome):
107
+ match detach:
108
+ case False{}:
109
+ {==}
110
+ case True{}:
111
+ {==}
112
+
113
+ def Laws.native_then_effect_accepted(detach, outcome):
114
+ match detach:
115
+ case False{}:
116
+ {==}
117
+ case True{}:
118
+ {==}
119
+
120
+ def Laws.release_before_restore_rejected(outcome):
121
+ {==}
122
+
123
+ def Laws.free_threaded_detaches(release_gil):
124
+ match release_gil:
125
+ case True{}:
126
+ {==}
127
+ case False{}:
128
+ {==}
129
+
130
+ # Leases
131
+
132
+ def Laws.acquire_requires_detached(state, pool, slot, attached):
133
+ match state:
134
+ case Thread.Attached{}:
135
+ {==}
136
+ case Thread.Detached{}:
137
+ Empty.absurd({Thread.detached_with(Thread.acquire(pool, slot)) == None{} : Maybe<Thread.Session>}, attached({==}))
138
+ case Thread.AttachedNative{}:
139
+ {==}
140
+ case Thread.DetachedNative{}:
141
+ {==}
142
+
143
+ def Laws.return_requires_detached(state, pool, slot, outcome, small_heap, cache_space, attached):
144
+ match state:
145
+ case Thread.Attached{}:
146
+ {==}
147
+ case Thread.Detached{}:
148
+ Empty.absurd({Thread.detached_with(Thread.release(pool, slot, outcome, small_heap, cache_space)) == None{} : Maybe<Thread.Session>}, attached({==}))
149
+ case Thread.AttachedNative{}:
150
+ {==}
151
+ case Thread.DetachedNative{}:
152
+ {==}
153
+
154
+ def Laws.reserved_unavailable(pool, slot):
155
+ match pool slot:
156
+ case [] _:
157
+ {==}
158
+ case _ <> _ 0n:
159
+ {==}
160
+ case _ <> tail 1n+pred:
161
+ Laws.reserved_unavailable(tail, pred)
162
+
163
+ def Laws.reserved_acquisition_rejected(pool, slot):
164
+ %Laws.reserved_unavailable(pool, slot) : {Thread.acquire(Thread.reserve(pool, slot), slot) == Thread.acquire_if(_, Thread.reserve(pool, slot), slot) : Maybe<List<&2, Thread.Context>>}
165
+ {==}
166
+
167
+ def pool_none_discriminator(value: Maybe<List<&2, Thread.Context>>) -> Type:
168
+ match value:
169
+ case None{}:
170
+ Unit
171
+ case Some{_}:
172
+ Empty
173
+
174
+ def pool_none_not_some(-next: List<&2, Thread.Context>,
175
+ acquired: {None{} == Some{next} : Maybe<List<&2, Thread.Context>>}) -> Empty:
176
+ %acquired : pool_none_discriminator(_)
177
+ Unit{}
178
+
179
+ def acquire_if_allowed(allowed: Bool, -pool: List<&2, Thread.Context>, -slot: Nat,
180
+ -next: List<&2, Thread.Context>,
181
+ acquired: {Thread.acquire_if(allowed, pool, slot) == Some{next} : Maybe<List<&2, Thread.Context>>})
182
+ -> {allowed == True{} : Bool}:
183
+ match allowed:
184
+ case True{}:
185
+ {==}
186
+ case False{}:
187
+ Empty.absurd({False{} == True{} : Bool}, pool_none_not_some(next, acquired))
188
+
189
+ def Laws.acquisition_requires_available(pool, slot, next, acquired):
190
+ acquire_if_allowed(Thread.available(pool, slot), pool, slot, next, acquired)
191
+
192
+ def Laws.reserved_returnable(pool, slot, free):
193
+ match pool slot:
194
+ case [] _:
195
+ not_true({False{} == True{} : Bool}, free)
196
+ case _ <> _ 0n:
197
+ {==}
198
+ case _ <> tail 1n+pred:
199
+ Laws.reserved_returnable(tail, pred, free)
200
+
201
+ def Laws.unleased_return_rejected(pool, slot, outcome, small_heap, cache_space, unleased):
202
+ %unleased : {Thread.release(pool, slot, outcome, small_heap, cache_space) == Thread.finish_if(_, pool, slot, outcome, small_heap, cache_space) : Maybe<List<&2, Thread.Context>>}
203
+ {==}
204
+
205
+ def completion_not_leased(outcome: Thread.Outcome, small_heap: Bool, cache_space: Bool)
206
+ -> {Thread.leased(Some{Thread.completion(outcome, small_heap, cache_space)}) == False{} : Bool}:
207
+ match outcome small_heap cache_space:
208
+ case Thread.Succeeded{} True{} True{}:
209
+ {==}
210
+ case Thread.Succeeded{} True{} False{}:
211
+ {==}
212
+ case Thread.Succeeded{} False{} _:
213
+ {==}
214
+ case Thread.Failed{} _ _:
215
+ {==}
216
+
217
+ def Laws.double_return_rejected(pool, slot, outcome, small_heap, cache_space):
218
+ match pool slot:
219
+ case [] _:
220
+ {==}
221
+ case _ <> _ 0n:
222
+ completion_not_leased(outcome, small_heap, cache_space)
223
+ case _ <> tail 1n+pred:
224
+ Laws.double_return_rejected(tail, pred, outcome, small_heap, cache_space)
225
+
226
+ def Laws.context_update_isolated(pool, target, other, value, different):
227
+ match pool target other:
228
+ case [] _ _:
229
+ {==}
230
+ case head <> _ 0n 0n:
231
+ Empty.absurd({Some{value} == Some{head} : Maybe<&2, Thread.Context>}, true_not_false(different))
232
+ case _ <> _ 0n 1n+_:
233
+ {==}
234
+ case _ <> _ 1n+_ 0n:
235
+ {==}
236
+ case _ <> tail 1n+pred 1n+rest:
237
+ Laws.context_update_isolated(tail, pred, rest, value, different)
238
+
239
+ def Laws.failure_isolated(small_heap, cache_space, pool, target, other, different):
240
+ Laws.context_update_isolated(pool, target, other, Thread.Discarded{}, different)
241
+
242
+ # Reuse
243
+
244
+ def Laws.failed_context_discarded(small_heap, cache_space):
245
+ {==}
246
+
247
+ def Laws.failed_context_unavailable(small_heap, cache_space, pool, slot):
248
+ match pool slot:
249
+ case [] _:
250
+ {==}
251
+ case _ <> _ 0n:
252
+ {==}
253
+ case _ <> tail 1n+pred:
254
+ Laws.failed_context_unavailable(small_heap, cache_space, tail, pred)
255
+
256
+ def Laws.successful_context_reusable():
257
+ {==}
258
+
259
+ def idle_discriminator(value: Thread.Context) -> Type:
260
+ match value:
261
+ case Thread.Idle{}:
262
+ Empty
263
+ case Thread.Leased{}:
264
+ Unit
265
+ case Thread.Discarded{}:
266
+ Unit
267
+
268
+ def discarded_empty(reusable: {Thread.Discarded{} == Thread.Idle{} : Thread.Context}) -> Empty:
269
+ %reusable : idle_discriminator(_)
270
+ Unit{}
271
+
272
+ def discarded_not_idle(-A: Type, reusable: {Thread.Discarded{} == Thread.Idle{} : Thread.Context}) -> A:
273
+ Empty.absurd(A, discarded_empty(reusable))
274
+
275
+ def Laws.reusable_requires_success(outcome, small_heap, cache_space, reusable):
276
+ match outcome small_heap cache_space:
277
+ case Thread.Succeeded{} _ _:
278
+ {==}
279
+ case Thread.Failed{} _ _:
280
+ discarded_not_idle({Thread.Failed{} == Thread.Succeeded{} : Thread.Outcome}, reusable)
281
+
282
+ def Laws.reusable_requires_small_heap(outcome, small_heap, cache_space, reusable):
283
+ match outcome small_heap cache_space:
284
+ case _ True{} _:
285
+ {==}
286
+ case Thread.Succeeded{} False{} _:
287
+ discarded_not_idle({False{} == True{} : Bool}, reusable)
288
+ case Thread.Failed{} False{} _:
289
+ discarded_not_idle({False{} == True{} : Bool}, reusable)
290
+
291
+ def Laws.reusable_requires_cache_space(outcome, small_heap, cache_space, reusable):
292
+ match outcome small_heap cache_space:
293
+ case _ _ True{}:
294
+ {==}
295
+ case Thread.Succeeded{} True{} False{}:
296
+ discarded_not_idle({False{} == True{} : Bool}, reusable)
297
+ case Thread.Succeeded{} False{} False{}:
298
+ discarded_not_idle({False{} == True{} : Bool}, reusable)
299
+ case Thread.Failed{} _ False{}:
300
+ discarded_not_idle({False{} == True{} : Bool}, reusable)
301
+
302
+ # Whole invocations
303
+
304
+ # Rounds start and end attached and never touch the pool.
305
+ def rounds_attached(detach: Bool, outcomes: List<&2, Thread.Outcome>, -rest: List<Thread.Operation>, -pool: List<&2, Thread.Context>)
306
+ -> {Thread.run_session(List.append(&1, Thread.Operation, Thread.rounds(detach, outcomes), rest), Thread.Session{Thread.Attached{}, pool}) == Thread.run_session(rest, Thread.Session{Thread.Attached{}, pool}) : Maybe<Thread.Session>}:
307
+ match detach outcomes:
308
+ case _ Nil{}:
309
+ {==}
310
+ case True{} _ <> tail:
311
+ rounds_attached(True{}, tail, rest, pool)
312
+ case False{} _ <> tail:
313
+ rounds_attached(False{}, tail, rest, pool)
314
+
315
+ # Returning a lease overwrites the Leased mark it was acquired with.
316
+ def set_twice(pool: List<&2, Thread.Context>, slot: Nat, -first: Thread.Context, -second: Thread.Context)
317
+ -> {List.set(&2, Thread.Context, List.set(&2, Thread.Context, pool, slot, first), slot, second) == List.set(&2, Thread.Context, pool, slot, second) : List<&2, Thread.Context>}:
318
+ match pool slot:
319
+ case [] _:
320
+ {==}
321
+ case _ <> _ 0n:
322
+ {==}
323
+ case head <> tail 1n+pred:
324
+ %set_twice(tail, pred, first, second) : {head <> List.set(&2, Thread.Context, List.set(&2, Thread.Context, tail, pred, first), pred, second) == head <> _ : List<&2, Thread.Context>}
325
+ {==}
326
+
327
+ # The operations after acquisition, and the session an invocation should end in.
328
+ def after_acquire(slot: Nat, detach: Bool, +outcomes: List<&2, Thread.Outcome>, small_heap: Bool, cache_space: Bool) -> List<Thread.Operation>:
329
+ Thread.Restore{} <> List.append(&1, Thread.Operation, Thread.rounds(detach, outcomes),
330
+ Thread.return_call(slot, Thread.health(outcomes), small_heap, cache_space))
331
+
332
+ def finished(pool: List<&2, Thread.Context>, slot: Nat, outcomes: List<&2, Thread.Outcome>, small_heap: Bool, cache_space: Bool) -> Maybe<Thread.Session>:
333
+ Some{Thread.Session{Thread.Attached{}, Thread.finish(pool, slot, Thread.health(outcomes), small_heap, cache_space)}}
334
+
335
+ def returned(slot: Nat, +outcomes: List<&2, Thread.Outcome>, small_heap: Bool, cache_space: Bool) -> List<Thread.Operation>:
336
+ Thread.return_call(slot, Thread.health(outcomes), small_heap, cache_space)
337
+
338
+ def Laws.invocation_returns_attached(pool, slot, detach, outcomes, small_heap, cache_space, free):
339
+ %Equal.sym(Bool, Thread.available(pool, slot), True{}, free) : {Maybe.bind(&1, Thread.Session, Thread.Session, Thread.detached_with(Thread.acquire_if(_, pool, slot)), next => Thread.run_session(after_acquire(slot, detach, outcomes, small_heap, cache_space), next)) == finished(pool, slot, outcomes, small_heap, cache_space) : Maybe<Thread.Session>}
340
+ %Equal.sym(Maybe<Thread.Session>,
341
+ Thread.run_session(List.append(&1, Thread.Operation, Thread.rounds(detach, outcomes), returned(slot, outcomes, small_heap, cache_space)), Thread.Session{Thread.Attached{}, Thread.reserve(pool, slot)}),
342
+ Thread.run_session(returned(slot, outcomes, small_heap, cache_space), Thread.Session{Thread.Attached{}, Thread.reserve(pool, slot)}),
343
+ rounds_attached(detach, outcomes, returned(slot, outcomes, small_heap, cache_space), Thread.reserve(pool, slot))) : {_ == finished(pool, slot, outcomes, small_heap, cache_space) : Maybe<Thread.Session>}
344
+ %Equal.sym(Bool, Thread.returnable(Thread.reserve(pool, slot), slot), True{}, Laws.reserved_returnable(pool, slot, free)) : {Maybe.bind(&1, Thread.Session, Thread.Session, Thread.detached_with(Thread.finish_if(_, Thread.reserve(pool, slot), slot, Thread.health(outcomes), small_heap, cache_space)), next => Thread.run_session([Thread.Restore{}], next)) == finished(pool, slot, outcomes, small_heap, cache_space) : Maybe<Thread.Session>}
345
+ %Equal.sym(List<&2, Thread.Context>,
346
+ List.set(&2, Thread.Context, Thread.reserve(pool, slot), slot, Thread.completion(Thread.health(outcomes), small_heap, cache_space)),
347
+ List.set(&2, Thread.Context, pool, slot, Thread.completion(Thread.health(outcomes), small_heap, cache_space)),
348
+ set_twice(pool, slot, Thread.Leased{}, Thread.completion(Thread.health(outcomes), small_heap, cache_space))) : {Some{Thread.Session{Thread.Attached{}, _}} == finished(pool, slot, outcomes, small_heap, cache_space) : Maybe<Thread.Session>}
349
+ {==}
350
+
351
+ def Laws.leased_invocation_rejected(pool, slot, detach, outcomes, small_heap, cache_space, busy):
352
+ %Equal.sym(Bool, Thread.available(pool, slot), False{}, busy) : {Maybe.bind(&1, Thread.Session, Thread.Session, Thread.detached_with(Thread.acquire_if(_, pool, slot)), next => Thread.run_session(after_acquire(slot, detach, outcomes, small_heap, cache_space), next)) == None{} : Maybe<Thread.Session>}
353
+ {==}
354
+
355
+ def Laws.failure_poisons_lease(before, after):
356
+ match before:
357
+ case []:
358
+ {==}
359
+ case Thread.Succeeded{} <> tail:
360
+ Laws.failure_poisons_lease(tail, after)
361
+ case Thread.Failed{} <> _:
362
+ {==}