From d147285d644157bc12487584578b5389b56eea31 Mon Sep 17 00:00:00 2001 From: David Faure Date: Mon, 14 Jan 2013 12:58:31 +0100 Subject: [PATCH] Add Q_COREAPP_STARTUP_FUNCTION macro. This is necessary for initializing things in a library, which require a QCoreApplication instance (unlike Q_CONSTRUCTOR_FUNCTION, which runs before that). Example use cases: KCrash (segv handler), and KCheckAccelerators (debugging tool triggered by magic key combination). Change-Id: I5f4c4699dd4d21aea72b007989ba57467e86ed10 Reviewed-by: Olivier Goffart Reviewed-by: Thiago Macieira --- .../src_corelib_kernel_qcoreapplication.cpp | 7 ++ src/corelib/kernel/qcoreapplication.cpp | 65 +++++++++++++++++++ src/corelib/kernel/qcoreapplication.h | 8 +++ .../corelib/global/qglobal/tst_qglobal.cpp | 31 ++++++++- 4 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qcoreapplication.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qcoreapplication.cpp index e677797323..6029c0e4ec 100644 --- a/src/corelib/doc/snippets/code/src_corelib_kernel_qcoreapplication.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qcoreapplication.cpp @@ -57,7 +57,14 @@ foreach (const QString &path, app.libraryPaths()) //! [3] +// Called once QCoreApplication exists +static void preRoutineMyDebugTool() +{ + MyDebugTool* tool = new MyDebugTool(QCoreApplication::instance()); + QCoreApplication::instance()->installEventFilter(tool); +} +Q_COREAPP_STARTUP_FUNCTION(preRoutineMyDebugTool) //! [3] diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 8ede244145..c9f973b52f 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -194,8 +194,32 @@ extern "C" void Q_CORE_EXPORT qt_startup_hook() { } +typedef QList QStartUpFuncList; +Q_GLOBAL_STATIC(QStartUpFuncList, preRList) typedef QList QVFuncList; Q_GLOBAL_STATIC(QVFuncList, postRList) +static QBasicMutex globalPreRoutinesMutex; + +/*! + \internal + + Adds a global routine that will be called from the QCoreApplication + constructor. The public API is Q_COREAPP_STARTUP_FUNCTION. +*/ +void qAddPreRoutine(QtStartUpFunction p) +{ + QStartUpFuncList *list = preRList(); + if (!list) + return; + // Due to C++11 parallel dynamic initialization, this can be called + // from multiple threads. +#ifndef QT_NO_THREAD + QMutexLocker locker(&globalPreRoutinesMutex); +#endif + if (QCoreApplication::instance()) + p(); + list->prepend(p); // in case QCoreApplication is re-created, see qt_call_pre_routines +} void qAddPostRoutine(QtCleanUpFunction p) { @@ -213,6 +237,21 @@ void qRemovePostRoutine(QtCleanUpFunction p) list->removeAll(p); } +static void qt_call_pre_routines() +{ + QStartUpFuncList *list = preRList(); + if (!list) + return; +#ifndef QT_NO_THREAD + QMutexLocker locker(&globalPreRoutinesMutex); +#endif + // Unlike qt_call_post_routines, we don't empty the list, because + // Q_COREAPP_STARTUP_FUNCTION is a macro, so the user expects + // the function to be executed every time QCoreApplication is created. + for (int i = 0; i < list->count(); ++i) + list->at(i)(); +} + void Q_CORE_EXPORT qt_call_post_routines() { QVFuncList *list = 0; @@ -637,6 +676,7 @@ void QCoreApplication::init() d->processCommandLineArguments(); + qt_call_pre_routines(); qt_startup_hook(); } @@ -2324,6 +2364,31 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc mainThread->setEventDispatcher(eventDispatcher); } +/*! + \macro Q_COREAPP_STARTUP_FUNCTION(QtStartUpFunction ptr) + \since 5.1 + \relates QCoreApplication + \reentrant + + Adds a global function that will be called from the QCoreApplication + constructor. This macro is normally used to initialize libraries + for program-wide functionality, without requiring the application to + call into the library for initialization. + + The function specified by \a ptr should take no arguments and should + return nothing. For example: + + \snippet code/src_corelib_kernel_qcoreapplication.cpp 3 + + Note that the startup function will run at the end of the QCoreApplication constructor, + before any GUI initialization. If GUI code is required in the function, + use a timer (or a queued invocation) to perform the initialization later on, + from the event loop. + + If QCoreApplication is deleted and another QCoreApplication is created, + the startup function will be invoked again. +*/ + /*! \fn void qAddPostRoutine(QtCleanUpFunction ptr) \relates QCoreApplication diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 185aea53d1..0dfad6f23e 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -220,12 +220,20 @@ public: \ { return QCoreApplication::translate(#context, sourceText, disambiguation, n); } \ private: +typedef void (*QtStartUpFunction)(); typedef void (*QtCleanUpFunction)(); +Q_CORE_EXPORT void qAddPreRoutine(QtStartUpFunction); Q_CORE_EXPORT void qAddPostRoutine(QtCleanUpFunction); Q_CORE_EXPORT void qRemovePostRoutine(QtCleanUpFunction); Q_CORE_EXPORT QString qAppName(); // get application name +#define Q_COREAPP_STARTUP_FUNCTION(AFUNC) \ + static void AFUNC ## _ctor_function() { \ + qAddPreRoutine(AFUNC); \ + } \ + Q_CONSTRUCTOR_FUNCTION(AFUNC ## _ctor_function) + #if defined(Q_OS_WIN) && !defined(QT_NO_DEBUG_STREAM) Q_CORE_EXPORT QString decodeMSG(const MSG &); Q_CORE_EXPORT QDebug operator<<(QDebug, const MSG &); diff --git a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp index 72a2863fdd..47add61b05 100644 --- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp +++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp @@ -55,6 +55,8 @@ private slots: void checkptr(); void qstaticassert(); void qConstructorFunction(); + void qCoreAppStartupFunction(); + void qCoreAppStartupFunctionRestart(); void isEnum(); void qAlignOf(); }; @@ -303,6 +305,33 @@ void tst_QGlobal::qConstructorFunction() QCOMPARE(qConstructorFunctionValue, 123); } +static int qStartupFunctionValue; +static void myStartupFunc() +{ + Q_ASSERT(QCoreApplication::instance()); + if (QCoreApplication::instance()) + qStartupFunctionValue += 124; +} + +Q_COREAPP_STARTUP_FUNCTION(myStartupFunc) + +void tst_QGlobal::qCoreAppStartupFunction() +{ + QCOMPARE(qStartupFunctionValue, 0); + int argc = 1; + char *argv[] = { const_cast("tst_qglobal") }; + QCoreApplication app(argc, argv); + QCOMPARE(qStartupFunctionValue, 124); +} + +void tst_QGlobal::qCoreAppStartupFunctionRestart() +{ + qStartupFunctionValue = 0; + qCoreAppStartupFunction(); + qStartupFunctionValue = 0; + qCoreAppStartupFunction(); +} + struct isEnum_A { int n_; }; @@ -532,5 +561,5 @@ void tst_QGlobal::qAlignOf() #undef TEST_AlignOf_RValueRef #undef TEST_AlignOf_impl -QTEST_MAIN(tst_QGlobal) +QTEST_APPLESS_MAIN(tst_QGlobal) #include "tst_qglobal.moc"