aboutsummaryrefslogtreecommitdiff
path: root/docs/briefs/tb0003-facet-based-capability-dispatch.rst
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-09-02 16:33:03 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-09-02 16:33:03 +0200
commitde85ad8d0558d0950506a7e64e7a5f2d48dc8485 (patch)
tree1560e809732fc40c7d99c2190d41f6576476b071 /docs/briefs/tb0003-facet-based-capability-dispatch.rst
parent60b06d950c0716bfcb6dcde84ee248cb09433727 (diff)
downloadkernel-de85ad8d0558d0950506a7e64e7a5f2d48dc8485.tar.xz
kernel-de85ad8d0558d0950506a7e64e7a5f2d48dc8485.zip
docs: add additional brief and guide drafts
Diffstat (limited to 'docs/briefs/tb0003-facet-based-capability-dispatch.rst')
-rw-r--r--docs/briefs/tb0003-facet-based-capability-dispatch.rst8
1 files changed, 7 insertions, 1 deletions
diff --git a/docs/briefs/tb0003-facet-based-capability-dispatch.rst b/docs/briefs/tb0003-facet-based-capability-dispatch.rst
index 321c2694..2cace2f3 100644
--- a/docs/briefs/tb0003-facet-based-capability-dispatch.rst
+++ b/docs/briefs/tb0003-facet-based-capability-dispatch.rst
@@ -387,12 +387,14 @@ Drawbacks and Limitations
Stated as plainly as the benefits above, because a design write-up that only lists advantages is not documenting an architecture — it is advertising one.
* **String-keyed identity has no uniqueness guarantee.** ``facet_id`` equality is ``std::string_view`` content equality. Two unrelated facets that happen to share a literal — a typo, a copy-pasted ``id`` line, or an honest independent choice of the same string by two different students — become, from the mechanism's point of view, *the same facet*. A ``query_facet()`` handling one of them will hand back a pointer, ``static_cast`` will "succeed" in the sense of not crashing, and the caller will silently operate on an object through the wrong type's vtable. Nothing in the type system catches this; it is a runtime, likely intermittent, memory-safety bug. Unlike COM's GUIDs (128 bits, generated to make an accidental collision practically impossible) or Rust's compiler-derived ``TypeId``, there is no mechanism in TeachOS enforcing that two ``facet_id`` values with the same name really do refer to the same facet, or catching it when they do not.
+
+ The project has since narrowed, without closing, this risk by moving from bare, single-word identifiers (an earlier ``isa_claim``, ``block``) to a structured, dotted convention: a short category prefix, a role, and a specific name — ``sig.dev.isa`` and ``clm.drv.isa`` for the ISA identification pair, ``type.dev.bus`` for the structural bus facet, ``prot.bus.base`` for ``bus_protocol``, ``fs.spec.block``/``fs.spec.char`` for the two special-file facets. This is the same instinct behind COM's GUIDs and Java/OSGi's reverse-DNS package names: give every id enough structure that an *accidental* collision needs two authors to independently choose the same category, the same role, and the same specific name, rather than just the same common word. It is a real mitigation — but it is still string equality with no compiler-enforced uniqueness behind it, so a careless copy-paste of a full dotted id (rather than just a bare word) remains exactly as silent a failure as before.
* **The delegation chain is manual and unenforced.** Every ``query_facet()`` override that does not recognize a requested id must remember to fall through to its base class's ``query_facet()``. Forgetting this (easy to do — it compiles cleanly either way) silently makes every facet contributed by the base class unreachable through that subtype, with no compiler diagnostic and no crash — only a ``has_facet()`` that quietly returns false where it should not, discovered (if it is discovered at all) as a device that mysteriously never matches a driver it should have.
* **No enumeration or reflection.** There is no way to ask a ``device`` or a ``facet_registry::entry`` "what facets do you support" in general — only "do you support *this specific* facet", one id at a time. A caller must already know which tag to ask for. This is the direct cost of the expression-problem trade-off described above: the operation "list every facet a device has" is not something the mechanism was designed to answer, and adding it would need either an explicit per-type registration list (defeating the "device layer knows nothing about facets" property) or a hand-maintained catalogue kept separately from every facet definition, with the drift risk that implies.
* **Dispatch cost is linear in the number of facets a concrete type supports**, not constant. Each ``query_facet()`` override is a manual if/else-if chain; a type supporting five facets does up to five string-view comparisons per query, whereas a real vtable slot is one indirect call regardless of how many other virtual functions the type has. For device counts and facet counts in a teaching kernel this is immaterial, but it is a genuine, quantifiable difference from the polymorphism this mechanism deliberately avoids, worth naming rather than glossing over.
* **No interface-versioning discipline yet.** Changing the shape of an existing facet (adding a pure virtual method, changing a signature) breaks every type that implements it, exactly as with any C++ abstract base class — facet dispatch does nothing to soften this. COM's answer (never modify a shipped interface; mint a new GUID for a new shape, and let objects implement both old and new side by side) is a real, well-tested discipline for exactly this problem that TeachOS has not yet adopted. Today, changing a facet's shape means finding and updating every implementer by hand.
* **Inheritance- and composition-published facets are not equally discoverable.** As shown above, a composed facet (``ram_disk``'s ``block_node``) is invisible to ``device.facet<T>()`` and reachable only through the registry, while an inherited facet is reachable both ways. This asymmetry is intentional and documented here, but it is exactly the kind of thing a newcomer gets wrong once before internalizing it.
-* **Thread-safety is the caller's problem, not the mechanism's.** ``query_facet()`` itself performs no synchronization; a facet pointer obtained from a device is only as valid as whatever else is happening to that device concurrently (unbinding, teardown). ``facet_registry`` protects its own internal bookkeeping with a lock and hands back only ``weak_ptr``-backed handles for exactly this reason, but a raw ``T *`` obtained via ``device.facet<T>()`` carries no such protection on its own. The project's general locking primitive, ``kapi::tracked_mutex`` (``kapi/kapi/tracked_mutex.hpp``), is available for a caller that needs to hold a facet pointer across a window where the underlying device could otherwise be torn down; this brief does not attempt to restate the project's broader locking discipline.
+* **Thread-safety is the caller's problem, not the mechanism's.** ``query_facet()`` itself performs no synchronization; a facet pointer obtained from a device is only as valid as whatever else is happening to that device concurrently (unbinding, teardown). ``facet_registry`` protects its own internal bookkeeping with a lock and hands back only ``weak_ptr``-backed handles for exactly this reason, but a raw ``T *`` obtained via ``device.facet<T>()`` carries no such protection on its own. The project's general locking primitive, ``kapi::tracked_mutex`` (``kapi/kapi/tracked_mutex.hpp``), is available for a caller that needs to hold a facet pointer across a window where the underlying device could otherwise be torn down; this brief does not attempt to restate the project's broader locking discipline. See :doc:`tb0004-device-tree-ownership-and-registries` for what ``tracked_mutex`` does and does not guarantee, and for the ``weak_ptr``-based ownership discipline ``facet_registry`` relies on.
When (Not) to Reach for a Facet
-----------------------------------
@@ -428,3 +430,7 @@ References
``kernel/kernel/drivers/storage/ram_disk.cpp`` — the composed-facet example this brief quotes from directly.
``kapi/kapi/tracked_mutex.hpp`` — the locking primitive referenced in *Drawbacks and Limitations*, above.
+
+ :doc:`tb0004-device-tree-ownership-and-registries` — device ownership (``shared_ptr``/``weak_ptr``), the three registries a facet query can reach through, and ``tracked_mutex`` in full.
+
+ :doc:`../guides/device-drivers` — a practical, worked-example walkthrough of matching and binding, which is the other half of what a facet's ``id`` gets used for.