1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qsystemsemaphore.h" |
5 | #include "qsystemsemaphore_p.h" |
6 | |
7 | #if QT_CONFIG(systemsemaphore) |
8 | #include <QtCore/q20memory.h> |
9 | #include <errno.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | using namespace QtIpcCommon; |
14 | using namespace Qt::StringLiterals; |
15 | |
16 | inline void QSystemSemaphorePrivate::constructBackend() |
17 | { |
18 | visit(lambda: [](auto p) { q20::construct_at(p); }); |
19 | } |
20 | |
21 | inline void QSystemSemaphorePrivate::destructBackend() |
22 | { |
23 | visit(lambda: [](auto p) { std::destroy_at(p); }); |
24 | } |
25 | |
26 | /*! |
27 | \class QSystemSemaphore |
28 | \inmodule QtCore |
29 | \since 4.4 |
30 | |
31 | \brief The QSystemSemaphore class provides a general counting system semaphore. |
32 | |
33 | A system semaphore is a generalization of \l QSemaphore. Typically, a |
34 | semaphore is used to protect a certain number of identical resources. |
35 | |
36 | Like its lighter counterpart, a QSystemSemaphore can be |
37 | accessed from multiple \l {QThread} {threads}. Unlike QSemaphore, a |
38 | QSystemSemaphore can also be accessed from multiple \l {QProcess} |
39 | {processes}. This means QSystemSemaphore is a much heavier class, so |
40 | if your application doesn't need to access your semaphores across |
41 | multiple processes, you will probably want to use QSemaphore. |
42 | |
43 | Semaphores support two fundamental operations, acquire() and release(): |
44 | |
45 | acquire() tries to acquire one resource. If there isn't a resource |
46 | available, the call blocks until a resource becomes available. Then |
47 | the resource is acquired and the call returns. |
48 | |
49 | release() releases one resource so it can be acquired by another |
50 | process. The function can also be called with a parameter n > 1, |
51 | which releases n resources. |
52 | |
53 | System semaphores are identified by a key, represented by \l QNativeIpcKey. A |
54 | key can be created in a cross-platform manner by using platformSafeKey(). A |
55 | system semaphore is created by the QSystemSemaphore constructor when passed |
56 | an access mode parameter of AccessMode::Create. Once it is created, other |
57 | processes may attach to the same semaphore using the same key and an access |
58 | mode parameter of AccessMode::Open. |
59 | |
60 | Example: Create a system semaphore |
61 | \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 0 |
62 | |
63 | For details on the key types, platform-specific limitations, and |
64 | interoperability with older or non-Qt applications, see the \l{Native IPC |
65 | Keys} documentation. That includes important information for sandboxed |
66 | applications on Apple platforms, including all apps obtained via the Apple |
67 | App Store. |
68 | |
69 | \sa {Inter-Process Communication}, QSharedMemory, QSemaphore |
70 | */ |
71 | |
72 | /*! |
73 | Requests a system semaphore identified by the legacy key \a key. |
74 | */ |
75 | QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode) |
76 | : QSystemSemaphore(legacyNativeKey(key), initialValue, mode) |
77 | { |
78 | } |
79 | |
80 | /*! |
81 | Requests a system semaphore for the specified \a key. The parameters |
82 | \a initialValue and \a mode are used according to the following |
83 | rules, which are system dependent. |
84 | |
85 | In Unix, if the \a mode is \l {QSystemSemaphore::} {Open} and the |
86 | system already has a semaphore identified by \a key, that semaphore |
87 | is used, and the semaphore's resource count is not changed, i.e., \a |
88 | initialValue is ignored. But if the system does not already have a |
89 | semaphore identified by \a key, it creates a new semaphore for that |
90 | key and sets its resource count to \a initialValue. |
91 | |
92 | In Unix, if the \a mode is \l {QSystemSemaphore::} {Create} and the |
93 | system already has a semaphore identified by \a key, that semaphore |
94 | is used, and its resource count is set to \a initialValue. If the |
95 | system does not already have a semaphore identified by \a key, it |
96 | creates a new semaphore for that key and sets its resource count to |
97 | \a initialValue. |
98 | |
99 | In Windows, \a mode is ignored, and the system always tries to |
100 | create a semaphore for the specified \a key. If the system does not |
101 | already have a semaphore identified as \a key, it creates the |
102 | semaphore and sets its resource count to \a initialValue. But if the |
103 | system already has a semaphore identified as \a key it uses that |
104 | semaphore and ignores \a initialValue. |
105 | |
106 | The \l {QSystemSemaphore::AccessMode} {mode} parameter is only used |
107 | in Unix systems to handle the case where a semaphore survives a |
108 | process crash. In that case, the next process to allocate a |
109 | semaphore with the same \a key will get the semaphore that survived |
110 | the crash, and unless \a mode is \l {QSystemSemaphore::} {Create}, |
111 | the resource count will not be reset to \a initialValue but will |
112 | retain the initial value it had been given by the crashed process. |
113 | |
114 | \sa acquire(), key() |
115 | */ |
116 | QSystemSemaphore::QSystemSemaphore(const QNativeIpcKey &key, int initialValue, AccessMode mode) |
117 | : d(new QSystemSemaphorePrivate(key.type())) |
118 | { |
119 | setNativeKey(key, initialValue, mode); |
120 | } |
121 | |
122 | /*! |
123 | The destructor destroys the QSystemSemaphore object, but the |
124 | underlying system semaphore is not removed from the system unless |
125 | this instance of QSystemSemaphore is the last one existing for that |
126 | system semaphore. |
127 | |
128 | Two important side effects of the destructor depend on the system. |
129 | In Windows, if acquire() has been called for this semaphore but not |
130 | release(), release() will not be called by the destructor, nor will |
131 | the resource be released when the process exits normally. This would |
132 | be a program bug which could be the cause of a deadlock in another |
133 | process trying to acquire the same resource. In Unix, acquired |
134 | resources that are not released before the destructor is called are |
135 | automatically released when the process exits. |
136 | */ |
137 | QSystemSemaphore::~QSystemSemaphore() |
138 | { |
139 | d->cleanHandle(); |
140 | } |
141 | |
142 | /*! |
143 | \enum QSystemSemaphore::AccessMode |
144 | |
145 | This enum is used by the constructor and setKey(). Its purpose is to |
146 | enable handling the problem in Unix implementations of semaphores |
147 | that survive a crash. In Unix, when a semaphore survives a crash, we |
148 | need a way to force it to reset its resource count, when the system |
149 | reuses the semaphore. In Windows, where semaphores can't survive a |
150 | crash, this enum has no effect. |
151 | |
152 | \value Open If the semaphore already exists, its initial resource |
153 | count is not reset. If the semaphore does not already exist, it is |
154 | created and its initial resource count set. |
155 | |
156 | \value Create QSystemSemaphore takes ownership of the semaphore and |
157 | sets its resource count to the requested value, regardless of |
158 | whether the semaphore already exists by having survived a crash. |
159 | This value should be passed to the constructor, when the first |
160 | semaphore for a particular key is constructed and you know that if |
161 | the semaphore already exists it could only be because of a crash. In |
162 | Windows, where a semaphore can't survive a crash, Create and Open |
163 | have the same behavior. |
164 | */ |
165 | |
166 | /*! |
167 | This function works the same as the constructor. It reconstructs |
168 | this QSystemSemaphore object. If the new \a key is different from |
169 | the old key, calling this function is like calling the destructor of |
170 | the semaphore with the old key, then calling the constructor to |
171 | create a new semaphore with the new \a key. The \a initialValue and |
172 | \a mode parameters are as defined for the constructor. |
173 | |
174 | This function is useful if the native key was shared from another process. |
175 | See \l{Native IPC Keys} for more information. |
176 | |
177 | \sa QSystemSemaphore(), nativeIpcKey() |
178 | */ |
179 | void QSystemSemaphore::setNativeKey(const QNativeIpcKey &key, int initialValue, AccessMode mode) |
180 | { |
181 | if (key == d->nativeKey && mode == Open) |
182 | return; |
183 | if (!isKeyTypeSupported(type: key.type())) { |
184 | d->setError(e: KeyError, message: tr(sourceText: "%1: unsupported key type" ) |
185 | .arg(a: "QSystemSemaphore::setNativeKey"_L1 )); |
186 | return; |
187 | } |
188 | |
189 | d->clearError(); |
190 | d->cleanHandle(); |
191 | if (key.type() == d->nativeKey.type()) { |
192 | // we can reuse the backend |
193 | d->nativeKey = key; |
194 | } else { |
195 | // we must recreate the backend |
196 | d->destructBackend(); |
197 | d->nativeKey = key; |
198 | d->constructBackend(); |
199 | } |
200 | d->initialValue = initialValue; |
201 | d->handle(mode); |
202 | } |
203 | |
204 | /*! |
205 | Returns the key assigned to this system semaphore. The key is the |
206 | name by which the semaphore can be accessed from other processes. |
207 | |
208 | You can use the native key to access system semaphores that have not been |
209 | created by Qt, or to grant access to non-Qt applications. See \l{Native IPC |
210 | Keys} for more information. |
211 | |
212 | \sa setNativeKey() |
213 | */ |
214 | QNativeIpcKey QSystemSemaphore::nativeIpcKey() const |
215 | { |
216 | return d->nativeKey; |
217 | } |
218 | |
219 | /*! |
220 | This function works the same as the constructor. It reconstructs |
221 | this QSystemSemaphore object. If the new \a key is different from |
222 | the old key, calling this function is like calling the destructor of |
223 | the semaphore with the old key, then calling the constructor to |
224 | create a new semaphore with the new \a key. The \a initialValue and |
225 | \a mode parameters are as defined for the constructor. |
226 | |
227 | \sa QSystemSemaphore(), key() |
228 | */ |
229 | void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode) |
230 | { |
231 | setNativeKey(key: legacyNativeKey(key), initialValue, mode); |
232 | } |
233 | |
234 | /*! |
235 | Returns the legacy key assigned to this system semaphore. The key is the |
236 | name by which the semaphore can be accessed from other processes. |
237 | |
238 | \sa setKey() |
239 | */ |
240 | QString QSystemSemaphore::key() const |
241 | { |
242 | return QNativeIpcKeyPrivate::legacyKey(key: d->nativeKey); |
243 | } |
244 | |
245 | /*! |
246 | Acquires one of the resources guarded by this semaphore, if there is |
247 | one available, and returns \c true. If all the resources guarded by this |
248 | semaphore have already been acquired, the call blocks until one of |
249 | them is released by another process or thread having a semaphore |
250 | with the same key. |
251 | |
252 | If false is returned, a system error has occurred. Call error() |
253 | to get a value of QSystemSemaphore::SystemSemaphoreError that |
254 | indicates which error occurred. |
255 | |
256 | \sa release() |
257 | */ |
258 | bool QSystemSemaphore::acquire() |
259 | { |
260 | return d->modifySemaphore(count: -1); |
261 | } |
262 | |
263 | /*! |
264 | Releases \a n resources guarded by the semaphore. Returns \c true |
265 | unless there is a system error. |
266 | |
267 | Example: Create a system semaphore having five resources; acquire |
268 | them all and then release them all. |
269 | |
270 | \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 1 |
271 | |
272 | This function can also "create" resources. For example, immediately |
273 | following the sequence of statements above, suppose we add the |
274 | statement: |
275 | |
276 | \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 2 |
277 | |
278 | Ten new resources are now guarded by the semaphore, in addition to |
279 | the five that already existed. You would not normally use this |
280 | function to create more resources. |
281 | |
282 | \sa acquire() |
283 | */ |
284 | bool QSystemSemaphore::release(int n) |
285 | { |
286 | if (n == 0) |
287 | return true; |
288 | if (n < 0) { |
289 | qWarning(msg: "QSystemSemaphore::release: n is negative." ); |
290 | return false; |
291 | } |
292 | return d->modifySemaphore(count: n); |
293 | } |
294 | |
295 | /*! |
296 | Returns a value indicating whether an error occurred, and, if so, |
297 | which error it was. |
298 | |
299 | \sa errorString() |
300 | */ |
301 | QSystemSemaphore::SystemSemaphoreError QSystemSemaphore::error() const |
302 | { |
303 | return d->error; |
304 | } |
305 | |
306 | /*! |
307 | \enum QSystemSemaphore::SystemSemaphoreError |
308 | |
309 | \value NoError No error occurred. |
310 | |
311 | \value PermissionDenied The operation failed because the caller |
312 | didn't have the required permissions. |
313 | |
314 | \value KeyError The operation failed because of an invalid key. |
315 | |
316 | \value AlreadyExists The operation failed because a system |
317 | semaphore with the specified key already existed. |
318 | |
319 | \value NotFound The operation failed because a system semaphore |
320 | with the specified key could not be found. |
321 | |
322 | \value OutOfResources The operation failed because there was |
323 | not enough memory available to fill the request. |
324 | |
325 | \value UnknownError Something else happened and it was bad. |
326 | */ |
327 | |
328 | /*! |
329 | Returns a text description of the last error that occurred. If |
330 | error() returns an \l {QSystemSemaphore::SystemSemaphoreError} {error |
331 | value}, call this function to get a text string that describes the |
332 | error. |
333 | |
334 | \sa error() |
335 | */ |
336 | QString QSystemSemaphore::errorString() const |
337 | { |
338 | return d->errorString; |
339 | } |
340 | |
341 | void QSystemSemaphorePrivate::setUnixErrorString(QLatin1StringView function) |
342 | { |
343 | // EINVAL is handled in functions so they can give better error strings |
344 | switch (errno) { |
345 | case EPERM: |
346 | case EACCES: |
347 | errorString = QSystemSemaphore::tr(sourceText: "%1: permission denied" ).arg(a: function); |
348 | error = QSystemSemaphore::PermissionDenied; |
349 | break; |
350 | case EEXIST: |
351 | errorString = QSystemSemaphore::tr(sourceText: "%1: already exists" ).arg(a: function); |
352 | error = QSystemSemaphore::AlreadyExists; |
353 | break; |
354 | case ENOENT: |
355 | errorString = QSystemSemaphore::tr(sourceText: "%1: does not exist" ).arg(a: function); |
356 | error = QSystemSemaphore::NotFound; |
357 | break; |
358 | case ERANGE: |
359 | case ENOSPC: |
360 | case EMFILE: |
361 | errorString = QSystemSemaphore::tr(sourceText: "%1: out of resources" ).arg(a: function); |
362 | error = QSystemSemaphore::OutOfResources; |
363 | break; |
364 | case ENAMETOOLONG: |
365 | errorString = QSystemSemaphore::tr(sourceText: "%1: key too long" ).arg(a: function); |
366 | error = QSystemSemaphore::KeyError; |
367 | break; |
368 | default: |
369 | errorString = QSystemSemaphore::tr(sourceText: "%1: unknown error: %2" ) |
370 | .arg(args&: function, args: qt_error_string(errno)); |
371 | error = QSystemSemaphore::UnknownError; |
372 | #if defined QSYSTEMSEMAPHORE_DEBUG |
373 | qDebug() << errorString << "key" << key << "errno" << errno << EINVAL; |
374 | #endif |
375 | } |
376 | } |
377 | |
378 | bool QSystemSemaphore::isKeyTypeSupported(QNativeIpcKey::Type type) |
379 | { |
380 | if (!isIpcSupported(ipcType: IpcType::SystemSemaphore, type)) |
381 | return false; |
382 | using Variant = decltype(QSystemSemaphorePrivate::backend); |
383 | return Variant::staticVisit(keyType: type, lambda: [](auto ptr) { |
384 | using Impl = std::decay_t<decltype(*ptr)>; |
385 | return Impl::runtimeSupportCheck(); |
386 | }); |
387 | } |
388 | |
389 | QNativeIpcKey QSystemSemaphore::platformSafeKey(const QString &key, QNativeIpcKey::Type type) |
390 | { |
391 | return QtIpcCommon::platformSafeKey(key, ipcType: IpcType::SystemSemaphore, type); |
392 | } |
393 | |
394 | QNativeIpcKey QSystemSemaphore::legacyNativeKey(const QString &key, QNativeIpcKey::Type type) |
395 | { |
396 | return QtIpcCommon::legacyPlatformSafeKey(key, ipcType: IpcType::SystemSemaphore, type); |
397 | } |
398 | |
399 | QT_END_NAMESPACE |
400 | |
401 | #include "moc_qsystemsemaphore.cpp" |
402 | |
403 | #endif // QT_CONFIG(systemsemaphore) |
404 | |