From c68694163b20b475710d0c1b61d8ae3a79a992fd Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Mon, 7 Dec 2020 14:18:01 +0100
Subject: [PATCH] Add CountingSemaphore

---
 SimoxUtility/CMakeLists.txt                |  2 +
 SimoxUtility/threads.h                     |  1 +
 SimoxUtility/threads/CountingSemaphore.cpp | 42 +++++++++++++++
 SimoxUtility/threads/CountingSemaphore.h   | 61 ++++++++++++++++++++++
 4 files changed, 106 insertions(+)
 create mode 100644 SimoxUtility/threads/CountingSemaphore.cpp
 create mode 100644 SimoxUtility/threads/CountingSemaphore.h

diff --git a/SimoxUtility/CMakeLists.txt b/SimoxUtility/CMakeLists.txt
index 02e16fe9d..2745556a2 100644
--- a/SimoxUtility/CMakeLists.txt
+++ b/SimoxUtility/CMakeLists.txt
@@ -88,6 +88,7 @@ SET(SOURCES
     shapes/json_conversions.cpp
 
     threads/system_thread_id.cpp
+    threads/CountingSemaphore.cpp
 )
 
 SET(INCLUDES
@@ -256,6 +257,7 @@ SET(INCLUDES
     backport/span/gcc_header.h
 
     threads/system_thread_id.h
+    threads/CountingSemaphore.h
 )
 
 simox_generate_subdir_headers(
diff --git a/SimoxUtility/threads.h b/SimoxUtility/threads.h
index cd685bff0..7c9962102 100644
--- a/SimoxUtility/threads.h
+++ b/SimoxUtility/threads.h
@@ -2,4 +2,5 @@
 
 // This file is generated!
 
+#include "threads/CountingSemaphore.h"
 #include "threads/system_thread_id.h"
diff --git a/SimoxUtility/threads/CountingSemaphore.cpp b/SimoxUtility/threads/CountingSemaphore.cpp
new file mode 100644
index 000000000..9e6687e62
--- /dev/null
+++ b/SimoxUtility/threads/CountingSemaphore.cpp
@@ -0,0 +1,42 @@
+#include "CountingSemaphore.h"
+
+
+namespace simox::threads
+{
+
+    CountingSemaphore::CountingSemaphore()
+    {}
+
+    CountingSemaphore::CountingSemaphore(unsigned int count) : _count(count)
+    {}
+
+
+    void CountingSemaphore::notify()
+    {
+        std::lock_guard<std::mutex> lock(_mutex);
+        ++_count;
+        _condition.notify_one();
+    }
+
+    void CountingSemaphore::wait()
+    {
+        std::unique_lock<std::mutex> lock(_mutex);
+        _condition.wait(lock, [this]()
+        {
+            return _count > 0;
+        });
+        --_count;
+    }
+
+    bool CountingSemaphore::try_wait()
+    {
+        std::unique_lock<std::mutex> lock(_mutex);
+        if (_count > 0)
+        {
+            --_count;
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/SimoxUtility/threads/CountingSemaphore.h b/SimoxUtility/threads/CountingSemaphore.h
new file mode 100644
index 000000000..a7bc78f90
--- /dev/null
+++ b/SimoxUtility/threads/CountingSemaphore.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+
+
+namespace simox::threads
+{
+
+    /**
+     * @brief A counting semaphore.
+     *
+     * Threads can enter when the internal count is > 0.
+     * Notifiying the semaphore increments the count and allows threads to enter.
+     * A thread can wait until it may enter. When it enters, it decrements
+     * the internal count.
+     *
+     * Can be used e.g. in a Producer-Consumer pattern.
+     * The producer signals new jobs via `notify()`, while the consumer waits
+     * for new jobs via `wait()`.
+     */
+    class CountingSemaphore
+    {
+    public:
+
+        /// Construct an initially blocking semaphore (initial count 0).
+        CountingSemaphore();
+        /// Construct a semaphore with the given count.
+        CountingSemaphore(unsigned int count);
+
+        /**
+         * @brief Signal that one waiting thread may continue.
+         * Also known as `post()` or `signal()`.
+         */
+        void notify();
+
+        /**
+         * @brief Wait until a thread may enter.
+         */
+        void wait();
+
+        /**
+         * @brief Try to enter. If the semaphore is currently blocking, return false.
+         * @return True if entering was successful, false if semaphore was blocking.
+         */
+        bool try_wait();
+
+
+    private:
+
+        /// The mutex for _condition and _count.
+        std::mutex _mutex;
+        /// The condition variable to wake up waking threads.
+        std::condition_variable _condition;
+
+        /// The current count. Waiting threads may enter when > 0.
+        unsigned int _count = 0;
+
+    };
+
+}
-- 
GitLab