@@ -65,13 +65,87 @@ module Scheduler = struct
6565 }
6666end
6767
68+ module Watch_trie : sig
69+ (* * Specialized trie for fsevent watches *)
70+ type 'a t
71+
72+ val empty : 'a t
73+
74+ val to_list : 'a t -> (Path.External .t * 'a ) list
75+
76+ type 'a add =
77+ | Under_existing_node
78+ | Inserted of
79+ { new_t : 'a t
80+ ; removed : (Path.External .t * 'a ) list
81+ }
82+
83+ val add : 'a t -> Path.External .t -> 'a Lazy .t -> 'a add
84+ end = struct
85+ (* the invariant is that a node can contain either a value or branches, but
86+ not both *)
87+ type 'a t =
88+ | Leaf of Path.External .t * 'a
89+ | Branch of 'a t String.Map .t
90+
91+ type 'a add =
92+ | Under_existing_node
93+ | Inserted of
94+ { new_t : 'a t
95+ ; removed : (Path.External .t * 'a ) list
96+ }
97+
98+ let empty = Branch String.Map. empty
99+
100+ let to_list t =
101+ let rec loop t acc =
102+ match t with
103+ | Leaf (k , v ) -> (k, v) :: acc
104+ | Branch m -> String.Map. fold m ~init: acc ~f: loop
105+ in
106+ loop t []
107+
108+ let rec path p a = function
109+ | [] -> Leaf (p, a)
110+ | x :: xs -> Branch (String.Map. singleton x (path p a xs))
111+
112+ let add t key v =
113+ (* wrong in general, but this is only needed for fsevents *)
114+ let comps =
115+ match String. split ~on: '/' (Path.External. to_string key) with
116+ | "" :: comps -> comps
117+ | _ ->
118+ (* fsevents gives us only absolute paths *)
119+ assert false
120+ in
121+ let rec add comps t =
122+ match (comps, t) with
123+ | _ , Leaf (_ , _ ) -> Under_existing_node
124+ | [] , Branch _ ->
125+ Inserted { new_t = Leaf (key, Lazy. force v); removed = to_list t }
126+ | x :: xs , Branch m -> (
127+ match String.Map. find m x with
128+ | None ->
129+ Inserted
130+ { new_t = Branch (String.Map. set m x (path key (Lazy. force v) xs))
131+ ; removed = []
132+ }
133+ | Some m' -> (
134+ match add xs m' with
135+ | Under_existing_node -> Under_existing_node
136+ | Inserted i ->
137+ Inserted { i with new_t = Branch (String.Map. set m x i.new_t) }))
138+ in
139+ add comps t
140+ end
141+
68142type kind =
69143 | Fswatch of
70144 { pid : Pid .t
71145 ; wait_for_watches_established : unit -> unit
72146 }
73147 | Fsevents of
74- { mutable external_ : Fsevents .t Path.External.Map .t
148+ { mutable external_ : Fsevents .t Watch_trie .t
75149 ; runloop : Fsevents.RunLoop .t
76150 ; scheduler : Scheduler .t
77151 ; source : Fsevents .t
@@ -179,7 +253,8 @@ let shutdown t =
179253 (fun () ->
180254 Fsevents. stop fsevents.source;
181255 Fsevents. stop fsevents.sync;
182- Path.External.Map. iter fsevents.external_ ~f: Fsevents. stop;
256+ Watch_trie. to_list fsevents.external_
257+ |> List. iter ~f: (fun (_ , fs ) -> Fsevents. stop fs);
183258 Fsevents.RunLoop. stop fsevents.runloop)
184259
185260let buffer_capacity = 65536
@@ -522,7 +597,7 @@ let create_fsevents ~(scheduler : Scheduler.t) =
522597 | Ok () -> ()
523598 | Error exn ->
524599 Code_error. raise " fsevents callback raised" [ (" exn" , Exn. to_dyn exn ) ]);
525- let external_ = Path.External.Map . empty in
600+ let external_ = Watch_trie . empty in
526601 let runloop =
527602 Mutex. lock mutex;
528603 while ! runloop_ref = None do
@@ -567,17 +642,19 @@ let add_watch t path =
567642 | Path. In_source_tree _ -> (* already watched by source watcher *) ()
568643 | In_build_dir _ ->
569644 Code_error. raise " attempted to watch a directory in build" []
570- | External ext ->
571- f.external_ < -
572- Path.External.Map. update f.external_ ext ~f: (function
573- | Some _ as s -> s
574- | None ->
575- let watch =
576- fsevents f.scheduler ~paths: [ path ]
577- (fsevents_standard_event ~ignored_files: t.ignored_files)
578- in
579- Fsevents. start watch f.runloop;
580- Some watch));
645+ | External ext -> (
646+ let watch =
647+ lazy
648+ (fsevents f.scheduler ~paths: [ path ]
649+ (fsevents_standard_event ~ignored_files: t.ignored_files))
650+ in
651+ match Watch_trie. add f.external_ ext watch with
652+ | Watch_trie. Under_existing_node -> ()
653+ | Inserted { new_t; removed } ->
654+ let watch = Lazy. force watch in
655+ Fsevents. start watch f.runloop;
656+ List. iter removed ~f: (fun (_ , fs ) -> Fsevents. stop fs);
657+ f.external_ < - new_t));
581658 Ok ()
582659 | Fswatch _ ->
583660 (* Here we assume that the path is already being watched because the coarse
0 commit comments