Changeset 7195

Show
Ignore:
Timestamp:
02/23/10 06:47:00 (7 months ago)
Author:
dbaelde
Message:

Add a notion of clock sub-occurrence and the corresponding occurs-check
in clock unification. This allows to detect bad situations like
add([s,cross(f,s)]) with "a dependency of a clock on itself".
By the way, I commit a preliminary support for clocks in cross().
Error messages are still shitty and super spacey ;)

Location:
trunk/liquidsoap/src
Files:
6 modified

Legend:

Unmodified
Added
Removed
  • trunk/liquidsoap/src/clock.ml

    r7194 r7195  
    6363 
    6464  val mutable outputs = [] 
    65  
    6665  method attach s = outputs <- s::outputs 
     66 
     67  val mutable sub_clocks : Source.clock_variable list = [] 
     68  method attach_clock c = sub_clocks <- c::sub_clocks 
     69  method sub_clocks = sub_clocks 
    6770 
    6871  method end_tick = 
  • trunk/liquidsoap/src/clock.mli

    r7194 r7195  
    2525  method id : string 
    2626  method attach : Source.active_source -> unit 
     27  method attach_clock : Source.clock_variable -> unit 
     28  method sub_clocks : Source.clock_variable list 
    2729  method end_tick : unit 
    2830  method start : unit 
     
    3739 
    3840type clock_variable = Source.clock_variable 
    39 val create_unknown : Source.active_source list -> clock_variable 
    40 val create_known   : clock                     -> clock_variable 
    41 val unify : clock_variable -> clock_variable -> bool 
     41val to_string      : clock_variable -> string 
     42val create_unknown : sources:(Source.active_source list) -> 
     43                     sub_clocks:(clock_variable list) -> 
     44                     clock_variable 
     45val create_known : clock -> clock_variable 
     46val unify : clock_variable -> clock_variable -> unit 
  • trunk/liquidsoap/src/lang/lang.ml

    r7171 r7195  
    626626          Printf.printf "ERROR: %s!\n" s ; 
    627627          exit 1 
     628      | Source.Clock_conflict (a,b) -> 
     629          (* TODO better printing of clock errors: we don't have position 
     630           *   information, use the source's ID *) 
     631          Printf.printf 
     632            "An operator cannot belong to two clocks (%s, %s).\n" 
     633            a b ; 
     634          exit 1 
     635      | Source.Clock_loop (a,b) -> 
     636          Printf.printf 
     637            "Cannot unify two dependent clocks: %s, %s.\n" 
     638            a b ; 
     639          exit 1 
    628640      | e -> print_error "Unknown error" ; raise e 
    629641 
  • trunk/liquidsoap/src/operators/cross.ml

    r7052 r7195  
    22 
    33  Liquidsoap, a programmable stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    4343  inherit operator ~name:"cross" kind [s] as super 
    4444 
    45   method stype = s#stype (* This actually depends on [f]. *) 
     45  method stype = s#stype (* This should actually depend on [f]. *) 
     46 
     47  val my_clock = new Clock.clock "cross_clock" 
     48 
     49  method set_clock = 
     50    let my_clock = Clock.create_known my_clock in 
     51    (* The source must belong to our clock, since we need occasional 
     52     * control on its flow (to fold an end of track over a beginning). *) 
     53    Clock.unify my_clock s#clock ; 
     54    (* Our external clock should stricly contain the internal my_clock. *) 
     55    Clock.unify 
     56      self#clock 
     57      (Clock.create_unknown ~sources:[] ~sub_clocks:[my_clock]) 
    4658 
    4759  (* The played source will be [s] at first, but can become a combination of 
     
    5264  method private source_get ab = 
    5365    source#get ab ; 
     66    (* TODO it might be abusive to end the tick all the time, 
     67     *   maybe we didn't consume till the end of the current frame *) 
     68    my_clock#end_tick ; 
     69    (* TODO for now, we need to handle this (possibly passive) source 
     70     *   ourselves since the clock only takes care of the active guys *) 
    5471    source#after_output 
    5572 
    56   val mutable activation = [] 
    57  
    58   method private wake_up activator = 
    59     activation <- (self:>source)::activator ; 
    60     s#get_ready ~dynamic:true activation ; 
     73  method private wake_up _ = 
     74    s#get_ready ~dynamic:true [(self:>source)] ; 
    6175    source <- s ; 
    62     source#get_ready activation ; 
    63     Lang.iter_sources (fun s -> s#get_ready ~dynamic:true activation) f 
     76    source#get_ready [(self:>source)] ; 
     77    (* TODO when using f we'll have to force our clock on it *) 
     78    Lang.iter_sources (fun s -> s#get_ready ~dynamic:true [(self:>source)]) f 
    6479 
    6580  method private sleep = 
     
    175190      in 
    176191        source#leave (self:>source) ; 
    177         s#get_ready activation ; 
     192        s#get_ready [(self:>source)] ; 
    178193        source <- s ; 
    179194        status <- `After inhibit 
  • trunk/liquidsoap/src/source.ml

    r7194 r7195  
    7878  * are forced to wallclock. *) 
    7979 
    80 class type ['a] proto_clock = 
     80class type ['a,'b] proto_clock = 
    8181object 
     82 
    8283  method id : string 
     84 
     85  (** Attach an active source. 
     86    * For now this only works before #start, but in a near future 
     87    * dynamic attaching/detaching should be supported. *) 
    8388  method attach : 'a -> unit 
     89 
     90  (** Attach a sub_clock, get all subclocks, see below. *) 
     91  method attach_clock : 'b -> unit 
     92  method sub_clocks : 'b list 
     93 
    8494  method start : unit 
    8595  method stop : unit 
     96 
    8697end 
    8798 
    8899(** {1 Clock variables} 
    89   * Used to infer what clock a source belongs to. *) 
     100  * Used to infer what clock a source belongs to. 
     101  * Each variable comes with 
     102  *   - a list of active sources belonging to the clock, unused during 
     103  *     inference/unification, but animated by the clock when running 
     104  *   - a list of sub-clocks, used during unification's occurs-check 
     105  *     to avoid cycles which would result in unsound behavior 
     106  *     e.g. add([s,cross(f,s)]). 
     107  * Clock constants are objects of type proto_clock, but need to also 
     108  * maintain the information attached to variables. 
     109  * 
     110  * The unification algorithm can be described as follows, ignoring 
     111  * the active source maintenance. 
     112  * X[Y1,Y2,..,Yn] denotes a variable or constant clock with the set Gamma 
     113  *    of subclocks, 
     114  *    from a first-order unification perspective it should be thought of 
     115  *    as a term X(Y1,Y2,..,Yn,...) where the second ... denotes a 
     116  *    row variable: we don't know if there are more parameters there 
     117  *    (more subclocks) 
     118  * We write X[Gamma] with Gamma list of clocks, and X[..Y..] when 
     119  * Y belongs to the subclocks of X, or the subclocks of the subclocks, 
     120  * etc. 
     121  * Unification rules are: 
     122  *   X[..Y..] = Y[..]    ---> ERROR (occurs-check) 
     123  *   c1[...]  = c2[...]  ---> ERROR (rigid-rigid) 
     124  *   X[Gamma] = Y[Delta] ---> X,Y:=Z[Gamma,Delta] 
     125  *      Here Gamma,Delta denotes an union. It is possible that two 
     126  *      distinct variables might become unified, in which case we'll 
     127  *      end up with two occurrences of the same subclock. 
     128  *) 
    90129 
    91130type 'a var = 
    92131  | Link of 'a link_t ref 
    93   | Known of 'a proto_clock 
     132  | Known of ('a,'a var) proto_clock 
    94133and 'a link_t = 
    95   | Unknown of 'a list 
     134  | Unknown of 'a list * 'a var list 
    96135  | Same_as of 'a var 
    97136 
     
    108147  Known c 
    109148 
    110 let create_unknown l = 
    111   let clock = Link (ref (Unknown l)) in 
     149let create_unknown ~sources ~sub_clocks = 
     150  let clock = Link (ref (Unknown (sources,sub_clocks))) in 
    112151    clocks := clock :: !clocks ; 
    113152    clock 
     
    119158let rec variable_to_string = function 
    120159  | Link {contents=Same_as c} -> variable_to_string c 
    121   | Link ({contents=Unknown l} as r) -> 
    122       Printf.sprintf "?(%x:%d)" (Obj.magic r) (List.length l) 
    123   | Known c -> c#id 
    124  
    125 exception Clock_conflict 
     160  | Link ({contents=Unknown (sources,clocks)} as r) -> 
     161      Printf.sprintf "?(%x:%d)[%s]" 
     162        (Obj.magic r) 
     163        (List.length sources) 
     164        (String.concat "," (List.map variable_to_string clocks)) 
     165  | Known c -> 
     166      Printf.sprintf "%s[%s]" 
     167        c#id 
     168        (String.concat "," (List.map variable_to_string c#sub_clocks)) 
     169 
     170(** Equality modulo dereferencing, does not identify two variables 
     171  * with the same sources and clocks. *) 
     172let var_eq a b = 
     173  let a = deref a in 
     174  let b = deref b in 
     175    match a, b with 
     176      | Link a, Link b -> a==b 
     177      | Known a, Known b -> a=b 
     178      | _,_ -> false 
     179 
     180exception Clock_conflict of string * string 
     181exception Clock_loop of string * string 
     182 
     183let rec sub_clocks = function 
     184  | Known c -> c#sub_clocks 
     185  | Link {contents = Unknown (_,sc)} -> sc 
     186  | Link {contents = Same_as x} -> sub_clocks x 
     187 
     188let occurs_check x y = 
     189  let rec aux = function 
     190    | [] -> () 
     191    | []::tl -> aux tl 
     192    | (x'::clocks)::tl -> 
     193        if var_eq x x' then 
     194          raise (Clock_loop (variable_to_string x, variable_to_string y)) ; 
     195        aux (sub_clocks x' :: clocks :: tl) 
     196  in 
     197    aux [sub_clocks y] 
     198 
     199let occurs_check x y = 
     200  occurs_check x y ; 
     201  occurs_check y x 
    126202 
    127203let rec unify a b = 
     
    129205    | Link {contents=Same_as a}, _ -> unify a b 
    130206    | _, Link {contents=Same_as b} -> unify a b 
    131     | Known s, Known s' -> s = s' 
    132     | Link ({contents=Unknown la} as ra), Link ({contents=Unknown lb} as rb) -> 
    133         let merge = Link (ref (Unknown (la@lb))) in 
     207    | Known s, Known s' -> if s <> s' then 
     208        raise (Clock_conflict (variable_to_string a, variable_to_string b)) 
     209    | Link ({contents=Unknown (sa,ca)} as ra), 
     210      Link ({contents=Unknown (sb,cb)} as rb) -> 
     211        (* TODO perhaps optimize ca@cb *) 
     212        occurs_check a b ; 
     213        let merge = Link (ref (Unknown (sa@sb,ca@cb))) in 
    134214          ra := Same_as merge ; 
    135           rb := Same_as merge ; 
    136           true 
    137     | Known c, Link ({contents=Unknown l} as r) 
    138     | Link ({contents=Unknown l} as r), Known c -> 
    139         List.iter c#attach l ; 
    140         r := Same_as (Known c) ; 
    141         true 
     215          rb := Same_as merge 
     216    | Known c, Link ({contents=Unknown (s,sc)} as r) 
     217    | Link ({contents=Unknown (s,sc)} as r), Known c -> 
     218        occurs_check (Known c) (Link r) ; 
     219        List.iter c#attach s ; 
     220        List.iter c#attach_clock sc ; 
     221        r := Same_as (Known c) 
    142222 
    143223let assign_clocks ~default = 
     
    219299   * changed anymore: a source lives in only one time flow. *) 
    220300 
    221   val clock : active_source var = create_unknown [] 
     301  val clock : active_source var = create_unknown ~sources:[] ~sub_clocks:[] 
    222302 
    223303  method clock = clock 
    224304 
    225305  method set_clock = 
    226     List.iter 
    227       (fun s -> 
    228          if not (unify self#clock s#clock) then 
    229            raise Clock_conflict) 
    230       sources 
     306    List.iter (fun s -> unify self#clock s#clock) sources 
    231307 
    232308  (** Startup/shutdown. 
     
    439515    has_outputs := true ; 
    440516    ignore 
    441       (unify self#clock (create_unknown [(self:>active_source)])) ; 
     517      (unify 
     518         self#clock 
     519         (create_unknown ~sources:[(self:>active_source)] ~sub_clocks:[])) ; 
    442520    self#set_sources [] 
    443521 
     
    481559  method id : string 
    482560  method attach : active_source -> unit 
     561  method attach_clock : clock_variable -> unit 
     562  method sub_clocks : clock_variable list 
    483563  method start : unit 
    484564  method stop : unit 
  • trunk/liquidsoap/src/source.mli

    r7194 r7195  
    151151val has_outputs : unit -> bool 
    152152 
     153(** {1 Clocks} *) 
     154 
    153155class type clock = 
    154156object 
    155157  method id : string 
    156158  method attach : active_source -> unit 
     159 
    157160  method start : unit 
    158161  method stop : unit 
     162 
     163  method attach_clock : clock_variable -> unit 
     164  method sub_clocks : clock_variable list 
    159165end 
    160166 
    161 exception Clock_conflict 
     167exception Clock_conflict of string*string 
     168exception Clock_loop of string*string 
    162169 
    163170module Clock_variables : 
    164171sig 
    165172  val to_string : clock_variable -> string 
    166   val create_unknown : active_source list -> clock_variable 
    167   val create_known   : clock              -> clock_variable 
    168   val unify : clock_variable -> clock_variable -> bool 
     173  val create_unknown : sources:(active_source list) -> 
     174                       sub_clocks:(clock_variable list) -> 
     175                       clock_variable 
     176  val create_known : clock -> clock_variable 
     177  val unify : clock_variable -> clock_variable -> unit 
    169178  val get_clocks : default:clock -> clock list 
    170179end