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