summaryrefslogtreecommitdiffstats
path: root/old/libqtuitest/qalternatestack_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'old/libqtuitest/qalternatestack_unix.cpp')
-rw-r--r--old/libqtuitest/qalternatestack_unix.cpp525
1 files changed, 525 insertions, 0 deletions
diff --git a/old/libqtuitest/qalternatestack_unix.cpp b/old/libqtuitest/qalternatestack_unix.cpp
new file mode 100644
index 0000000..016102b
--- /dev/null
+++ b/old/libqtuitest/qalternatestack_unix.cpp
@@ -0,0 +1,525 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of QtUiTest.
+**
+** $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$
+**
+****************************************************************************/
+#undef _FORTIFY_SOURCE
+
+#include "qalternatestack_p.h"
+
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <QDebug>
+
+#ifdef QTUITEST_HAVE_VALGRIND
+# include <valgrind/valgrind.h>
+/* Make sure we have the right version of valgrind.h */
+# if !defined(VALGRIND_STACK_REGISTER) || !defined(VALGRIND_STACK_DEREGISTER) || !defined(VALGRIND_STACK_CHANGE)
+# warn You have Valgrind but are missing stack annotation macros, disabling Valgrind features.
+# undef QTUITEST_HAVE_VALGRIND
+# endif
+#endif
+
+struct QAlternateStackPrivate
+{
+ QAlternateStack* q;
+
+ // The memory containing the alternate stack.
+ QByteArray stackBuffer;
+ char* stackBegin;
+ int stackSize;
+
+#ifdef QTUITEST_HAVE_VALGRIND
+ int stackValgrindId;
+#endif
+
+ void allocate(int);
+
+ // Alternate and original stack objects.
+ stack_t alternateStack;
+ stack_t originalStack;
+
+ // Sigaction used to jump to new stack, and original sigaction.
+ struct sigaction alternateAction;
+ struct sigaction originalAction;
+
+ // jmp_buf for jumping between stacks.
+ sigjmp_buf alternateJmp;
+ sigjmp_buf originalJmp;
+
+ // Starting point of the new stack.
+ QAlternateStackEntryPoint entryFunction;
+
+ // Argument to be passed to entryFunction.
+ QVariant entryData;
+
+ // All instances of QAlternateStack.
+ static QList<QAlternateStack*> instances;
+};
+QList<QAlternateStack*> QAlternateStackPrivate::instances;
+
+QTUITEST_EXPORT char*& qalternatestack_stackbuf()
+{ static char* ret = 0; return ret; }
+QTUITEST_EXPORT int& qalternatestack_stackbuf_len()
+{ static int ret = 0; return ret; }
+
+#ifndef QT_NO_DEBUG
+QString qobject_to_string(QObject* object)
+{
+ QString ret;
+ {
+ QDebug dbg(&ret);
+ dbg << object;
+ }
+ return ret;
+}
+#endif
+
+void qt_sigaltstack(stack_t const* ss, stack_t* oss)
+{
+ if (0 != sigaltstack(ss, oss)) {
+ qFatal("QAlternateStack: error in sigaltstack: %s", strerror(errno));
+ }
+}
+
+void qt_sigaction(int signum, struct sigaction const* act, struct sigaction* oldact)
+{
+ if (0 != sigaction(signum,act,oldact)) {
+ qFatal("QAlternateStack: error in sigaction: %s", strerror(errno));
+ }
+}
+
+void qt_raise(int signum)
+{
+ if (-1 == raise(signum)) {
+ qFatal("QAlternateStack: error in raise: %s", strerror(errno));
+ }
+}
+
+QAlternateStackPrivate* qt_next_stack;
+
+static const int JMP_SWITCHFROM = 2;
+static const int JMP_SWITCHTO = 3;
+static const int JMP_FINISH = 4;
+static const int QALTERNATESTACK_SIGNAL = SIGUSR2;
+
+/*
+ This wrapper function is called when we first enter an alternate stack,
+ via the raising of QALTERNATESTACK_SIGNAL.
+*/
+void qt_entry_function_wrapper(int)
+{
+ // Establish this context into the alternate jmp_buf.
+ // We cannot have the whole stack run during a signal handler,
+ // so we store our context when we are first called, then return and allow
+ // the original stack to jump back to us.
+
+ int value = sigsetjmp(qt_next_stack->alternateJmp, 0);
+
+ // We might be here for these reasons:
+ //
+ // 1. We are handling the signal and just called setjmp.
+ // value == 0.
+ if (0 == value) {
+ // Return from the signal handler; the original stack will jump back
+ // to us via longjmp.
+ return;
+ }
+ // 2. We are switched to from the original stack.
+ // value == JMP_SWITCHTO.
+ else if (JMP_SWITCHTO == value) {
+ }
+ // Whoops, programmer error.
+ else Q_ASSERT(0);
+
+ QAlternateStackPrivate* stack = qt_next_stack;
+ stack->entryFunction(stack->q, stack->entryData);
+
+ // OK, jump back to the main stack and let it know that we're finished.
+ // We can't just return here; if we did, we'd jump back to the signal
+ // handler frame, but the signal handler is already finished.
+ siglongjmp(stack->originalJmp, JMP_FINISH);
+}
+
+/*!
+ \internal
+ \class QAlternateStack
+ \inpublicgroup QtUiTestModule
+ \brief The QAlternateStack class provides a call stack.
+
+ QAlternateStack can be used to switch between two call stacks without
+ using threads.
+
+ There is one anticipated use for this class, and that is to wait for a
+ specific amount of time, while processing events, without hanging if a
+ nested event loop occurs.
+
+ Example:
+ \code
+ void runsInAlternateStack(QAlternateStack* stack, const QVariant& data)
+ {
+ int foo = data.toInt();
+ // ...
+ // Need to wait for about 1500 milliseconds here, while processing events.
+ // Process events in the main stack to avoid hanging on nested event loops.
+ QTimer::singleShot(1500, stack, SLOT(switchTo()));
+ stack->switchFrom();
+
+ // Execution resumes here in ~1500ms, as long as the main stack is
+ // processing events, even if there was a nested event loop.
+ // ...
+
+ // Don't need the stack any more
+ stack->deleteLater();
+ }
+
+ void runsInMainStack()
+ {
+ QAlternateStack* stack = new QAlternateStack;
+ stack->start(runsInAlternateStack, 123);
+ }
+ \endcode
+
+ Using this class makes code more difficult to understand. Use an
+ alternative where possible.
+*/
+
+/*!
+ \relates QAlternateStack
+ \typedef QAlternateStackEntryPoint
+
+ Typedef for a pointer to a function with the signature
+ \c{void my_function(QAlternateStack*,const QVariant&)}.
+
+ Used as an entry point to a new stack.
+*/
+
+/*!
+ Creates an alternate stack of \a size bytes with the given \a parent.
+
+ Behavior is undefined if the alternate stack exceeds \a size.
+*/
+QAlternateStack::QAlternateStack(quint32 size, QObject* parent)
+ : QObject(parent),
+ d(new QAlternateStackPrivate)
+{
+ Q_ASSERT_X(QAlternateStack::isAvailable(), "QAlternateStack",
+ "QAlternateStack is not available on this platform!");
+
+ d->q = this;
+ d->entryFunction = 0;
+
+ d->allocate(size);
+
+#ifdef QTUITEST_HAVE_VALGRIND
+ d->stackValgrindId = VALGRIND_STACK_REGISTER(d->stackBegin, d->stackBegin+d->stackSize);
+#endif
+
+ QAlternateStackPrivate::instances << this;
+}
+
+void QAlternateStackPrivate::allocate(int size)
+{
+ if (!qalternatestack_stackbuf()) {
+ // Allocate space for the new stack.
+ stackBuffer.resize(size);
+ stackBegin = stackBuffer.data();
+ stackSize = size;
+ return;
+ }
+
+ /*
+ On some systems, it has been found that due to limitations in the kernel and/or threading
+ implementation, setting the stack pointer to point to memory allocated anywhere other than
+ the main stack causes a crash (bug 209341).
+
+ This code works around this bug by using a pool of memory which was earlier allocated on
+ the stack. qalternatestack_stackbuf() and qalternatestack_stackbuf_len() must be
+ explicitly initialized to point to a block of memory and to tell us its length.
+ */
+
+ // Find the first chunk of unused memory.
+ char* begin = qalternatestack_stackbuf();
+ char* end = qalternatestack_stackbuf() + qalternatestack_stackbuf_len();
+ char* found = 0;
+ int total_used = 0;
+
+ for (char* buf = begin; buf <= end - size && !found; buf += size) {
+ char* buf_end = buf + size;
+ // Is this block of memory definitely unused?
+ bool used = false;
+ foreach (QAlternateStack* st, QAlternateStackPrivate::instances) {
+ char* st_buf = st->d->stackBegin;
+ char* st_buf_end = st->d->stackBegin + st->d->stackSize;
+ // If we start after this stack ends, or we end before it finishes, we're OK.
+ if (buf >= st_buf_end || buf_end <= st_buf) {
+ } else {
+ used = true;
+ total_used += (st_buf_end - st_buf);
+ break;
+ }
+ }
+ if (!used) found = buf;
+ }
+
+ if (!found) {
+ qFatal( "QtUiTest: QAlternateStack ran out of memory! There seem to be %d stacks using a "
+ "total of %d bytes (out of %d), and a stack of size %d was requested. "
+ "Please increase the size of qalternatestack_stackbuf!",
+ QAlternateStackPrivate::instances.count(), total_used,
+ qalternatestack_stackbuf_len(), size);
+ }
+ stackBegin = found;
+ stackSize = size;
+}
+
+/*!
+ Destroys the alternate stack.
+
+ Behavior is undefined if the stack is destroyed while currently active.
+*/
+QAlternateStack::~QAlternateStack()
+{
+#ifdef QTUITEST_HAVE_VALGRIND
+ VALGRIND_STACK_DEREGISTER(d->stackValgrindId);
+#endif
+
+ delete d;
+ d = 0;
+ QAlternateStackPrivate::instances.removeAll(this);
+}
+
+/*!
+ Switch from the original stack to the alternate stack, and start
+ executing \a entry in the alternate stack.
+ This stack and \a data will be passed as an argument to \a entry.
+
+ This function should be called when first switching to an alternate
+ stack. When resuming a stack that is already active, use switchTo().
+
+ \sa switchTo(), switchFrom()
+*/
+void QAlternateStack::start(QAlternateStackEntryPoint entry, const QVariant& data)
+{
+ Q_ASSERT_X(!isActive(), "QAlternateStack::start",
+ qPrintable(QString("`start' called while already active. sender(): %1")
+ .arg(qobject_to_string(sender()))));
+
+ // Set up the alternate stack to be jumped to.
+ d->alternateStack.ss_sp = d->stackBegin;
+ d->alternateStack.ss_flags = 0;
+ d->alternateStack.ss_size = d->stackSize;
+ qt_sigaltstack(&d->alternateStack, &d->originalStack);
+
+ // Set up signal handler to jump to entry function wrapper in alternate stack.
+ d->alternateAction.sa_handler = qt_entry_function_wrapper;
+ sigemptyset(&d->alternateAction.sa_mask);
+ d->alternateAction.sa_flags = SA_ONSTACK;
+ qt_sigaction(QALTERNATESTACK_SIGNAL, &d->alternateAction, &d->originalAction);
+
+ // Raise the signal, jumping to the alternate stack.
+ qt_next_stack = d;
+ qt_raise(QALTERNATESTACK_SIGNAL);
+
+ // signal handler returns immediately after storing its context into
+ // alternateJmp. Restore the old signal handler.
+ qt_sigaltstack(&d->originalStack, 0);
+ qt_sigaction(QALTERNATESTACK_SIGNAL, &d->originalAction, 0);
+
+ // OK, now store the entry function and data and jump to the new stack.
+ d->entryFunction = entry;
+ d->entryData = data;
+
+ switchTo();
+}
+
+/*!
+ Switch from the original stack to the alternate stack.
+
+ This function can be called to resume execution in an alternate stack.
+
+ If execution has been suspended in the alternate stack by a call to
+ switchFrom(), switchTo() will resume executing at that point.
+ If the alternate stack has completed execution or hasn't started, this
+ function does nothing and returns immediately.
+
+ It is an error to call this function from the alternate stack.
+*/
+void QAlternateStack::switchTo()
+{
+ // We must not be the currently active stack.
+ Q_ASSERT_X(!isCurrentStack(), "QAlternateStack::switchTo",
+ qPrintable(QString("`switchTo' called in currently active stack. sender(): %1")
+ .arg(qobject_to_string(sender()))));
+
+ // Store where we currently are.
+ int value = sigsetjmp(d->originalJmp, 1);
+
+ // Now it really gets tricky.
+ // At this particular point, we could be here for these reasons:
+ //
+ // 1. switchTo() was actually called from the original stack
+ // and we just returned from setjmp.
+ // value == 0.
+ //
+ // 2. The alternate stack switched to the original stack
+ // by calling switchFrom().
+ // value == JMP_SWITCHFROM.
+ //
+ // 3. The alternate stack finished execution and jumped back to us.
+ // value == JMP_FINISH.
+
+ // Case 1: just returned from setjmp.
+ // Jump to the other stack.
+ if (0 == value) {
+ // If there is no entry function, that means `start' has not been called or we have
+ // finished already. In other words, there's no more to process on this stack, so
+ // just return immediately.
+ if (!d->entryFunction) return;
+
+ // This function never returns; from here we will jump back to
+ // the above call to setjmp() via case 2 or 3.
+ siglongjmp(d->alternateJmp, JMP_SWITCHTO);
+ }
+
+ // Case 2: switchFrom() called in alternate stack.
+ if (value == JMP_SWITCHFROM) {
+ // Don't need to do anything, just return.
+ return;
+ }
+
+ // Case 3: The alternate stack finished execution.
+ // Just need to do a little cleanup.
+ if (value == JMP_FINISH) {
+ d->entryFunction = 0;
+ d->entryData = QVariant();
+ return;
+ }
+
+ // Whoops, bad programmer.
+ Q_ASSERT(0);
+}
+
+/*!
+ Switch from the alternate stack to the original stack.
+
+ Once execution takes place in the alternate stack, there are two ways
+ to return to the original stack.
+
+ The first is simply to return from the entry function passed to the
+ constructor, at which point the switchTo() function in the original
+ stack will return.
+
+ The second is to call switchFrom(). This will cause the switchTo()
+ function to return in the original stack. switchFrom() will not
+ return until switchTo() is called again in the original stack, which
+ is not guaranteed to happen.
+
+ It is an error to call this function from the original stack.
+*/
+void QAlternateStack::switchFrom()
+{
+ // We must be the currently active stack.
+ Q_ASSERT_X(isCurrentStack(), "QAlternateStack::switchFrom",
+ qPrintable(QString("`switchFrom' called from wrong stack. sender(): %1")
+ .arg(qobject_to_string(sender()))));
+
+ int value = sigsetjmp(d->alternateJmp, 1);
+
+ // Two possibilities:
+ //
+ // 1. We just called setjmp and will now jump to main stack.
+ // value == 0
+ if (0 == value) {
+ siglongjmp(d->originalJmp, JMP_SWITCHFROM);
+ }
+ // 2. We just jumped to here from main stack.
+ // value == JMP_SWITCHTO
+ else if (JMP_SWITCHTO == value) {
+ }
+ // Whoops, bad programmer.
+ else Q_ASSERT(0);
+}
+
+/*!
+ Returns true if the stack has started (entry function has been called)
+ and not yet finished (entry function has not returned).
+
+ \sa isCurrentStack()
+*/
+bool QAlternateStack::isActive() const
+{ return d->entryFunction; }
+
+/*!
+ Returns true if the currently used stack is this QAlternateStack.
+
+ \sa isActive()
+*/
+bool QAlternateStack::isCurrentStack() const
+{
+ // Test if a stack-allocated variable resides within the alternate
+ // stack buffer.
+ char dummy = 1;
+ quint32 diff = &dummy - d->stackBegin;
+ return (diff < (quint32)d->stackSize);
+}
+
+/*!
+ Returns all created QAlternateStack objects.
+*/
+QList<QAlternateStack*> QAlternateStack::instances()
+{ return QAlternateStackPrivate::instances; }
+
+/*!
+ Returns true if QAlternateStack is usable on this platform.
+
+ Usage of QAlternateStack when this function returns false will typically
+ result in a fatal error at runtime.
+*/
+bool QAlternateStack::isAvailable()
+{
+#ifdef Q_OS_MAC
+ return false;
+#endif
+ return true;
+}
+