Import Debian changes 20180207-1
[hcoop/debian/mlton.git] / basis-library / mlton / finalizable.sml
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2003-2006 Henry Cejtin, Matthew Fluet, Suresh
2 * Jagannathan, and Stephen Weeks.
3 *
4 * MLton is released under a BSD-style license.
5 * See the file MLton-LICENSE for details.
6 *)
7
8structure MLtonFinalizable: MLTON_FINALIZABLE =
9struct
10
11structure List =
12 struct
13 open List
14
15 fun push (l, x) = l := x :: !l
16
17 fun foreach (l, f) = app f l
18 end
19
20datatype 'a t = T of {afters: (unit -> unit) list ref,
21 finalizers: ('a -> unit) list ref,
22 value: 'a ref}
23
24fun touch (T {value, ...}) = Primitive.MLton.Finalizable.touch value
25
26fun withValue (f as T {value, ...}, g) =
27 DynamicWind.wind (fn () => g (!value),
28 fn () => touch f)
29
30fun addFinalizer (T {finalizers, ...}, f) =
31 List.push (finalizers, f)
32
33val finalize =
34 let
35 val r: {clean: unit -> unit,
36 isAlive: unit -> bool} list ref = ref []
37 fun clean l =
38 List.foldl (fn (z as {clean: unit -> unit, isAlive},
39 (gotOne, zs)) =>
40 if isAlive ()
41 then (gotOne, z :: zs)
42 else (clean (); (true, zs)))
43 (false, []) l
44 val _ = MLtonSignal.handleGC (fn () => r := #2 (clean (!r)))
45 val _ =
46 Cleaner.addNew
47 (Cleaner.atExit, fn () =>
48 let
49 val l = !r
50 (* Must clear r so that the handler doesn't interfere and so that
51 * all other references to the finalizers are dropped.
52 *)
53 val _ = r := []
54 fun loop l =
55 let
56 val _ = MLtonGC.collect ()
57 val (gotOne, l) = clean l
58 in
59 if gotOne
60 then loop l
61 else ()
62 end
63 in
64 loop l
65 end)
66 in
67 fn z => r := z :: !r
68 end
69
70fun new (v: 'a): 'a t =
71 let
72 val afters = ref []
73 val finalizers = ref []
74 val value = ref v
75 val f = T {afters = afters,
76 finalizers = finalizers,
77 value = value}
78 val weak = MLtonWeak.new value
79 fun clean () =
80 (List.foreach (!finalizers, fn f => f v)
81 ; List.foreach (!afters, fn f => f ()))
82 fun isAlive () = isSome (MLtonWeak.get weak)
83 val _ = finalize {clean = clean, isAlive = isAlive}
84 in
85 f
86 end
87
88fun finalizeBefore (T {afters, ...}, f) =
89 List.push (afters, fn () => touch f)
90
91end