diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/object-system.org | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/docs/object-system.org b/docs/object-system.org new file mode 100644 index 0000000..a2f7929 --- /dev/null +++ b/docs/object-system.org @@ -0,0 +1,149 @@ +* Goals +The object manager: +- works as a uniform interface to the resources exposed by the system; +- provides a consistent way of tracking usage and managing access to parts of the system. + +* The Object System +An object is something that exists as a part of the RNT system. An object has a "type", which denotes what it represents, and "protocols", which denote what it can do. Programs do not interact with objects directly, but do so through a handle. An object keeps track of how many handles to it are currently open, this is its reference count. + +A "protocol"[fn:1] represents the ability of an object to perform a given set of operations. For instance, an object that has a directory protocol can be seen as one that exposes an operation, =list=, that enumerates its children, and another operator, =open=, that, given a key, returns a handle to a specific child. Just like objects, programs do not interact with protocols directly, but do so through an interface. A handle is itself an interface, that, among other things, exposes an operation to retrieve interfaces to additional protocols of the object it represents. + +As a convention, we can denote a protocol as an ML structure as follows: +#+begin_src ocaml + module Directory : sig + type t + + val from : Handle.t -> t option + + val list : t -> string list + val open : t -> string -> Handle.t option + end +#+end_src +In the example above, =Directory= is a protocol, and =Directory.t= is an interface. =list= is an operation that, given a =Directory= interface, enumerates the contents of the corresponding object. =open= is an operation that, given a =Directory= interface and a key, returns a =Handle= interface to the object associated with the given key, if it exists. + +=from= is not an operation of =Directory= per se. Rather, it can be seen as a polymorphic operation belonging to =Handle= that, given a handle and a protocol, returns an interface to that protocol iff it is supported by the corresponding object. + +It is convenient to refer to interfaces using the name of the protocol they belong to, so a "=Directory= interface" is simply a directory, and a "=Handle= interface" is simply a handle. + +What, more concretely, is a =Handle=? A =Handle= is the following protocol: +#+begin_src ocaml + module Handle : sig + type t + + val into : t -> (protocol -> 'a option) -> 'a option + + val copy : t -> t option + val release : t -> unit + end +#+end_src + +=copy= duplicates the handle, if it supports being duplicated. =release= releases the handle, decrementing the reference count of the object. No further operations may be called on a released handle. + +# Should this be inside `Handle`? +=into= deserves some explanation. First, =protocol=, the type, is defined as an extensible variant: +#+begin_src ocaml + type protocol = .. +#+end_src + +Suppose we are to implement the =Directory= module outlined above. We can add a variant to this record, and use =into= to implement =from= in the following manner: + +#+begin_src ocaml + module Directory = struct + type t = < list : string list; open : string -> Handle.t option > + + type protocol += Directory of t + + let from h = Handle.into h (function Directory o -> Some o | _ -> None) + + let list o = o#list + let open o k = o#open k + end +#+end_src + +This gives us a way of implementing interfaces out of a corresponding OCaml object[fn:2]. We are, however, still missing a way of implementing objects themselves. For this, we first need to define what an object is, concretely: + +#+begin_src ocaml + module Object = struct + type t = < reference : unit; release : unit; protocols : protocols list > + end +#+end_src + +How do we construct a =protocol=? Going back to our =Directory= structure, we may modify it slightly: + +#+begin_src ocaml + module Directory : sig + type t + + val from : Handle.t -> t option + + val list : t -> string list + val open : t -> string -> Handle.t option + + module Provider : sig + type provider = < list : string list; open : string -> Handle.t option > + + val make : provider -> protocol + end + end +#+end_src + +=Directory.Provider.make= provides a way of constructing a =Directory= interface out of an OCaml object. =Directory.Provider.provider= has the same structure as the concrete definition of =Directory.t= above, although the similarities should be treated as merely incidental. A handle can be instantiated out of an =Object.t= in the same way: + +#+begin_src ocaml + module Handle : sig + type t + + val into : t -> (protocol -> 'a option) -> 'a option + + val copy : t -> t option + val release : t -> unit + + module Provider : sig + val make : Object.t -> t + end + end +#+end_src + +We can then implement an object exposing a =Directory= protocol as follows: +#+begin_src ocaml + class empty_directory = object (self) + method reference = () + method release = () + + method protocols = [Directory.Provider.make self] + + method list = [] + method open _ = None + end +#+end_src + +[fn:1]: This was originally called a "capability", but represented something different than the term "capability" in "capability-based security". In the latter, an object in our sense could be considered a capability. +[fn:2]: This could just as well have been a record of functions - there is nothing specific to the OCaml object system here. + +* The Object Tree +RNT exposes its resources as a hierarchy of objects. The root of this tree is an object that implements, at least, the =Directory= and =Registry= protocols. A handle to the root of this tree can be obtained by calling =Kernel.ObjectManager.root=. =Registry= is declared as follows: +#+begin_src ocaml + module Registry : sig + type t + + val from : Handle.t -> t option + + val register : key -> Handle.t -> (unit, Concepts.Condition.condition) result + val unregister : key -> (unit, Concepts.Condition.condition) result + end +#+end_src +=register= and =unregister=, as implemented by the root of the hierarchy, allows components to register resources that can be retrieved by users of the system through the corresponding =Directory= interface. The handle passed as an argument to =register= must support being "copy"ed. + +As a convenience, a =path= represents a pattern of traversal the object tree. The path ="branch" @/ "master" @/ "multigroup" @/ "universe" @/ this= represents the object that can be retrieved by, from a given reference point, "open"ing the child denoted by ="branch"= on the given reference point, then "open"ing the child denoted ="master"= on the retrieved object, and so on. Every traversal assumes that the object in question has a =Directory= protocol. The ability to construct and manipulate paths is implemented by the =Path= module: +#+begin_src ocaml + module Path : sig + type path + + val this : path + val (@/) : string -> path -> path + + val lookup : Handle.t -> path -> Handle.t option + end +#+end_src + +=ObjectManager.lookup= is a convenience function that applies =Path.lookup= to the root of the object tree. |
