1 | // Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com> |
2 | // Copyright (C) 2016 The Qt Company Ltd. |
3 | // Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com> |
4 | // Copyright (C) 2022 Intel Corporation. |
5 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
6 | |
7 | #include "qsystemsemaphore.h" |
8 | #include "qsystemsemaphore_p.h" |
9 | |
10 | #include <qdebug.h> |
11 | #include <qfile.h> |
12 | #include <qcoreapplication.h> |
13 | |
14 | #if QT_CONFIG(posix_sem) |
15 | |
16 | #include <sys/types.h> |
17 | #include <fcntl.h> |
18 | #include <errno.h> |
19 | |
20 | #ifdef Q_OS_UNIX |
21 | # include "private/qcore_unix_p.h" |
22 | #else |
23 | #define EINTR_LOOP_VAL(var, val, cmd) \ |
24 | (void)var; var = cmd |
25 | #define EINTR_LOOP(var, cmd) EINTR_LOOP_VAL(var, -1, cmd) |
26 | #endif |
27 | |
28 | // OpenBSD 4.2 doesn't define EIDRM, see BUGS section: |
29 | // http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 |
30 | #if defined(Q_OS_OPENBSD) && !defined(EIDRM) |
31 | #define EIDRM EINVAL |
32 | #endif |
33 | |
34 | QT_BEGIN_NAMESPACE |
35 | |
36 | using namespace Qt::StringLiterals; |
37 | |
38 | bool QSystemSemaphorePosix::runtimeSupportCheck() |
39 | { |
40 | static const bool result = []() { |
41 | sem_open(name: "/" , oflag: 0, 0, 0); // this WILL fail |
42 | return errno != ENOSYS; |
43 | }(); |
44 | return result; |
45 | } |
46 | |
47 | bool QSystemSemaphorePosix::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode) |
48 | { |
49 | if (semaphore != SEM_FAILED) |
50 | return true; // we already have a semaphore |
51 | |
52 | const QByteArray semName = QFile::encodeName(fileName: self->nativeKey.nativeKey()); |
53 | if (semName.isEmpty()) { |
54 | self->setError(e: QSystemSemaphore::KeyError, |
55 | message: QSystemSemaphore::tr(sourceText: "%1: key is empty" ) |
56 | .arg(a: "QSystemSemaphore::handle"_L1 )); |
57 | return false; |
58 | } |
59 | |
60 | // Always try with O_EXCL so we know whether we created the semaphore. |
61 | int oflag = O_CREAT | O_EXCL; |
62 | for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) { |
63 | do { |
64 | semaphore = ::sem_open(name: semName.constData(), oflag: oflag, 0600, self->initialValue); |
65 | } while (semaphore == SEM_FAILED && errno == EINTR); |
66 | if (semaphore == SEM_FAILED && errno == EEXIST) { |
67 | if (mode == QSystemSemaphore::Create) { |
68 | if (::sem_unlink(name: semName.constData()) == -1 && errno != ENOENT) { |
69 | self->setUnixErrorString("QSystemSemaphore::handle (sem_unlink)"_L1 ); |
70 | return false; |
71 | } |
72 | // Race condition: the semaphore might be recreated before |
73 | // we call sem_open again, so we'll retry several times. |
74 | maxTries = 3; |
75 | } else { |
76 | // Race condition: if it no longer exists at the next sem_open |
77 | // call, we won't realize we created it, so we'll leak it later. |
78 | oflag &= ~O_EXCL; |
79 | maxTries = 2; |
80 | } |
81 | } else { |
82 | break; |
83 | } |
84 | } |
85 | |
86 | if (semaphore == SEM_FAILED) { |
87 | self->setUnixErrorString("QSystemSemaphore::handle"_L1 ); |
88 | return false; |
89 | } |
90 | |
91 | createdSemaphore = (oflag & O_EXCL) != 0; |
92 | |
93 | return true; |
94 | } |
95 | |
96 | void QSystemSemaphorePosix::cleanHandle(QSystemSemaphorePrivate *self) |
97 | { |
98 | if (semaphore != SEM_FAILED) { |
99 | if (::sem_close(sem: semaphore) == -1) { |
100 | self->setUnixErrorString("QSystemSemaphore::cleanHandle (sem_close)"_L1 ); |
101 | #if defined QSYSTEMSEMAPHORE_DEBUG |
102 | qDebug("QSystemSemaphore::cleanHandle sem_close failed." ); |
103 | #endif |
104 | } |
105 | semaphore = SEM_FAILED; |
106 | } |
107 | |
108 | if (createdSemaphore) { |
109 | const QByteArray semName = QFile::encodeName(fileName: self->nativeKey.nativeKey()); |
110 | if (::sem_unlink(name: semName) == -1 && errno != ENOENT) { |
111 | self->setUnixErrorString("QSystemSemaphore::cleanHandle (sem_unlink)"_L1 ); |
112 | #if defined QSYSTEMSEMAPHORE_DEBUG |
113 | qDebug("QSystemSemaphorePosix::cleanHandle sem_unlink failed." ); |
114 | #endif |
115 | } |
116 | createdSemaphore = false; |
117 | } |
118 | } |
119 | |
120 | bool QSystemSemaphorePosix::modifySemaphore(QSystemSemaphorePrivate *self, int count) |
121 | { |
122 | if (!handle(self, mode: QSystemSemaphore::Open)) |
123 | return false; |
124 | |
125 | if (count > 0) { |
126 | int cnt = count; |
127 | do { |
128 | if (::sem_post(sem: semaphore) == -1) { |
129 | self->setUnixErrorString("QSystemSemaphore::modifySemaphore (sem_post)"_L1 ); |
130 | #if defined QSYSTEMSEMAPHORE_DEBUG |
131 | qDebug("QSystemSemaphorePosix::modify sem_post failed %d %d" , count, errno); |
132 | #endif |
133 | // rollback changes to preserve the SysV semaphore behavior |
134 | for ( ; cnt < count; ++cnt) { |
135 | int res; |
136 | EINTR_LOOP(res, ::sem_wait(semaphore)); |
137 | } |
138 | return false; |
139 | } |
140 | --cnt; |
141 | } while (cnt > 0); |
142 | } else { |
143 | int res; |
144 | EINTR_LOOP(res, ::sem_wait(semaphore)); |
145 | if (res == -1) { |
146 | // If the semaphore was removed be nice and create it and then modifySemaphore again |
147 | if (errno == EINVAL || errno == EIDRM) { |
148 | semaphore = SEM_FAILED; |
149 | return modifySemaphore(self, count); |
150 | } |
151 | self->setUnixErrorString("QSystemSemaphore::modifySemaphore (sem_wait)"_L1 ); |
152 | #if defined QSYSTEMSEMAPHORE_DEBUG |
153 | qDebug("QSystemSemaphorePosix::modify sem_wait failed %d %d" , count, errno); |
154 | #endif |
155 | return false; |
156 | } |
157 | } |
158 | |
159 | self->clearError(); |
160 | return true; |
161 | } |
162 | |
163 | QT_END_NAMESPACE |
164 | |
165 | #endif // QT_CONFIG(posix_sem) |
166 | |