Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/Guide/CONCURRENCY.md
diff options
context:
space:
mode:
authorLeitwolf112026-10-18 20:00:39 +0300
committerLeitwolf112026-10-18 20:00:39 +0300
commit8d1fb4807edc7920fbecd0554c9c61625827e19c (patch)
treedd3d7e1d4f48ca27140a2bd5f970817ad02ae35a /Guide/CONCURRENCY.md
downloadprogmasoft-cpp-8d1fb4807edc7920fbecd0554c9c61625827e19c.tar.gz
Write the first revision of Progmasoft C++ Style
Progmasoft C++ Style is a style guide for C++20, written for anyone who wants a complete rule set to adopt. It writes down the conventions the Progmasoft C++ projects already follow (naming, the clang-format and clang-tidy profiles, exception-free error handling, Doxygen) and decides what they left open. Thirteen chapters under Guide/: 1. Principles: what the guide optimizes for and how to decide a case it does not cover. 2. Formatting: the full .clang-format profile, the reason for each choice, and the rules the tool cannot apply. 3. Naming: casing for every kind of name, acronyms, choosing words, namespaces, files. 4. Files and headers: extensions, #pragma once, includes, what goes in a header, the order inside a class, file size, vendored code. 5. Language: functions, auto, initialization, const and constexpr, casts, integers, strings, enumerations, templates and concepts, lambdas, control flow, and the features not used. 6. Types and classes: struct or class, final, construction, the rule of zero, inheritance, operators, strong types, optional and variant. 7. Errors: no exceptions by default, Expected and Error, invariants, fatal defects, destructors, diagnostics. 8. Ownership and memory: owners and borrowers, views and lifetimes, RAII, containers and deterministic iteration, allocation, undefined behavior. 9. Comments and documentation: Doxygen for public declarations, comments that explain why, TODOs that name an issue. 10. C interfaces: C11 headers, prefixes, opaque types, ownership and failure across the boundary, stability. 11. Concurrency: sharing less, threads, locks, thread-safety annotations, atomics, statics. 12. Testing: unit tests, fuzzing, sanitizers, benchmarks, coverage. 13. Tooling: the compiler flags, the full .clang-tidy profile with the reason for every disabled check, other checks, version pinning. The guide prescribes no test, fuzzing or benchmark framework and no error library; it prescribes how they are used. ADOPTING.md explains how a project points to the guide, copies the profiles and records its deviations. CONTRIBUTING.md explains how to propose a change. The text is licensed under CC BY 4.0. The configuration and code examples are dedicated to the public domain under CC0 1.0, so projects can copy them under their own license. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'Guide/CONCURRENCY.md')
-rw-r--r--Guide/CONCURRENCY.md73
1 files changed, 73 insertions, 0 deletions
diff --git a/Guide/CONCURRENCY.md b/Guide/CONCURRENCY.md
new file mode 100644
index 0000000..9099de3
--- /dev/null
+++ b/Guide/CONCURRENCY.md
@@ -0,0 +1,73 @@
+<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> -->
+<!-- SPDX-License-Identifier: CC-BY-4.0 -->
+
+# 11. Concurrency
+
+Concurrent code is correct only when it is correct for every interleaving, and testing finds few of them. The rules
+below make the shared state small and visible, so that a reader can check it.
+
+## 11.1 Share less
+
+- **Prefer no sharing**: give each thread its own data and combine the results at the end. A result that several
+ threads compute is merged in a fixed order, so the output does not depend on scheduling.
+- **Immutable data may be shared freely.** An object that is fully built before the threads start and never changed
+ afterwards needs no lock.
+- **Everything else that more than one thread touches** is protected by one mutex, or is an atomic, and the comment of
+ the member says which.
+
+## 11.2 Threads
+
+- **`std::jthread`**, which joins on destruction and carries a stop token, instead of `std::thread`.
+- **No detached threads.** Every thread has an owner that joins it.
+- **A thread pool or an executor** for many short tasks, owned by the component that needs it, not a global.
+- **Name long-lived threads** when the platform allows it, so they are recognizable in a debugger and a profiler.
+
+## 11.3 Locks
+
+- **`std::scoped_lock`** to lock, and `std::unique_lock` only when a condition variable or a deferred lock needs it.
+ Never call `lock()` and `unlock()` by hand.
+- **Hold a lock for as short as possible**, and never while calling code you do not control: a callback, a virtual
+ function of an unknown implementation, I/O.
+- **Lock several mutexes with one `std::scoped_lock`**, which avoids deadlock, or document a lock order and keep to it.
+- **A condition variable waits with a predicate**: `condition.wait(lock, [&] { return ready; })`, never a bare `wait`,
+ which wakes spuriously.
+
+## 11.4 Thread-safety annotations
+
+With Clang, annotate the shared state and the functions that need a lock, and build with `-Wthread-safety` as an error:
+
+```cpp
+class Cache final
+{
+public:
+ void
+ Insert(Key key, Value value) EXAMPLE_EXCLUDES(mutex_);
+
+private:
+ Mutex mutex_;
+ std::unordered_map<Key, Value> entries_ EXAMPLE_GUARDED_BY(mutex_);
+};
+```
+
+The annotation macros wrap Clang's `capability`, `guarded_by`, `requires_capability` and `locks_excluded` attributes,
+and expand to nothing on other compilers. `Mutex` is a thin wrapper of `std::mutex` that carries the `capability`
+attribute, because the standard type does not.
+
+## 11.5 Atomics
+
+- **An atomic is for one value**, such as a counter or a flag. Two atomics that must change together need a mutex.
+- **Sequentially consistent by default**: `counter.fetch_add(1)`. A weaker memory order (`acquire`, `release`,
+ `relaxed`) needs a comment that names the operation it synchronizes with and why the weaker order is enough.
+- **No `volatile` for synchronization.** It is for memory-mapped hardware, not for threads.
+
+## 11.6 Statics and globals
+
+- A function-local `static` that is initialized once is thread-safe in C++; use it for a lazily built constant table.
+ Do not use it for mutable state.
+- No mutable global state. When a process-wide resource is unavoidable, such as a signal handler or a C library that
+ must be initialized once, wrap it in one object with a documented lifetime.
+
+## 11.7 Testing concurrent code
+
+ThreadSanitizer runs the concurrent tests in CI (see [Testing](TESTING.md#123-sanitizers)). A test that relies on
+`sleep` to order threads is wrong; it uses a latch, a barrier or a condition the threads wait for.