File: fcl_cstr.ml

package info (click to toggle)
facile 1.1-9
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 664 kB
  • ctags: 1,702
  • sloc: ml: 6,848; makefile: 127; sh: 21
file content (293 lines) | stat: -rw-r--r-- 8,950 bytes parent folder | download | duplicates (7)
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
(***********************************************************************)
(*                                                                     *)
(*                           FaCiLe                                    *)
(*                 A Functional Constraint Library                     *)
(*                                                                     *)
(*            Nicolas Barnier, Pascal Brisset, LOG, CENA               *)
(*                                                                     *)
(* Copyright 2004 CENA. All rights reserved. This file is distributed  *)
(* under the terms of the GNU Lesser General Public License.           *)
(***********************************************************************)
(* $Id: fcl_cstr.ml,v 1.43 2004/09/03 13:23:11 barnier Exp $ *)

open Printf

exception DontKnow

type priority = int
let nb_priorities = 3

let immediate = 0
let normal = 1
let later = 2

type t = {
    id : int;
    name: string;
    priority : priority;
    solved : bool array;
    woken : bool array;
    nb_solved : int Fcl_stak.ref;
    fprint : out_channel -> unit;
    update : int -> unit;
    init : unit -> unit;
    check : unit -> bool;
    delay : t -> unit;
    not : unit -> t
  } 

let gen_int = Fcl_misc.gen_int_fun ()

let array_set_true t i =
  t.(i) <- true;
  Fcl_stak.trail (fun () -> t.(i) <- false)

let create ?(name = "anonymous") ?(nb_wakings = 1) ?fprint ?(priority = normal) ?init ?check ?not update delay =
  if nb_wakings < 1 then begin
    let msg = "Cstr.create: nb_wakings must be greater or equal to 1" in
    Fcl_debug.fatal_error msg end;
  let solved = Array.create nb_wakings false
  and nb_solved = Fcl_stak.ref 0 in
  let update i =
    if update i then
      if Pervasives.not solved.(i) then begin
       	Fcl_stak.set nb_solved (Fcl_stak.get nb_solved + 1);
       	array_set_true solved i
      end in
  {
  id = gen_int ();
  name = name;
  priority = priority;
  update = update;
  delay = delay;
  solved = solved;
  woken = Array.create nb_wakings false;
  nb_solved = nb_solved;
  fprint = (match fprint with Some f -> f | None -> fun c -> fprintf c "%s" name);
  init =
   (match init with
     Some i -> i
   (* For not breaking constraints that don't use waking ids and
      rely on being woken at post time. If update must not be called
      at post time (e.g. because it is suspended to on_subst and the
      code rely on the fact that the variable really is instantiated)
      and waking ids are not used, init must be defined. *)
   | None when nb_wakings = 1 -> fun () -> ignore (update 0)
   (* otherwise we do nothing *)
   | _ -> fun () -> ());
  check = (match check with Some c -> c | None -> fun () -> Fcl_debug.fatal_error (name ^ ": check callback undefined"));
  not = (match not with Some n -> n | None -> fun () -> Fcl_debug.fatal_error (name ^ ": not callback undefined"))
} 
    

let fprint chan ct = Printf.fprintf chan "%d: " ct.id; ct.fprint chan
let self_delay c = c.delay
let check c = c.check ()
let solved c = c.solved

let is_solved ct = Fcl_stak.get ct.nb_solved = Array.length ct.woken

let queue = Array.create nb_priorities []
and already_in_wake = ref false
and next_priority = ref nb_priorities

let reset_queue () =
  for i = 0 to nb_priorities -1 do
    queue.(i) <- []
  done;
  next_priority := nb_priorities;
  already_in_wake := false;;

let assert_empty_queue () =
  assert(
    try
      for i = 0 to nb_priorities -1 do
	if queue.(i) <> [] then raise Exit
      done;
      !next_priority = nb_priorities && not !already_in_wake
    with
      Exit -> false);;

exception Wake_all

let wake_all () =
  if not !already_in_wake
  then begin
    already_in_wake := true;
    try
      while !next_priority < nb_priorities do
	match queue.(!next_priority) with
      	    [] -> incr next_priority
	  | (c, i) :: cs ->
	      queue.(!next_priority) <- cs;
	      Fcl_debug.call 'c' (fun s -> fprintf s "%s(%d)#update(%d)\n" c.name c.id i);
	      if not c.solved.(i) then c.update i;
	      Fcl_debug.call 'c' (fun s -> fprintf s "%s(%d)#updated(%d)%s\n" c.name c.id i (if is_solved c then "*" else ""));
	      c.woken.(i) <- false (* not trailed *)
      done;
      already_in_wake := false
   (* To avoid being in a state where already_in_wake = true after an
      uncaught exception during the while loop. *)
    with e -> 
      reset_queue ();
      raise e
  end


