CppCommon  1.0.4.1
C++ Common Library
named_semaphore.cpp
Go to the documentation of this file.
1 
10 
11 #include "errors/fatal.h"
13 
14 #include <algorithm>
15 #include <cassert>
16 
17 #if defined(__APPLE__)
18 #include "threads/thread.h"
19 #endif
20 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
21 #include <fcntl.h>
22 #include <semaphore.h>
23 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
24 #include <windows.h>
25 #undef Yield
26 #undef max
27 #undef min
28 #endif
29 
30 namespace CppCommon {
31 
33 
34 class NamedSemaphore::Impl
35 {
36 public:
37  Impl(const std::string& name, int resources) : _name(name), _resources(resources)
38  {
39  assert((resources > 0) && "Named semaphore resources counter must be greater than zero!");
40 
41 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
42  _owner = true;
43  // Try to create a named binary semaphore
44  _semaphore = sem_open(name.c_str(), (O_CREAT | O_EXCL), 0666, resources);
45  if (_semaphore == SEM_FAILED)
46  {
47  // Try to open a named binary semaphore
48  _semaphore = sem_open(name.c_str(), O_CREAT, 0666, resources);
49  if (_semaphore == SEM_FAILED)
50  throwex SystemException("Failed to initialize a named semaphore!");
51  else
52  _owner = false;
53  }
54 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
55  _semaphore = CreateSemaphoreA(nullptr, resources, resources, name.c_str());
56  if (_semaphore == nullptr)
57  throwex SystemException("Failed to create or open a named semaphore!");
58 #endif
59  }
60 
61  ~Impl()
62  {
63 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
64  int result = sem_close(_semaphore);
65  if (result != 0)
66  fatality(SystemException("Failed to close a named semaphore!"));
67  // Unlink the named semaphore (owner only)
68  if (_owner)
69  {
70  result = sem_unlink(_name.c_str());
71  if (result != 0)
72  fatality(SystemException("Failed to unlink a named semaphore!"));
73  }
74 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
75  if (!CloseHandle(_semaphore))
76  fatality(SystemException("Failed to close a named semaphore!"));
77 #endif
78  }
79 
80  const std::string& name() const
81  {
82  return _name;
83  }
84 
85  int resources() const noexcept
86  {
87  return _resources;
88  }
89 
90  bool TryLock()
91  {
92 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
93  int result = sem_trywait(_semaphore);
94  if ((result != 0) && (errno != EAGAIN))
95  throwex SystemException("Failed to try lock a named semaphore!");
96  return (result == 0);
97 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
98  DWORD result = WaitForSingleObject(_semaphore, 0);
99  if ((result != WAIT_OBJECT_0) && (result != WAIT_TIMEOUT))
100  throwex SystemException("Failed to try lock a named semaphore!");
101  return (result == WAIT_OBJECT_0);
102 #endif
103  }
104 
105  bool TryLockFor(const Timespan& timespan)
106  {
107  if (timespan < 0)
108  return TryLock();
109 #if defined(__APPLE__)
110  // Calculate a finish timestamp
111  Timestamp finish = NanoTimestamp() + timespan;
112 
113  // Try to acquire lock at least one time
114  if (TryLock())
115  return true;
116  else
117  {
118  // Try lock or yield for the given timespan
119  while (NanoTimestamp() < finish)
120  {
121  if (TryLock())
122  return true;
123  else
124  Thread::Yield();
125  }
126 
127  // Failed to acquire lock
128  return false;
129  }
130 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__CYGWIN__)
131  struct timespec timeout;
132  timeout.tv_sec = timespan.seconds();
133  timeout.tv_nsec = timespan.nanoseconds() % 1000000000;
134  int result = sem_timedwait(_semaphore, &timeout);
135  if ((result != 0) && (errno != ETIMEDOUT))
136  throwex SystemException("Failed to try lock a named semaphore for the given timeout!");
137  return (result == 0);
138 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
139  DWORD result = WaitForSingleObject(_semaphore, std::max((DWORD)1, (DWORD)timespan.milliseconds()));
140  if ((result != WAIT_OBJECT_0) && (result != WAIT_TIMEOUT))
141  throwex SystemException("Failed to try lock a named semaphore for the given timeout!");
142  return (result == WAIT_OBJECT_0);
143 #endif
144  }
145 
146  void Lock()
147  {
148 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
149  int result = sem_wait(_semaphore);
150  if (result != 0)
151  throwex SystemException("Failed to lock a named semaphore!");
152 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
153  DWORD result = WaitForSingleObject(_semaphore, INFINITE);
154  if (result != WAIT_OBJECT_0)
155  throwex SystemException("Failed to lock a named semaphore!");
156 #endif
157  }
158 
159  void Unlock()
160  {
161 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
162  int result = sem_post(_semaphore);
163  if (result != 0)
164  throwex SystemException("Failed to unlock a named semaphore!");
165 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
166  if (!ReleaseSemaphore(_semaphore, 1, nullptr))
167  throwex SystemException("Failed to unlock a named semaphore!");
168 #endif
169  }
170 
171 private:
172  std::string _name;
173  int _resources;
174 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
175  sem_t* _semaphore;
176  bool _owner;
177 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
178  HANDLE _semaphore;
179 #endif
180 };
181 
183 
184 NamedSemaphore::NamedSemaphore(const std::string& name, int resources)
185 {
186  // Check implementation storage parameters
187  [[maybe_unused]] ValidateAlignedStorage<sizeof(Impl), alignof(Impl), StorageSize, StorageAlign> _;
188  static_assert((StorageSize >= sizeof(Impl)), "NamedSemaphore::StorageSize must be increased!");
189  static_assert(((StorageAlign % alignof(Impl)) == 0), "NamedSemaphore::StorageAlign must be adjusted!");
190 
191  // Create the implementation instance
192  new(&_storage)Impl(name, resources);
193 }
194 
196 {
197  // Delete the implementation instance
198  reinterpret_cast<Impl*>(&_storage)->~Impl();
199 }
200 
201 const std::string& NamedSemaphore::name() const { return impl().name(); }
202 int NamedSemaphore::resources() const noexcept { return impl().resources(); }
203 
204 bool NamedSemaphore::TryLock() { return impl().TryLock(); }
205 bool NamedSemaphore::TryLockFor(const Timespan& timespan) { return impl().TryLockFor(timespan); }
206 
207 void NamedSemaphore::Lock() { impl().Lock(); }
208 void NamedSemaphore::Unlock() { impl().Unlock(); }
209 
210 } // namespace CppCommon
NamedSemaphore(const std::string &name, int resources)
Default class constructor.
int resources() const noexcept
Get the semaphore resources counter.
bool TryLockFor(const Timespan &timespan)
Try to acquire semaphore for the given timespan.
void Unlock()
Release semaphore.
const std::string & name() const
Get the semaphore name.
void Lock()
Acquire semaphore with block.
bool TryLock()
Try to acquire semaphore without block.
static void Yield() noexcept
Yield to other threads.
Definition: thread.cpp:143
#define throwex
Throw extended exception macro.
Definition: exceptions.h:23
Fatal abort execution definition.
#define fatality(...)
Fatal abort execution extended macro.
Definition: fatal.h:22
Named semaphore synchronization primitive definition.
C++ Common project definitions.
Definition: token_bucket.h:15
Semaphore synchronization primitive definition.
Thread definition.
Aligned storage validator definition.