aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/EphemeralRelation.ml73
-rw-r--r--lib/kernel/EphemeralRelation.mli36
-rw-r--r--lib/kernel/Generator.ml19
-rw-r--r--lib/kernel/Lifecycle.ml15
-rw-r--r--lib/kernel/Lifecycle.mli10
-rw-r--r--lib/kernel/Storage.ml7
-rw-r--r--lib/kernel/Storage.mli1
-rw-r--r--lib/kernel/SubstantialRelation.ml59
-rw-r--r--lib/kernel/SubstantialRelation.mli8
9 files changed, 183 insertions, 45 deletions
diff --git a/lib/kernel/EphemeralRelation.ml b/lib/kernel/EphemeralRelation.ml
new file mode 100644
index 0000000..b403e13
--- /dev/null
+++ b/lib/kernel/EphemeralRelation.ml
@@ -0,0 +1,73 @@
+module type DERIVATION = sig
+ type plan
+
+ val identity : plan -> Concepts.Hash.hash
+
+ val describe :
+ plan -> (Protocols.Schematics.relation_description, Concepts.Condition.condition) result
+
+ val modes : plan -> (Concepts.Mode.t, Concepts.Condition.condition) result
+
+ val generate :
+ plan ->
+ Protocols.Generative.binding ->
+ (Protocols.Handle.t, Concepts.Condition.condition) result
+end
+
+module Make (D : DERIVATION) = struct
+ let pinned_tag = "rnt/pinned/1"
+
+ let identity ?pinned plan =
+ let derivation = D.identity plan in
+ match pinned with
+ | None -> derivation
+ | Some branch ->
+ pinned_tag ^ Concepts.Hash.to_raw_string derivation ^ Concepts.Hash.to_raw_string branch
+ |> Bytes.of_string
+ |> Concepts.Hash.hash_of_bytes
+
+ class ephemeral (plan : D.plan) address declaration description edges =
+ object (self)
+ inherit Lifecycle.counted
+ inherit Protocols.Generative.membership
+ val heading = Concepts.Mode.attributes_of_map description
+ method destroy = List.iter Protocols.Handle.release edges
+ method predicate = Ok None
+ method local_constraints = Ok None
+ method modes = Ok declaration
+
+ (* A cursor holds a reference to its relation, so the edges it reads
+ outlive a release of the relation that produced it. *)
+ method generate binding =
+ D.generate plan binding
+ |> Result.map (fun cursor ->
+ new Lifecycle.retaining cursor (self :> Lifecycle.lifecycle) |> Protocols.Handle.make )
+
+ method enumerate = self#generate Protocols.Generative.nothing
+ method describe () = Ok (Protocols.Schematics.Relation description)
+
+ method protocols : Protocols.Handle.protocol list =
+ let carried =
+ [ Protocols.Relation.make self;
+ Protocols.Schematics.make self;
+ Protocols.Generative.make self ]
+ in
+ if Concepts.Mode.exhaustible declaration BatSet.String.empty then
+ Protocols.Enumerable.make self :: carried
+ else carried
+
+ method hash : Concepts.Hash.hash = address
+ end
+
+ let derive ?pinned ?(edges = []) plan =
+ let open Utilities.Result in
+ let derived =
+ let* declaration = D.modes plan in
+ let* description = D.describe plan in
+ Ok
+ ( new ephemeral plan (identity ?pinned plan) declaration description edges
+ |> Protocols.Handle.make )
+ in
+ Result.iter_error (fun _ -> List.iter Protocols.Handle.release edges) derived;
+ derived
+end
diff --git a/lib/kernel/EphemeralRelation.mli b/lib/kernel/EphemeralRelation.mli
new file mode 100644
index 0000000..d082394
--- /dev/null
+++ b/lib/kernel/EphemeralRelation.mli
@@ -0,0 +1,36 @@
+module type DERIVATION = sig
+ type plan
+
+ (** the hash of the plan, taken over the branch-local names of its
+ inputs rather than over their content addresses. The idea is to
+ have the same expression denote one object on every branch and
+ at every point in time. *)
+ val identity : plan -> Concepts.Hash.hash
+
+ val describe :
+ plan -> (Protocols.Schematics.relation_description, Concepts.Condition.condition) result
+
+ val modes : plan -> (Concepts.Mode.t, Concepts.Condition.condition) result
+
+ val generate :
+ plan ->
+ Protocols.Generative.binding ->
+ (Protocols.Handle.t, Concepts.Condition.condition) result
+end
+
+module Make (D : DERIVATION) : sig
+ (** unpinned, the identity of the derivation itself; pinned to a
+ branch commit, the identity of that derivation against one
+ reconstructible state, which is the only form for which equal
+ identity implies equal extension. *)
+ val identity : ?pinned:Concepts.Hash.hash -> D.plan -> Concepts.Hash.hash
+
+ (** edges are the handles the derivation reads, which are released
+ when the relation is also. Rejection of a derivation surfaces
+ here instead of when a cursor is being drained. *)
+ val derive :
+ ?pinned:Concepts.Hash.hash ->
+ ?edges:Protocols.Handle.t list ->
+ D.plan ->
+ (Protocols.Handle.t, Concepts.Condition.condition) result
+end
diff --git a/lib/kernel/Generator.ml b/lib/kernel/Generator.ml
index 10f6865..204699f 100644
--- a/lib/kernel/Generator.ml
+++ b/lib/kernel/Generator.ml
@@ -10,7 +10,7 @@ let start produce =
let resume k = Effect.Deep.continue k ()
-class producer_cursor produce on_release =
+class producer_cursor produce finally =
object (self)
inherit Lifecycle.null
inherit Identity.of_id
@@ -19,13 +19,18 @@ class producer_cursor produce on_release =
val mutable state : [`Fresh | `Live of (unit, step) Effect.Deep.continuation | `Done] = `Fresh
method protocols : Protocols.Handle.protocol list = Protocols.[Cursor.make self]
+
+ method private finish =
+ state <- `Done;
+ Option.iter (fun f -> f ()) finally
+
method private step : (Concepts.Tuple.t option, Concepts.Condition.condition) result =
let settle = function
| Element (tuple, k) ->
state <- `Live k;
Ok (Some tuple)
| Finished r ->
- state <- `Done;
+ self#finish;
Result.map (fun () -> None) r
in
match state with
@@ -47,12 +52,8 @@ class producer_cursor produce on_release =
Ok Protocols.Cursor.{tuples= BatFingerTree.empty; exhausted= (state = `Done)}
else fill 0 BatFingerTree.empty
- method! release =
- (match state with
- | `Live _ -> Option.iter (fun f -> f ()) on_release
- | `Fresh | `Done -> ());
- state <- `Done
+ method! release = match state with `Fresh | `Live _ -> self#finish | `Done -> ()
end
-let cursor_of ?on_release produce =
- new producer_cursor produce on_release |> Protocols.Handle.make
+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 ff67ecf..772cb6e 100644
--- a/lib/kernel/Lifecycle.ml
+++ b/lib/kernel/Lifecycle.ml
@@ -23,7 +23,18 @@ class virtual counted = object (self)
true
method release =
- Atomic.decr references;
- if Atomic.get references <= 0 then
+ if Atomic.fetch_and_add references (-1) = 1 then
self#destroy
end
+
+class retaining inner (owner : lifecycle) = object
+ inherit counted
+ initializer ignore owner#reference
+
+ method destroy =
+ Protocols.Handle.release inner;
+ owner#release
+
+ method hash = Protocols.Handle.hash inner
+ method protocols = Protocols.Handle.protocols inner
+end
diff --git a/lib/kernel/Lifecycle.mli b/lib/kernel/Lifecycle.mli
index f110768..fa3abee 100644
--- a/lib/kernel/Lifecycle.mli
+++ b/lib/kernel/Lifecycle.mli
@@ -10,3 +10,13 @@ end
class null : lifecycle
class virtual counted : controlled_lifecycle
+
+(** stands for [inner], carrying what it carries, while holding a
+ reference to [owner], so what [inner] reads outlives a release of
+ the object that produced it. *)
+class retaining : Protocols.Handle.t -> lifecycle -> object
+ inherit lifecycle
+ method destroy : unit
+ method hash : Concepts.Hash.hash
+ method protocols : Protocols.Handle.protocol list
+end
diff --git a/lib/kernel/Storage.ml b/lib/kernel/Storage.ml
index 665b1a7..d50735c 100644
--- a/lib/kernel/Storage.ml
+++ b/lib/kernel/Storage.ml
@@ -36,11 +36,14 @@ module Make (S : Abstract.Storage.STORAGE) = struct
let _ = S.abort tx in
Error condition
- let with_transaction connection body =
+ let within start connection body =
let open Utilities.Result in
- let* tx = S.start connection in
+ let* tx = start connection in
body tx |> finish tx
+ let with_transaction connection = within S.start connection
+ let with_read connection = within S.start_read connection
+
let store_blob tx blob =
let open Utilities.Result in
let address = Concepts.Hash.hash_of_blob blob in
diff --git a/lib/kernel/Storage.mli b/lib/kernel/Storage.mli
index 3c036ca..53bb666 100644
--- a/lib/kernel/Storage.mli
+++ b/lib/kernel/Storage.mli
@@ -12,6 +12,7 @@ module Make : functor (S : Abstract.Storage.STORAGE) -> sig
val finish : S.transaction -> ('a, Concepts.Condition.condition) result -> ('a, Concepts.Condition.condition) result
val with_transaction : S.connection -> (S.transaction -> ('a, Concepts.Condition.condition) result) -> ('a, Concepts.Condition.condition) result
+ val with_read : S.connection -> (S.transaction -> ('a, Concepts.Condition.condition) result) -> ('a, Concepts.Condition.condition) result
val store_blob : S.transaction -> Concepts.Blob.t -> (Concepts.Hash.hash, Concepts.Condition.condition) result
val get_req: S.transaction -> S.address -> (Concepts.Blob.t, Concepts.Condition.condition) result
end
diff --git a/lib/kernel/SubstantialRelation.ml b/lib/kernel/SubstantialRelation.ml
index 136c146..52ce83c 100644
--- a/lib/kernel/SubstantialRelation.ml
+++ b/lib/kernel/SubstantialRelation.ml
@@ -120,30 +120,34 @@ module Make (S : Abstract.Storage.STORAGE) = struct
kvs
|> List.map (fun (name, domain) ->
Concepts.Encoding.Bencode.as_string domain
- |> Result.map (fun domain -> (name, domain)))
+ |> Result.map (fun domain ->
+ (name, {Protocols.Schematics.domain; provenance= []}) ) )
|> Utilities.List.sequence
- |> Result.map (fun kvs ->
- List.fold_left
- (fun acc (name, domain) -> BatMap.String.add name domain acc)
- BatMap.String.empty kvs)
+ |> Result.map BatMap.String.of_list
- let enumerate storage relation =
+ let generate storage relation binding =
let open Utilities.Result in
- let* tx = S.start storage in
- let* node = tuple_node tx relation in
- let closed = ref false in
- let close () = if not !closed then begin closed := true; ignore (S.abort tx) end in
+ let* tx = S.start_read storage in
+ let close () = ignore (S.abort tx) in
+ let* node = tuple_node tx relation |> Result.map_error (fun c -> close (); c) in
(* The fold is suspended by [yield], so the tree is walked one node
and one tuple at a time rather than materialized before the
first tuple is produced. *)
let produce ~yield =
- let walked = TupleSet.iter tx (fun _ tuple -> yield tuple) node in
- close ();
- walked
+ TupleSet.iter tx
+ (fun _ tuple -> if Protocols.Generative.satisfies binding tuple then yield tuple)
+ node
in
- Ok (Generator.cursor_of ~on_release:close produce)
+ Ok (Generator.cursor_of ~finally:close produce)
+
+ let enumerate storage relation = generate storage relation Protocols.Generative.nothing
+
+ let modes_of description =
+ Concepts.Mode.of_list
+ [ Concepts.Mode.enumerable Concepts.Cardinality.Finite;
+ {Concepts.Mode.bound= Concepts.Mode.attributes_of_map description; affords= Decides} ]
- class relation storage value =
+ class relation storage value description =
object (self)
inherit Lifecycle.null
@@ -151,18 +155,16 @@ module Make (S : Abstract.Storage.STORAGE) = struct
val storage : S.connection = storage
val relation : t = value
+ val declaration = modes_of description
method predicate = Ok relation.predicate
method local_constraints = Ok relation.local_constraints
(* The protocol takes a tuple, not bytes: encoding is this object's business, and a caller
that had to produce the exact stored bytes would have to know this encoding to do it. *)
method contains (tuple : Concepts.Tuple.t) =
- SI.with_transaction storage (fun tx -> contains_tuple tx relation tuple)
+ SI.with_read storage (fun tx -> contains_tuple tx relation tuple)
- method describe () =
- let open Utilities.Result in
- let* description = SI.with_transaction storage (fun tx -> schema_of tx relation) in
- Ok (Protocols.Schematics.Relation description)
+ method describe () = Ok (Protocols.Schematics.Relation description)
(* A substantial relation is finitely enumerable, so it carries [Enumerable] as well as
[Relation]. A procedural relation would carry only the latter, which is how an evaluator
@@ -171,30 +173,39 @@ module Make (S : Abstract.Storage.STORAGE) = struct
no name resolution, but cancellation should eventually be checked between tuples. *)
method enumerate = enumerate storage relation
+ method modes : (Concepts.Mode.t, Concepts.Condition.condition) result = Ok declaration
+ method generate binding = generate storage relation binding
+
method protocols : Protocols.Handle.protocol list =
[ Protocols.Relation.make self
; Protocols.Enumerable.make self
+ ; Protocols.Generative.make self
; Protocols.Schematics.make self ]
method hash = hash relation
end
+ (* A relation never changes its schema, so it is read once here and
+ the description and modes are served from it on every request. *)
+ let instantiate tx conn relation =
+ schema_of tx relation
+ |> Result.map (fun description -> new relation conn relation description |> Protocols.Handle.make)
+
let make conn ~schematics ?predicate ?local_constraints ?indexes () =
let open Utilities.Result in
SI.with_transaction conn (fun tx ->
let* relation = empty tx ~schematics ?predicate ?local_constraints ?indexes () in
let* _ = store tx relation in
- Ok (new relation conn relation |> Protocols.Handle.make) )
+ instantiate tx conn relation )
let load_value tx addr =
let open Utilities.Result in
let* data = SI.get_req tx (S.Hash addr) in
Representation.of_blob data
- let wrap conn relation =
- new relation conn relation |> Protocols.Handle.make |> Result.ok
+ let wrap conn relation = SI.with_read conn (fun tx -> instantiate tx conn relation)
let load tx conn addr =
- load_value tx addr |> Utilities.Result.fmap (wrap conn)
+ load_value tx addr |> Utilities.Result.fmap (instantiate tx conn)
let assert_tuple tx relation tuple =
let open Utilities.Result in
diff --git a/lib/kernel/SubstantialRelation.mli b/lib/kernel/SubstantialRelation.mli
index 5061e5d..a36c802 100644
--- a/lib/kernel/SubstantialRelation.mli
+++ b/lib/kernel/SubstantialRelation.mli
@@ -47,18 +47,10 @@ module Make (S : Abstract.Storage.STORAGE) : sig
val contains_tuple :
S.transaction -> t -> Concepts.Tuple.t -> (bool, Concepts.Condition.condition) result
- (** decodes the schematics at [schematics relation] -- the body of
- [Protocols.Schematics.describe] for this kind of relation. *)
val schema_of :
S.transaction ->
t ->
(Protocols.Schematics.relation_description, Concepts.Condition.condition) result
- (** a handle carrying [Protocols.Cursor] over every tuple currently
- asserted, decoded via [Concepts.Tuple.Representation.of_blob] --
- the body of [Protocols.Relation.enumerate] for this kind of
- relation. Opens its own storage transaction, held for the
- cursor's whole lifetime rather than just this call -- see
- [Kernel.Generator] and docs/design/evaluator.md, "Shape". *)
val enumerate : S.connection -> t -> (Protocols.Handle.t, Concepts.Condition.condition) result
end