DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
Semaphore.cpp
Go to the documentation of this file.
1#include "pch.h"
7#include "System/Char.h"
10#include <chrono>
11#include <mutex>
12#include <condition_variable>
13
14#if defined(_WIN32)
15#include <windows.h>
16#endif
17
18namespace DotNetDupe {
19 namespace System {
20 namespace Threading {
21
22 struct Semaphore::Impl {
23 std::mutex mutex;
24 std::condition_variable cv;
25 };
26
27 Semaphore::Semaphore(int initialCount, int maximumCount)
28 : _count(initialCount), _maxCount(maximumCount), _name(""), _hHandle(nullptr), _pImpl(new Impl()) {}
29
30 static bool s_semDummyCreatedNew = false;
31 Semaphore::Semaphore(const String& sName, int initialCount, int maximumCount, bool openAlways)
32 : Semaphore(initialCount, maximumCount, sName, openAlways, s_semDummyCreatedNew) {}
33
34 Semaphore::Semaphore(int initialCount, int maximumCount, const String& sName, bool openAlways)
35 : Semaphore(initialCount, maximumCount, sName, openAlways, s_semDummyCreatedNew) {}
36
37#if defined(_WIN32)
38 static HANDLE OpenOrCreateWin32Semaphore(const std::wstring& wsName, int initialCount, int maximumCount, bool openAlways, bool& bCreatedNew) {
40 HANDLE hHandle = ::CreateSemaphoreW(NULL, initialCount, maximumCount, wsName.c_str());
41 if (hHandle != NULL) {
42 bCreatedNew = (::GetLastError() != ERROR_ALREADY_EXISTS);
43 return hHandle;
44 }
45 if (::GetLastError() == ERROR_ACCESS_DENIED) {
46 throw UnauthorizedAccessException("Access denied creating Semaphore synchronization object.");
47 }
48
50 bCreatedNew = false;
51 if (!openAlways) {
52 throw WaitHandleCannotBeOpenedException("Semaphore creation returned null handle and openAlways is false.");
53 }
54 hHandle = ::OpenSemaphoreW(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, FALSE, wsName.c_str());
55 if (hHandle == NULL) {
56 if (::GetLastError() == ERROR_ACCESS_DENIED) {
57 throw UnauthorizedAccessException("Access denied opening existing Semaphore synchronization object.");
58 }
59 throw WaitHandleCannotBeOpenedException("Failed to open existing semaphore with SYNCHRONIZE access.");
60 }
61 return hHandle;
62 }
63#endif
64
65 Semaphore::Semaphore(int initialCount, int maximumCount, const String& sName, bool openAlways, bool& bCreatedNew)
66 : _count(initialCount), _maxCount(maximumCount), _name(sName), _hHandle(nullptr), _pImpl(new Impl()) {
67#if defined(_WIN32)
69 if (!_name.IsEmpty()) {
70 std::wstring wsName = Utils::StringConvert::Utf8ToWChar(_name.GetRawString());
71 _hHandle = OpenOrCreateWin32Semaphore(wsName, initialCount, maximumCount, openAlways, bCreatedNew);
72 } else {
73 bCreatedNew = true;
74 }
75#else
76 bCreatedNew = true;
77#endif
78 }
79
82#if defined(_WIN32)
83 if (_hHandle != nullptr) {
84 ::CloseHandle((HANDLE)_hHandle);
85 _hHandle = nullptr;
86 }
87#endif
88 if (_pImpl != nullptr) {
89 delete _pImpl;
90 _pImpl = nullptr;
91 }
92 }
93
96 SmartPointer<Semaphore> pResult = nullptr;
97 if (TryOpenExisting(sName, pResult)) {
98 return pResult;
99 }
100 throw WaitHandleCannotBeOpenedException("No semaphore handle of the given name exists.");
101 }
102
105 pResult = SmartPointer<Semaphore>();
106 if (sName.IsEmpty()) return false;
107
108#if defined(_WIN32)
110 std::wstring wsName = Utils::StringConvert::Utf8ToWChar(sName.GetRawString());
111 HANDLE h = ::OpenSemaphoreW(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, FALSE, wsName.c_str());
112 if (h == NULL) return false;
113
115 spSem->_name = sName;
116 spSem->_hHandle = h;
117 pResult = std::move(spSem);
118 return true;
119#else
120 return false;
121#endif
122 }
123
126#if defined(_WIN32)
127 if (_hHandle != nullptr) {
128 DWORD dwWaitResult = ::WaitForSingleObject((HANDLE)_hHandle, INFINITE);
129 return (dwWaitResult == WAIT_OBJECT_0);
130 }
131#endif
132 if (!_pImpl) return false;
133 std::unique_lock<std::mutex> lock(_pImpl->mutex);
134 _pImpl->cv.wait(lock, [this]() { return _count > 0; });
135 --_count;
136 return true;
137 }
138
139 static bool WaitForSemaphoreCv(Semaphore::Impl* pImpl, int& count, int msTimeout) {
141 std::unique_lock<std::mutex> lock(pImpl->mutex);
142 bool bRes = pImpl->cv.wait_for(lock, std::chrono::milliseconds(msTimeout), [&count]() { return count > 0; });
143 if (!bRes) throw TimeoutException("The wait operation timed out.");
144 --count;
145 return true;
146 }
147
148 static int ReleaseSemaphoreCv(Semaphore::Impl* pImpl, int& count, int maxCount, int releaseCount) {
150 std::lock_guard<std::mutex> lock(pImpl->mutex);
151 if (count + releaseCount > maxCount) {
152 throw SemaphoreFullException("Semaphore count exceeded maximum count.");
153 }
154 int prev = count;
155 count += releaseCount;
156 for (int i = 0; i < releaseCount; ++i) pImpl->cv.notify_one();
157 return prev;
158 }
159
160 bool Semaphore::WaitOne(int millisecondsTimeout) {
162#if defined(_WIN32)
163 if (_hHandle != nullptr) {
164 DWORD dwWaitResult = ::WaitForSingleObject((HANDLE)_hHandle, (DWORD)millisecondsTimeout);
165 if (dwWaitResult == WAIT_TIMEOUT) {
166 throw TimeoutException("The wait operation timed out.");
167 }
168 return (dwWaitResult == WAIT_OBJECT_0);
169 }
170#endif
171 if (!_pImpl) return false;
172 return WaitForSemaphoreCv(_pImpl, _count, millisecondsTimeout);
173 }
174
175 int Semaphore::Release(int releaseCount) {
177#if defined(_WIN32)
178 if (_hHandle != nullptr) {
179 LONG previousCount = 0;
180 if (!::ReleaseSemaphore((HANDLE)_hHandle, releaseCount, &previousCount)) {
181 throw SemaphoreFullException("Semaphore count exceeded maximum count.");
182 }
183 return (int)previousCount;
184 }
185#endif
186 if (!_pImpl) return 0;
187 return ReleaseSemaphoreCv(_pImpl, _count, _maxCount, releaseCount);
188 }
189 }
190 }
191}
Represents a Unicode character code point mirroring .NET System.Char.
Limits the number of threads that can access a resource or pool of resources concurrently.
Exception thrown when the Semaphore::Release method is called on a full semaphore.
Provides reference-counted and weak pointer memory management primitives ensuring zero raw ownership.
Utility routines for high-performance UTF-8, UTF-16, and wide-character string conversions.
Defines the exception thrown when the time allotted for a process or operation has expired.
The exception that is thrown when the operating system denies access because of an I/O error or a spe...
Exception thrown when an attempt to open a named system wait handle fails.
A unified smart pointer that supports both unique and shared ownership semantics.
static SmartPointer< T > NewShared()
Creates a Shared SmartPointer, default constructing T.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
Definition String.h:74
const char * GetRawString() const
Definition String.cpp:230
SemaphoreFullException(const String &sMessage)
Initializes a new instance of the SemaphoreFullException class with a specified error message.
bool WaitOne() override
Blocks the current thread until the current WaitHandle receives a signal.
~Semaphore() override
Destructor releasing semaphore resources.
Definition Semaphore.cpp:80
static bool TryOpenExisting(const String &sName, SmartPointer< Semaphore > &pResult)
Opens an existing named system semaphore, and returns a value that indicates whether the operation su...
int Release(int releaseCount=1) override
Exits the semaphore and returns the previous count.
static SmartPointer< Semaphore > OpenExisting(const String &sName)
Opens an existing named system semaphore.
Definition Semaphore.cpp:94
Semaphore(int initialCount, int maximumCount)
Initializes a new instance of the Semaphore class, specifying the initial number of entries and the m...
Definition Semaphore.cpp:27
WaitHandleCannotBeOpenedException(const String &sMessage)
Initializes a new instance of the WaitHandleCannotBeOpenedException class with a specified error mess...
TimeoutException(const String &sMessage)
Initializes a new instance of the TimeoutException class with a specified error message.
UnauthorizedAccessException()
Initializes a new instance of the UnauthorizedAccessException class with a default message.
Definition Exception.cpp:52
static std::wstring Utf8ToWChar(const char *pUtf8Str)
Converts a null-terminated UTF-8 char string into a UTF-16 std::wstring.
static bool WaitForSemaphoreCv(Semaphore::Impl *pImpl, int &count, int msTimeout)
static int ReleaseSemaphoreCv(Semaphore::Impl *pImpl, int &count, int maxCount, int releaseCount)
static HANDLE OpenOrCreateWin32Semaphore(const std::wstring &wsName, int initialCount, int maximumCount, bool openAlways, bool &bCreatedNew)
Definition Semaphore.cpp:38