Cbeam
Loading...
Searching...
No Matches
named_recursive_mutex.hpp
Go to the documentation of this file.
1/*
2Copyright (c) 2025 acrion innovations GmbH
3Authors: Stefan Zipproth, s.zipproth@acrion.ch
4
5This file is part of Cbeam, see https://github.com/acrion/cbeam and https://cbeam.org
6
7Cbeam is offered under a commercial and under the AGPL license.
8For commercial licensing, contact us at https://acrion.ch/sales. For AGPL licensing, see below.
9
10AGPL licensing:
11
12Cbeam is free software: you can redistribute it and/or modify
13it under the terms of the GNU Affero General Public License as published by
14the Free Software Foundation, either version 3 of the License, or
15(at your option) any later version.
16
17Cbeam is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20GNU Affero General Public License for more details.
21
22You should have received a copy of the GNU Affero General Public License
23along with Cbeam. If not, see <https://www.gnu.org/licenses/>.
24*/
25
26#pragma once
27
29
30#ifdef _WIN32
32#else
33 #include <cbeam/config.hpp>
34
35 #if HAVE_PTHREAD_H
36 #include <pthread.h> // for pthread_mutex_t, pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock, pthread_mutexattr_destroy, pthread_mutexattr_init, pthread_mutexattr_setpshared, pthread_mutexattr_settype, PTHREA...
37 #endif
38
39 #if HAVE_SYS_MMAN_H
40 #include <sys/mman.h> // for munmap, mmap, shm_open, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE
41 #endif
42
43 #if HAVE_UNISTD_H
44 #include <unistd.h> // for close, ftruncate, NULL
45 #endif
46
47 #if HAVE_FCNTL_H
48 #include <fcntl.h> // for O_CREAT, O_RDWR, S_IRUSR, S_IWUSR
49 #endif
50
51 #if HAVE_POSIX_SHM_H
52 #include <sys/posix_shm.h> // for PSHMNAMLEN
53 #endif
54#endif
55
56#include <string> // for std::operator+, std::string
57
58namespace cbeam::concurrency
59{
60 inline std::size_t get_max_shm_name_length()
61 {
62#if _WIN32
63 return MAX_PATH;
64#elif defined(__APPLE__)
65 return PSHMNAMLEN; // under __APPLE__ the maximum shm name length is not NAME_MAX, but PSHMNAMLEN, which is shorter
66#elif defined(NAME_MAX)
67 return NAME_MAX;
68#else
69 #error Unsupported platform
70#endif
71 }
72
85 {
86 public:
97 named_recursive_mutex(const std::string& name)
98 {
99 if (name.length() > get_max_shm_name_length())
100 {
101 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex: '" + name + "' exceeds maximum lengths for shm names of " + std::to_string(get_max_shm_name_length()));
102 }
103
104#ifdef _WIN32
105 _mutex = CreateMutexA(NULL, FALSE, name.c_str());
106 if (_mutex == NULL)
107 {
108 throw cbeam::error::system_error("Failed to create mutex: " + name);
109 }
110#else
111 int fd = shm_open(name.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
112 bool is_new = true;
113
114 if (fd == -1)
115 {
116 if (errno == EEXIST)
117 {
118 fd = shm_open(name.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
119 if (fd == -1)
120 {
121 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex: Failed to open existing shared memory: " + name);
122 }
123 is_new = false;
124 }
125 else
126 {
127 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex: Failed to open shared memory: " + name);
128 }
129 }
130
131 if (is_new) // On macOS, ftruncate fails if the shared memory object, previously created with shm_open using the same name, has already been truncated to the same size by a prior ftruncate call.
132 {
133 if (ftruncate(fd, sizeof(pthread_mutex_t)) == -1)
134 {
135 close(fd);
136 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex: Failed to truncate shared memory: " + name);
137 }
138 }
139
140 void* addr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
141 if (addr == MAP_FAILED)
142 {
143 close(fd);
144 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex: Failed to map shared memory: " + name);
145 }
146
147 close(fd);
148
149 _mutex = reinterpret_cast<pthread_mutex_t*>(addr);
150
151 pthread_mutexattr_t attr;
152 pthread_mutexattr_init(&attr);
153 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
154 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
155
156 if (pthread_mutex_init(_mutex, &attr) != 0)
157 {
158 munmap(addr, sizeof(pthread_mutex_t));
159 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex: Failed to initialize mutex: " + name);
160 }
161
162 pthread_mutexattr_destroy(&attr);
163#endif
164 }
165
172 virtual ~named_recursive_mutex() noexcept
173 {
174#ifdef _WIN32
175 if (_mutex != NULL)
176 {
177 CloseHandle(_mutex);
178 }
179#else
180 pthread_mutex_destroy(_mutex);
181 munmap(_mutex, sizeof(pthread_mutex_t));
182#endif
183 }
184
193 void lock() const
194 {
195#ifdef _WIN32
196 DWORD waitResult = WaitForSingleObject(_mutex, INFINITE);
197 switch (waitResult)
198 {
199 case WAIT_OBJECT_0:
200 break;
201 case WAIT_ABANDONED:
202 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex::lock()");
203 }
204#else
205 if (pthread_mutex_lock(_mutex) != 0)
206 {
207 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex::lock()");
208 }
209#endif
210 }
211
220 void unlock() const
221 {
222#ifdef _WIN32
223 if (!ReleaseMutex(_mutex))
224 {
225 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex::unlock()");
226 }
227#else
228 if (pthread_mutex_unlock(_mutex) != 0)
229 {
230 throw cbeam::error::system_error("cbeam::concurrency::named_recursive_mutex::unlock()");
231 }
232#endif
233 }
234
235 // Copy and move constructors and assignment operators are deleted to prevent copying and moving of the mutex.
240
241 private:
242#ifdef _WIN32
243 HANDLE _mutex;
244#elif __linux__ || __APPLE__
245 pthread_mutex_t* _mutex;
246#endif
247 };
248}
named_recursive_mutex & operator=(const named_recursive_mutex &)=delete
named_recursive_mutex(const named_recursive_mutex &)=delete
named_recursive_mutex & operator=(named_recursive_mutex &&)=delete
void lock() const
Acquires the mutex lock.
Definition named_recursive_mutex.hpp:193
virtual ~named_recursive_mutex() noexcept
Destructor for named_recursive_mutex.
Definition named_recursive_mutex.hpp:172
void unlock() const
Releases the mutex lock.
Definition named_recursive_mutex.hpp:220
named_recursive_mutex(const std::string &name)
Constructs a named_recursive_mutex with a specified name.
Definition named_recursive_mutex.hpp:97
named_recursive_mutex(named_recursive_mutex &&)=delete
Custom exception class for handling system-level errors in a cross-platform manner.
Definition system_error.hpp:83
Provides concurrency primitives and abstractions for multithreaded programming. It features the power...
Definition message_manager.hpp:47
std::size_t get_max_shm_name_length()
Definition named_recursive_mutex.hpp:60
Header file to manage inclusion of windows.h with specific settings.