summaryrefslogtreecommitdiff
path: root/Documentation
diff options
context:
space:
mode:
authorBrian Gianforcaro <bgianf@serenityos.org>2021-09-05 01:21:02 -0700
committerAndreas Kling <kling@serenityos.org>2021-09-05 20:08:57 +0200
commit293e7ccfc703ceb646b395eeaf164b5c197d6bb2 (patch)
tree6b9391829809ce47412f4a90886f4b8f43c09a35 /Documentation
parent3e45c3ed9070209f0114391eb8293fdb4134f14b (diff)
downloadserenity-293e7ccfc703ceb646b395eeaf164b5c197d6bb2.zip
Documentation: Add Patterns.md
The purpose of this document is to track and describe the various patterns used through the SerenityOS code base.
Diffstat (limited to 'Documentation')
-rw-r--r--Documentation/Patterns.md82
1 files changed, 82 insertions, 0 deletions
diff --git a/Documentation/Patterns.md b/Documentation/Patterns.md
new file mode 100644
index 0000000000..d7d1b603c2
--- /dev/null
+++ b/Documentation/Patterns.md
@@ -0,0 +1,82 @@
+# SerenityOS patterns
+
+## Introduction
+
+Over time numerous reoccurring patterns have emerged from or were adopted by
+the serenity code base. This document aims to track and describe them so they
+can be propagated further and keep the code base consistent.
+
+## Intrusive Lists
+
+[Intrusive lists](https://www.data-structures-in-practice.com/intrusive-linked-lists/) are common in the Kernel and in some specific cases
+are used in the SerenityOS userland. A data structure is said to be
+"intrusive" when each element holds the metadata that tracks the
+element's membership in the data structure. In the case of a list, this
+means that every element in an intrusive linked list has a node embedded
+inside of it. The main advantage of intrusive
+data structures is you don't need to worry about handling out of memory (OOM)
+on insertion into the data structure. This means error handling code is
+much simpler than say, using a `Vector` in environments that need to be durable
+to OOM.
+
+The common pattern for declaring an intrusive list is to add the storage
+for the intrusive list node as a private member. A public type alias is
+then used to expose the list type to anyone who might need to create it.
+Here is an example from the `Region` class in the Kernel:
+
+```cpp
+class Region final
+ : public Weakable<Region> {
+
+public:
+
+... snip ...
+
+private:
+ bool m_syscall_region : 1 { false };
+
+ IntrusiveListNode<Region> m_memory_manager_list_node;
+ IntrusiveListNode<Region> m_vmobject_list_node;
+
+public:
+ using ListInMemoryManager = IntrusiveList<Region, RawPtr<Region>, &Region::m_memory_manager_list_node>;
+ using ListInVMObject = IntrusiveList<Region, RawPtr<Region>, &Region::m_vmobject_list_node>;
+};
+```
+
+You can then use the list by referencing the public type alias like so:
+
+```cpp
+class MemoryManager {
+
+... snip ...
+
+ Region::ListInMemoryManager m_kernel_regions;
+ Vector<UsedMemoryRange> m_used_memory_ranges;
+ Vector<PhysicalMemoryRange> m_physical_memory_ranges;
+ Vector<ContiguousReservedMemoryRange> m_reserved_memory_ranges;
+};
+```
+
+## Static Assertions of the size of a type
+
+It's a universal pattern to use `static_assert` to validate the size of a
+type matches the author's expectations. Unfortunately when these assertions
+fail they don't give you the values that actually caused the failure. This
+forces one to go investigate by printing out the size, or checking it in a
+debugger, etc.
+
+For this reason `AK::AssertSize` was added. It exploits the fact that the
+compiler will emit template argument values for compiler errors to provide
+debugging information. Instead of getting no information you'll get the actual
+type sizes in your compiler error output.
+
+Example Usage:
+
+```cpp
+#include <AK/StdLibExtras.h>
+
+struct Empty { };
+
+static_assert(AssertSize<Empty, 1>());
+```