Source file picos_std_finally.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
open Picos
type ('a, _) tdt =
| Transferred : ('a, [> `Transferred ]) tdt
| Borrowed : ('a, [> `Borrowed ]) tdt
| Dropped : ('a, [> `Dropped ]) tdt
| Resource : {
mutable resource : 'a;
release : 'a -> unit;
transferred_or_dropped : Trigger.t;
}
-> ('a, [> `Resource ]) tdt
type 'a instance =
('a, [ `Transferred | `Borrowed | `Dropped | `Resource ]) tdt Atomic.t
let[@inline never] error
(case : (_, [< `Transferred | `Borrowed | `Dropped ]) tdt) =
invalid_arg
(match case with
| Transferred -> "transferred"
| Dropped -> "dropped"
| Borrowed -> "borrowed")
let[@inline never] check_released () =
Fiber.check (Fiber.current ());
error Dropped
let forbidden release x =
match Fiber.current () with
| fiber -> begin
if Fiber.exchange fiber ~forbid:true then release x
else
match release x with
| () -> Fiber.set fiber ~forbid:false
| exception exn ->
Fiber.set fiber ~forbid:false;
raise exn
end
| exception _exn ->
release x
let[@inline never] release_and_reraise exn x release =
let bt = Printexc.get_raw_backtrace () in
forbidden release x;
Printexc.raise_with_backtrace exn bt
let[@inline never] release_and_return value x release =
forbidden release x;
value
let rec drop instance =
match Atomic.get instance with
| Transferred | Dropped -> ()
| Borrowed as case -> error case
| Resource r as before ->
if Atomic.compare_and_set instance before Dropped then begin
forbidden r.release r.resource;
Trigger.signal r.transferred_or_dropped
end
else drop instance
let[@inline never] drop_and_reraise_as bt instance exn =
drop instance;
Printexc.raise_with_backtrace exn bt
let[@inline never] drop_and_reraise exn instance =
let bt = Printexc.get_raw_backtrace () in
drop_and_reraise_as bt instance exn
let await_transferred_or_dropped instance result =
match Atomic.get instance with
| Transferred | Dropped -> result
| Borrowed as case ->
error case
| Resource r -> begin
match Trigger.await r.transferred_or_dropped with
| None ->
drop instance;
result
| Some (exn, bt) ->
drop_and_reraise_as bt instance exn
end
let[@inline never] instantiate instance scope =
match scope instance with
| result -> await_transferred_or_dropped instance result
| exception exn -> drop_and_reraise exn instance
let[@inline never] instantiate release acquire scope =
let instance =
Sys.opaque_identity
begin
let transferred_or_dropped = Trigger.create () in
let state =
Resource { resource = Obj.magic (); release; transferred_or_dropped }
in
Atomic.make state
end
in
let (Resource r : (_, [ `Resource ]) tdt) = Obj.magic (Atomic.get instance) in
r.resource <- acquire ();
instantiate instance scope
let[@inline never] rec transfer from scope =
match Atomic.get from with
| (Transferred | Borrowed) as case -> error case
| Dropped -> check_released ()
| Resource r as before ->
let into = Atomic.make Transferred in
if Atomic.compare_and_set from before Transferred then begin
Atomic.set into before;
match
Trigger.signal r.transferred_or_dropped;
scope into
with
| result -> await_transferred_or_dropped into result
| exception exn -> drop_and_reraise exn into
end
else transfer from scope
let[@inline never] rec borrow instance scope =
match Atomic.get instance with
| (Transferred | Dropped | Borrowed) as case -> error case
| Resource r as before ->
if Atomic.compare_and_set instance before Borrowed then begin
match scope r.resource with
| result ->
Atomic.set instance before;
result
| exception exn ->
Atomic.set instance before;
raise exn
end
else borrow instance scope
let[@inline never] rec move from scope =
match Atomic.get from with
| (Transferred | Borrowed) as case -> error case
| Dropped -> check_released ()
| Resource r as before ->
if Atomic.compare_and_set from before Transferred then begin
match
Trigger.signal r.transferred_or_dropped;
scope r.resource
with
| result ->
forbidden r.release r.resource;
result
| exception exn -> release_and_reraise exn r.resource r.release
end
else move from scope
let[@inline never] finally x scope release =
match scope x with
| y -> release_and_return y x release
| exception exn -> release_and_reraise exn x release
let[@inline never] finally release acquire scope =
let x = acquire () in
finally x scope release
let[@inline never] lastly action scope =
match scope () with
| value -> release_and_return value () action
| exception exn -> release_and_reraise exn () action
external ( let@ ) : ('a -> 'b) -> 'a -> 'b = "%apply"