From 9c9b9a9345867ed55a45f01e16ea393fbd8aabbc Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 1 Sep 2026 10:28:51 +0200 Subject: docs: update facet dispatch brief --- docs/briefs/tb0003-facet-based-capability-dispatch.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'docs/briefs') diff --git a/docs/briefs/tb0003-facet-based-capability-dispatch.rst b/docs/briefs/tb0003-facet-based-capability-dispatch.rst index 7027a700..321c2694 100644 --- a/docs/briefs/tb0003-facet-based-capability-dispatch.rst +++ b/docs/briefs/tb0003-facet-based-capability-dispatch.rst @@ -55,7 +55,7 @@ Every facet type — a pure abstract interface, in the ordinary C++ sense — de struct block_special_file { - constexpr auto static id = kapi::capabilities::facet_id{"block"}; + constexpr auto static id = kapi::capabilities::facet_id{"fs.spec.block"}; virtual ~block_special_file() = default; [[nodiscard]] virtual auto read_block(std::size_t block_index, std::span buffer) @@ -133,7 +133,7 @@ A driver has to be able to say *what it claims to support* using the same vocabu // arch/x86_64/arch/bus/isa.hpp struct isa_claim { - constexpr auto static id = kapi::capabilities::facet_id{"isa_claim"}; + constexpr auto static id = kapi::capabilities::facet_id{"clm.drv.isa"}; virtual ~isa_claim() = default; [[nodiscard]] virtual auto supported_names() const -> std::span = 0; }; @@ -264,7 +264,7 @@ The two device-model facets already shown (``isa_signature``/``isa_claim``, ``bl { struct temperature_sensor { - constexpr auto static id = kapi::capabilities::facet_id{"temperature_sensor"}; + constexpr auto static id = kapi::capabilities::facet_id{"sns.cap.temp"}; virtual ~temperature_sensor() = default; [[nodiscard]] virtual auto read_millicelsius() const -> kstd::result = 0; }; @@ -386,7 +386,7 @@ 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 short word 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 currently 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. +* **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 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. -- cgit v1.2.3