package owee

  1. Overview
  2. Docs

Source file owee_marker.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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
type 'result service = ..

type _ service +=
   | Name : string service
   | Traverse : ((Obj.t -> 'acc -> 'acc) -> 'acc -> 'acc) service
   | Locate : Owee_location.t list service

type 'a service_result =
  | Success of 'a
  | Unsupported_service
  | Unmanaged_object

let magic_potion = Obj.repr (ref ())

type 'a marker = {
  magic_potion: Obj.t;
  service: 'result. 'a -> 'result service -> 'result service_result;
}
let size_marker = 2

type 'a cycle_marker = {
  magic_potion: Obj.t;
  original: 'a marker;
  unique_id: int;
  mutable users: int;
}
let size_cycle_marker = 4

let unique_ids = ref 0

let fresh_name () =
  incr unique_ids;
  !unique_ids

let make_cycle_marker (marker : _ marker) = {
  magic_potion;
  original = marker;
  unique_id = fresh_name ();
  users = 0
}

let is_marker obj =
  if Obj.tag obj = 0 && Obj.size obj >= 2 && Obj.field obj 0 == magic_potion then
    if Obj.size obj = size_marker then `Marker
    else if Obj.size obj = size_cycle_marker then `Cycle_marker
    else `No
  else `No

let find_marker t =
  let rec aux (obj : 'a) i j =
    if i >= j then `No else
      let obj' = Obj.field (Obj.repr obj) i in
      match is_marker obj' with
      | `Marker -> `Marker (i, (Obj.obj obj' : 'a marker))
      | `Cycle_marker -> `Cycle_marker (i, (Obj.obj obj' : 'a cycle_marker))
      | `No -> aux obj (i + 1) j
  in
  let obj = Obj.repr t in
  if Obj.tag obj < Obj.lazy_tag
  then aux t 0 (Obj.size obj)
  else `No

let query_service t service =
  match find_marker t with
  | `No -> Unmanaged_object
  | `Marker (_,marker) | `Cycle_marker (_,{original = marker; _}) ->
    marker.service t service

module type T0 = sig
  type t
  val service : t -> 'result service -> 'result service_result
end

module Unsafe0 (M : T0) : sig
  val marker : M.t marker
end = struct
  let marker = M.({ magic_potion; service })
end

type 'a marked = {
  cell: 'a;
  marker: 'a marked marker;
}

let make_marked cell marker = {cell; marker}

let get t = t.cell

module Safe0 (M : T0) : sig
  val mark : M.t -> M.t marked
end = struct
  include Unsafe0(struct
      type t = M.t marked
      let service obj (type a) (request : a service) : a service_result =
        M.service obj.cell request
    end)
  let mark cell = make_marked cell marker
end

(******)

module type T1 = sig
  type 'x t
  val service : 'x t -> 'result service -> 'result service_result
end

module Unsafe1 (M : T1) : sig
  val marker : 'x M.t marker
end = struct
  let marker = M.({ magic_potion; service })
end

module Safe1 (M : T1) : sig
  val mark : 'a M.t -> 'a M.t marked
end = struct
  include Unsafe1(struct
      type 'a t = 'a M.t marked
      let service obj (type a) (request : a service) : a service_result =
        M.service obj.cell request
    end)
  let mark cell = make_marked cell marker
end

module type T2 = sig
  type ('x, 'y) t
  val service : ('x, 'y) t -> 'result service -> 'result service_result
end

module Unsafe2 (M : T2) : sig
  val marker : ('x, 'y) M.t marker
end = struct
  let marker = M.({ magic_potion; service })
end

module Safe2 (M : T2) : sig
  val mark : ('a, 'b) M.t -> ('a, 'b) M.t marked
end = struct
  include Unsafe2(struct
      type ('a, 'b) t = ('a, 'b) M.t marked
      let service obj (type a) (request : a service) : a service_result =
        M.service obj.cell request
    end)
  let mark cell = make_marked cell marker
end

module type T3 = sig
  type ('x, 'y, 'z) t
  val service : ('x, 'y, 'z) t -> 'result service -> 'result service_result
end

module Unsafe3 (M : T3) : sig
  val marker : ('x, 'y, 'z) M.t marker
end = struct
  let marker = M.({ magic_potion; service })
end

module Safe3 (M : T3) : sig
  val mark : ('a, 'b, 'c) M.t -> ('a, 'b, 'c) M.t marked
end = struct
  include Unsafe3(struct
      type ('a, 'b, 'c) t = ('a, 'b, 'c) M.t marked
      let service obj (type a) (request : a service) : a service_result =
        M.service obj.cell request
    end)
  let mark cell = make_marked cell marker
end

(* Cycle detection *)

type cycle = {
  (* FIXME: someday, find better than an hashtable, or maybe just drop cycle
            detection to some library like Phystable? *)
  seen_ids: (int, unit) Hashtbl.t;
  (* Cause uncessary retention, switch to weak array?*)
  mutable seen_objs: Obj.t list;
}

let seen cycle obj =
  match find_marker obj with
  | `No -> `Unmanaged
  | `Marker _ -> `Not_seen
  | `Cycle_marker (_,marker) ->
    if Hashtbl.mem cycle.seen_ids marker.unique_id then
      `Seen marker.unique_id
    else
      `Not_seen

let add_to_cycle cycle (obj : 'a) (marker : 'a cycle_marker) =
  marker.users <- marker.users + 1;
  Hashtbl.add cycle.seen_ids marker.unique_id ();
  cycle.seen_objs <- Obj.repr obj :: cycle.seen_objs;
  `Now_seen marker.unique_id

let update_marker (obj : 'a) (field : int) (marker : 'a marker) =
  Obj.set_field (Obj.repr obj) field (Obj.repr marker)

let update_cycle_marker (obj : 'a) (field : int) (marker : 'a cycle_marker) =
  Obj.set_field (Obj.repr obj) field (Obj.repr marker)

let mark_seen cycle obj =
  match find_marker obj with
  | `No -> `Unmanaged
  | `Marker (i,marker) ->
    let marker = make_cycle_marker marker in
    update_cycle_marker obj i marker;
    add_to_cycle cycle obj marker
  | `Cycle_marker (_,marker) ->
    if Hashtbl.mem cycle.seen_ids marker.unique_id then
      `Already_seen marker.unique_id
    else
      add_to_cycle cycle obj marker

let unmark_seen obj =
  match find_marker obj with
  | `Cycle_marker (i,marker) ->
    marker.users <- marker.users - 1;
    if marker.users = 0 then
      update_marker obj i marker.original
  | `Marker _ ->
    prerr_endline "UNEXPECTED MARKER";
    assert false
  | `No ->
    prerr_endline "UNEXPECTED UNMANAGED";
    assert false

let end_cycle cycle =
  Hashtbl.reset cycle.seen_ids;
  let seen_objs = cycle.seen_objs in
  cycle.seen_objs <- [];
  List.iter unmark_seen seen_objs

let start_cycle () =
  let cycle = { seen_ids = Hashtbl.create 7; seen_objs = [] } in
  Gc.finalise end_cycle cycle;
  cycle
OCaml

Innovation. Community. Security.