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 "qsharedmemory.h"
5#include "qsharedmemory_p.h"
6
7#include "qtipccommon_p.h"
8#include "qsystemsemaphore.h"
9
10#include <q20memory.h>
11#include <qdebug.h>
12#ifdef Q_OS_WIN
13# include <qt_windows.h>
14#endif
15#include <errno.h>
16
17#ifndef MAX_PATH
18# define MAX_PATH PATH_MAX
19#endif
20
21QT_BEGIN_NAMESPACE
22
23#if QT_CONFIG(sharedmemory)
24
25using namespace QtIpcCommon;
26using namespace Qt::StringLiterals;
27
28QSharedMemoryPrivate::~QSharedMemoryPrivate()
29{
30 destructBackend();
31}
32
33inline void QSharedMemoryPrivate::constructBackend()
34{
35 using namespace q20;
36 visit(lambda: [](auto p) { construct_at(p); });
37}
38
39inline void QSharedMemoryPrivate::destructBackend()
40{
41 visit(lambda: [](auto p) { std::destroy_at(p); });
42}
43
44#if QT_CONFIG(systemsemaphore)
45inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const
46{
47 if (isIpcSupported(ipcType: IpcType::SharedMemory, type: QNativeIpcKey::Type::Windows)
48 && nativeKey.type() == QNativeIpcKey::Type::Windows) {
49 // native keys are plain kernel object names, limited to MAX_PATH
50 auto suffix = "_sem"_L1;
51 QString semkey = nativeKey.nativeKey();
52 semkey.truncate(MAX_PATH - suffix.size() - 1);
53 semkey += suffix;
54 return { semkey, QNativeIpcKey::Type::Windows };
55 }
56
57 // System V and POSIX keys appear to operate in different namespaces, so we
58 // can just use the same native key
59 return nativeKey;
60}
61#endif
62
63/*!
64 \class QSharedMemory
65 \inmodule QtCore
66 \since 4.4
67
68 \brief The QSharedMemory class provides access to a shared memory segment.
69
70 QSharedMemory provides access to a \l{Shared Memory}{shared memory segment}
71 by multiple threads and processes. Shared memory segments are identified by a
72 key, represented by \l QNativeIpcKey. A key can be created in a
73 cross-platform manner by using platformSafeKey().
74
75 One QSharedMemory object must create() the segment and this call specifies
76 the size of the segment. All other processes simply attach() to the segment
77 that must already exist. After either operation is successful, the
78 application may call data() to obtain a pointer to the data.
79
80 To support non-atomic operations, QSharedMemory provides API to gain
81 exclusive access: you may lock the shared memory with lock() before reading
82 from or writing to the shared memory, but remember to release the lock with
83 unlock() after you are done.
84
85 By default, QSharedMemory automatically destroys the shared memory segment
86 when the last instance of QSharedMemory is \l{detach()}{detached} from the
87 segment, and no references to the segment remain.
88
89 For details on the key types, platform-specific limitations, and
90 interoperability with older or non-Qt applications, see the \l{Native IPC
91 Keys} documentation. That includes important information for sandboxed
92 applications on Apple platforms, including all apps obtained via the Apple
93 App Store.
94
95 \sa {Inter-Process Communication}, QSystemSemaphore
96 */
97
98/*!
99 \overload QSharedMemory()
100
101 Constructs a shared memory object with the given \a parent. The shared memory
102 object's key is not set by the constructor, so the shared memory object does
103 not have an underlying shared memory segment attached. The key must be set
104 with setNativeKey() before create() or attach() can be used.
105
106 \sa setNativeKey()
107 */
108
109QSharedMemory::QSharedMemory(QObject *parent)
110 : QSharedMemory(QNativeIpcKey(), parent)
111{
112}
113
114/*!
115 \overload
116
117 Constructs a shared memory object with the given \a parent and with
118 its key set to \a key. Because its key is set, its create() and
119 attach() functions can be called.
120
121 \sa setNativeKey(), create(), attach()
122 */
123QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
124 : QObject(*new QSharedMemoryPrivate(key.type()), parent)
125{
126 setNativeKey(key);
127}
128
129/*!
130 Constructs a shared memory object with the given \a parent and with
131 the legacy key set to \a key. Because its key is set, its create() and
132 attach() functions can be called.
133
134 \sa setKey(), create(), attach()
135 */
136QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
137 : QSharedMemory(legacyNativeKey(key), parent)
138{
139}
140
141/*!
142 The destructor clears the key, which forces the shared memory object
143 to \l {detach()} {detach} from its underlying shared memory
144 segment. If this shared memory object is the last one connected to
145 the shared memory segment, the detach() operation destroys the
146 shared memory segment.
147
148 \sa detach(), isAttached()
149 */
150QSharedMemory::~QSharedMemory()
151{
152 Q_D(QSharedMemory);
153 if (isAttached())
154 detach();
155 d->cleanHandle();
156}
157
158/*!
159 \overload
160
161 Sets the legacy \a key for this shared memory object. If \a key is the same
162 as the current key, the function returns without doing anything. Otherwise,
163 if the shared memory object is attached to an underlying shared memory
164 segment, it will \l {detach()} {detach} from it before setting the new key.
165 This function does not do an attach().
166
167 You can call key() to retrieve the legacy key. This function is mostly the
168 same as:
169
170 \code
171 shm.setNativeKey(QSharedMemory::legacyNativeKey(key));
172 \endcode
173
174 except that it enables obtaining the legacy key using key().
175
176 \sa key(), nativeKey(), isAttached()
177*/
178void QSharedMemory::setKey(const QString &key)
179{
180 setNativeKey(legacyNativeKey(key));
181}
182
183/*!
184 \since 4.8
185 \fn void QSharedMemory::setNativeKey(const QString &key, QNativeIpcKey::Type type)
186
187 Sets the native, platform specific, \a key for this shared memory object of
188 type \a type (the type parameter has been available since Qt 6.6). If \a key
189 is the same as the current native key, the function returns without doing
190 anything. Otherwise, if the shared memory object is attached to an underlying
191 shared memory segment, it will \l {detach()} {detach} from it before setting
192 the new key. This function does not do an attach().
193
194 This function is useful if the native key was shared from another process,
195 though the application must take care to ensure the key type matches what the
196 other process expects. See \l{Native IPC Keys} for more information.
197
198 Portable native keys can be obtained using platformSafeKey().
199
200 You can call nativeKey() to retrieve the native key.
201
202 \sa nativeKey(), nativeIpcKey(), isAttached()
203*/
204
205/*!
206 \since 6.6
207
208 Sets the native, platform specific, \a key for this shared memory object. If
209 \a key is the same as the current native key, the function returns without
210 doing anything. Otherwise, if the shared memory object is attached to an
211 underlying shared memory segment, it will \l {detach()} {detach} from it
212 before setting the new key. This function does not do an attach().
213
214 This function is useful if the native key was shared from another process.
215 See \l{Native IPC Keys} for more information.
216
217 Portable native keys can be obtained using platformSafeKey().
218
219 You can call nativeKey() to retrieve the native key.
220
221 \sa nativeKey(), nativeIpcKey(), isAttached()
222*/
223void QSharedMemory::setNativeKey(const QNativeIpcKey &key)
224{
225 Q_D(QSharedMemory);
226 if (key == d->nativeKey && key.isEmpty())
227 return;
228 if (!isKeyTypeSupported(type: key.type())) {
229 d->setError(e: KeyError, message: tr(s: "%1: unsupported key type")
230 .arg(a: "QSharedMemory::setNativeKey"_L1));
231 return;
232 }
233
234 if (isAttached())
235 detach();
236 d->cleanHandle();
237 if (key.type() == d->nativeKey.type()) {
238 // we can reuse the backend
239 d->nativeKey = key;
240 } else {
241 // we must recreate the backend
242 d->destructBackend();
243 d->nativeKey = key;
244 d->constructBackend();
245 }
246}
247
248bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode)
249{
250 if (!cleanHandle())
251 return false;
252#if QT_CONFIG(systemsemaphore)
253 const QString legacyKey = QNativeIpcKeyPrivate::legacyKey(key: nativeKey);
254 const QNativeIpcKey semKey = legacyKey.isEmpty()
255 ? semaphoreNativeKey()
256 : QSystemSemaphore::legacyNativeKey(key: legacyKey, type: nativeKey.type());
257 systemSemaphore.setNativeKey(key: semKey, initialValue: 1, mode);
258 if (systemSemaphore.error() != QSystemSemaphore::NoError) {
259 QString function = "QSharedMemoryPrivate::initKey"_L1;
260 errorString = QSharedMemory::tr(s: "%1: unable to set key on lock (%2)")
261 .arg(args&: function, args: systemSemaphore.errorString());
262 switch(systemSemaphore.error()) {
263 case QSystemSemaphore::PermissionDenied:
264 error = QSharedMemory::PermissionDenied;
265 break;
266 case QSystemSemaphore::KeyError:
267 error = QSharedMemory::KeyError;
268 break;
269 case QSystemSemaphore::AlreadyExists:
270 error = QSharedMemory::AlreadyExists;
271 break;
272 case QSystemSemaphore::NotFound:
273 error = QSharedMemory::NotFound;
274 break;
275 case QSystemSemaphore::OutOfResources:
276 error = QSharedMemory::OutOfResources;
277 break;
278 case QSystemSemaphore::UnknownError:
279 default:
280 error = QSharedMemory::UnknownError;
281 break;
282 }
283 return false;
284 }
285#else
286 Q_UNUSED(mode);
287#endif
288 errorString = QString();
289 error = QSharedMemory::NoError;
290 return true;
291}
292
293/*!
294 Returns the legacy key assigned with setKey() to this shared memory, or a null key
295 if no key has been assigned, or if the segment is using a nativeKey(). The
296 key is the identifier used by Qt applications to identify the shared memory
297 segment.
298
299 You can find the native, platform specific, key used by the operating system
300 by calling nativeKey().
301
302 \sa setKey(), setNativeKey()
303 */
304QString QSharedMemory::key() const
305{
306 Q_D(const QSharedMemory);
307 return QNativeIpcKeyPrivate::legacyKey(key: d->nativeKey);
308}
309
310/*!
311 \since 4.8
312
313 Returns the native, platform specific, key for this shared memory object. The
314 native key is the identifier used by the operating system to identify the
315 shared memory segment.
316
317 You can use the native key to access shared memory segments that have not
318 been created by Qt, or to grant shared memory access to non-Qt applications.
319 See \l{Native IPC Keys} for more information.
320
321 \sa setNativeKey(), nativeIpcKey()
322*/
323QString QSharedMemory::nativeKey() const
324{
325 Q_D(const QSharedMemory);
326 return d->nativeKey.nativeKey();
327}
328
329/*!
330 \since 6.6
331
332 Returns the key type for this shared memory object. The key type complements
333 the nativeKey() as the identifier used by the operating system to identify
334 the shared memory segment.
335
336 You can use the native key to access shared memory segments that have not
337 been created by Qt, or to grant shared memory access to non-Qt applications.
338 See \l{Native IPC Keys} for more information.
339
340 \sa nativeKey(), setNativeKey()
341*/
342QNativeIpcKey QSharedMemory::nativeIpcKey() const
343{
344 Q_D(const QSharedMemory);
345 return d->nativeKey;
346}
347
348/*!
349 Creates a shared memory segment of \a size bytes with the key passed to the
350 constructor or set with setNativeKey(), then attaches to
351 the new shared memory segment with the given access \a mode and returns
352 \tt true. If a shared memory segment identified by the key already exists,
353 the attach operation is not performed and \tt false is returned. When the
354 return value is \tt false, call error() to determine which error occurred.
355
356 \sa error()
357 */
358bool QSharedMemory::create(qsizetype size, AccessMode mode)
359{
360 Q_D(QSharedMemory);
361 QLatin1StringView function = "QSharedMemory::create"_L1;
362
363#if QT_CONFIG(systemsemaphore)
364 if (!d->initKey(mode: QSystemSemaphore::Create))
365 return false;
366 QSharedMemoryLocker lock(this);
367 if (!d->nativeKey.isEmpty() && !d->tryLocker(locker: &lock, function))
368 return false;
369#else
370 if (!d->initKey({}))
371 return false;
372#endif
373
374 if (size <= 0) {
375 d->error = QSharedMemory::InvalidSize;
376 d->errorString =
377 QSharedMemory::tr(s: "%1: create size is less then 0").arg(a: function);
378 return false;
379 }
380
381 if (!d->create(sz: size))
382 return false;
383
384 return d->attach(mode);
385}
386
387/*!
388 Returns the size of the attached shared memory segment. If no shared
389 memory segment is attached, 0 is returned.
390
391 \note The size of the segment may be larger than the requested size that was
392 passed to create().
393
394 \sa create(), attach()
395 */
396qsizetype QSharedMemory::size() const
397{
398 Q_D(const QSharedMemory);
399 return d->size;
400}
401
402/*!
403 \enum QSharedMemory::AccessMode
404
405 \value ReadOnly The shared memory segment is read-only. Writing to
406 the shared memory segment is not allowed. An attempt to write to a
407 shared memory segment created with ReadOnly causes the program to
408 abort.
409
410 \value ReadWrite Reading and writing the shared memory segment are
411 both allowed.
412*/
413
414/*!
415 Attempts to attach the process to the shared memory segment
416 identified by the key that was passed to the constructor or to a
417 call to setNativeKey(). The access \a mode is \l {QSharedMemory::}
418 {ReadWrite} by default. It can also be \l {QSharedMemory::}
419 {ReadOnly}. Returns \c true if the attach operation is successful. If
420 false is returned, call error() to determine which error occurred.
421 After attaching the shared memory segment, a pointer to the shared
422 memory can be obtained by calling data().
423
424 \sa isAttached(), detach(), create()
425 */
426bool QSharedMemory::attach(AccessMode mode)
427{
428 Q_D(QSharedMemory);
429
430 if (isAttached() || !d->initKey(mode: {}))
431 return false;
432#if QT_CONFIG(systemsemaphore)
433 QSharedMemoryLocker lock(this);
434 if (!d->nativeKey.isEmpty() && !d->tryLocker(locker: &lock, function: "QSharedMemory::attach"_L1))
435 return false;
436#endif
437
438 if (isAttached() || !d->handle())
439 return false;
440
441 return d->attach(mode);
442}
443
444/*!
445 Returns \c true if this process is attached to the shared memory
446 segment.
447
448 \sa attach(), detach()
449 */
450bool QSharedMemory::isAttached() const
451{
452 Q_D(const QSharedMemory);
453 return (nullptr != d->memory);
454}
455
456/*!
457 Detaches the process from the shared memory segment. If this was the
458 last process attached to the shared memory segment, then the shared
459 memory segment is released by the system, i.e., the contents are
460 destroyed. The function returns \c true if it detaches the shared
461 memory segment. If it returns \c false, it usually means the segment
462 either isn't attached, or it is locked by another process.
463
464 \sa attach(), isAttached()
465 */
466bool QSharedMemory::detach()
467{
468 Q_D(QSharedMemory);
469 if (!isAttached())
470 return false;
471
472#if QT_CONFIG(systemsemaphore)
473 QSharedMemoryLocker lock(this);
474 if (!d->nativeKey.isEmpty() && !d->tryLocker(locker: &lock, function: "QSharedMemory::detach"_L1))
475 return false;
476#endif
477
478 return d->detach();
479}
480
481/*!
482 Returns a pointer to the contents of the shared memory segment, if one is
483 attached. Otherwise it returns null. The value returned by this function will
484 not change until a \l {detach()}{detach} happens, so it is safe to store this
485 pointer.
486
487 If the memory operations are not atomic, you may lock the shared memory with
488 lock() before reading from or writing, but remember to release the lock with
489 unlock() after you are done.
490
491 \sa attach()
492 */
493void *QSharedMemory::data()
494{
495 Q_D(QSharedMemory);
496 return d->memory;
497}
498
499/*!
500 Returns a const pointer to the contents of the shared memory segment, if one
501 is attached. Otherwise it returns null. The value returned by this function
502 will not change until a \l {detach()}{detach} happens, so it is safe to store
503 this pointer.
504
505 If the memory operations are not atomic, you may lock the shared memory with
506 lock() before reading from or writing, but remember to release the lock with
507 unlock() after you are done.
508
509 \sa attach(), create()
510 */
511const void *QSharedMemory::constData() const
512{
513 Q_D(const QSharedMemory);
514 return d->memory;
515}
516
517/*!
518 \overload data()
519 */
520const void *QSharedMemory::data() const
521{
522 Q_D(const QSharedMemory);
523 return d->memory;
524}
525
526#if QT_CONFIG(systemsemaphore)
527/*!
528 This is a semaphore that locks the shared memory segment for access
529 by this process and returns \c true. If another process has locked the
530 segment, this function blocks until the lock is released. Then it
531 acquires the lock and returns \c true. If this function returns \c false,
532 it means that you have ignored a false return from create() or attach(),
533 that you have set the key with setNativeKey() or that
534 QSystemSemaphore::acquire() failed due to an unknown system error.
535
536 \sa unlock(), data(), QSystemSemaphore::acquire()
537 */
538bool QSharedMemory::lock()
539{
540 Q_D(QSharedMemory);
541 if (d->lockedByMe) {
542 qWarning(msg: "QSharedMemory::lock: already locked");
543 return true;
544 }
545 if (d->systemSemaphore.acquire()) {
546 d->lockedByMe = true;
547 return true;
548 }
549 const auto function = "QSharedMemory::lock"_L1;
550 d->errorString = QSharedMemory::tr(s: "%1: unable to lock").arg(a: function);
551 d->error = QSharedMemory::LockError;
552 return false;
553}
554
555/*!
556 Releases the lock on the shared memory segment and returns \c true, if
557 the lock is currently held by this process. If the segment is not
558 locked, or if the lock is held by another process, nothing happens
559 and false is returned.
560
561 \sa lock()
562 */
563bool QSharedMemory::unlock()
564{
565 Q_D(QSharedMemory);
566 if (!d->lockedByMe)
567 return false;
568 d->lockedByMe = false;
569 if (d->systemSemaphore.release())
570 return true;
571 const auto function = "QSharedMemory::unlock"_L1;
572 d->errorString = QSharedMemory::tr(s: "%1: unable to unlock").arg(a: function);
573 d->error = QSharedMemory::LockError;
574 return false;
575}
576#endif // QT_CONFIG(systemsemaphore)
577
578/*!
579 \enum QSharedMemory::SharedMemoryError
580
581 \value NoError No error occurred.
582
583 \value PermissionDenied The operation failed because the caller
584 didn't have the required permissions.
585
586 \value InvalidSize A create operation failed because the requested
587 size was invalid.
588
589 \value KeyError The operation failed because of an invalid key.
590
591 \value AlreadyExists A create() operation failed because a shared
592 memory segment with the specified key already existed.
593
594 \value NotFound An attach() failed because a shared memory segment
595 with the specified key could not be found.
596
597 \value LockError The attempt to lock() the shared memory segment
598 failed because create() or attach() failed and returned false, or
599 because a system error occurred in QSystemSemaphore::acquire().
600
601 \value OutOfResources A create() operation failed because there was
602 not enough memory available to fill the request.
603
604 \value UnknownError Something else happened and it was bad.
605*/
606
607/*!
608 Returns a value indicating whether an error occurred, and, if so,
609 which error it was.
610
611 \sa errorString()
612 */
613QSharedMemory::SharedMemoryError QSharedMemory::error() const
614{
615 Q_D(const QSharedMemory);
616 return d->error;
617}
618
619/*!
620 Returns a text description of the last error that occurred. If
621 error() returns an \l {QSharedMemory::SharedMemoryError} {error
622 value}, call this function to get a text string that describes the
623 error.
624
625 \sa error()
626 */
627QString QSharedMemory::errorString() const
628{
629 Q_D(const QSharedMemory);
630 return d->errorString;
631}
632
633void QSharedMemoryPrivate::setUnixErrorString(QLatin1StringView function)
634{
635 // EINVAL is handled in functions so they can give better error strings
636 switch (errno) {
637 case EACCES:
638 errorString = QSharedMemory::tr(s: "%1: permission denied").arg(a: function);
639 error = QSharedMemory::PermissionDenied;
640 break;
641 case EEXIST:
642 errorString = QSharedMemory::tr(s: "%1: already exists").arg(a: function);
643 error = QSharedMemory::AlreadyExists;
644 break;
645 case ENOENT:
646 errorString = QSharedMemory::tr(s: "%1: doesn't exist").arg(a: function);
647 error = QSharedMemory::NotFound;
648 break;
649 case EMFILE:
650 case ENOMEM:
651 case ENOSPC:
652 errorString = QSharedMemory::tr(s: "%1: out of resources").arg(a: function);
653 error = QSharedMemory::OutOfResources;
654 break;
655 default:
656 errorString = QSharedMemory::tr(s: "%1: unknown error: %2")
657 .arg(args&: function, args: qt_error_string(errno));
658 error = QSharedMemory::UnknownError;
659#if defined QSHAREDMEMORY_DEBUG
660 qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
661#endif
662 }
663}
664
665bool QSharedMemory::isKeyTypeSupported(QNativeIpcKey::Type type)
666{
667 if (!isIpcSupported(ipcType: IpcType::SharedMemory, type))
668 return false;
669 using Variant = decltype(QSharedMemoryPrivate::backend);
670 return Variant::staticVisit(keyType: type, lambda: [](auto ptr) {
671 using Impl = std::decay_t<decltype(*ptr)>;
672 return Impl::runtimeSupportCheck();
673 });
674}
675
676QNativeIpcKey QSharedMemory::platformSafeKey(const QString &key, QNativeIpcKey::Type type)
677{
678 return QtIpcCommon::platformSafeKey(key, ipcType: IpcType::SharedMemory, type);
679}
680
681QNativeIpcKey QSharedMemory::legacyNativeKey(const QString &key, QNativeIpcKey::Type type)
682{
683 return QtIpcCommon::legacyPlatformSafeKey(key, ipcType: IpcType::SharedMemory, type);
684}
685
686#endif // QT_CONFIG(sharedmemory)
687
688QT_END_NAMESPACE
689
690#include "moc_qsharedmemory.cpp"
691

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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