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

source code of qtbase/src/corelib/ipc/qsystemsemaphore.cpp