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

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