diff options
Diffstat (limited to 'lib/kernel')
| -rw-r--r-- | lib/kernel/Branch.ml | 4 | ||||
| -rw-r--r-- | lib/kernel/BranchManager.ml | 7 | ||||
| -rw-r--r-- | lib/kernel/EphemeralRelation.ml | 74 | ||||
| -rw-r--r-- | lib/kernel/Generator.ml | 4 | ||||
| -rw-r--r-- | lib/kernel/Lifecycle.ml | 4 | ||||
| -rw-r--r-- | lib/kernel/Lifecycle.mli | 1 | ||||
| -rw-r--r-- | lib/kernel/Multigroup.ml | 27 | ||||
| -rw-r--r-- | lib/kernel/Namespace.ml | 10 | ||||
| -rw-r--r-- | lib/kernel/Namespace.mli | 1 | ||||
| -rw-r--r-- | lib/kernel/Path.ml | 31 | ||||
| -rw-r--r-- | lib/kernel/Session.ml | 3 | ||||
| -rw-r--r-- | lib/kernel/SubstantialRelation.ml | 7 |
12 files changed, 106 insertions, 67 deletions
diff --git a/lib/kernel/Branch.ml b/lib/kernel/Branch.ml index ac8978b..25c2e7b 100644 --- a/lib/kernel/Branch.ml +++ b/lib/kernel/Branch.ml @@ -126,8 +126,8 @@ module Make (S : Abstract.Storage.STORAGE) = struct let* violations = Protocols.Admission.check admission in Ok (List.map - (fun (d, tuples) -> - Path.(to_string ("multigroup" @/ m @/ "denial" @/ d @/ this)), tuples ) + (fun (d, witness) -> + Path.(to_string ("multigroup" @/ m @/ "denial" @/ d @/ this)), witness ) violations ) ) in Protocols. diff --git a/lib/kernel/BranchManager.ml b/lib/kernel/BranchManager.ml index 206aae9..886d100 100644 --- a/lib/kernel/BranchManager.ml +++ b/lib/kernel/BranchManager.ml @@ -10,11 +10,8 @@ module Error = struct let refused violations = condition "refused" "The state is not admitted: a denial in it holds tuples" (List.fold_left - (fun ps (path, tuples) -> - ps - & path - |=| Concepts.Value.String - (String.concat "; " (List.map Concepts.Tuple.to_string tuples)) ) + (fun ps (path, witness) -> + ps & path |=| Concepts.Value.String (Concepts.Tuple.to_string witness) ) empty violations ) end diff --git a/lib/kernel/EphemeralRelation.ml b/lib/kernel/EphemeralRelation.ml index 8cae91f..c0ec241 100644 --- a/lib/kernel/EphemeralRelation.ml +++ b/lib/kernel/EphemeralRelation.ml @@ -18,8 +18,12 @@ module Error = struct let unbound_program name = condition "unbound-program" - "A stored relation has to be instantiated by its evaluator before it can be enumerated" + "A relation generated by a program has to be given a state to run within before it is read" ("relation" |=| Concepts.Value.String name) + + let undecidable_membership () = + condition "undecidable-membership" + "A relation with no membership test and no end was asked whether it holds a tuple" empty end module Persisted = struct @@ -59,7 +63,7 @@ let persisted = function Some {Persisted.name; schematics; program} | _ -> None -class ephemeral_relation ?program description value enumerate inputs = +class ephemeral_relation ?program ?decide description value modes enumerate inputs = object (self) inherit Lifecycle.counted inherit Identity.of_id @@ -67,19 +71,24 @@ class ephemeral_relation ?program description value enumerate inputs = val relation : t = value method destroy = List.iter Protocols.Handle.release inputs method enumerate : (Protocols.Handle.t, Concepts.Condition.condition) result = enumerate () + method modes : (Concepts.Mode.t, Concepts.Condition.condition) result = modes () method describe () = Ok (Protocols.Schematics.Relation description) - (* TODO: We do not need to scan, but simply apply a membership - criteria (set of constraints. For now this works, but it's - inneficient and hurts data independence, as there is an - assymetry between ephemeral and substantial relations. *) method contains (tuple : Concepts.Tuple.t) = let open Utilities.Result in - let* cursor = self#enumerate in - Protocols.Handle.releasing cursor (fun () -> - let* scan = Protocols.Cursor.require cursor in - Protocols.Cursor.exists scan (fun member -> - Concepts.Hash.hash_equals (Concepts.Tuple.hash member) (Concepts.Tuple.hash tuple) ) ) + match decide with + | Some decide -> decide tuple + | None -> + let* modes = self#modes in + if not (Concepts.Mode.exhaustible modes BatSet.String.empty) then + Error (Error.undecidable_membership ()) + else + let* cursor = self#enumerate in + Protocols.Handle.releasing cursor (fun () -> + let* scan = Protocols.Cursor.require cursor in + Protocols.Cursor.exists scan (fun member -> + Concepts.Hash.hash_equals (Concepts.Tuple.hash member) + (Concepts.Tuple.hash tuple) ) ) method protocols : Protocols.Handle.protocol list = let protocols = @@ -96,11 +105,13 @@ class ephemeral_relation ?program description value enumerate inputs = :: protocols end -let instantiate ?(inputs = []) ?program description value enumerate = - new ephemeral_relation ?program description value enumerate inputs |> Protocols.Handle.make +let instantiate ?(inputs = []) ?program ?decide ~modes description value enumerate = + new ephemeral_relation ?program ?decide description value modes enumerate inputs + |> Protocols.Handle.make let literal description tuples = instantiate description + ~modes:(fun () -> Ok [Concepts.(Mode.mode [] (Cardinality.bounded (List.length tuples)))]) {name= None; schematics= `Temporary description; program= None} (fun () -> Ok (Generator.cursor_of (fun ~yield -> List.iter yield tuples; Ok ()))) @@ -113,6 +124,7 @@ let enumerate_owned relation = object method reference = true method release = Protocols.Handle.release relation + method is_managed = true end in match @@ -128,18 +140,29 @@ let enumerate_owned relation = method to_string = Protocols.Handle.to_string cursor end ) -let run name program within () = - let open Utilities.Result in - match program, within with - | Some program, Some within -> - let* executable = Protocols.Executable.require program in - Result.bind (Protocols.Executable.invoke executable [] ~within) enumerate_owned - | _ -> Error (Error.unbound_program name) - -let view ?within description program = - instantiate ~program description +let asking f relation = + Protocols.Handle.releasing relation (fun () -> Result.bind (Protocols.Relation.require relation) f) + +(* What a program generates is known only from the relation it + returns, so reading it, asking its modes and asking for a tuple + all invoke it. *) +let generated ?program ?within name description value = + let invoking f () = + let open Utilities.Result in + match program, within with + | Some program, Some within -> + let* executable = Protocols.Executable.require program in + Result.bind (Protocols.Executable.invoke executable [] ~within) f + | _ -> Error (Error.unbound_program name) + in + instantiate ?program + ~decide:(fun tuple -> invoking (asking (fun r -> Protocols.Relation.contains r tuple)) ()) + ~modes:(invoking (asking Protocols.Relation.modes)) + description value (invoking enumerate_owned) + +let of_program ?within description program = + generated ~program ?within (Protocols.Handle.to_string program) description {name= None; schematics= `Temporary description; program= None} - (run (Protocols.Handle.to_string program) (Some program) within) module Make (S : Abstract.Storage.STORAGE) = struct module SI = Storage.Make (S) @@ -147,9 +170,8 @@ module Make (S : Abstract.Storage.STORAGE) = struct let restore ?program ?within ({Persisted.name; schematics; program= pointer} : Persisted.t) description = - instantiate ?program description + generated ?program ?within name description {name= Some name; schematics= `Persisted schematics; program= Some pointer} - (run name program within) let load ?bind connection (record : Persisted.t) = let open Utilities.Result in diff --git a/lib/kernel/Generator.ml b/lib/kernel/Generator.ml index 272bb86..b83de3b 100644 --- a/lib/kernel/Generator.ml +++ b/lib/kernel/Generator.ml @@ -12,7 +12,7 @@ let resume k = Effect.Deep.continue k () class producer_cursor produce finally = object (self) - inherit Lifecycle.null + inherit Lifecycle.counted inherit Identity.of_id method to_string = "producer-cursor" val mutable state : [`Fresh | `Live of (unit, step) Effect.Deep.continuation | `Done] = `Fresh @@ -49,7 +49,7 @@ class producer_cursor produce finally = if limit <= 0 then Ok Protocols.Cursor.{tuples= BatFingerTree.empty; exhausted= state = `Done} else fill 0 BatFingerTree.empty - method! release = match state with `Fresh | `Live _ -> self#finish | `Done -> () + method destroy = match state with `Fresh | `Live _ -> self#finish | `Done -> () end let cursor_of ?finally produce = new producer_cursor produce finally |> Protocols.Handle.make diff --git a/lib/kernel/Lifecycle.ml b/lib/kernel/Lifecycle.ml index ac6bb20..4fb12b9 100644 --- a/lib/kernel/Lifecycle.ml +++ b/lib/kernel/Lifecycle.ml @@ -1,6 +1,7 @@ class type lifecycle = object method reference : bool method release : unit + method is_managed : bool end class type virtual controlled_lifecycle = object @@ -12,6 +13,7 @@ class null = object method reference = true method release = () + method is_managed = false end class exclusive = @@ -19,6 +21,7 @@ class exclusive = val taken = Atomic.make false method reference = Atomic.compare_and_set taken false true method release = Atomic.set taken false + method is_managed = true end class virtual counted = @@ -27,6 +30,7 @@ class virtual counted = method virtual destroy : unit method reference = Atomic.incr references; true method release = if Atomic.fetch_and_add references (-1) = 1 then self#destroy + method is_managed = true end class retaining inner (owner : lifecycle) = diff --git a/lib/kernel/Lifecycle.mli b/lib/kernel/Lifecycle.mli index d4ef178..a323197 100644 --- a/lib/kernel/Lifecycle.mli +++ b/lib/kernel/Lifecycle.mli @@ -1,6 +1,7 @@ class type lifecycle = object method reference : bool method release : unit + method is_managed : bool end class type virtual controlled_lifecycle = object diff --git a/lib/kernel/Multigroup.ml b/lib/kernel/Multigroup.ml index eaef8c3..f01a983 100644 --- a/lib/kernel/Multigroup.ml +++ b/lib/kernel/Multigroup.ml @@ -69,6 +69,12 @@ module Make (S : Abstract.Storage.STORAGE) = struct condition "incomplete-multigroup" "A stored multigroup is missing part of its expected structure. Is your storage corrupted?" ("address" |=| Concepts.Value.String (Concepts.Hash.to_hum_string addr)) + + let unjudgeable denial = + condition "unjudgeable-denial" + "A denial does not declare that reading it with nothing bound ends, so the state cannot \ + be judged" + ("denial" |=| Concepts.Value.String denial) end module Representation = Concepts.Encoding.Record.Make (struct @@ -177,16 +183,17 @@ module Make (S : Abstract.Storage.STORAGE) = struct let held (name, relation) = let open Utilities.Result in Protocols.Handle.releasing relation (fun () -> - let* enumerable = Protocols.Enumerable.require relation in - let* cursor = Protocols.Enumerable.enumerate enumerable in - Protocols.Handle.releasing cursor (fun () -> - let* scan = Protocols.Cursor.require cursor in - let* first = Protocols.Cursor.next scan in - match first with - | None -> Ok [] - | Some tuple -> - let* rest = Protocols.Cursor.drain scan () in - Ok [name, tuple :: BatFingerTree.to_list rest] ) ) + let* denial = Protocols.Relation.require relation in + let* modes = Protocols.Relation.modes denial in + if not (Concepts.Mode.exhaustible modes BatSet.String.empty) then + Error (Error.unjudgeable name) + else + let* enumerable = Protocols.Enumerable.require relation in + let* cursor = Protocols.Enumerable.enumerate enumerable in + Protocols.Handle.releasing cursor (fun () -> + let* scan = Protocols.Cursor.require cursor in + let* first = Protocols.Cursor.next scan in + Ok (Option.to_list (Option.map (fun witness -> name, witness) first)) ) ) class multigroup ?bind conn value schema_tree denial_tree = object (self) diff --git a/lib/kernel/Namespace.ml b/lib/kernel/Namespace.ml index 340e813..c7c0767 100644 --- a/lib/kernel/Namespace.ml +++ b/lib/kernel/Namespace.ml @@ -1,18 +1,22 @@ class namespace = object (self) - inherit Lifecycle.null + inherit Lifecycle.counted inherit Identity.of_id method to_string = "namespace" val entries : (string, Protocols.Handle.t) BatMap.t Atomic.t = Atomic.make BatMap.empty method protocols = Protocols.[Directory.make self; Registry.make self] + method destroy = Atomic.get entries |> BatMap.values |> BatEnum.iter Protocols.Handle.release method update key reference value = match Utilities.Atomic.mswap entries (fun e -> - if BatMap.find_opt key e = reference then + let existing = BatMap.find_opt key e in + if existing = reference then begin + Option.map Protocols.Handle.release existing |> ignore; match value with - | Some v -> Ok (BatMap.add key v e) + | Some v -> Ok (BatMap.add key (Protocols.Handle.move v) e) | None -> Ok (BatMap.remove key e) + end else Error () ) with | Ok _ -> Ok true diff --git a/lib/kernel/Namespace.mli b/lib/kernel/Namespace.mli index ec77724..394e44a 100644 --- a/lib/kernel/Namespace.mli +++ b/lib/kernel/Namespace.mli @@ -2,6 +2,7 @@ class namespace : object inherit Protocols.Handle.obj inherit Protocols.Directory.implementation inherit Protocols.Registry.implementation + method destroy : unit end val make : unit -> Protocols.Handle.t diff --git a/lib/kernel/Path.ml b/lib/kernel/Path.ml index 4e36125..0814b1c 100644 --- a/lib/kernel/Path.ml +++ b/lib/kernel/Path.ml @@ -25,37 +25,33 @@ module Error = struct end let walking ?missing f g handle path = + let open Protocols in + let open Utilities.Result in let rec walk handle = function - | [] -> g handle + | [] -> g (Handle.keep handle) | x :: xs -> ( - let open Utilities.Result in - let open Protocols in let* dir = Handle.require Directory.from handle |> Result.map_error Concepts.Condition.(complement ("before-segment" |=| Concepts.Value.String x)) in - let* elem = Directory.find dir x in + let* elem = Directory.find dir x |> Result.map (Option.map Handle.autorelease) in match elem with | None -> ( match missing with Some m -> m () | None -> Error (Error.path_not_found path) ) | Some elem -> f x handle (fun () -> walk elem xs) ) in - walk handle path - |> Result.map_error - Concepts.Condition.(complement ("path" |=| Concepts.Value.String (to_string path))) + Handle.with_autorelease (fun () -> + walk handle path + |> Result.map_error + Concepts.Condition.(complement ("path" |=| Concepts.Value.String (to_string path))) ) -(* Handles we find in the middle of a walk get released once we're - done with them, but never the caller's root. If you leak one and - its refcount never drops, nothing under that path gets collected, - and an exclusive object, like a session, stays locked forever. *) -let releasing root handle f = if handle == root then f () else Protocols.Handle.releasing handle f -let lookup handle path = walking (fun _ parent -> releasing handle parent) Result.ok handle path +let lookup handle path = walking (fun _ _ f -> f ()) Result.ok handle path let find handle path = walking ~missing:(fun () -> Ok None) - (fun _ parent -> releasing handle parent) + (fun _ _ f -> f ()) (fun h -> Ok (Some h)) handle path @@ -63,9 +59,8 @@ let update handle path key reference value = let open Utilities.Result in let open Protocols in let* target = lookup handle path in - releasing handle target (fun () -> - let* registry = Handle.require Registry.from target in - Registry.update registry key reference value ) + let* registry = Handle.require Registry.from target in + Registry.update registry key reference value (* The changes at this object, and those beneath each child they reach. *) let split changes = @@ -119,7 +114,7 @@ let assoc_all handle changes = | None when binds beneath -> Ok [x, Some (fresh beneath)] | None -> Ok [] | Some child -> - releasing handle child (fun () -> apply (prefix @ [x]) child beneath) + Handle.releasing child (fun () -> apply (prefix @ [x]) child beneath) |> Result.map (fun child' -> [x, Some child']) ) below |> Utilities.List.sequence diff --git a/lib/kernel/Session.ml b/lib/kernel/Session.ml index 1abd278..231f518 100644 --- a/lib/kernel/Session.ml +++ b/lib/kernel/Session.ml @@ -4,7 +4,7 @@ module Make (S : Abstract.Storage.STORAGE) = struct class session storage state = object (self) - inherit Lifecycle.exclusive + inherit Lifecycle.counted inherit Identity.of_id method to_string = "session" val storage : S.connection = storage @@ -13,6 +13,7 @@ module Make (S : Abstract.Storage.STORAGE) = struct method pin state' = Atomic.set state state' method protocols = Protocols.[Directory.make self; Session.make self] method list = Ok (BatFingerTree.singleton "branch") + method destroy = () method find = function diff --git a/lib/kernel/SubstantialRelation.ml b/lib/kernel/SubstantialRelation.ml index 533893c..09aec90 100644 --- a/lib/kernel/SubstantialRelation.ml +++ b/lib/kernel/SubstantialRelation.ml @@ -116,6 +116,13 @@ module Make (S : Abstract.Storage.STORAGE) = struct let* node = tuple_node tx relation in TupleSet.mem tx (Concepts.Tuple.hash tuple) node ) + method modes = + Ok + Concepts. + [ Mode.mode [] Cardinality.Finite; + Mode.mode (List.map fst (BatMap.String.bindings description)) (Cardinality.bounded 1) + ] + method describe () = Ok (Protocols.Schematics.Relation description) method enumerate = enumerate connection relation |
