diff --git a/src/CountingSemaphore.hh b/src/CountingSemaphore.hh new file mode 100644 index 0000000..ddd632c --- /dev/null +++ b/src/CountingSemaphore.hh @@ -0,0 +1,69 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef LIBPEPADAPTER_COUNTING_SEMAPHORE_HH +#define LIBPEPADAPTER_COUNTING_SEMAPHORE_HH + +#include +#include +#include + +namespace pEp { + class CountingSemaphore { + std::mutex mtx; + std::condition_variable cv; + + // To synchronize threads, ALWAYS use types + std::atomic_uint _count; + + public: + CountingSemaphore(unsigned count = 0) : _count(count) {} + + // Change the stored count. + CountingSemaphore& operator=(unsigned count) + { + std::unique_lock lock(mtx); + _count.store (count); + if (count != 0) + cv.notify_one (); + return *this; + } + + // Return the current stored count. + unsigned load() + { + return _count.load (); + } + + // Atomically decrement the stored count, blocking in case the count + // is currently zero. When the method terminates the counter is + // guaranteed to have been decremented by one unit. + // + // This is Dijkstra's P operation, used to atomically acquire a resource. + void p() + { + std::unique_lock lock(mtx); + // FIXME: is the loop even needed? Any received notification will + // wake up only one thread, which will see the count as non-zero... + // I guess an if conditional would work just as well as a while + // loop, but I will follow the canonical pattern just in case some + // new bizarre operation other than V is added later. + while (_count.load() == 0) + cv.wait(lock); + _count --; + } + + // Atomically increment the stored count. This may wake up one thread + // which is currently executing the p method. + // + // This is Dijkstra's V operation, used to atomically release a resource. + void v() + { + std::unique_lock lock(mtx); + _count ++; + cv.notify_one(); + } + }; +} // namespace pEp + +#endif // LIBPEPADAPTER_COUNTING_SEMAPHORE_HH