let schedule_one_cstr ((cstr, i) as c) =
  Fcl_debug.call 'c' (fun s -> fprintf s "%s(%d)#scheduled(%d) - (woken:%b, solved:%b)\n" cstr.name cstr.id i cstr.woken.(i) cstr.solved.(i));
  if not (cstr.woken.(i) || cstr.solved.(i)) then begin
    Fcl_debug.call 'c' (fun s -> fprintf s "wake %d(%d): " cstr.id i; cstr.fprint s; fprintf s "\n");
    let p = cstr.priority in
    queue.(p) <- c :: queue.(p);
    next_priority := Fcl_misc.Operators.min !next_priority p;
    array_set_true cstr.woken i end


(* Management of active contraints *)
module Store = struct
  let size_store = 1024
  let store = ref (Weak.create size_store)
  let next_free = ref 0
    
  let compress_or_extend () =
    let size = Weak.length !store in
    let rec look_for_free from to_ =
      if to_ < size then
      	match Weak.get !store to_ with
      	  None -> copy (max (to_ + 1) from) to_
      	| Some _ -> look_for_free from (to_ + 1)
      else size (* Full *)
    and copy from to_ =
      assert(from > to_);
      if from < size then
      	match Weak.get !store from with
      	  None -> copy (from + 1) to_
      	| some_c ->
	    Weak.set !store to_ some_c;
	    Weak.set !store from None;
	    look_for_free (from + 1) (to_ + 1)
      else to_ in
    next_free := look_for_free 0 0;
    if !next_free > size / 2 then begin
      let old_store = !store in
      store := Weak.create (size * 2);
      Weak.blit old_store 0 (!store) 0 size;
    end
      
  let add = fun c ->
    if not (is_solved c) then begin
      let size = Weak.length !store in
      if !next_free >= size then begin
      	assert(!next_free = size);
      	compress_or_extend () (* Set next_free *)
      end;
      Weak.set !store !next_free (Some c);
      incr next_free;
      let id = c.id in
      Fcl_stak.trail
      	(fun () ->
        (* le weak pointer de c a ete eventuellement supprime par le GC et
	   la compression *)
	  match Weak.get !store (!next_free - 1) with
	    Some c' when c'.id <> id -> ()
	  | _ -> decr next_free)
    end
	
  let active_store () =
    let rec loop active i = 
      if i < !next_free then
      	loop
	  (match Weak.get !store i with
	    None -> active
      	  | Some c ->
	      if is_solved c then active else (c::active))
	  (i+1)
      else
      	active in
    loop [] 0
end

let active_store = Store.active_store
  

let post c =
  Fcl_debug.call 'c' (fun s -> fprintf s "post: "; c.fprint s; fprintf s "\n");
  let current_status = !already_in_wake in
  already_in_wake := true; (* Because #init may wake constraints and we want
			      to schedule them correctly *)
  begin
    try (* Because #init may fail or raise any other exception *)
      c.init ()
    with
      e ->
  	reset_queue ();
	raise e
  end;
  if not (is_solved c) then begin
    c.delay c;
    Store.add c;
  end;
  already_in_wake := current_status;
  wake_all ()

  (* post pour les dmons *)
let init c =
  c.init ();
  c.delay c;;


let rec one () =
  let delay _ = ()
  and check () = true
  and update _ = true
  and not = zero in 
  create ~priority:immediate ~name:"one" ~check ~not update delay
and zero () =
  let delay _ = ()
  and check () = false
  and update _ = Fcl_stak.fail "zero"
  and not = one in 
  create ~priority:immediate ~name:"zero" ~check ~not update delay

let one = one ()
let zero = one.not ()

let id c = c.id
let name c = c.name
let priority c = c.priority

(* Un objet avec des contraintes qui lui sont attaches *)
type event = (t * int) list Fcl_stak.ref
let new_event () = Fcl_stak.ref []
let schedule (event : event) = List.iter schedule_one_cstr (Fcl_stak.get event)
let register event ?(waking_id=0) cstr =
  let nb_wakings = Array.length cstr.woken in
  if waking_id >= nb_wakings then begin
    let msg =
      Printf.sprintf
	"nb_wakings less one (%d) must be equal to maximum waking_id (here %d) in constraint %s" (nb_wakings - 1) waking_id cstr.name in
    Fcl_debug.fatal_error msg end;
    
  let current = Fcl_stak.get event in
  if not (is_solved cstr)
  then Fcl_stak.set event ((cstr, waking_id) :: current)
let registered = Fcl_stak.get

let delay events ?waking_id c =
  List.iter
    (fun event -> register event ?waking_id c)
    events

let conjunction = function
    [] -> one
  | [cstr] -> cstr
  | cstrs ->
      let update _ = true
      and delay _ = ()
      and init () = List.iter (fun c -> post c) cstrs
      and fprint chan =
	List.iter (fun c -> Printf.fprintf chan "%a\n" fprint c) cstrs in
      create ~fprint ~init ~name:"conjunction" update delay

let not ct = ct.not ()