1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qlock_p.h"
43
44 #ifdef QT_NO_QWS_MULTIPROCESS
45
46 QT_BEGIN_NAMESPACE
47
48 /* no multiprocess - use a dummy */
49
QLock(const QString &,char,bool)50 QLock::QLock(const QString & /*filename*/, char /*id*/, bool /*create*/)
51 : type(Read), data(0)
52 {
53 }
54
~QLock()55 QLock::~QLock()
56 {
57 }
58
isValid() const59 bool QLock::isValid() const
60 {
61 return true;
62 }
63
lock(Type t)64 void QLock::lock(Type t)
65 {
66 data = (QLockData *)-1;
67 type = t;
68 }
69
unlock()70 void QLock::unlock()
71 {
72 data = 0;
73 }
74
locked() const75 bool QLock::locked() const
76 {
77 return data;
78 }
79
80 QT_END_NAMESPACE
81
82 #else // QT_NO_QWS_MULTIPROCESS
83
84 #if defined(Q_OS_DARWIN)
85 # define QT_NO_SEMAPHORE
86 #endif
87
88 #include "qwssignalhandler_p.h"
89
90 #include <unistd.h>
91 #include <sys/types.h>
92 #include <sys/ipc.h>
93 #if defined(QT_NO_SEMAPHORE)
94 # include <sys/stat.h>
95 # include <sys/file.h>
96 #elif !defined(QT_POSIX_IPC)
97 # include <sys/sem.h>
98 #else
99 # include <semaphore.h>
100 #endif
101 #include <string.h>
102 #include <errno.h>
103
104 #include <private/qcore_unix_p.h> // overrides QT_OPEN
105
106 QT_BEGIN_NAMESPACE
107
108 #define MAX_LOCKS 200 // maximum simultaneous read locks
109
110 class QLockData
111 {
112 public:
113 #if defined(QT_NO_SEMAPHORE) || defined(QT_POSIX_IPC)
114 QByteArray file;
115 #endif
116 #if !defined(QT_POSIX_IPC)
117 int id;
118 #else
119 sem_t *id; // Read mode resource counter
120 sem_t *rsem; // Read mode lock
121 sem_t *wsem; // Write mode lock
122 #endif
123 int count;
124 bool owned;
125 };
126
127
128 /*!
129 \class QLock
130 \brief The QLock class is a wrapper for a system shared semaphore.
131
132 \ingroup qws
133
134 \internal
135
136 It is used by \l{Qt for Embedded Linux} for synchronizing access to the graphics
137 card and shared memory region between processes.
138 */
139
140 /*!
141 \enum QLock::Type
142
143 \value Read
144 \value Write
145 */
146
147 /*!
148 Creates a lock. \a filename is the file path of the Unix-domain
149 socket the \l{Qt for Embedded Linux} client is using. \a id is the name of the
150 particular lock to be created on that socket. If \a create is true
151 the lock is to be created (as the Qt for Embedded Linux server does); if \a
152 create is false the lock should exist already (as the Qt for Embedded Linux
153 client expects).
154 */
155 QLock::QLock(const QString &filename, char id, bool create)
156 {
157 data = new QLockData;
158 data->count = 0;
159 #if defined(QT_NO_SEMAPHORE)
160 data->file = filename.toLocal8Bit() + id;
161 for (int x = 0; x < 2; ++x) {
162 data->id = QT_OPEN(data->file.constData(), O_RDWR | (x ? O_CREAT : 0), S_IRWXU);
163 if (data->id != -1 || !create) {
164 data->owned = x;
165 break;
166 }
167 }
168 #elif !defined(QT_POSIX_IPC)
169 key_t semkey = ftok(filename.toLocal8Bit().constData(), id);
170 data->id = semget(semkey, 0, 0);
171 data->owned = create;
172 if (create) {
173 qt_semun arg;
174 arg.val = 0;
175 if (data->id != -1)
176 semctl(data->id, 0, IPC_RMID, arg);
177 data->id = semget(semkey, 1, IPC_CREAT | 0600);
178 arg.val = MAX_LOCKS;
179 semctl(data->id, 0, SETVAL, arg);
180 }
181 #else
182 data->file = filename.toLocal8Bit() + id;
183 data->owned = create;
184
185 char ids[3] = { 'c', 'r', 'w' };
186 sem_t **sems[3] = { &data->id, &data->rsem, &data->wsem };
187 unsigned short initialValues[3] = { MAX_LOCKS, 1, 1 };
188 for (int i = 0; i < 3; ++i) {
189 QByteArray file = data->file + ids[i];
190 do {
191 *sems[i] = sem_open(file.constData(), 0, 0666, 0);
192 } while (*sems[i] == SEM_FAILED && errno == EINTR);
193 if (create) {
194 if (*sems[i] != SEM_FAILED) {
195 sem_close(*sems[i]);
196 sem_unlink(file.constData());
197 }
198 do {
199 *sems[i] = sem_open(file.constData(), O_CREAT, 0666, initialValues[i]);
200 } while (*sems[i] == SEM_FAILED && errno == EINTR);
201 }
202 }
203 #endif
204 if (!isValid()) {
205 qWarning("QLock::QLock: Cannot %s semaphore %s '%c' (%d, %s)",
206 (create ? "create" : "get"), qPrintable(filename), id,
207 errno, strerror(errno));
208 }
209
210 #ifndef QT_NO_QWS_SIGNALHANDLER
211 QWSSignalHandler::instance()->addLock(this);
212 #endif
213 }
214
215 /*!
216 Destroys a lock
217 */
218 QLock::~QLock()
219 {
220 #ifndef QT_NO_QWS_SIGNALHANDLER
221 QWSSignalHandler::instance()->removeLock(this);
222 #endif
223
224 while (locked())
225 unlock();
226
227 #if defined(QT_NO_SEMAPHORE)
228 if (isValid())
229 QT_CLOSE(data->id);
230 #elif defined(QT_POSIX_IPC)
231 if (data->id != SEM_FAILED)
232 sem_close(data->id);
233 if (data->rsem != SEM_FAILED)
234 sem_close(data->rsem);
235 if (data->wsem != SEM_FAILED)
236 sem_close(data->wsem);
237 #endif
238
239 if (data->owned) {
240 #if defined(QT_NO_SEMAPHORE)
241 unlink(data->file.constData());
242 #elif !defined(QT_POSIX_IPC)
243 qt_semun semval;
244 semval.val = 0;
245 semctl(data->id, 0, IPC_RMID, semval);
246 #else
247 char ids[3] = { 'c', 'r', 'w' };
248 for (int i = 0; i < 3; ++i) {
249 QByteArray file = data->file + ids[i];
250 sem_unlink(file.constData());
251 }
252 #endif
253 }
254 delete data;
255 data = 0;
256 }
257
258 /*!
259 Returns true if the lock constructor was successful; returns false if
260 the lock could not be created or was not available to connect to.
261 */
262 bool QLock::isValid() const
263 {
264 #if !defined(QT_POSIX_IPC)
265 return data && data->id != -1;
266 #else
267 return data && data->id != SEM_FAILED && data->rsem != SEM_FAILED && data->wsem != SEM_FAILED;
268 #endif
269 }
270
271 /*!
272 Locks the semaphore with a lock of type \a t. Locks can either be
273 \c Read or \c Write. If a lock is \c Read, attempts by other
274 processes to obtain \c Read locks will succeed, and \c Write
275 attempts will block until the lock is unlocked. If locked as \c
276 Write, all attempts to lock by other processes will block until
277 the lock is unlocked. Locks are stacked: i.e. a given QLock can be
278 locked multiple times by the same process without blocking, and
279 will only be unlocked after a corresponding number of unlock()
280 calls.
281 */
282 void QLock::lock(Type t)
283 {
284 if (!isValid())
285 return;
286
287 if (!data->count) {
288 type = t;
289
290 int rv;
291 #if defined(QT_NO_SEMAPHORE)
292 int op = type == Write ? LOCK_EX : LOCK_SH;
293
294 EINTR_LOOP(rv, flock(data->id, op));
295 #elif !defined(QT_POSIX_IPC)
296 sembuf sops;
297 sops.sem_num = 0;
298 sops.sem_op = type == Write ? -MAX_LOCKS : -1;
299 sops.sem_flg = SEM_UNDO;
300
301 EINTR_LOOP(rv, semop(data->id, &sops, 1));
302 #else
303 if (type == Write) {
304 EINTR_LOOP(rv, sem_wait(data->rsem));
305 if (rv != -1) {
306 EINTR_LOOP(rv, sem_wait(data->wsem));
307 if (rv == -1)
308 sem_post(data->rsem);
309 }
310 } else {
311 EINTR_LOOP(rv, sem_wait(data->wsem));
312 if (rv != -1) {
313 EINTR_LOOP(rv, sem_trywait(data->rsem));
314 if (rv != -1 || errno == EAGAIN) {
315 EINTR_LOOP(rv, sem_wait(data->id));
316 if (rv == -1) {
317 int semval;
318 sem_getvalue(data->id, &semval);
319 if (semval == MAX_LOCKS)
320 sem_post(data->rsem);
321 }
322 }
323 rv = sem_post(data->wsem);
324 }
325 }
326 #endif
327 if (rv == -1) {
328 qDebug("QLock::lock(): %s", strerror(errno));
329 return;
330 }
331 } else if (type == Read && t == Write) {
332 qDebug("QLock::lock(): Attempt to lock for write while locked for read");
333 }
334 data->count++;
335 }
336
337 /*!
338 Unlocks the semaphore. If other processes were blocking waiting to
339 lock() the semaphore, one of them will wake up and succeed in
340 locking.
341 */
342 void QLock::unlock()
343 {
344 if (!isValid())
345 return;
346
347 if (data->count > 0) {
348 data->count--;
349 if (!data->count) {
350 int rv;
351 #if defined(QT_NO_SEMAPHORE)
352 EINTR_LOOP(rv, flock(data->id, LOCK_UN));
353 #elif !defined(QT_POSIX_IPC)
354 sembuf sops;
355 sops.sem_num = 0;
356 sops.sem_op = type == Write ? MAX_LOCKS : 1;
357 sops.sem_flg = SEM_UNDO;
358
359 EINTR_LOOP(rv, semop(data->id, &sops, 1));
360 #else
361 if (type == Write) {
362 sem_post(data->wsem);
363 rv = sem_post(data->rsem);
364 } else {
365 EINTR_LOOP(rv, sem_wait(data->wsem));
366 if (rv != -1) {
367 sem_post(data->id);
368 int semval;
369 sem_getvalue(data->id, &semval);
370 if (semval == MAX_LOCKS)
371 sem_post(data->rsem);
372 rv = sem_post(data->wsem);
373 }
374 }
375 #endif
376 if (rv == -1)
377 qDebug("QLock::unlock(): %s", strerror(errno));
378 }
379 } else {
380 qDebug("QLock::unlock(): Unlock without corresponding lock");
381 }
382 }
383
384 /*!
385 Returns true if the lock is currently held by the current process;
386 otherwise returns false.
387 */
388 bool QLock::locked() const
389 {
390 return isValid() && data->count > 0;
391 }
392
393 QT_END_NAMESPACE
394
395 #endif // QT_NO_QWS_MULTIPROCESS
396