Qt 4.8
qsystemsemaphore_unix.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtCore 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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qsystemsemaphore.h"
43 #include "qsystemsemaphore_p.h"
44 
45 #include <qcoreapplication.h>
46 #include <qdebug.h>
47 #include <qfile.h>
48 
49 #ifndef QT_NO_SYSTEMSEMAPHORE
50 
51 #include <sys/types.h>
52 #include <sys/ipc.h>
53 #ifndef QT_POSIX_IPC
54 #include <sys/sem.h>
55 #endif
56 #include <fcntl.h>
57 #include <errno.h>
58 
59 #include "private/qcore_unix_p.h"
60 
61 // OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
62 // http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
63 #if defined(Q_OS_OPENBSD) && !defined(EIDRM)
64 #define EIDRM EINVAL
65 #endif
66 
67 //#define QSYSTEMSEMAPHORE_DEBUG
68 
70 
72 #ifndef QT_POSIX_IPC
73  unix_key(-1), semaphore(-1), createdFile(false),
74 #else
75  semaphore(SEM_FAILED),
76 #endif
77  createdSemaphore(false), error(QSystemSemaphore::NoError)
78 {
79 }
80 
82 {
83  // EINVAL is handled in functions so they can give better error strings
84  switch (errno) {
85  case EPERM:
86  case EACCES:
87  errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function);
89  break;
90  case EEXIST:
91  errorString = QCoreApplication::translate("QSystemSemaphore", "%1: already exists").arg(function);
93  break;
94  case ENOENT:
95  errorString = QCoreApplication::translate("QSystemSemaphore", "%1: does not exist").arg(function);
97  break;
98  case ERANGE:
99  case ENOMEM:
100  case ENOSPC:
101  case EMFILE:
102  case ENFILE:
103  case EOVERFLOW:
104  errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function);
106  break;
107  case ENAMETOOLONG:
108  errorString = QCoreApplication::translate("QSystemSemaphore", "%1: name error").arg(function);
110  break;
111  default:
112  errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error %2").arg(function).arg(errno);
114 #ifdef QSYSTEMSEMAPHORE_DEBUG
115  qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
116 #endif
117  break;
118  }
119 }
120 
126 #ifndef QT_POSIX_IPC
128 {
129  if (-1 != unix_key)
130  return unix_key;
131 
132  if (key.isEmpty()) {
133  errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
135  return -1;
136  }
137 
138  // ftok requires that an actual file exists somewhere
140  if (-1 == built) {
141  errorString = QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
143  return -1;
144  }
145  createdFile = (1 == built);
146 
147  // Get the unix key for the created file
148  unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
149  if (-1 == unix_key) {
150  errorString = QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
152  return -1;
153  }
154 
155  // Get semaphore
156  semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
157  if (-1 == semaphore) {
158  if (errno == EEXIST)
159  semaphore = semget(unix_key, 1, 0600 | IPC_CREAT);
160  if (-1 == semaphore) {
161  setErrorString(QLatin1String("QSystemSemaphore::handle"));
162  cleanHandle();
163  return -1;
164  }
165  if (mode == QSystemSemaphore::Create) {
166  createdSemaphore = true;
167  createdFile = true;
168  }
169  } else {
170  createdSemaphore = true;
171  // Force cleanup of file, it is possible that it can be left over from a crash
172  createdFile = true;
173  }
174 
175  // Created semaphore so initialize its value.
176  if (createdSemaphore && initialValue >= 0) {
177  qt_semun init_op;
178  init_op.val = initialValue;
179  if (-1 == semctl(semaphore, 0, SETVAL, init_op)) {
180  setErrorString(QLatin1String("QSystemSemaphore::handle"));
181  cleanHandle();
182  return -1;
183  }
184  }
185 
186  return unix_key;
187 }
188 #else
190 {
191  if (semaphore != SEM_FAILED)
192  return true; // we already have a semaphore
193 
194  if (fileName.isEmpty()) {
195  errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
197  return false;
198  }
199 
201 
202  // Always try with O_EXCL so we know whether we created the semaphore.
203  int oflag = O_CREAT | O_EXCL;
204  for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) {
205  do {
206  semaphore = sem_open(semName.constData(), oflag, 0666, initialValue);
207  } while (semaphore == SEM_FAILED && errno == EINTR);
208  if (semaphore == SEM_FAILED && errno == EEXIST) {
209  if (mode == QSystemSemaphore::Create) {
210  if (sem_unlink(semName.constData()) == -1 && errno != ENOENT) {
211  setErrorString(QLatin1String("QSystemSemaphore::handle (sem_unlink)"));
212  return false;
213  }
214  // Race condition: the semaphore might be recreated before
215  // we call sem_open again, so we'll retry several times.
216  maxTries = 3;
217  } else {
218  // Race condition: if it no longer exists at the next sem_open
219  // call, we won't realize we created it, so we'll leak it later.
220  oflag &= ~O_EXCL;
221  maxTries = 2;
222  }
223  } else {
224  break;
225  }
226  }
227  if (semaphore == SEM_FAILED) {
228  setErrorString(QLatin1String("QSystemSemaphore::handle"));
229  return false;
230  }
231 
232  createdSemaphore = (oflag & O_EXCL) != 0;
233  return true;
234 }
235 #endif // QT_POSIX_IPC
236 
243 {
244 #ifndef QT_POSIX_IPC
245  unix_key = -1;
246 
247  // remove the file if we made it
248  if (createdFile) {
250  createdFile = false;
251  }
252 
253  if (createdSemaphore) {
254  if (-1 != semaphore) {
255  if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) {
256  setErrorString(QLatin1String("QSystemSemaphore::cleanHandle"));
257 #ifdef QSYSTEMSEMAPHORE_DEBUG
258  qDebug("QSystemSemaphore::cleanHandle semctl failed.");
259 #endif
260  }
261  semaphore = -1;
262  }
263  createdSemaphore = false;
264  }
265 #else
266  if (semaphore != SEM_FAILED) {
267  if (sem_close(semaphore) == -1) {
268  setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_close)"));
269 #ifdef QSYSTEMSEMAPHORE_DEBUG
270  qDebug() << QLatin1String("QSystemSemaphore::cleanHandle sem_close failed.");
271 #endif
272  }
273  semaphore = SEM_FAILED;
274  }
275 
276  if (createdSemaphore) {
277  if (sem_unlink(QFile::encodeName(fileName).constData()) == -1 && errno != ENOENT) {
278  setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_unlink)"));
279 #ifdef QSYSTEMSEMAPHORE_DEBUG
280  qDebug() << QLatin1String("QSystemSemaphore::cleanHandle sem_unlink failed.");
281 #endif
282  }
283  createdSemaphore = false;
284  }
285 #endif // QT_POSIX_IPC
286 }
287 
292 {
293 #ifndef QT_POSIX_IPC
294  if (-1 == handle())
295  return false;
296 
297  struct sembuf operation;
298  operation.sem_num = 0;
299  operation.sem_op = count;
300  operation.sem_flg = SEM_UNDO;
301 
302  register int res;
303  EINTR_LOOP(res, semop(semaphore, &operation, 1));
304  if (-1 == res) {
305  // If the semaphore was removed be nice and create it and then modifySemaphore again
306  if (errno == EINVAL || errno == EIDRM) {
307  semaphore = -1;
308  cleanHandle();
309  handle();
310  return modifySemaphore(count);
311  }
312  setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore"));
313 #ifdef QSYSTEMSEMAPHORE_DEBUG
314  qDebug() << QLatin1String("QSystemSemaphore::modify failed") << count << semctl(semaphore, 0, GETVAL) << errno << EIDRM << EINVAL;
315 #endif
316  return false;
317  }
318 #else
319  if (!handle())
320  return false;
321 
322  if (count > 0) {
323  int cnt = count;
324  do {
325  if (sem_post(semaphore) == -1) {
326  setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_post)"));
327 #ifdef QSYSTEMSEMAPHORE_DEBUG
328  qDebug() << QLatin1String("QSystemSemaphore::modify sem_post failed") << count << errno;
329 #endif
330  // rollback changes to preserve the SysV semaphore behavior
331  for ( ; cnt < count; ++cnt) {
332  register int res;
333  EINTR_LOOP(res, sem_wait(semaphore));
334  }
335  return false;
336  }
337  --cnt;
338  } while (cnt > 0);
339  } else {
340  register int res;
341  EINTR_LOOP(res, sem_wait(semaphore));
342  if (res == -1) {
343  // If the semaphore was removed be nice and create it and then modifySemaphore again
344  if (errno == EINVAL || errno == EIDRM) {
345  semaphore = SEM_FAILED;
346  return modifySemaphore(count);
347  }
348  setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_wait)"));
349 #ifdef QSYSTEMSEMAPHORE_DEBUG
350  qDebug() << QLatin1String("QSystemSemaphore::modify sem_wait failed") << count << errno;
351 #endif
352  return false;
353  }
354  }
355 #endif // QT_POSIX_IPC
356 
357  return true;
358 }
359 
361 
362 #endif // QT_NO_SYSTEMSEMAPHORE
void setErrorString(const QString &function)
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
#define EINTR_LOOP(var, cmd)
Definition: qcore_unix_p.h:96
#define error(msg)
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
The QSystemSemaphore class provides a general counting system semaphore.
static QString tr(const char *sourceText, const char *comment=0, int n=-1)
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
The QString class provides a Unicode character string.
Definition: qstring.h:83
#define O_CREAT
static QString translate(const char *context, const char *key, const char *disambiguation=0, Encoding encoding=CodecForTr)
Q_CORE_EXPORT void qDebug(const char *,...)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
AccessMode
This enum is used by the constructor and setKey().
static int createUnixKeyFile(const QString &fileName)
Creates the unix file if needed.
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
QString arg(qlonglong a, int fieldwidth=0, int base=10, const QChar &fillChar=QLatin1Char(' ')) const Q_REQUIRED_RESULT
Definition: qstring.cpp:7186
QSystemSemaphore::SystemSemaphoreError error
HANDLE handle(QSystemSemaphore::AccessMode mode=QSystemSemaphore::Open)
bool remove()
Removes the file specified by fileName().
Definition: qfile.cpp:715
static QByteArray encodeName(const QString &fileName)
By default, this function converts fileName to the local 8-bit encoding determined by the user&#39;s loca...
Definition: qfile.cpp:528
#define O_EXCL
int errno