├── src ├── .gitignore ├── conftests │ ├── libevent1.h │ ├── eventfd.h │ └── libevent2.h ├── eventdispatcher_libevent_config_p.h ├── tco.h ├── tco_impl.h ├── common.h ├── wsainit.h ├── libevent2-emul.h ├── eventdispatcher_libevent_config.h ├── qt4compat.h ├── eventdispatcher_libevent.h ├── eventdispatcher_libevent.pro ├── tco_win32_libevent.cpp ├── eventdispatcher_libevent_p.h ├── socknot_p.cpp ├── tco_eventfd.cpp ├── eventdispatcher_libevent_config.cpp ├── tco_pipe.cpp ├── eventdispatcher_libevent_p.cpp ├── timers_p.cpp └── eventdispatcher_libevent.cpp ├── lib ├── .gitignore └── eventdispatcher_libevent.pri ├── .gitignore ├── .gitmodules ├── tests ├── eventdispatcher.h ├── eventdispatcherqpa.h └── local.pri ├── .travis.yml ├── src-gui ├── eventdispatcher_libevent_qpa.pro ├── eventdispatcher_libevent_qpa.h └── eventdispatcher_libevent_qpa.cpp ├── LICENSE └── README.md /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.user 2 | -------------------------------------------------------------------------------- /src/conftests/libevent1.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /src/conftests/eventfd.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | pkgconfig 2 | *.a 3 | *.prl 4 | *.pc 5 | -------------------------------------------------------------------------------- /src/conftests/libevent2.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.moc 4 | *.prl 5 | moc_*.cpp 6 | Makefile 7 | Makefile.* 8 | /*.sh 9 | *.pro.user 10 | /tests/tst_* 11 | .qmake.stash 12 | *.gch -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/qt_eventdispatcher_tests"] 2 | path = tests/qt_eventdispatcher_tests 3 | url = git://github.com/sjinks/qt_eventdispatcher_tests.git 4 | -------------------------------------------------------------------------------- /lib/eventdispatcher_libevent.pri: -------------------------------------------------------------------------------- 1 | HEADERS += $$PWD/eventdispatcher_libevent.h 2 | 3 | unix { 4 | CONFIG += link_pkgconfig 5 | PKGCONFIG += eventdispatcher_libevent 6 | } 7 | else:win32 { 8 | LIBS += -L$$PWD -leventdispatcher_libevent 9 | INCLUDEPATH += $$PWD 10 | DEPENDPATH += $$PWD 11 | } 12 | -------------------------------------------------------------------------------- /tests/eventdispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_H 2 | #define EVENTDISPATCHER_H 3 | 4 | #include "eventdispatcher_libevent.h" 5 | 6 | class EventDispatcher : public EventDispatcherLibEvent { 7 | Q_OBJECT 8 | public: 9 | explicit EventDispatcher(QObject* parent = 0) : EventDispatcherLibEvent(parent) {} 10 | }; 11 | 12 | #endif // EVENTDISPATCHER_H 13 | -------------------------------------------------------------------------------- /tests/eventdispatcherqpa.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHERQPA_H 2 | #define EVENTDISPATCHERQPA_H 3 | 4 | #include "eventdispatcher_libevent_qpa.h" 5 | 6 | class EventDispatcherQPA : public EventDispatcherLibEventQPA { 7 | Q_OBJECT 8 | public: 9 | explicit EventDispatcherQPA(QObject* parent = 0) : EventDispatcherLibEventQPA(parent) {} 10 | }; 11 | 12 | #endif // EVENTDISPATCHERQPA_H 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | sudo: required 4 | dist: trusty 5 | 6 | env: 7 | - QT_SELECT=4 8 | - QT_SELECT=5 9 | 10 | compiler: 11 | - gcc 12 | - clang 13 | 14 | branches: 15 | only: 16 | - master 17 | 18 | before_install: 19 | - sudo apt-get -qq update 20 | - sudo apt-get -qq -y install libqt4-dev libqt4-private-dev qtbase5-dev qtbase5-private-dev qtdeclarative5-dev qtchooser libevent-dev 21 | - git submodule update --init --recursive 22 | 23 | script: 24 | - ./build.sh 25 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent_config_p.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBEVENT_CONFIG_P_H 2 | #define EVENTDISPATCHER_LIBEVENT_CONFIG_P_H 3 | 4 | #include "qt4compat.h" 5 | 6 | struct event_config; 7 | 8 | class Q_DECL_HIDDEN EventDispatcherLibEventConfigPrivate { 9 | public: 10 | EventDispatcherLibEventConfigPrivate(void); 11 | ~EventDispatcherLibEventConfigPrivate(void); 12 | 13 | bool avoidMethod(const char* method); 14 | bool requireFeatures(int features); 15 | bool setConfiguration(int config); 16 | private: 17 | event_config* m_cfg; 18 | 19 | friend class EventDispatcherLibEventPrivate; 20 | }; 21 | 22 | #endif // EVENTDISPATCHER_LIBEVENT_CONFIG_P_H 23 | -------------------------------------------------------------------------------- /src-gui/eventdispatcher_libevent_qpa.pro: -------------------------------------------------------------------------------- 1 | QT += gui-private 2 | CONFIG += staticlib create_prl link_prl 3 | TEMPLATE = lib 4 | TARGET = eventdispatcher_libevent_qpa 5 | SOURCES = eventdispatcher_libevent_qpa.cpp 6 | HEADERS = eventdispatcher_libevent_qpa.h 7 | DESTDIR = ../lib 8 | 9 | LIBS += -L$$PWD/../lib -leventdispatcher_libevent 10 | INCLUDEPATH += $$PWD/../src 11 | DEPENDPATH += $$PWD/../src 12 | PRE_TARGETDEPS += $$DESTDIR/../lib/libeventdispatcher_libevent.a 13 | 14 | headers.files = eventdispatcher_libevent_qpa.h 15 | 16 | unix { 17 | target.path = /usr/lib 18 | headers.path = /usr/include 19 | } 20 | else { 21 | headers.path = $$DESTDIR 22 | target.path = $$DESTDIR 23 | } 24 | 25 | INSTALLS += target headers 26 | -------------------------------------------------------------------------------- /src-gui/eventdispatcher_libevent_qpa.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBEVENT_QPA_H 2 | #define EVENTDISPATCHER_LIBEVENT_QPA_H 3 | 4 | #include "eventdispatcher_libevent.h" 5 | 6 | #if QT_VERSION < 0x050000 7 | # error This code requires at least Qt 5 8 | #endif 9 | 10 | class EventDispatcherLibEventQPA : public EventDispatcherLibEvent { 11 | Q_OBJECT 12 | public: 13 | explicit EventDispatcherLibEventQPA(QObject* parent = 0); 14 | virtual ~EventDispatcherLibEventQPA(void) Q_DECL_OVERRIDE; 15 | 16 | bool processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE; 17 | bool hasPendingEvents(void) Q_DECL_OVERRIDE; 18 | void flush(void) Q_DECL_OVERRIDE; 19 | 20 | private: 21 | Q_DISABLE_COPY(EventDispatcherLibEventQPA) 22 | }; 23 | 24 | #endif // EVENTDISPATCHER_LIBEVENT_QPA_H 25 | -------------------------------------------------------------------------------- /src/tco.h: -------------------------------------------------------------------------------- 1 | #ifndef TCO_H 2 | #define TCO_H 3 | 4 | #if QT_VERSION >= 0x040600 5 | #include 6 | #endif 7 | #include "qt4compat.h" 8 | 9 | class ThreadCommunicationObjectPrivate; 10 | 11 | class Q_DECL_HIDDEN ThreadCommunicationObject { 12 | public: 13 | ThreadCommunicationObject(void); 14 | ~ThreadCommunicationObject(void); 15 | 16 | bool valid(void) const; 17 | 18 | bool wakeUp(void); 19 | bool awaken(void); 20 | 21 | qintptr fd(void) const; 22 | private: 23 | Q_DISABLE_COPY(ThreadCommunicationObject) 24 | Q_DECLARE_PRIVATE(ThreadCommunicationObject) 25 | 26 | #if QT_VERSION >= 0x040600 27 | QScopedPointer d_ptr; 28 | #else 29 | ThreadCommunicationObjectPrivate* d_ptr; 30 | #endif 31 | 32 | }; 33 | 34 | #endif // TCO_H 35 | -------------------------------------------------------------------------------- /src/tco_impl.h: -------------------------------------------------------------------------------- 1 | #include "tco.h" 2 | 3 | ThreadCommunicationObject::ThreadCommunicationObject(void) 4 | : d_ptr(new ThreadCommunicationObjectPrivate()) 5 | { 6 | } 7 | 8 | ThreadCommunicationObject::~ThreadCommunicationObject(void) 9 | { 10 | #if QT_VERSION < 0x040600 11 | delete this->d_ptr; 12 | this->d_ptr = 0; 13 | #endif 14 | } 15 | 16 | bool ThreadCommunicationObject::valid(void) const 17 | { 18 | Q_D(const ThreadCommunicationObject); 19 | return d->valid(); 20 | } 21 | 22 | bool ThreadCommunicationObject::wakeUp(void) 23 | { 24 | Q_D(ThreadCommunicationObject); 25 | return d->wakeUp(); 26 | } 27 | 28 | bool ThreadCommunicationObject::awaken(void) 29 | { 30 | Q_D(ThreadCommunicationObject); 31 | return d->awaken(); 32 | } 33 | 34 | qintptr ThreadCommunicationObject::fd(void) const 35 | { 36 | Q_D(const ThreadCommunicationObject); 37 | return d->readfd(); 38 | } 39 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDEWIN_H 2 | #define INCLUDEWIN_H 3 | 4 | #if defined __cplusplus 5 | 6 | #include 7 | 8 | #ifdef Q_OS_WIN 9 | #ifndef WIN32_LEAN_AND_MEAN 10 | #define WIN32_LEAN_AND_MEAN 11 | #endif 12 | #include 13 | #include 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "qt4compat.h" 27 | 28 | #ifdef Q_OS_WIN 29 | #include "wsainit.h" 30 | #endif 31 | 32 | #endif //defined(__cplusplus) 33 | 34 | #if defined(SJ_LIBEVENT_MAJOR) && SJ_LIBEVENT_MAJOR == 1 35 | # include "libevent2-emul.h" 36 | #else 37 | # include 38 | #endif 39 | 40 | #endif // INCLUDEWIN_H 41 | -------------------------------------------------------------------------------- /src/wsainit.h: -------------------------------------------------------------------------------- 1 | #ifndef WSAINIT_H 2 | #define WSAINIT_H 3 | 4 | #include "common.h" 5 | 6 | Q_DECL_HIDDEN inline bool WSAInitialized(void) 7 | { 8 | SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 9 | if (s == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) { 10 | return false; 11 | } 12 | 13 | ::closesocket(s); 14 | return true; 15 | } 16 | 17 | class Q_DECL_HIDDEN WSAInitializer { 18 | public: 19 | WSAInitializer(void) 20 | : m_success(false) 21 | { 22 | WORD wVersionRequested = MAKEWORD(2, 2); 23 | WSADATA wsaData; 24 | int err = WSAStartup(wVersionRequested, &wsaData); 25 | if (Q_LIKELY(!err)) { 26 | this->m_success = true; 27 | } 28 | else { 29 | qWarning("%s: WSA initialization failed: %d", Q_FUNC_INFO, err); 30 | } 31 | } 32 | 33 | ~WSAInitializer(void) 34 | { 35 | if (Q_LIKELY(this->m_success)) { 36 | WSACleanup(); 37 | } 38 | } 39 | private: 40 | bool m_success; 41 | }; 42 | 43 | #endif // WSAINIT_H 44 | -------------------------------------------------------------------------------- /src-gui/eventdispatcher_libevent_qpa.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libevent_qpa.h" 5 | 6 | EventDispatcherLibEventQPA::EventDispatcherLibEventQPA(QObject* parent) 7 | : EventDispatcherLibEvent(parent) 8 | { 9 | } 10 | 11 | EventDispatcherLibEventQPA::~EventDispatcherLibEventQPA(void) 12 | { 13 | } 14 | 15 | bool EventDispatcherLibEventQPA::processEvents(QEventLoop::ProcessEventsFlags flags) 16 | { 17 | bool sent_events = QWindowSystemInterface::sendWindowSystemEvents(flags); 18 | 19 | if (EventDispatcherLibEvent::processEvents(flags)) { 20 | return true; 21 | } 22 | 23 | return sent_events; 24 | } 25 | 26 | bool EventDispatcherLibEventQPA::hasPendingEvents(void) 27 | { 28 | return EventDispatcherLibEvent::hasPendingEvents() || QWindowSystemInterface::windowSystemEventsQueued(); 29 | } 30 | 31 | void EventDispatcherLibEventQPA::flush(void) 32 | { 33 | if (qApp) { 34 | qApp->sendPostedEvents(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013 Vladimir Kolesnikov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/libevent2-emul.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENT2_EVENT_H 2 | #define EVENT2_EVENT_H 3 | 4 | #define SJ_LIBEVENT_EMULATION 1 5 | 6 | #include 7 | #ifndef EV_H_ 8 | // libevent emulation by libev 9 | # include 10 | #endif 11 | #include "qt4compat.h" 12 | 13 | typedef int evutil_socket_t; 14 | typedef void(*event_callback_fn)(evutil_socket_t, short, void*); 15 | 16 | Q_DECL_HIDDEN inline struct event* event_new(struct event_base* base, evutil_socket_t fd, short int events, event_callback_fn callback, void* callback_arg) 17 | { 18 | struct event* e = new struct event; 19 | event_set(e, fd, events, callback, callback_arg); 20 | event_base_set(base, e); 21 | return e; 22 | } 23 | 24 | Q_DECL_HIDDEN inline void event_free(struct event* e) 25 | { 26 | delete e; 27 | } 28 | 29 | #ifdef EV_H_ 30 | Q_DECL_HIDDEN inline int event_reinit(struct event_base* base) 31 | { 32 | Q_UNUSED(base); 33 | qWarning("%s emulation is not supported.", Q_FUNC_INFO); 34 | return 1; 35 | } 36 | 37 | #define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz)) 38 | #define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp)) 39 | #define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp)) 40 | #define evutil_timercmp(tvp, uvp, cmp) \ 41 | (((tvp)->tv_sec == (uvp)->tv_sec) ? \ 42 | ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ 43 | ((tvp)->tv_sec cmp (uvp)->tv_sec)) 44 | 45 | #endif 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /tests/local.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD $$PWD/../src 2 | DEPENDPATH += $$PWD $$PWD/../src 3 | 4 | HEADERS += $$PWD/eventdispatcher.h 5 | 6 | CONFIG *= link_prl 7 | LIBS += -L$$OUT_PWD/$$DESTDIR/../lib 8 | 9 | !CONFIG(console) { 10 | HEADERS += $$PWD/eventdispatcherqpa.h 11 | LIBS += -leventdispatcher_libevent_qpa 12 | INCLUDEPATH += $$PWD/../src-gui 13 | DEPENDPATH += $$PWD/../src-gui 14 | 15 | win32: CONFIG += windows 16 | } 17 | else { 18 | LIBS += -leventdispatcher_libevent 19 | } 20 | 21 | unix|*-g++* { 22 | equals(QMAKE_PREFIX_STATICLIB, ""): QMAKE_PREFIX_STATICLIB = lib 23 | equals(QMAKE_EXTENSION_STATICLIB, ""): QMAKE_EXTENSION_STATICLIB = a 24 | 25 | PRE_TARGETDEPS *= $$OUT_PWD/$$DESTDIR/../lib/$${QMAKE_PREFIX_STATICLIB}eventdispatcher_libevent$${LIB_SUFFIX}.$${QMAKE_EXTENSION_STATICLIB} 26 | 27 | !CONFIG(console) { 28 | PRE_TARGETDEPS *= $$OUT_PWD/$$DESTDIR/../lib/$${QMAKE_PREFIX_STATICLIB}eventdispatcher_libevent_qpa$${LIB_SUFFIX}.$${QMAKE_EXTENSION_STATICLIB} 29 | } 30 | } 31 | else:win32 { 32 | PRE_TARGETDEPS *= $$OUT_PWD/$$DESTDIR/../lib/eventdispatcher_libevent$${LIB_SUFFIX}.lib 33 | !CONFIG(console) { 34 | PRE_TARGETDEPS *= $$OUT_PWD/$$DESTDIR/../lib/eventdispatcher_libevent_qpa$${LIB_SUFFIX}.lib 35 | } 36 | } 37 | 38 | equals(TARGET, tst_qsocketnotifier):win32 { 39 | LIBS += $$QMAKE_LIBS_NETWORK 40 | } 41 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent_config.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBEVENT_CONFIG_H 2 | #define EVENTDISPATCHER_LIBEVENT_CONFIG_H 3 | 4 | #include 5 | #if QT_VERSION >= 0x040600 6 | # include 7 | #endif 8 | 9 | class EventDispatcherLibEventConfigPrivate; 10 | 11 | class EventDispatcherLibEventConfig { 12 | Q_GADGET 13 | Q_FLAGS(Feature Features) 14 | Q_FLAGS(Config Configuration) 15 | public: 16 | EventDispatcherLibEventConfig(void); 17 | ~EventDispatcherLibEventConfig(void); 18 | 19 | enum Feature { 20 | ev_ET = 0x01, 21 | ev_O1 = 0x02, 22 | ev_FDs = 0x04 23 | }; 24 | 25 | enum Config { 26 | cfg_NoLock = 0x01, 27 | cfg_IgnoreEnvironment = 0x02, 28 | cfg_StartupIOCP = 0x04, 29 | cfg_NoCacheTime = 0x08, 30 | cfg_EPollChangelist = 0x10 31 | }; 32 | 33 | Q_DECLARE_FLAGS(Features, Feature) 34 | Q_DECLARE_FLAGS(Configuration, Config) 35 | 36 | bool avoidMethod(const QLatin1String& method); 37 | bool requireFeatures(Features f); 38 | bool setConfiguration(Configuration cfg); 39 | 40 | private: 41 | Q_DECLARE_PRIVATE(EventDispatcherLibEventConfig) 42 | #if QT_VERSION >= 0x040600 43 | QScopedPointer d_ptr; 44 | #else 45 | EventDispatcherLibEventConfigPrivate* d_ptr; 46 | #endif 47 | 48 | friend class EventDispatcherLibEventPrivate; 49 | }; 50 | 51 | Q_DECLARE_OPERATORS_FOR_FLAGS(EventDispatcherLibEventConfig::Features) 52 | Q_DECLARE_OPERATORS_FOR_FLAGS(EventDispatcherLibEventConfig::Configuration) 53 | 54 | #endif // EVENTDISPATCHER_LIBEVENT_CONFIG_H 55 | -------------------------------------------------------------------------------- /src/qt4compat.h: -------------------------------------------------------------------------------- 1 | #ifndef QT4COMPAT_H 2 | #define QT4COMPAT_H 3 | 4 | #include 5 | #include 6 | 7 | #if !defined(Q_UNREACHABLE) && !defined(Q_ASSUME) 8 | # if defined(Q_CC_INTEL) || defined(Q_CC_MSVC) 9 | # define Q_UNREACHABLE() __assume(0) 10 | # define Q_ASSUME(s) __assume(s) 11 | # elif defined(Q_CC_CLANG) 12 | # define Q_UNREACHABLE() __builtin_unreachable() 13 | # elif defined(Q_CC_GNU) && defined(__GNUC__) && defined(__GNUC_MINOR__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 14 | # define Q_UNREACHABLE() __builtin_unreachable() 15 | # endif 16 | #endif 17 | 18 | #if !defined(Q_LIKELY) && !defined(Q_UNLIKELY) 19 | # ifdef __GNUC__ 20 | # define Q_LIKELY(expr) __builtin_expect(!!(expr), true) 21 | # define Q_UNLIKELY(expr) __builtin_expect(!!(expr), false) 22 | # endif 23 | #endif 24 | 25 | #ifndef Q_FUNC_INFO 26 | # if defined(_MSC_VER) 27 | # define Q_FUNC_INFO __FUNCSIG__ 28 | # elif defined(__GNUC__) || defined(_DIAB_TOOL) 29 | # define Q_FUNC_INFO __PRETTY_FUNCTION__ 30 | # endif 31 | #endif 32 | 33 | #ifndef Q_LIKELY 34 | # define Q_LIKELY(s) s 35 | #endif 36 | 37 | #ifndef Q_UNLIKELY 38 | # define Q_UNLIKELY(s) s 39 | #endif 40 | 41 | #ifndef Q_UNREACHABLE 42 | # define Q_UNREACHABLE() Q_ASSERT(false) 43 | #endif 44 | 45 | #ifndef Q_ASSUME 46 | # define Q_ASSUME(s) if (s) {} else { Q_UNREACHABLE(); } 47 | #endif 48 | 49 | #ifndef Q_DECL_HIDDEN 50 | # define Q_DECL_HIDDEN 51 | #endif 52 | 53 | #ifndef QT_STRINGIFY 54 | # define QT_STRINGIFY2(x) #x 55 | # define QT_STRINGIFY(x) QT_STRINGIFY2(x) 56 | #endif 57 | 58 | #ifndef Q_FUNC_INFO 59 | # if defined(Q_OS_SOLARIS) || defined(Q_CC_XLC) 60 | # define Q_FUNC_INFO __FILE__ "(line number unavailable)" 61 | # else 62 | # define Q_FUNC_INFO __FILE__ ":" QT_STRINGIFY(__LINE__) 63 | # endif 64 | #endif 65 | 66 | #ifndef Q_EMIT 67 | # define Q_EMIT emit 68 | #endif 69 | 70 | #if QT_VERSION < 0x050000 71 | namespace Qt { // Sorry 72 | enum TimerType { 73 | PreciseTimer, 74 | CoarseTimer, 75 | VeryCoarseTimer 76 | }; 77 | } 78 | 79 | typedef qptrdiff qintptr; 80 | #endif 81 | 82 | #endif // QT4COMPAT_H 83 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBEVENT_H 2 | #define EVENTDISPATCHER_LIBEVENT_H 3 | 4 | #include 5 | 6 | class EventDispatcherLibEventPrivate; 7 | class EventDispatcherLibEventConfig; 8 | 9 | class EventDispatcherLibEvent : public QAbstractEventDispatcher { 10 | Q_OBJECT 11 | public: 12 | explicit EventDispatcherLibEvent(QObject* parent = 0); 13 | EventDispatcherLibEvent(const EventDispatcherLibEventConfig& config, QObject* parent = 0); 14 | virtual ~EventDispatcherLibEvent(void); 15 | 16 | struct event_base* eventBase(void) const; 17 | 18 | virtual bool processEvents(QEventLoop::ProcessEventsFlags flags); 19 | virtual bool hasPendingEvents(void); 20 | 21 | virtual void registerSocketNotifier(QSocketNotifier* notifier); 22 | virtual void unregisterSocketNotifier(QSocketNotifier* notifier); 23 | 24 | virtual void registerTimer( 25 | int timerId, 26 | int interval, 27 | #if QT_VERSION >= 0x050000 28 | Qt::TimerType timerType, 29 | #endif 30 | QObject* object 31 | ); 32 | 33 | virtual bool unregisterTimer(int timerId); 34 | virtual bool unregisterTimers(QObject* object); 35 | virtual QList registeredTimers(QObject* object) const; 36 | #if QT_VERSION >= 0x050000 37 | virtual int remainingTime(int timerId); 38 | #endif 39 | 40 | #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 41 | virtual bool registerEventNotifier(QWinEventNotifier* notifier); 42 | virtual void unregisterEventNotifier(QWinEventNotifier* notifier); 43 | #endif 44 | 45 | virtual void wakeUp(void); 46 | virtual void interrupt(void); 47 | virtual void flush(void); 48 | 49 | protected: 50 | EventDispatcherLibEvent(EventDispatcherLibEventPrivate& dd, QObject* parent = 0); 51 | 52 | public: 53 | void reinitialize(void); 54 | 55 | private: 56 | Q_DISABLE_COPY(EventDispatcherLibEvent) 57 | Q_DECLARE_PRIVATE(EventDispatcherLibEvent) 58 | #if QT_VERSION >= 0x040600 59 | QScopedPointer d_ptr; 60 | #else 61 | EventDispatcherLibEventPrivate* d_ptr; 62 | #endif 63 | }; 64 | 65 | #endif // EVENTDISPATCHER_LIBEVENT_H 66 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent.pro: -------------------------------------------------------------------------------- 1 | QT -= gui 2 | TARGET = eventdispatcher_libevent 3 | TEMPLATE = lib 4 | DESTDIR = ../lib 5 | CONFIG += staticlib create_prl precompile_header 6 | 7 | HEADERS += \ 8 | eventdispatcher_libevent.h \ 9 | eventdispatcher_libevent_p.h \ 10 | eventdispatcher_libevent_config.h \ 11 | eventdispatcher_libevent_config_p.h \ 12 | libevent2-emul.h \ 13 | qt4compat.h \ 14 | tco.h \ 15 | tco_impl.h \ 16 | common.h 17 | 18 | SOURCES += \ 19 | eventdispatcher_libevent.cpp \ 20 | eventdispatcher_libevent_p.cpp \ 21 | timers_p.cpp \ 22 | socknot_p.cpp \ 23 | eventdispatcher_libevent_config.cpp 24 | 25 | PRECOMPILED_HEADER = common.h 26 | 27 | headers.files = eventdispatcher_libevent.h eventdispatcher_libevent_config.h 28 | 29 | unix { 30 | CONFIG += create_pc 31 | 32 | system('cc -E $$PWD/conftests/eventfd.h -o /dev/null 2> /dev/null') { 33 | SOURCES += tco_eventfd.cpp 34 | } 35 | else { 36 | SOURCES += tco_pipe.cpp 37 | } 38 | 39 | system('pkg-config --exists libevent') { 40 | CONFIG += link_pkgconfig 41 | PKGCONFIG += libevent 42 | } 43 | else { 44 | system('cc -E $$PWD/conftests/libevent2.h -o /dev/null 2> /dev/null') { 45 | DEFINES += SJ_LIBEVENT_MAJOR=2 46 | } 47 | else:system('cc -E $$PWD/conftests/libevent1.h -o /dev/null 2> /dev/null') { 48 | DEFINES += SJ_LIBEVENT_MAJOR=1 49 | } 50 | else { 51 | warning("Assuming libevent 1.x") 52 | DEFINES += SJ_LIBEVENT_MAJOR=1 53 | } 54 | 55 | LIBS += -levent_core 56 | } 57 | 58 | target.path = /usr/lib 59 | headers.path = /usr/include 60 | 61 | QMAKE_PKGCONFIG_NAME = eventdispatcher_libevent 62 | QMAKE_PKGCONFIG_DESCRIPTION = "Libevent-based event dispatcher for Qt" 63 | QMAKE_PKGCONFIG_LIBDIR = $$target.path 64 | QMAKE_PKGCONFIG_INCDIR = $$headers.path 65 | QMAKE_PKGCONFIG_DESTDIR = pkgconfig 66 | } 67 | else { 68 | LIBS += -levent 69 | headers.path = $$DESTDIR 70 | target.path = $$DESTDIR 71 | } 72 | 73 | win32 { 74 | SOURCES += tco_win32_libevent.cpp 75 | HEADERS += wsainit.h 76 | LIBS += $$QMAKE_LIBS_NETWORK 77 | CONFIG -= staticlib 78 | CONFIG += dll 79 | } 80 | 81 | INSTALLS += target headers 82 | -------------------------------------------------------------------------------- /src/tco_win32_libevent.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #if QT_VERSION >= 0x040400 4 | # include 5 | #endif 6 | 7 | class Q_DECL_HIDDEN ThreadCommunicationObjectPrivate { 8 | public: 9 | ThreadCommunicationObjectPrivate(void) 10 | : fd(), isvalid(false) 11 | #if QT_VERSION >= 0x040400 12 | , wakeups() 13 | #endif 14 | { 15 | this->fd[0] = INVALID_SOCKET; 16 | this->fd[1] = INVALID_SOCKET; 17 | 18 | if (0 != evutil_socketpair(AF_INET, SOCK_STREAM, 0, this->fd)) { 19 | qFatal("%s: evutil_socketpair() failed: %d", Q_FUNC_INFO, WSAGetLastError()); 20 | } 21 | 22 | evutil_make_socket_nonblocking(this->fd[0]); 23 | evutil_make_socket_nonblocking(this->fd[1]); 24 | 25 | this->isvalid = true; 26 | } 27 | 28 | ~ThreadCommunicationObjectPrivate(void) 29 | { 30 | if (SOCKET(this->fd[0]) != INVALID_SOCKET) { 31 | ::closesocket(this->fd[0]); 32 | ::closesocket(this->fd[1]); 33 | } 34 | } 35 | 36 | bool wakeUp(void) 37 | { 38 | Q_ASSERT(this->isvalid); 39 | 40 | #if QT_VERSION >= 0x040400 41 | if (this->wakeups.testAndSetAcquire(0, 1)) 42 | #endif 43 | { 44 | const char c = '.'; 45 | int res = ::send(this->fd[1], &c, 1, 0); 46 | 47 | if (Q_UNLIKELY(1 != res)) { 48 | qWarning("%s: send() failed: %d", Q_FUNC_INFO, WSAGetLastError()); 49 | return false; 50 | } 51 | } 52 | 53 | return true; 54 | } 55 | 56 | bool awaken(void) 57 | { 58 | Q_ASSERT(this->isvalid); 59 | 60 | char buf[16]; 61 | int res; 62 | do { 63 | res = ::recv(this->fd[0], buf, sizeof(buf), 0); 64 | } while (res == sizeof(buf)); 65 | 66 | if (Q_UNLIKELY(SOCKET_ERROR == res)) { 67 | qErrnoWarning("%s: recv() failed: %d", Q_FUNC_INFO, WSAGetLastError()); 68 | } 69 | 70 | #if QT_VERSION >= 0x040400 71 | if (Q_UNLIKELY(!this->wakeups.testAndSetRelease(1, 0))) { 72 | qCritical("%s: internal error, testAndSetRelease(1, 0) failed!", Q_FUNC_INFO); 73 | res = -1; 74 | } 75 | #endif 76 | 77 | return res != SOCKET_ERROR; 78 | } 79 | 80 | bool valid(void) const { return this->isvalid; } 81 | qintptr readfd(void) const { return this->fd[0]; } 82 | 83 | private: 84 | qintptr fd[2]; 85 | bool isvalid; 86 | #if QT_VERSION >= 0x040400 87 | QAtomicInt wakeups; 88 | #endif 89 | }; 90 | 91 | #include "tco_impl.h" 92 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent_p.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBEVENT_P_H 2 | #define EVENTDISPATCHER_LIBEVENT_P_H 3 | 4 | #include "common.h" 5 | #include "tco.h" 6 | 7 | class EventDispatcherLibEvent; 8 | class EventDispatcherLibEventConfig; 9 | class EventDispatcherLibEventPrivate; 10 | 11 | struct SocketNotifierInfo { 12 | QSocketNotifier* sn; 13 | struct event* ev; 14 | }; 15 | 16 | struct TimerInfo { 17 | EventDispatcherLibEventPrivate* self; 18 | QObject* object; 19 | struct event* ev; 20 | struct timeval when; 21 | int timerId; 22 | int interval; 23 | Qt::TimerType type; 24 | }; 25 | 26 | Q_DECLARE_TYPEINFO(SocketNotifierInfo, Q_PRIMITIVE_TYPE); 27 | Q_DECLARE_TYPEINFO(TimerInfo, Q_PRIMITIVE_TYPE); 28 | 29 | class Q_DECL_HIDDEN EventDispatcherLibEventPrivate { 30 | public: 31 | EventDispatcherLibEventPrivate(EventDispatcherLibEvent* const q); 32 | EventDispatcherLibEventPrivate(EventDispatcherLibEvent* const q, const EventDispatcherLibEventConfig& cfg); 33 | ~EventDispatcherLibEventPrivate(void); 34 | bool processEvents(QEventLoop::ProcessEventsFlags flags); 35 | void registerSocketNotifier(QSocketNotifier* notifier); 36 | void unregisterSocketNotifier(QSocketNotifier* notifier); 37 | void registerTimer(int timerId, int interval, Qt::TimerType type, QObject* object); 38 | bool unregisterTimer(int timerId); 39 | bool unregisterTimers(QObject* object); 40 | QList registeredTimers(QObject* object) const; 41 | int remainingTime(int timerId) const; 42 | 43 | struct event_base* eventBase(void) const; 44 | 45 | typedef QMultiHash SocketNotifierHash; 46 | typedef QHash TimerHash; 47 | typedef QPair, QEvent*> PendingEvent; 48 | typedef QList EventList; 49 | 50 | private: 51 | Q_DISABLE_COPY(EventDispatcherLibEventPrivate) 52 | Q_DECLARE_PUBLIC(EventDispatcherLibEvent) 53 | EventDispatcherLibEvent* const q_ptr; 54 | 55 | bool m_interrupt; 56 | struct event_base* m_base; 57 | struct event* m_wakeup; 58 | ThreadCommunicationObject* m_tco; 59 | SocketNotifierHash m_notifiers; 60 | TimerHash m_timers; 61 | EventList m_event_list; 62 | bool m_awaken; 63 | 64 | void initialize(const EventDispatcherLibEventConfig* cfg); 65 | 66 | static void calculateCoarseTimerTimeout(TimerInfo* info, const struct timeval& now, struct timeval& when); 67 | static void calculateNextTimeout(TimerInfo* info, const struct timeval& now, struct timeval& delta); 68 | 69 | static void socket_notifier_callback(evutil_socket_t fd, short int events, void* arg); 70 | static void timer_callback(evutil_socket_t fd, short int events, void* arg); 71 | static void wake_up_handler(evutil_socket_t fd, short int events, void* arg); 72 | 73 | bool disableSocketNotifiers(bool disable); 74 | void killSocketNotifiers(void); 75 | bool disableTimers(bool disable); 76 | void killTimers(void); 77 | }; 78 | 79 | #endif // EVENTDISPATCHER_LIBEVENT_P_H 80 | -------------------------------------------------------------------------------- /src/socknot_p.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "eventdispatcher_libevent_p.h" 3 | 4 | void EventDispatcherLibEventPrivate::registerSocketNotifier(QSocketNotifier* notifier) 5 | { 6 | evutil_socket_t sockfd = notifier->socket(); 7 | short int what; 8 | switch (notifier->type()) { 9 | case QSocketNotifier::Read: what = EV_READ; break; 10 | case QSocketNotifier::Write: what = EV_WRITE; break; 11 | case QSocketNotifier::Exception: /// FIXME 12 | return; 13 | 14 | default: 15 | Q_ASSERT(false); 16 | return; 17 | } 18 | 19 | what |= EV_PERSIST; 20 | struct event* ev = event_new(this->m_base, sockfd, what, EventDispatcherLibEventPrivate::socket_notifier_callback, this); 21 | Q_CHECK_PTR(ev); 22 | event_add(ev, 0); 23 | 24 | SocketNotifierInfo data; 25 | data.sn = notifier; 26 | data.ev = ev; 27 | this->m_notifiers.insertMulti(sockfd, data); 28 | } 29 | 30 | void EventDispatcherLibEventPrivate::unregisterSocketNotifier(QSocketNotifier* notifier) 31 | { 32 | evutil_socket_t sockfd = notifier->socket(); 33 | SocketNotifierHash::Iterator it = this->m_notifiers.find(sockfd); 34 | while (it != this->m_notifiers.end() && it.key() == sockfd) { 35 | SocketNotifierInfo& data = it.value(); 36 | if (data.sn == notifier) { 37 | event_del(data.ev); 38 | event_free(data.ev); 39 | it = this->m_notifiers.erase(it); 40 | } 41 | else { 42 | ++it; 43 | } 44 | } 45 | } 46 | 47 | void EventDispatcherLibEventPrivate::socket_notifier_callback(int fd, short int events, void* arg) 48 | { 49 | EventDispatcherLibEventPrivate* disp = static_cast(arg); 50 | SocketNotifierHash::Iterator it = disp->m_notifiers.find(fd); 51 | while (it != disp->m_notifiers.end() && it.key() == fd) { 52 | SocketNotifierInfo& data = it.value(); 53 | QSocketNotifier::Type type = data.sn->type(); 54 | 55 | if ((QSocketNotifier::Read == type && (events & EV_READ)) || (QSocketNotifier::Write == type && (events & EV_WRITE))) { 56 | PendingEvent event(data.sn, new QEvent(QEvent::SockAct)); 57 | disp->m_event_list.append(event); 58 | } 59 | 60 | ++it; 61 | } 62 | } 63 | 64 | bool EventDispatcherLibEventPrivate::disableSocketNotifiers(bool disable) 65 | { 66 | SocketNotifierHash::Iterator it = this->m_notifiers.begin(); 67 | while (it != this->m_notifiers.end()) { 68 | SocketNotifierInfo& data = it.value(); 69 | if (disable) { 70 | event_del(data.ev); 71 | } 72 | else { 73 | event_add(data.ev, 0); 74 | } 75 | 76 | ++it; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | void EventDispatcherLibEventPrivate::killSocketNotifiers(void) 83 | { 84 | if (!this->m_notifiers.isEmpty()) { 85 | EventDispatcherLibEventPrivate::SocketNotifierHash::Iterator it = this->m_notifiers.begin(); 86 | while (it != this->m_notifiers.end()) { 87 | SocketNotifierInfo& data = it.value(); 88 | event_del(data.ev); 89 | event_free(data.ev); 90 | ++it; 91 | } 92 | 93 | this->m_notifiers.clear(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/tco_eventfd.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | #include 4 | #include "tco.h" 5 | 6 | #if QT_VERSION >= 0x040400 7 | # include 8 | #endif 9 | 10 | #if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) 11 | # define MY_EFD_CLOEXEC EFD_CLOEXEC 12 | # define MY_EFD_NONBLOCK EFD_NONBLOCK 13 | #else 14 | # define MY_EFD_CLOEXEC 0 15 | # define MY_EFD_NONBLOCK 0 16 | #endif 17 | 18 | class Q_DECL_HIDDEN ThreadCommunicationObjectPrivate { 19 | public: 20 | ThreadCommunicationObjectPrivate(void) 21 | : fd(-1), isvalid(false) 22 | #if QT_VERSION >= 0x040400 23 | , wakeups() 24 | #endif 25 | { 26 | int flags = MY_EFD_CLOEXEC | MY_EFD_NONBLOCK; 27 | int res = ::eventfd(0, flags); 28 | this->fd = -1; 29 | 30 | if (-1 == res) { 31 | if (EINVAL == errno && flags) { 32 | res = ::eventfd(0, 0); 33 | } 34 | 35 | if (res == -1) { 36 | qErrnoWarning("%s: eventfd() failed", Q_FUNC_INFO); 37 | return; 38 | } 39 | 40 | if (-1 == this->set_cloexec()) { 41 | qErrnoWarning("%s: Unable to set close on exec flag", Q_FUNC_INFO); 42 | } 43 | 44 | if (-1 == this->make_nonblocking()) { 45 | qErrnoWarning("%s: Unable to make the descriptor non-blocking", Q_FUNC_INFO); 46 | } 47 | } 48 | 49 | this->fd = res; 50 | this->isvalid = true; 51 | } 52 | 53 | ~ThreadCommunicationObjectPrivate(void) 54 | { 55 | if (this->fd != -1) { 56 | ::close(this->fd); 57 | } 58 | } 59 | 60 | bool wakeUp(void) 61 | { 62 | Q_ASSERT(this->isvalid); 63 | 64 | #if QT_VERSION >= 0x040400 65 | if (this->wakeups.testAndSetAcquire(0, 1)) 66 | #endif 67 | { 68 | int res; 69 | eventfd_t val = 1; 70 | do { 71 | res = eventfd_write(this->fd, val); 72 | } while (Q_UNLIKELY(res == -1 && errno == EINTR)); 73 | 74 | if (Q_UNLIKELY(-1 == res)) { 75 | qErrnoWarning("%s: eventfd_write() failed", Q_FUNC_INFO); 76 | return false; 77 | } 78 | } 79 | 80 | return true; 81 | } 82 | 83 | bool awaken(void) 84 | { 85 | Q_ASSERT(this->isvalid); 86 | 87 | eventfd_t value; 88 | int res; 89 | do { 90 | res = eventfd_read(this->fd, &value); 91 | } while (Q_UNLIKELY(-1 == res && EINTR == errno)); 92 | 93 | if (Q_UNLIKELY(-1 == res)) { 94 | qErrnoWarning("%s: eventfd_read() failed", Q_FUNC_INFO); 95 | } 96 | 97 | #if QT_VERSION >= 0x040400 98 | if (Q_UNLIKELY(!this->wakeups.testAndSetRelease(1, 0))) { 99 | qCritical("%s: internal error, testAndSetRelease(1, 0) failed!", Q_FUNC_INFO); 100 | res = -1; 101 | } 102 | #endif 103 | 104 | return res != -1; 105 | } 106 | 107 | bool valid(void) const { return this->isvalid; } 108 | qintptr readfd(void) const { return this->fd; } 109 | 110 | private: 111 | int fd; 112 | bool isvalid; 113 | #if QT_VERSION >= 0x040400 114 | QAtomicInt wakeups; 115 | #endif 116 | 117 | int set_cloexec(void) 118 | { 119 | return ::fcntl(this->fd, F_SETFD, FD_CLOEXEC); 120 | } 121 | 122 | int make_nonblocking(void) 123 | { 124 | return ::fcntl(this->fd, F_SETFL, O_NONBLOCK | ::fcntl(this->fd, F_GETFL)); 125 | } 126 | }; 127 | 128 | #undef MY_EFD_CLOEXEC 129 | #undef MY_EFD_NONBLOCK 130 | 131 | #include "tco_impl.h" 132 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent_config.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "eventdispatcher_libevent_config.h" 3 | 4 | #ifndef SJ_LIBEVENT_EMULATION 5 | # include "eventdispatcher_libevent_config_p.h" 6 | #else 7 | class EventDispatcherLibEventConfigPrivate {}; 8 | #endif 9 | 10 | EventDispatcherLibEventConfig::EventDispatcherLibEventConfig(void) 11 | : d_ptr(new EventDispatcherLibEventConfigPrivate()) 12 | { 13 | } 14 | 15 | EventDispatcherLibEventConfig::~EventDispatcherLibEventConfig(void) 16 | { 17 | #if QT_VERSION < 0x040600 18 | delete this->d_ptr; 19 | this->d_ptr = 0; 20 | #endif 21 | } 22 | 23 | #ifdef SJ_LIBEVENT_EMULATION 24 | bool EventDispatcherLibEventConfig::avoidMethod(const QLatin1String&) { return false; } 25 | bool EventDispatcherLibEventConfig::requireFeatures(Features) { return false; } 26 | bool EventDispatcherLibEventConfig::setConfiguration(Configuration) { return false; } 27 | #else 28 | bool EventDispatcherLibEventConfig::avoidMethod(const QLatin1String& method) 29 | { 30 | Q_D(EventDispatcherLibEventConfig); 31 | return d->avoidMethod(method.latin1()); 32 | } 33 | 34 | bool EventDispatcherLibEventConfig::requireFeatures(Features f) 35 | { 36 | int features = 0; 37 | 38 | if (f & EventDispatcherLibEventConfig::ev_ET) { 39 | features |= EV_FEATURE_ET; 40 | } 41 | 42 | if (f & EventDispatcherLibEventConfig::ev_O1) { 43 | features |= EV_FEATURE_O1; 44 | } 45 | 46 | if (f & EventDispatcherLibEventConfig::ev_FDs) { 47 | features |= EV_FEATURE_FDS; 48 | } 49 | 50 | Q_D(EventDispatcherLibEventConfig); 51 | return d->requireFeatures(features); 52 | } 53 | 54 | bool EventDispatcherLibEventConfig::setConfiguration(Configuration cfg) 55 | { 56 | int config = 0; 57 | 58 | if (cfg & EventDispatcherLibEventConfig::cfg_NoLock) { 59 | config |= EVENT_BASE_FLAG_NOLOCK; 60 | } 61 | 62 | if (cfg & EventDispatcherLibEventConfig::cfg_IgnoreEnvironment) { 63 | config |= EVENT_BASE_FLAG_IGNORE_ENV; 64 | } 65 | 66 | if (cfg & EventDispatcherLibEventConfig::cfg_StartupIOCP) { 67 | config |= EVENT_BASE_FLAG_STARTUP_IOCP; 68 | } 69 | 70 | if (cfg & EventDispatcherLibEventConfig::cfg_NoCacheTime) { 71 | config |= EVENT_BASE_FLAG_NO_CACHE_TIME; 72 | } 73 | 74 | if (cfg & EventDispatcherLibEventConfig::cfg_EPollChangelist) { 75 | config |= EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST; 76 | } 77 | 78 | Q_D(EventDispatcherLibEventConfig); 79 | return d->setConfiguration(config); 80 | } 81 | 82 | 83 | EventDispatcherLibEventConfigPrivate::EventDispatcherLibEventConfigPrivate(void) 84 | : m_cfg(0) 85 | { 86 | this->m_cfg = event_config_new(); 87 | Q_CHECK_PTR(this->m_cfg); 88 | } 89 | 90 | EventDispatcherLibEventConfigPrivate::~EventDispatcherLibEventConfigPrivate(void) 91 | { 92 | if (this->m_cfg) { 93 | event_config_free(this->m_cfg); 94 | this->m_cfg = 0; 95 | } 96 | } 97 | 98 | bool EventDispatcherLibEventConfigPrivate::avoidMethod(const char* method) 99 | { 100 | return 0 == event_config_avoid_method(this->m_cfg, method); 101 | } 102 | 103 | bool EventDispatcherLibEventConfigPrivate::requireFeatures(int features) 104 | { 105 | return 0 == event_config_require_features(this->m_cfg, features); 106 | } 107 | 108 | bool EventDispatcherLibEventConfigPrivate::setConfiguration(int config) 109 | { 110 | return 0 == event_config_set_flag(this->m_cfg, config); 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/tco_pipe.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | #include "tco.h" 4 | 5 | #if QT_VERSION >= 0x040400 6 | # include 7 | #endif 8 | 9 | #if defined(Q_OS_LINUX) && defined(O_CLOEXEC) 10 | # define THREADSAFE_CLOEXEC_SUPPORTED 1 11 | namespace libcsupplement { 12 | inline int pipe2(int[], int) { errno = ENOSYS; return -1; } 13 | } 14 | 15 | using namespace libcsupplement; 16 | #else 17 | # define THREADSAFE_CLOEXEC_SUPPORTED 0 18 | #endif 19 | 20 | class Q_DECL_HIDDEN ThreadCommunicationObjectPrivate { 21 | public: 22 | ThreadCommunicationObjectPrivate(void) 23 | : fd(), isvalid(false) 24 | #if QT_VERSION >= 0x040400 25 | , wakeups() 26 | #endif 27 | { 28 | this->fd[0] = -1; 29 | this->fd[1] = -1; 30 | 31 | int res; 32 | 33 | #if defined(Q_OS_INTEGRITY) || defined(Q_OS_VXWORKS) 34 | if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, this->fd)) { 35 | qErrnoWarning("%s: Failed to create a socket pair", Q_FUNC_INFO); 36 | return -1; 37 | } 38 | #else 39 | # if THREADSAFE_CLOEXEC_SUPPORTED 40 | res = ::pipe2(this->fd, O_CLOEXEC | O_NONBLOCK); 41 | if (res != -1 && errno != ENOSYS) { 42 | if (-1 == res) { 43 | qErrnoWarning("%s: Failed to create a pipe", Q_FUNC_INFO); 44 | } 45 | 46 | this->isvalid = (res != -1); 47 | return; 48 | } 49 | # endif // THREADSAFE_CLOEXEC_SUPPORTED 50 | 51 | res = ::pipe(this->fd); 52 | if (-1 == res) { 53 | qErrnoWarning("%s: Failed to create a pipe", Q_FUNC_INFO); 54 | return; 55 | } 56 | #endif // defined(Q_OS_INTEGRITY) || defined(Q_OS_VXWORKS) 57 | 58 | if (-1 == this->set_cloexec()) { 59 | qErrnoWarning("%s: Unable to set close on exec flag", Q_FUNC_INFO); 60 | } 61 | 62 | if (-1 == this->make_nonblocking()) { 63 | qErrnoWarning("%s: Unable to make the descriptor non-blocking", Q_FUNC_INFO); 64 | } 65 | 66 | this->isvalid = true; 67 | } 68 | 69 | ~ThreadCommunicationObjectPrivate(void) 70 | { 71 | if (this->isvalid) { 72 | ::close(this->fd[0]); 73 | ::close(this->fd[1]); 74 | } 75 | } 76 | 77 | bool wakeUp(void) 78 | { 79 | Q_ASSERT(this->isvalid); 80 | 81 | #if QT_VERSION >= 0x040400 82 | if (this->wakeups.testAndSetAcquire(0, 1)) 83 | #endif 84 | { 85 | int res; 86 | char val = 1; 87 | do { 88 | res = ::write(this->fd[1], &val, sizeof(val)); 89 | } while (Q_UNLIKELY(res == -1 && errno == EINTR)); 90 | 91 | if (Q_UNLIKELY(-1 == res)) { 92 | qErrnoWarning("%s: write() failed", Q_FUNC_INFO); 93 | return false; 94 | } 95 | } 96 | 97 | return true; 98 | } 99 | 100 | bool awaken(void) 101 | { 102 | Q_ASSERT(this->isvalid); 103 | 104 | char buf[16]; 105 | int res; 106 | do { 107 | do { 108 | res = ::read(this->fd[0], buf, sizeof(buf)); 109 | } while (Q_UNLIKELY(-1 == res && EINTR == errno)); 110 | } while (res == sizeof(buf)); 111 | 112 | if (Q_UNLIKELY(-1 == res)) { 113 | qErrnoWarning("%s: read() failed", Q_FUNC_INFO); 114 | } 115 | 116 | #if QT_VERSION >= 0x040400 117 | if (Q_UNLIKELY(!this->wakeups.testAndSetRelease(1, 0))) { 118 | qCritical("%s: internal error, testAndSetRelease(1, 0) failed!", Q_FUNC_INFO); 119 | res = -1; 120 | } 121 | #endif 122 | 123 | return res != -1; 124 | } 125 | 126 | bool valid(void) const { return this->isvalid; } 127 | qintptr readfd(void) const { return this->fd[0]; } 128 | 129 | private: 130 | int fd[2]; 131 | bool isvalid; 132 | #if QT_VERSION >= 0x040400 133 | QAtomicInt wakeups; 134 | #endif 135 | 136 | int set_cloexec(void) 137 | { 138 | int res = ::fcntl(this->fd[0], F_SETFD, FD_CLOEXEC); 139 | if (res != -1) { 140 | res = ::fcntl(this->fd[1], F_SETFD, FD_CLOEXEC); 141 | } 142 | 143 | return res; 144 | } 145 | 146 | int make_nonblocking(void) 147 | { 148 | int res = ::fcntl(this->fd[0], F_SETFL, O_NONBLOCK | ::fcntl(this->fd[0], F_GETFL)); 149 | if (res != -1) { 150 | res = ::fcntl(this->fd[1], F_SETFL, O_NONBLOCK | ::fcntl(this->fd[1], F_GETFL)); 151 | } 152 | 153 | return res; 154 | } 155 | }; 156 | 157 | #undef THREADSAFE_CLOEXEC_SUPPORTED 158 | 159 | #include "tco_impl.h" 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qt_eventdispatcher_libevent [![Build Status](https://secure.travis-ci.org/sjinks/qt_eventdispatcher_libevent.png)](http://travis-ci.org/sjinks/qt_eventdispatcher_libevent) 2 | 3 | libevent-based event dispatcher for Qt 4 | 5 | ## Features 6 | * very fast :-) 7 | * compatibile with Qt 4 (Qt 4.2+) and Qt 5 8 | * does not use any private Qt headers 9 | * passes Qt 4 and Qt 5 event dispatcher, event loop, timer and socket notifier tests 10 | 11 | 12 | ## Unsupported Features 13 | * `QSocketNotifier::Exception` (libevent offers no support for this) 14 | * Qt 5/Windows only: `QWinEventNotifier` is not supported (`registerEventNotifier()` and `unregisterEventNotifier()` functions 15 | are currently implemented as stubs; libevent does not natively support Windows events and addition of the support 16 | to the event dispatcher will mean a completely different event loop code for Windows). 17 | 18 | 19 | ## Requirements 20 | * libevent >= 2.0.0 (the code seems to work with libevent 1.4.x but this has not been tested much — but the Qt tests are successfully passed though) 21 | * Qt >= 4.2 (tests from tests-qt4 were run only on Qt 4.8.x, 4.5.4, 4.3.0, 4.2.1) 22 | 23 | 24 | ## Build 25 | 26 | ``` 27 | cd src 28 | qmake 29 | make 30 | ``` 31 | 32 | Replace `make` with `nmake` if your are using Microsoft Visual C++. 33 | 34 | The above commands will generate the static library and `.prl` file in `../lib` directory. 35 | 36 | 37 | ## Install 38 | 39 | After completing Build step run 40 | 41 | *NIX: 42 | ``` 43 | sudo make install 44 | ``` 45 | 46 | Windows: 47 | ``` 48 | nmake install 49 | ``` 50 | 51 | For Windows this will copy `eventdispatcher_libevent.h` and `eventdispatcher_libevent_config.h` to `../lib` directory. 52 | For *NIX this will install eventdispatcher_libevent.h to `/usr/include`, `libeventdispatcher_libevent.a` and `libeventdispatcher_libevent.prl` to `/usr/lib`, `eventdispatcher_libevent.pc` to `/usr/lib/pkgconfig`. 53 | 54 | 55 | ## Usage (Qt 4) 56 | 57 | Simply include the header file and instantiate the dispatcher in `main()` 58 | before creating the Qt application object. 59 | 60 | ```c++ 61 | #include "eventdispatcher_libevent.h" 62 | 63 | int main(int argc, char** argv) 64 | { 65 | EventDispatcherLibEvent dispatcher; 66 | QCoreApplication app(argc, argv); 67 | 68 | // ... 69 | 70 | return app.exec(); 71 | } 72 | ``` 73 | 74 | And add these lines to the .pro file: 75 | 76 | ``` 77 | unix { 78 | CONFIG += link_pkgconfig 79 | PKGCONFIG += eventdispatcher_libevent 80 | } 81 | else:win32 { 82 | include(/path/to/qt_eventdispatcher_libevent/lib/eventdispatcher_libevent.pri) 83 | } 84 | ``` 85 | 86 | or 87 | 88 | ``` 89 | HEADERS += /path/to/eventdispatcher_libevent.h 90 | LIBS += -L/path/to/library -leventdispatcher_libevent 91 | ``` 92 | 93 | 94 | ## Usage (Qt 5) 95 | 96 | Simply include the header file and instantiate the dispatcher in `main()` 97 | before creating the Qt application object. 98 | 99 | ```c++ 100 | #include "eventdispatcher_libevent.h" 101 | 102 | int main(int argc, char** argv) 103 | { 104 | QCoreApplication::setEventDispatcher(new EventDispatcherLibEvent); 105 | QCoreApplication app(argc, argv); 106 | 107 | // ... 108 | 109 | return app.exec(); 110 | } 111 | ``` 112 | 113 | And add these lines to the .pro file: 114 | 115 | ``` 116 | unix { 117 | CONFIG += link_pkgconfig 118 | PKGCONFIG += eventdispatcher_libevent 119 | } 120 | else:win32 { 121 | include(/path/to/qt_eventdispatcher_libevent/lib/eventdispatcher_libevent.pri) 122 | } 123 | ``` 124 | 125 | or 126 | 127 | ``` 128 | HEADERS += /path/to/eventdispatcher_libevent.h 129 | LIBS += -L/path/to/library -leventdispatcher_libevent 130 | ``` 131 | 132 | Qt 5 allows to specify a custom event dispatcher for the thread: 133 | 134 | ```c++ 135 | QThread* thr = new QThread; 136 | thr->setEventDispatcher(new EventDispatcherLibEvent); 137 | ``` 138 | 139 | 140 | ## Interesting Facts 141 | 142 | `EventDispatcherLibEvent` is more compatible with Qt 4.2.x and 4.3.x than the native UNIX event dispatcher from those Qt's. 143 | 144 | For example, Qt 4.2.1 fails `tst_QTimer::livelock(zero timer)` and `tst_QTimer::livelock(non-zero timer)` tests 145 | (`'tester.postEventAtRightTime' returned FALSE`) and hangs in `tst_QEventLoop::processEventsExcludeTimers` test 146 | but `EventDispatcherLibEvent` passes them all! 147 | 148 | It should, however, be noted that these tests were taken from Qt 4.8 :-) 149 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent_p.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "eventdispatcher_libevent.h" 3 | #include "eventdispatcher_libevent_p.h" 4 | #include "eventdispatcher_libevent_config.h" 5 | #include "eventdispatcher_libevent_config_p.h" 6 | 7 | #ifdef Q_OS_WIN 8 | Q_GLOBAL_STATIC(WSAInitializer, wsa_initializer) 9 | #endif 10 | 11 | /** 12 | * Callback to report warnings and errors from libevent 13 | * 14 | * @param severity Message severity 15 | * @param msg Message 16 | * @internal 17 | */ 18 | static void event_log_callback(int severity, const char* msg) 19 | { 20 | switch (severity) { 21 | case _EVENT_LOG_WARN: qWarning("%s", msg); break; 22 | case _EVENT_LOG_ERR: qCritical("%s", msg); break; 23 | default: qDebug("%s", msg); break; 24 | } 25 | } 26 | 27 | /** 28 | * @brief Constructs the event dispatcher 29 | * @param q Pointer to event dispatcher's public interface 30 | */ 31 | EventDispatcherLibEventPrivate::EventDispatcherLibEventPrivate(EventDispatcherLibEvent* const q) 32 | : q_ptr(q), m_interrupt(false), m_base(0), m_wakeup(0), m_tco(0), 33 | m_notifiers(), m_timers(), m_event_list(), m_awaken(false) 34 | { 35 | this->initialize(0); 36 | } 37 | 38 | /** 39 | * @brief Constructs the event dispatcher 40 | * @param q Pointer to event dispatcher's public interface 41 | * @param cfg Configuration 42 | * @warning @a cfg parameter is ignored if the dispatcher is linked with libevent 1.x. 43 | * Configurations are supported since libevent 2.0 44 | */ 45 | EventDispatcherLibEventPrivate::EventDispatcherLibEventPrivate(EventDispatcherLibEvent* const q, const EventDispatcherLibEventConfig& cfg) 46 | : q_ptr(q), m_interrupt(false), m_base(0), m_wakeup(0), m_tco(0), 47 | m_notifiers(), m_timers(), m_event_list(), m_awaken(false) 48 | { 49 | #ifdef SJ_LIBEVENT_EMULATION 50 | Q_UNUSED(cfg) 51 | qWarning("LibEvent 1.x does not support custom configurations"); 52 | this->initialize(0); 53 | #else 54 | this->initialize(&cfg); 55 | #endif 56 | } 57 | 58 | /** 59 | * @brief Initializes the event dispatcher 60 | * @param cfg Event dispatcher configuration 61 | */ 62 | void EventDispatcherLibEventPrivate::initialize(const EventDispatcherLibEventConfig* cfg) 63 | { 64 | static bool init = false; 65 | if (!init) { 66 | init = true; 67 | 68 | #ifdef Q_OS_WIN 69 | if (!WSAInitialized()) { 70 | wsa_initializer(); 71 | } 72 | #endif 73 | 74 | event_set_log_callback(event_log_callback); 75 | 76 | #if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER > 0x02010100 77 | qAddPostRoutine(libevent_global_shutdown); 78 | #endif 79 | } 80 | 81 | #ifndef SJ_LIBEVENT_EMULATION 82 | if (cfg) { 83 | this->m_base = event_base_new_with_config(cfg->d_func()->m_cfg); 84 | if (!this->m_base) { 85 | qWarning("%s: Cannot create the event base with the specified configuration", Q_FUNC_INFO); 86 | } 87 | } 88 | #else 89 | Q_UNUSED(cfg) 90 | #endif 91 | 92 | if (!this->m_base) { 93 | this->m_base = event_base_new(); 94 | Q_CHECK_PTR(this->m_base); 95 | } 96 | 97 | this->m_tco = new ThreadCommunicationObject(); 98 | if (!this->m_tco->valid()) { 99 | qFatal("%s: failed to create a thread communication object", Q_FUNC_INFO); 100 | } 101 | 102 | this->m_wakeup = event_new(this->m_base, this->m_tco->fd(), EV_READ | EV_PERSIST, EventDispatcherLibEventPrivate::wake_up_handler, this); 103 | Q_CHECK_PTR(this->m_wakeup); 104 | event_add(this->m_wakeup, 0); 105 | } 106 | 107 | /** 108 | * @brief Destroys the event dispatcher 109 | */ 110 | EventDispatcherLibEventPrivate::~EventDispatcherLibEventPrivate(void) 111 | { 112 | if (this->m_wakeup) { 113 | event_del(this->m_wakeup); 114 | event_free(this->m_wakeup); 115 | this->m_wakeup = 0; 116 | } 117 | 118 | this->killTimers(); 119 | this->killSocketNotifiers(); 120 | 121 | if (this->m_base) { 122 | event_base_free(this->m_base); 123 | this->m_base = 0; 124 | } 125 | 126 | delete this->m_tco; 127 | } 128 | 129 | /** 130 | * @brief Processes pending events that match @a flags until there are no more events to process 131 | * @param flags 132 | * @return Whether an event has been processed 133 | * @retval true If an event has been processed; 134 | * @retval false If no events have been processed 135 | * 136 | * Processes pending events that match @a flags until there are no 137 | * more events to process. Returns true if an event was processed; 138 | * otherwise returns false. 139 | * 140 | * If the QEventLoop::WaitForMoreEvents flag is set in @a flags, the 141 | * behavior of this function is as follows: 142 | * 143 | * @list 144 | * @li If events are available, this function returns after processing them. 145 | * @li If no events are available, this function will wait until more 146 | * are available and return after processing newly available events. 147 | * @endlist 148 | * 149 | * If the QEventLoop::WaitForMoreEvents flag is not set in @a flags, 150 | * and no events are available, this function will return 151 | * immediately. 152 | * 153 | * @note This function does not process events continuously; it 154 | * returns after all available events are processed. 155 | */ 156 | bool EventDispatcherLibEventPrivate::processEvents(QEventLoop::ProcessEventsFlags flags) 157 | { 158 | Q_Q(EventDispatcherLibEvent); 159 | 160 | const bool exclude_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers); 161 | const bool exclude_timers = (flags & QEventLoop::X11ExcludeTimers); 162 | 163 | exclude_notifiers && this->disableSocketNotifiers(true); 164 | exclude_timers && this->disableTimers(true); 165 | 166 | this->m_interrupt = false; 167 | this->m_awaken = false; 168 | 169 | bool result = q->hasPendingEvents(); 170 | 171 | Q_EMIT q->awake(); 172 | #if QT_VERSION < 0x040500 173 | QCoreApplication::sendPostedEvents(0, (flags & QEventLoop::DeferredDeletion) ? -1 : 0); 174 | #else 175 | QCoreApplication::sendPostedEvents(); 176 | #endif 177 | 178 | const bool can_wait = !this->m_interrupt && (flags & QEventLoop::WaitForMoreEvents) && !result; 179 | if (can_wait) { 180 | Q_EMIT q->aboutToBlock(); 181 | } 182 | 183 | if (!this->m_interrupt) { 184 | event_base_loop(this->m_base, EVLOOP_ONCE | (can_wait ? 0 : EVLOOP_NONBLOCK)); 185 | 186 | #if QT_VERSION >= 0x040800 187 | EventList list; 188 | this->m_event_list.swap(list); 189 | #else 190 | EventList list(this->m_event_list); 191 | this->m_event_list.clear(); 192 | #endif 193 | 194 | result |= (list.size() > 0) | this->m_awaken; 195 | 196 | for (int i=0; itype() == QEvent::Timer) { 211 | QTimerEvent* te = static_cast(e.second); 212 | TimerHash::Iterator tit = this->m_timers.find(te->timerId()); 213 | if (tit != this->m_timers.end()) { 214 | TimerInfo* info = tit.value(); 215 | 216 | if (!event_pending(info->ev, EV_TIMEOUT, 0)) { // false in tst_QTimer::restartedTimerFiresTooSoon() 217 | EventDispatcherLibEventPrivate::calculateNextTimeout(info, now, delta); 218 | event_add(info->ev, &delta); 219 | } 220 | } 221 | } 222 | 223 | delete e.second; 224 | } 225 | } 226 | 227 | exclude_notifiers && this->disableSocketNotifiers(false); 228 | exclude_timers && this->disableTimers(false); 229 | 230 | return result; 231 | } 232 | 233 | /** 234 | * @brief event_base accessor 235 | * @return The internal event_base for this dispatcher 236 | */ 237 | struct event_base* EventDispatcherLibEventPrivate::eventBase(void) const 238 | { 239 | return this->m_base; 240 | } 241 | 242 | /** 243 | * @internal 244 | * @brief Wakeup handler 245 | * @param fd Not used 246 | * @param events Not used 247 | * @param arg Pointer to @c EventDispatcherLibEventPrivate instance 248 | */ 249 | void EventDispatcherLibEventPrivate::wake_up_handler(int fd, short int events, void* arg) 250 | { 251 | Q_UNUSED(fd) 252 | Q_UNUSED(events) 253 | 254 | EventDispatcherLibEventPrivate* disp = static_cast(arg); 255 | Q_ASSERT(disp != 0); 256 | 257 | disp->m_awaken = true; 258 | disp->m_tco->awaken(); 259 | } 260 | -------------------------------------------------------------------------------- /src/timers_p.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "eventdispatcher_libevent_p.h" 3 | 4 | void EventDispatcherLibEventPrivate::calculateCoarseTimerTimeout(TimerInfo* info, const struct timeval& now, struct timeval& when) 5 | { 6 | Q_ASSERT(info->interval > 20); 7 | // The coarse timer works like this: 8 | // - interval under 40 ms: round to even 9 | // - between 40 and 99 ms: round to multiple of 4 10 | // - otherwise: try to wake up at a multiple of 25 ms, with a maximum error of 5% 11 | // 12 | // We try to wake up at the following second-fraction, in order of preference: 13 | // 0 ms 14 | // 500 ms 15 | // 250 ms or 750 ms 16 | // 200, 400, 600, 800 ms 17 | // other multiples of 100 18 | // other multiples of 50 19 | // other multiples of 25 20 | // 21 | // The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups. 22 | 23 | int interval = info->interval; 24 | int msec = static_cast(info->when.tv_usec / 1000); 25 | int max_rounding = interval / 20; // 5% 26 | when = info->when; 27 | 28 | if (interval < 100 && (interval % 25) != 0) { 29 | if (interval < 50) { 30 | uint round_up = ((msec % 50) >= 25) ? 1 : 0; 31 | msec = ((msec >> 1) | round_up) << 1; 32 | } 33 | else { 34 | uint round_up = ((msec % 100) >= 50) ? 1 : 0; 35 | msec = ((msec >> 2) | round_up) << 2; 36 | } 37 | } 38 | else { 39 | int min = qMax(0, msec - max_rounding); 40 | int max = qMin(1000, msec + max_rounding); 41 | 42 | bool done = false; 43 | 44 | // take any round-to-the-second timeout 45 | if (min == 0) { 46 | msec = 0; 47 | done = true; 48 | } 49 | else if (max == 1000) { 50 | msec = 1000; 51 | done = true; 52 | } 53 | 54 | if (!done) { 55 | int boundary; 56 | 57 | // if the interval is a multiple of 500 ms and > 5000 ms, always round towards a round-to-the-second 58 | // if the interval is a multiple of 500 ms, round towards the nearest multiple of 500 ms 59 | if ((interval % 500) == 0) { 60 | if (interval >= 5000) { 61 | msec = msec >= 500 ? max : min; 62 | done = true; 63 | } 64 | else { 65 | boundary = 500; 66 | } 67 | } 68 | else if ((interval % 50) == 0) { 69 | // same for multiples of 250, 200, 100, 50 70 | uint tmp = interval / 50; 71 | if ((tmp % 4) == 0) { 72 | boundary = 200; 73 | } 74 | else if ((tmp % 2) == 0) { 75 | boundary = 100; 76 | } 77 | else if ((tmp % 5) == 0) { 78 | boundary = 250; 79 | } 80 | else { 81 | boundary = 50; 82 | } 83 | } 84 | else { 85 | boundary = 25; 86 | } 87 | 88 | if (!done) { 89 | int base = (msec / boundary) * boundary; 90 | int middle = base + boundary / 2; 91 | msec = (msec < middle) ? qMax(base, min) : qMin(base + boundary, max); 92 | } 93 | } 94 | } 95 | 96 | if (msec == 1000) { 97 | ++when.tv_sec; 98 | when.tv_usec = 0; 99 | } 100 | else { 101 | when.tv_usec = msec * 1000; 102 | } 103 | 104 | if (evutil_timercmp(&when, &now, <)) { 105 | when.tv_sec += interval / 1000; 106 | when.tv_usec += (interval % 1000) * 1000; 107 | if (when.tv_usec > 999999) { 108 | ++when.tv_sec; 109 | when.tv_usec -= 1000000; 110 | } 111 | } 112 | 113 | Q_ASSERT(evutil_timercmp(&now, &when, <=)); 114 | } 115 | 116 | void EventDispatcherLibEventPrivate::calculateNextTimeout(TimerInfo* info, const struct timeval& now, struct timeval& delta) 117 | { 118 | struct timeval tv_interval; 119 | struct timeval when; 120 | tv_interval.tv_sec = info->interval / 1000; 121 | tv_interval.tv_usec = (info->interval % 1000) * 1000; 122 | 123 | if (info->interval) { 124 | qlonglong tnow = (qlonglong(now.tv_sec) * 1000) + (now.tv_usec / 1000); 125 | qlonglong twhen = (qlonglong(info->when.tv_sec) * 1000) + (info->when.tv_usec / 1000); 126 | 127 | if ((info->interval < 1000 && twhen - tnow > 1500) || (info->interval >= 1000 && twhen - tnow > 1.2*info->interval)) { 128 | info->when = now; 129 | } 130 | } 131 | 132 | if (Qt::VeryCoarseTimer == info->type) { 133 | if (info->when.tv_usec >= 500000) { 134 | ++info->when.tv_sec; 135 | } 136 | 137 | info->when.tv_usec = 0; 138 | info->when.tv_sec += info->interval / 1000; 139 | if (info->when.tv_sec <= now.tv_sec) { 140 | info->when.tv_sec = now.tv_sec + info->interval / 1000; 141 | } 142 | 143 | when = info->when; 144 | } 145 | else if (Qt::PreciseTimer == info->type) { 146 | if (info->interval) { 147 | evutil_timeradd(&info->when, &tv_interval, &info->when); 148 | if (evutil_timercmp(&info->when, &now, <)) { 149 | evutil_timeradd(&now, &tv_interval, &info->when); 150 | } 151 | 152 | when = info->when; 153 | } 154 | else { 155 | when = now; 156 | } 157 | } 158 | else { 159 | evutil_timeradd(&info->when, &tv_interval, &info->when); 160 | if (evutil_timercmp(&info->when, &now, <)) { 161 | evutil_timeradd(&now, &tv_interval, &info->when); 162 | } 163 | 164 | EventDispatcherLibEventPrivate::calculateCoarseTimerTimeout(info, now, when); 165 | } 166 | 167 | evutil_timersub(&when, &now, &delta); 168 | } 169 | 170 | void EventDispatcherLibEventPrivate::registerTimer(int timerId, int interval, Qt::TimerType type, QObject* object) 171 | { 172 | struct timeval now; 173 | evutil_gettimeofday(&now, 0); 174 | 175 | TimerInfo* info = new TimerInfo; 176 | info->self = this; 177 | info->ev = event_new(this->m_base, -1, 0, EventDispatcherLibEventPrivate::timer_callback, info); 178 | info->timerId = timerId; 179 | info->interval = interval; 180 | info->type = type; 181 | info->object = object; 182 | info->when = now; // calculateNextTimeout() will take care of info->when 183 | Q_CHECK_PTR(info->ev); 184 | 185 | if (Qt::CoarseTimer == type) { 186 | if (interval >= 20000) { 187 | info->type = Qt::VeryCoarseTimer; 188 | } 189 | else if (interval <= 20) { 190 | info->type = Qt::PreciseTimer; 191 | } 192 | } 193 | 194 | struct timeval delta; 195 | EventDispatcherLibEventPrivate::calculateNextTimeout(info, now, delta); 196 | 197 | event_add(info->ev, &delta); 198 | this->m_timers.insert(timerId, info); 199 | } 200 | 201 | bool EventDispatcherLibEventPrivate::unregisterTimer(int timerId) 202 | { 203 | TimerHash::Iterator it = this->m_timers.find(timerId); 204 | if (it != this->m_timers.end()) { 205 | TimerInfo* info = it.value(); 206 | event_del(info->ev); 207 | event_free(info->ev); 208 | delete info; 209 | this->m_timers.erase(it); 210 | return true; 211 | } 212 | 213 | return false; 214 | } 215 | 216 | bool EventDispatcherLibEventPrivate::unregisterTimers(QObject* object) 217 | { 218 | TimerHash::Iterator it = this->m_timers.begin(); 219 | while (it != this->m_timers.end()) { 220 | TimerInfo* info = it.value(); 221 | if (object == info->object) { 222 | event_del(info->ev); 223 | event_free(info->ev); 224 | delete info; 225 | it = this->m_timers.erase(it); 226 | } 227 | else { 228 | ++it; 229 | } 230 | } 231 | 232 | return true; 233 | } 234 | 235 | QList EventDispatcherLibEventPrivate::registeredTimers(QObject* object) const 236 | { 237 | QList res; 238 | 239 | TimerHash::ConstIterator it = this->m_timers.constBegin(); 240 | while (it != this->m_timers.constEnd()) { 241 | TimerInfo* info = it.value(); 242 | if (object == info->object) { 243 | #if QT_VERSION < 0x050000 244 | QAbstractEventDispatcher::TimerInfo ti(it.key(), info->interval); 245 | #else 246 | QAbstractEventDispatcher::TimerInfo ti(it.key(), info->interval, info->type); 247 | #endif 248 | res.append(ti); 249 | } 250 | 251 | ++it; 252 | } 253 | 254 | return res; 255 | } 256 | 257 | int EventDispatcherLibEventPrivate::remainingTime(int timerId) const 258 | { 259 | TimerHash::ConstIterator it = this->m_timers.find(timerId); 260 | if (it != this->m_timers.end()) { 261 | const TimerInfo* info = it.value(); 262 | struct timeval when; 263 | 264 | int r = event_pending(info->ev, EV_TIMEOUT, &when); 265 | if (r) { 266 | struct timeval now; 267 | evutil_gettimeofday(&now, 0); 268 | 269 | qulonglong tnow = qulonglong(now.tv_sec) * 1000000 + now.tv_usec; 270 | qulonglong twhen = qulonglong(when.tv_sec) * 1000000 + when.tv_usec; 271 | 272 | if (tnow > twhen) { 273 | return 0; 274 | } 275 | 276 | return static_cast((twhen - tnow) / 1000); 277 | } 278 | } 279 | 280 | return -1; 281 | } 282 | 283 | void EventDispatcherLibEventPrivate::timer_callback(int fd, short int events, void* arg) 284 | { 285 | Q_ASSERT(-1 == fd); 286 | Q_ASSERT(events & EV_TIMEOUT); 287 | Q_UNUSED(fd) 288 | Q_UNUSED(events) 289 | 290 | TimerInfo* info = static_cast(arg); 291 | 292 | // Timer can be reactivated only after its callback finishes; processEvents() will take care of this 293 | PendingEvent event(info->object, new QTimerEvent(info->timerId)); 294 | info->self->m_event_list.append(event); 295 | } 296 | 297 | bool EventDispatcherLibEventPrivate::disableTimers(bool disable) 298 | { 299 | struct timeval now; 300 | if (!disable) { 301 | evutil_gettimeofday(&now, 0); 302 | } 303 | 304 | TimerHash::Iterator it = this->m_timers.begin(); 305 | while (it != this->m_timers.end()) { 306 | TimerInfo* info = it.value(); 307 | if (disable) { 308 | event_del(info->ev); 309 | } 310 | else { 311 | struct timeval delta; 312 | EventDispatcherLibEventPrivate::calculateNextTimeout(info, now, delta); 313 | event_add(info->ev, &delta); 314 | } 315 | 316 | ++it; 317 | } 318 | 319 | return true; 320 | } 321 | 322 | void EventDispatcherLibEventPrivate::killTimers(void) 323 | { 324 | if (!this->m_timers.isEmpty()) { 325 | TimerHash::Iterator it = this->m_timers.begin(); 326 | while (it != this->m_timers.end()) { 327 | TimerInfo* info = it.value(); 328 | event_del(info->ev); 329 | event_free(info->ev); 330 | delete info; 331 | ++it; 332 | } 333 | 334 | this->m_timers.clear(); 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /src/eventdispatcher_libevent.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "eventdispatcher_libevent.h" 3 | #include "eventdispatcher_libevent_p.h" 4 | 5 | /** 6 | * @class EventDispatcherLibEvent 7 | * @brief The EventDispatcherLibEvent class provides an interface to manage Qt's event queue. 8 | * 9 | * An event dispatcher receives events from the window system and other 10 | * sources. It then sends them to the QCoreApplication or QApplication 11 | * instance for processing and delivery. EventDispatcherLibEvent provides 12 | * fine-grained control over event delivery. 13 | * 14 | * For simple control of event processing use 15 | * QCoreApplication::processEvents(). 16 | * 17 | * For finer control of the application's event loop, call 18 | * instance() and call functions on the QAbstractEventDispatcher 19 | * object that is returned. 20 | * 21 | * To use EventDispatcherLibEvent, you must install it with 22 | * QCoreApplication::setEventDispatcher() or QThread::setEventDispatcher() 23 | * before a default event dispatcher has been installed. 24 | 25 | * The main event loop is started by calling 26 | * QCoreApplication::exec(), and stopped by calling 27 | * QCoreApplication::exit(). Local event loops can be created using 28 | * QEventLoop. 29 | * 30 | * Programs that perform long operations can call processEvents() 31 | * with a bitwise OR combination of various QEventLoop::ProcessEventsFlag 32 | * values to control which events should be delivered. 33 | */ 34 | 35 | /** 36 | * Constructs a new event dispatcher with the given @a parent 37 | * 38 | * @param parent Parent 39 | */ 40 | EventDispatcherLibEvent::EventDispatcherLibEvent(QObject* parent) 41 | : QAbstractEventDispatcher(parent), d_ptr(new EventDispatcherLibEventPrivate(this)) 42 | { 43 | } 44 | 45 | /** 46 | * Constructs a new event dispatcher with the given configuration @a config and @a parent 47 | * 48 | * @param config Event dispatcher configuration 49 | * @param parent Parent 50 | * @see EventDispatcherLibEventConfig 51 | */ 52 | EventDispatcherLibEvent::EventDispatcherLibEvent(const EventDispatcherLibEventConfig& config, QObject* parent) 53 | : QAbstractEventDispatcher(parent), d_ptr(new EventDispatcherLibEventPrivate(this, config)) 54 | { 55 | } 56 | 57 | /** 58 | * Destroys the event dispatcher 59 | */ 60 | EventDispatcherLibEvent::~EventDispatcherLibEvent(void) 61 | { 62 | #if QT_VERSION < 0x040600 63 | delete this->d_ptr; 64 | this->d_ptr = 0; 65 | #endif 66 | } 67 | 68 | /** 69 | * Retrieve the libevent event_base for this dispatcher 70 | * @return The event_base for this dispatcher 71 | */ 72 | struct event_base* EventDispatcherLibEvent::eventBase(void) const 73 | { 74 | const Q_D(EventDispatcherLibEvent); 75 | return d->eventBase(); 76 | } 77 | 78 | /** 79 | * @brief Processes pending events that match @a flags until there are no more events to process 80 | * @param flags 81 | * @return Whether an event has been processed 82 | * @retval true If an event has been processed; 83 | * @retval false If no events have been processed 84 | * 85 | * Processes pending events that match @a flags until there are no 86 | * more events to process. Returns true if an event was processed; 87 | * otherwise returns false. 88 | * 89 | * This function is especially useful if you have a long running 90 | * operation and want to show its progress without allowing user 91 | * input; i.e. by using the QEventLoop::ExcludeUserInputEvents flag. 92 | * 93 | * If the QEventLoop::WaitForMoreEvents flag is set in @a flags, the 94 | * behavior of this function is as follows: 95 | * 96 | * @list 97 | * @li If events are available, this function returns after processing them. 98 | * @li If no events are available, this function will wait until more 99 | * are available and return after processing newly available events. 100 | * @endlist 101 | * 102 | * If the QEventLoop::WaitForMoreEvents flag is not set in @a flags, 103 | * and no events are available, this function will return 104 | * immediately. 105 | * 106 | * @note This function does not process events continuously; it 107 | * returns after all available events are processed. 108 | * 109 | * @see hasPendingEvents() 110 | */ 111 | bool EventDispatcherLibEvent::processEvents(QEventLoop::ProcessEventsFlags flags) 112 | { 113 | Q_D(EventDispatcherLibEvent); 114 | return d->processEvents(flags); 115 | } 116 | 117 | /** 118 | * Returns @c true if there is an event waiting; otherwise returns @c false 119 | * 120 | * @return Whether there is an event waiting 121 | */ 122 | bool EventDispatcherLibEvent::hasPendingEvents(void) 123 | { 124 | extern uint qGlobalPostedEventsCount(); 125 | return qGlobalPostedEventsCount() > 0; 126 | } 127 | 128 | /** 129 | * Registers @a notifier with the event loop 130 | * 131 | * @param notifier Socket notifier to register 132 | * @warning @a notifier must belong to the same thread as the event dispatcher 133 | * @warning Socket notifiers can only be enabled from the thread they belong to 134 | */ 135 | void EventDispatcherLibEvent::registerSocketNotifier(QSocketNotifier* notifier) 136 | { 137 | #ifndef QT_NO_DEBUG 138 | if (notifier->socket() < 0) { 139 | qWarning("QSocketNotifier: Internal error: sockfd < 0"); 140 | return; 141 | } 142 | 143 | if (notifier->thread() != this->thread() || this->thread() != QThread::currentThread()) { 144 | qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); 145 | return; 146 | } 147 | #endif 148 | 149 | if (notifier->type() == QSocketNotifier::Exception) { 150 | return; 151 | } 152 | 153 | Q_D(EventDispatcherLibEvent); 154 | d->registerSocketNotifier(notifier); 155 | } 156 | 157 | /** 158 | * Unregisters @a notifier from the event dispatcher. 159 | * 160 | * @param notifier Socket notifier to unregister 161 | * @warning @a notifier must belong to the same thread as the event dispatcher 162 | * @warning Socket notifiers can only be disabled from the thread they belong to 163 | */ 164 | void EventDispatcherLibEvent::unregisterSocketNotifier(QSocketNotifier* notifier) 165 | { 166 | #ifndef QT_NO_DEBUG 167 | if (notifier->socket() < 0) { 168 | qWarning("QSocketNotifier: Internal error: sockfd < 0"); 169 | return; 170 | } 171 | 172 | if (notifier->thread() != this->thread() || this->thread() != QThread::currentThread()) { 173 | qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); 174 | return; 175 | } 176 | #endif 177 | 178 | // Short circuit, we do not support QSocketNotifier::Exception 179 | if (notifier->type() == QSocketNotifier::Exception) { 180 | return; 181 | } 182 | 183 | Q_D(EventDispatcherLibEvent); 184 | d->unregisterSocketNotifier(notifier); 185 | } 186 | 187 | /** 188 | * Register a timer with the specified @a timerId, @a interval, and @a timerType 189 | * for the given @a object. 190 | * 191 | * @param timerId Timer ID 192 | * @param interval Timer interval (msec); must be greater than 0 193 | * @param timerType Timer type (not available in Qt 4) 194 | * @param object Object for which the timer is registered 195 | * @warning @a object must belong to the same thread as the event dispatcher 196 | * @warning Timers can only be started from the thread the event dispatcher lives in 197 | */ 198 | void EventDispatcherLibEvent::registerTimer( 199 | int timerId, 200 | int interval, 201 | #if QT_VERSION >= 0x050000 202 | Qt::TimerType timerType, 203 | #endif 204 | QObject* object 205 | ) 206 | { 207 | #ifndef QT_NO_DEBUG 208 | if (timerId < 1 || interval < 0 || !object) { 209 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 210 | return; 211 | } 212 | 213 | if (object->thread() != this->thread() && this->thread() != QThread::currentThread()) { 214 | qWarning("%s: timers cannot be started from another thread", Q_FUNC_INFO); 215 | return; 216 | } 217 | #endif 218 | 219 | Qt::TimerType type; 220 | #if QT_VERSION >= 0x050000 221 | type = timerType; 222 | #else 223 | type = Qt::CoarseTimer; 224 | #endif 225 | 226 | Q_D(EventDispatcherLibEvent); 227 | d->registerTimer(timerId, interval, type, object); 228 | } 229 | 230 | /** 231 | * Unregisters the timer 232 | * 233 | * @param timerId ID of the timer 234 | * @return Whether the timer has been unregistered 235 | * @warning Timers can only be stopped from the same thread as the event dispatcher lives in 236 | */ 237 | bool EventDispatcherLibEvent::unregisterTimer(int timerId) 238 | { 239 | #ifndef QT_NO_DEBUG 240 | if (timerId < 1) { 241 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 242 | return false; 243 | } 244 | 245 | if (this->thread() != QThread::currentThread()) { 246 | qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO); 247 | return false; 248 | } 249 | #endif 250 | 251 | Q_D(EventDispatcherLibEvent); 252 | return d->unregisterTimer(timerId); 253 | } 254 | 255 | /** 256 | * Unregisters all timers for the given @a object 257 | * 258 | * @param object Object 259 | * @return Whether timers have been unregistered 260 | * @warning @a object must belong to the same thread as the event dispatcher 261 | * @warning Timers can only be started from the thread the event dispatcher lives in 262 | */ 263 | bool EventDispatcherLibEvent::unregisterTimers(QObject* object) 264 | { 265 | #ifndef QT_NO_DEBUG 266 | if (!object) { 267 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 268 | return false; 269 | } 270 | 271 | if (object->thread() != this->thread() && this->thread() != QThread::currentThread()) { 272 | qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO); 273 | return false; 274 | } 275 | #endif 276 | 277 | Q_D(EventDispatcherLibEvent); 278 | return d->unregisterTimers(object); 279 | } 280 | 281 | /** 282 | * @brief Returns a list of registered timers for the given @a object. 283 | * @param object Object 284 | * @return List of registered timers 285 | */ 286 | QList EventDispatcherLibEvent::registeredTimers(QObject* object) const 287 | { 288 | if (!object) { 289 | qWarning("%s: invalid argument", Q_FUNC_INFO); 290 | return QList(); 291 | } 292 | 293 | Q_D(const EventDispatcherLibEvent); 294 | return d->registeredTimers(object); 295 | } 296 | 297 | #if QT_VERSION >= 0x050000 298 | /** 299 | * Returns the remaining time in milliseconds with the given @a timerId. 300 | * 301 | * @param timerId Timer ID 302 | * @return The remaining time 303 | * @retval -1 If the timer is inactive 304 | * @retval 0 If the timer is overdue 305 | */ 306 | int EventDispatcherLibEvent::remainingTime(int timerId) 307 | { 308 | Q_D(const EventDispatcherLibEvent); 309 | return d->remainingTime(timerId); 310 | } 311 | #endif 312 | 313 | #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 314 | bool EventDispatcherLibEvent::registerEventNotifier(QWinEventNotifier* notifier) 315 | { 316 | Q_UNUSED(notifier) 317 | Q_UNIMPLEMENTED(); 318 | return false; 319 | } 320 | 321 | void EventDispatcherLibEvent::unregisterEventNotifier(QWinEventNotifier* notifier) 322 | { 323 | Q_UNUSED(notifier) 324 | Q_UNIMPLEMENTED(); 325 | } 326 | #endif 327 | 328 | /** 329 | * Wakes up the event loop. Thread-safe 330 | */ 331 | void EventDispatcherLibEvent::wakeUp(void) 332 | { 333 | Q_D(EventDispatcherLibEvent); 334 | d->m_tco->wakeUp(); 335 | } 336 | 337 | /** 338 | * Interrupts event dispatching. The event dispatcher will return from @c processEvents() 339 | * as soon as possible. 340 | */ 341 | void EventDispatcherLibEvent::interrupt(void) 342 | { 343 | Q_D(EventDispatcherLibEvent); 344 | d->m_interrupt = true; 345 | this->wakeUp(); 346 | } 347 | 348 | /** 349 | * @brief Does nothing 350 | */ 351 | void EventDispatcherLibEvent::flush(void) 352 | { 353 | } 354 | 355 | /** 356 | * @brief EventDispatcherLibEvent::EventDispatcherLibEvent 357 | * @param dd 358 | * @param parent 359 | * @internal 360 | */ 361 | EventDispatcherLibEvent::EventDispatcherLibEvent(EventDispatcherLibEventPrivate& dd, QObject* parent) 362 | : QAbstractEventDispatcher(parent), d_ptr(&dd) 363 | { 364 | } 365 | 366 | void EventDispatcherLibEvent::reinitialize(void) 367 | { 368 | Q_D(EventDispatcherLibEvent); 369 | event_reinit(d->m_base); 370 | } 371 | 372 | /** 373 | * @fn void EventDispatcherLibEvent::awake() 374 | * 375 | * This signal is emitted after the event loop returns from a function that could block. 376 | * 377 | * @see wakeUp() 378 | * @see aboutToBlock() 379 | */ 380 | 381 | /** 382 | * @fn void EventDispatcherLibEvent::aboutToBlock() 383 | * 384 | * This signal is emitted before the event loop calls a function that could block. 385 | * 386 | * @see awake() 387 | */ 388 | --------------------------------------------------------------------------------