VLink  2.0.0
A high-performance communication middleware
semaphore.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file semaphore.h
26  * @brief Counting semaphore confined to a single process.
27  *
28  * @details
29  * @c vlink::Semaphore offers classic Dijkstra P/V semantics for in-process synchronisation,
30  * built on top of @c std::mutex and the project @c ConditionVariable (which uses
31  * @c CLOCK_MONOTONIC on Linux so NTP-induced jumps do not skew @c wait timeouts). It is
32  * the right primitive for permit-style throttling, bounded-queue back-pressure, and
33  * producer/consumer handoff.
34  *
35  * Wait/post interaction between two threads:
36  *
37  * @verbatim
38  * Producer Consumer
39  * | |
40  * | release(n) -- counter+=n ---> |
41  * | | acquire(m): blocks while counter<m
42  * | | counter -= m; resumes
43  * @endverbatim
44  *
45  * @note
46  * - @c release() acquires the internal mutex, so it must not be called from a signal
47  * handler. Use @c SysSemaphore or @c sem_post directly for signal-context posts.
48  * - @c reset(true) wakes every blocked waiter and returns @c false to them; use only
49  * during controlled shutdown.
50  *
51  * @par Example
52  * @code
53  * vlink::Semaphore sem(0);
54  *
55  * std::thread producer([&] {
56  * do_work();
57  * sem.release();
58  * });
59  *
60  * if (sem.acquire(1, 100)) {
61  * consume_result();
62  * } else {
63  * handle_timeout();
64  * }
65  *
66  * producer.join();
67  * @endcode
68  */
69 
70 #pragma once
71 
72 #include <memory>
73 
74 #include "./macros.h"
75 
76 namespace vlink {
77 
78 /**
79  * @class Semaphore
80  * @brief In-process counting semaphore with an optional acquire timeout.
81  *
82  * @details
83  * The internal counter starts at the value supplied to the constructor; @c acquire
84  * decrements it (blocking when not enough permits are available) and @c release
85  * increments it while waking blocked acquirers.
86  */
87 class VLINK_EXPORT Semaphore final {
88  public:
89  /**
90  * @brief Sentinel value for @c acquire() meaning wait indefinitely.
91  */
92  static constexpr int kInfinite{-1};
93 
94  /**
95  * @brief Constructs a semaphore with the given initial permit count.
96  *
97  * @param count Initial permit count. Default: @c 0.
98  */
99  explicit Semaphore(size_t count = 0) noexcept;
100 
101  /**
102  * @brief Destructor. Wakes every blocked acquirer before tearing down internal state.
103  */
104  ~Semaphore() noexcept;
105 
106  /**
107  * @brief Decrements the counter by @p n, blocking when not enough permits are available.
108  *
109  * @details
110  * The call blocks while the counter is less than @p n. A timeout returns @c false
111  * without modifying the counter. When @c reset(true) is invoked while a thread is
112  * blocked, the wait returns @c false and the counter is restored to the initial value.
113  *
114  * @param n Number of permits requested. Default: @c 1.
115  * @param timeout_ms Maximum wait in milliseconds; @c kInfinite waits forever.
116  * @return @c true when the permits were acquired; @c false on timeout or reset.
117  */
118  bool acquire(size_t n = 1, int timeout_ms = kInfinite) noexcept;
119 
120  /**
121  * @brief Increments the counter by @p n and wakes blocked acquirers.
122  *
123  * @details
124  * Broadcasts the condition variable so every blocked acquirer can re-evaluate the
125  * permit count. Safe to call from any thread.
126  *
127  * @param n Number of permits to add. Default: @c 1.
128  */
129  void release(size_t n = 1) noexcept;
130 
131  /**
132  * @brief Restores the counter to the initial value supplied to the constructor.
133  *
134  * @details
135  * When @p interrupt_waiters is @c true every blocked acquirer is woken and its call
136  * returns @c false, which is the recommended way to release waiters during shutdown.
137  *
138  * @param interrupt_waiters When @c true, wakes blocked acquirers. Default: @c false.
139  */
140  void reset(bool interrupt_waiters = false) noexcept;
141 
142  /**
143  * @brief Returns a non-atomic snapshot of the current counter value.
144  *
145  * @details
146  * The value may change immediately after the call returns; treat the result as a
147  * diagnostic-only hint.
148  *
149  * @return Current permit count snapshot.
150  */
151  [[nodiscard]] size_t get_count() const noexcept;
152 
153  private:
154  struct Impl;
155  std::unique_ptr<Impl> impl_;
156 
158 };
159 
160 } // namespace vlink
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174