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