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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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