src/gui/embedded/qlock.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/embedded/qlock.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,336 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlock_p.h"
+
+
+#ifdef QT_NO_QWS_MULTIPROCESS
+
+QT_BEGIN_NAMESPACE
+
+/* no multiprocess - use a dummy */
+
+QLock::QLock(const QString & /*filename*/, char /*id*/, bool /*create*/)
+    : type(Read), data(0)
+{
+}
+
+QLock::~QLock()
+{
+}
+
+bool QLock::isValid() const
+{
+    return true;
+}
+
+void QLock::lock(Type t)
+{
+    data = (QLockData *)-1;
+    type = t;
+}
+
+void QLock::unlock()
+{
+    data = 0;
+}
+
+bool QLock::locked() const
+{
+    return data;
+}
+
+QT_END_NAMESPACE
+
+#else // QT_NO_QWS_MULTIPROCESS
+
+#include "qwssignalhandler_p.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#if defined(Q_OS_DARWIN)
+#   define Q_NO_SEMAPHORE
+#   include <sys/stat.h>
+#   include <sys/file.h>
+#else // Q_OS_DARWIN
+#   include <sys/sem.h>
+#   if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) && !defined(QT_LINUXBASE)) \
+    || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) \
+    || defined(Q_OS_BSDI)
+        /* union semun is defined by including <sys/sem.h> */
+#   else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+    int val;                    /* value for SETVAL */
+    struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
+    unsigned short *array;      /* array for GETALL, SETALL */
+};
+#   endif
+#endif // Q_OS_DARWIN
+#include <sys/ipc.h>
+#include <string.h>
+#include <errno.h>
+#include <qdebug.h>
+#include <signal.h>
+
+#include <private/qcore_unix_p.h> // overrides QT_OPEN
+
+
+QT_BEGIN_NAMESPACE
+
+#define MAX_LOCKS   200            // maximum simultaneous read locks
+
+class QLockData
+{
+public:
+#ifdef Q_NO_SEMAPHORE
+    QByteArray file;
+#endif // Q_NO_SEMAPHORE
+    int id;
+    int count;
+    bool owned;
+};
+
+/*!
+    \class QLock
+    \brief The QLock class is a wrapper for a System V shared semaphore.
+
+    \ingroup qws
+
+    \internal
+
+    It is used by \l{Qt for Embedded Linux} for synchronizing access to the graphics
+    card and shared memory region between processes.
+*/
+
+/*!
+    \enum QLock::Type
+
+    \value Read
+    \value Write
+*/
+
+/*!
+    \fn QLock::QLock(const QString &filename, char id, bool create)
+
+    Creates a lock. \a filename is the file path of the Unix-domain
+    socket the \l{Qt for Embedded Linux} client is using. \a id is the name of the
+    particular lock to be created on that socket. If \a create is true
+    the lock is to be created (as the Qt for Embedded Linux server does); if \a
+    create is false the lock should exist already (as the Qt for Embedded Linux
+    client expects).
+*/
+
+QLock::QLock(const QString &filename, char id, bool create)
+{
+    data = new QLockData;
+    data->count = 0;
+#ifdef Q_NO_SEMAPHORE
+    data->file = QString(filename+id).toLocal8Bit().constData();
+    for(int x = 0; x < 2; x++) {
+        data->id = QT_OPEN(data->file, O_RDWR | (x ? O_CREAT : 0), S_IRWXU);
+        if(data->id != -1 || !create) {
+            data->owned = x;
+            break;
+        }
+    }
+#else
+    key_t semkey = ftok(filename.toLocal8Bit().constData(), id);
+    data->id = semget(semkey,0,0);
+    data->owned = create;
+    if (create) {
+        semun arg; arg.val = 0;
+        if (data->id != -1)
+            semctl(data->id,0,IPC_RMID,arg);
+        data->id = semget(semkey,1,IPC_CREAT|0600);
+        arg.val = MAX_LOCKS;
+        semctl(data->id,0,SETVAL,arg);
+
+        QWSSignalHandler::instance()->addSemaphore(data->id);
+    }
+#endif
+    if (data->id == -1) {
+        int eno = errno;
+        qWarning("Cannot %s semaphore %s '%c'", (create ? "create" : "get"),
+                 qPrintable(filename), id);
+        qDebug() << "Error" << eno << strerror(eno);
+    }
+}
+
+/*!
+    \fn QLock::~QLock()
+
+    Destroys a lock
+*/
+
+QLock::~QLock()
+{
+    if (locked())
+        unlock();
+#ifdef Q_NO_SEMAPHORE
+    if(isValid()) {
+        QT_CLOSE(data->id);
+        if(data->owned)
+            unlink(data->file);
+    }
+#else
+    if(data->owned)
+        QWSSignalHandler::instance()->removeSemaphore(data->id);
+#endif
+    delete data;
+}
+
+/*!
+    \fn bool QLock::isValid() const
+
+    Returns true if the lock constructor was successful; returns false if
+    the lock could not be created or was not available to connect to.
+*/
+
+bool QLock::isValid() const
+{
+    return (data->id != -1);
+}
+
+/*!
+    Locks the semaphore with a lock of type \a t. Locks can either be
+    \c Read or \c Write. If a lock is \c Read, attempts by other
+    processes to obtain \c Read locks will succeed, and \c Write
+    attempts will block until the lock is unlocked. If locked as \c
+    Write, all attempts to lock by other processes will block until
+    the lock is unlocked. Locks are stacked: i.e. a given QLock can be
+    locked multiple times by the same process without blocking, and
+    will only be unlocked after a corresponding number of unlock()
+    calls.
+*/
+
+void QLock::lock(Type t)
+{
+    if (!data->count) {
+#ifdef Q_NO_SEMAPHORE
+        int op = LOCK_SH;
+        if(t == Write)
+            op = LOCK_EX;
+        for(int rv=1; rv;) {
+            rv = flock(data->id, op);
+            if (rv == -1 && errno != EINTR)
+                qDebug("Semop lock failure %s",strerror(errno));
+        }
+#else
+        sembuf sops;
+        sops.sem_num = 0;
+        sops.sem_flg = SEM_UNDO;
+
+        if (t == Write) {
+            sops.sem_op = -MAX_LOCKS;
+            type = Write;
+        } else {
+            sops.sem_op = -1;
+            type = Read;
+        }
+
+        int rv;
+        do {
+            rv = semop(data->id,&sops,1);
+            if (rv == -1 && errno != EINTR)
+                qDebug("Semop lock failure %s",strerror(errno));
+        } while (rv == -1 && errno == EINTR);
+#endif
+    }
+    data->count++;
+}
+
+/*!
+    \fn void QLock::unlock()
+
+    Unlocks the semaphore. If other processes were blocking waiting to
+    lock() the semaphore, one of them will wake up and succeed in
+    lock()ing.
+*/
+
+void QLock::unlock()
+{
+    if(data->count) {
+        data->count--;
+        if(!data->count) {
+#ifdef Q_NO_SEMAPHORE
+            for(int rv=1; rv;) {
+                rv = flock(data->id, LOCK_UN);
+                if (rv == -1 && errno != EINTR)
+                    qDebug("Semop lock failure %s",strerror(errno));
+            }
+#else
+            sembuf sops;
+            sops.sem_num = 0;
+            sops.sem_op = 1;
+            sops.sem_flg = SEM_UNDO;
+            if (type == Write)
+                sops.sem_op = MAX_LOCKS;
+
+            int rv;
+            do {
+                rv = semop(data->id,&sops,1);
+                if (rv == -1 && errno != EINTR)
+                    qDebug("Semop unlock failure %s",strerror(errno));
+            } while (rv == -1 && errno == EINTR);
+#endif
+        }
+    } else {
+        qDebug("Unlock without corresponding lock");
+    }
+}
+
+/*!
+    \fn bool QLock::locked() const
+
+    Returns true if the lock is currently held by the current process;
+    otherwise returns false.
+*/
+
+bool QLock::locked() const
+{
+    return (data->count > 0);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_MULTIPROCESS
+