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

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