Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsystemsemaphore.cpp
Go to the documentation of this file.
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
6
7#if QT_CONFIG(systemsemaphore)
8#include <QtCore/q20memory.h>
9#include <errno.h>
10
11QT_BEGIN_NAMESPACE
12
13using namespace QtIpcCommon;
14using namespace Qt::StringLiterals;
15
16inline void QSystemSemaphorePrivate::constructBackend()
17{
18 visit([](auto p) { q20::construct_at(p); });
19}
20
21inline void QSystemSemaphorePrivate::destructBackend()
22{
23 visit([](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 */
75QSystemSemaphore::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 */
116QSystemSemaphore::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*/
137QSystemSemaphore::~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 */
179void QSystemSemaphore::setNativeKey(const QNativeIpcKey &key, int initialValue, AccessMode mode)
180{
181 if (key == d->nativeKey && mode == Open)
182 return;
183 if (!isKeyTypeSupported(key.type())) {
184 d->setError(KeyError, tr("%1: unsupported key type")
185 .arg("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 */
214QNativeIpcKey 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 */
229void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
230{
231 setNativeKey(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 */
240QString QSystemSemaphore::key() const
241{
242 return QNativeIpcKeyPrivate::legacyKey(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 */
258bool QSystemSemaphore::acquire()
259{
260 return d->modifySemaphore(-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 */
284bool QSystemSemaphore::release(int n)
285{
286 if (n == 0)
287 return true;
288 if (n < 0) {
289 qWarning("QSystemSemaphore::release: n is negative.");
290 return false;
291 }
292 return d->modifySemaphore(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 */
301QSystemSemaphore::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 */
336QString QSystemSemaphore::errorString() const
337{
338 return d->errorString;
339}
340
341void 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("%1: permission denied").arg(function);
348 error = QSystemSemaphore::PermissionDenied;
349 break;
350 case EEXIST:
351 errorString = QSystemSemaphore::tr("%1: already exists").arg(function);
352 error = QSystemSemaphore::AlreadyExists;
353 break;
354 case ENOENT:
355 errorString = QSystemSemaphore::tr("%1: does not exist").arg(function);
356 error = QSystemSemaphore::NotFound;
357 break;
358 case ERANGE:
359 case ENOSPC:
360 case EMFILE:
361 errorString = QSystemSemaphore::tr("%1: out of resources").arg(function);
362 error = QSystemSemaphore::OutOfResources;
363 break;
364 case ENAMETOOLONG:
365 errorString = QSystemSemaphore::tr("%1: key too long").arg(function);
366 error = QSystemSemaphore::KeyError;
367 break;
368 default:
369 errorString = QSystemSemaphore::tr("%1: unknown error: %2")
370 .arg(function, 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
378bool QSystemSemaphore::isKeyTypeSupported(QNativeIpcKey::Type type)
379{
380 if (!isIpcSupported(IpcType::SystemSemaphore, type))
381 return false;
382 using Variant = decltype(QSystemSemaphorePrivate::backend);
383 return Variant::staticVisit(type, [](auto ptr) {
384 using Impl = std::decay_t<decltype(*ptr)>;
385 return Impl::runtimeSupportCheck();
386 });
387}
388
389QNativeIpcKey QSystemSemaphore::platformSafeKey(const QString &key, QNativeIpcKey::Type type)
390{
391 return QtIpcCommon::platformSafeKey(key, IpcType::SystemSemaphore, type);
392}
393
394QNativeIpcKey QSystemSemaphore::legacyNativeKey(const QString &key, QNativeIpcKey::Type type)
395{
396 return QtIpcCommon::legacyPlatformSafeKey(key, IpcType::SystemSemaphore, type);
397}
398
399QT_END_NAMESPACE
400
401#include "moc_qsystemsemaphore.cpp"
402
403#endif // QT_CONFIG(systemsemaphore)