I received an interesting piece of code during my recent discussion about the blocking queue: a fast semaphore implementation. The fast_semaphore class uses a regular C++ semaphore as fallback for waiting and unblocking the thread(s) while doing its magic using an atomic variable and memory fences of the new C++ memory model (see here and here).
I present to you, my shortened version of, Fast-Semaphore by Joe Seigh; C++ Implementation by Chris Thomasson:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#pragma once #include <mutex> #include <atomic> #include <condition_variable> #include <cassert> class semaphore { public: semaphore(int count) noexcept : m_count(count) { assert(count > -1); } void post() noexcept { { std::unique_lock<std::mutex> lock(m_mutex); ++m_count; } m_cv.notify_one(); } void wait() noexcept { std::unique_lock<std::mutex> lock(m_mutex); m_cv.wait(lock, [&]() { return m_count != 0; }); --m_count; } private: int m_count; std::mutex m_mutex; std::condition_variable m_cv; }; class fast_semaphore { public: fast_semaphore(int count) noexcept : m_count(count), m_semaphore(0) {} void post() { std::atomic_thread_fence(std::memory_order_release); int count = m_count.fetch_add(1, std::memory_order_relaxed); if (count < 0) m_semaphore.post(); } void wait() { int count = m_count.fetch_sub(1, std::memory_order_relaxed); if (count < 1) m_semaphore.wait(); std::atomic_thread_fence(std::memory_order_acquire); } private: std::atomic<int> m_count; semaphore m_semaphore; }; |
Being your semaphore is a full acq-rel cycle (as the underlying mutex locks & unlocks), you can presumably simplify to the following.
Yes, that works fine. I have a habit using the standalone fences wrt my work on the SPARC. Here is an interesting discussion:
https://groups.google.com/d/topic/lock-free/A1nzcMBGRzU/discussion
;^)
Btw, this exact algorithm can be found here:
https://www.haiku-os.org/legacy-docs/benewsletter/Issue1-26.html
I first learned about it from Joe Seigh who worked for IBM in the 80’s and 90’s. Not exactly sure if he helped with the Benaphore.
Thanks for this nice semaphore implementation, I come back time to time when I need something similar. I am wondering what do you think: is it possible to extend this technique to implement binary or counting semaphores without locking in all cases? I am not sure…