source: trunk/src/kernel/qeventloop_pm.cpp@ 208

Last change on this file since 208 was 180, checked in by dmik, 17 years ago

kernel: Make sure that eventloop is cleaned up after all post-routines (that may still use some eventloop services such as timers) are called and that start timer requests are ignored after the eventloop has been cleaned up during application termination. This fixes assertions and crashes that might have happened at program termination otherwise.

  • Property svn:keywords set to Id
File size: 49.7 KB
Line 
1/****************************************************************************
2** $Id: qeventloop_pm.cpp 180 2008-03-01 15:42:34Z dmik $
3**
4** Implementation of OS/2 startup routines and event handling
5**
6** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
7** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
8** Copyright (C) 2005 netlabs.org. Further OS/2 Development.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qeventloop_p.h"
39#include "qeventloop.h"
40#include "qapplication.h"
41#include "qptrlist.h"
42#include "qvaluelist.h"
43
44#include <sys/socket.h>
45
46#if defined(QT_THREAD_SUPPORT)
47# include "qmutex.h"
48# include "qthread.h"
49# include "qsemaphore.h"
50#endif // QT_THREAD_SUPPORT
51
52// entry point: PMWIN.305
53extern "C" HMQ APIENTRY WinQueueFromID( HAB hab, PID pid, PID tid );
54
55extern uint qGlobalPostedEventsCount();
56extern bool qt_pmEventFilter( QMSG* msg, MRESULT &result );
57
58#if !defined (QT_NO_SESSIONMANAGER)
59extern bool qt_app_canQuit(); // defined in qapplication_pm.cpp
60#endif
61
62static HAB qt_gui_hab = 0;
63static HMQ qt_gui_queue = 0;
64
65#ifdef QT_PM_NO_DOSTIMERS
66static bool dispatchTimer( uint, QMSG * );
67#endif
68static void activateZeroTimers();
69
70static void initTimers();
71static void cleanupTimers();
72
73static int numZeroTimers = 0; // number of full-speed timers
74
75// a flag to disable the warning when the console process dynamically
76// switches istelf to PM. Currently used by the UIC tool.
77bool qt_suppress_morph_warning = false;
78
79// Check that the current process is in PM mode. This is called by QEventLoop
80// and QApplication initialization routines and by initTimers() to ensure
81// that we are in PM mode and therefore it is possible to create the message
82// queue. "Morphing" to PM leaves the console attahed which can be used for
83// debugging.
84void qt_ensure_pm()
85{
86 PPIB ppib;
87 DosGetInfoBlocks( NULL, &ppib );
88 if( ppib->pib_ultype != 3 ) {
89#if defined(QT_CHECK_STATE)
90 if( !qt_suppress_morph_warning )
91 qWarning( "Qt: the program has not been linked as the Presentation "
92 "Manager application\n"
93 "but it uses GUI capabilities. Switching to PM mode "
94 "dynamically." );
95#endif
96 ppib->pib_ultype = 3;
97 }
98}
99
100enum
101{
102 // socket select notification (highest priority)
103 WM_U_SEM_SELECT = WM_SEM1,
104 // zero timer notification (lowest priority)
105 WM_U_SEM_ZEROTIMER = WM_SEM4,
106};
107
108/*****************************************************************************
109 Auxiliary object window class for dedicated message processing.
110 Declared in qwindowdefs_pm.h
111 *****************************************************************************/
112
113/*!
114 \class QPMObjectWindow qwindowdefs.h
115
116 The QPMObjectWindow class is an auxiliary class for dedicated message
117 processing. Its functionality is based on PM object windows. Once an instance
118 of this class is created, PM window messages can be sent or posted to it using
119 send() or post() methods. Subclasses should override the message() method to
120 process sent or posted messages. The hwnd() method is used whenever a PM
121 window handle of this object window is necessary to be passed as a HWND
122 argument to other calls and/or messages.
123
124 Instances of this class must be created on the main GUI thread only or on a
125 thread that has created a PM message queue and runs the PM message loop
126 \b itself. If you create an instance on a thread other than main, make sure
127 you destroy it before destroying the thread's message queue.
128
129 \note WM_CREATE and WM_DESTROY messages are processed internally and not
130 delivered do the message() method. Instead, you can use the constructor and
131 the destructor of the subclasses, respectively.
132
133 \note This class is non-portable!
134*/
135
136// returns HMQ of the current thread or NULL if no event queue has been created
137static HMQ qt_WinQueryQueue( HAB hab )
138{
139 PTIB ptib;
140 PPIB ppib;
141 DosGetInfoBlocks( &ptib, &ppib );
142 return WinQueueFromID( hab, ppib->pib_ulpid, ptib->tib_ptib2->tib2_ultid );
143}
144
145static QPtrList<HWND> qt_objWindows;
146
147/*!
148 Constructs a new object window for the current thread.
149 If \a deferred is FALSE, this method calls create() to create a PM object
150 window. Otherwise, you must call create() yourself before this object
151 window is able to process messages.
152*/
153QPMObjectWindow::QPMObjectWindow( bool deferred /* = FALSE */ ) :
154 w( NULLHANDLE )
155{
156 if ( !deferred )
157 create();
158}
159
160/*!
161 Destroys this object window.
162 This method calls destroy() to free the PM object window.
163*/
164QPMObjectWindow::~QPMObjectWindow()
165{
166 destroy();
167}
168
169/*!
170 Creates a PM object window.
171 Returns \c TRUE on success or \c FALSE otherwise.
172 The method does nothing but returns \c FALSE if the window has been
173 already created. The handle of the successfully created object window can
174 be obtained using the hwnd() method.
175
176 \note Must be called on the same thread that cosnstructed this instance.
177*/
178bool QPMObjectWindow::create()
179{
180 if ( w != NULLHANDLE )
181 return FALSE;
182
183 static const char *ClassName = "QPMObjectWindow";
184 static bool classRegistered = FALSE;
185
186 if ( !classRegistered ) {
187 WinRegisterClass( 0, ClassName, windowProc, 0, QWL_USER + sizeof(PVOID) );
188 classRegistered = TRUE;
189 }
190
191 w = WinCreateWindow( HWND_OBJECT, ClassName,
192 NULL, 0, 0, 0, 0, 0, NULL,
193 HWND_BOTTOM, 0, this, NULL );
194#ifndef QT_NO_DEBUG
195 if ( w == NULLHANDLE )
196 qSystemWarning( "QPMObjectWindow: Failed to create object window" );
197#endif
198
199 if ( w != NULLHANDLE && qt_gui_queue &&
200 qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
201 // store the handle in the list for clean destruction
202 qt_objWindows.append( &w );
203 }
204
205 return w != NULLHANDLE;
206}
207
208/*!
209 Destroys the PM object window.
210 Returns \c TRUE on success or \c FALSE otherwise.
211 The method does nothing but returns \c FALSE if the window has been
212 already destroyed (or never created).
213
214 \note Must be called on the same thread that cosnstructed this instance.
215*/
216bool QPMObjectWindow::destroy()
217{
218 if ( w == NULLHANDLE )
219 return FALSE;
220
221 if ( qt_gui_queue && qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
222 // remove the handle from the 'clean destruction' list
223 qt_objWindows.removeRef( &w );
224 }
225
226 HWND h = w;
227 w = 0; // tell windowProc() we're unsafe
228 WinDestroyWindow( h );
229
230 return TRUE;
231}
232
233//static
234MRESULT EXPENTRY QPMObjectWindow::windowProc( HWND hwnd, ULONG msg,
235 MPARAM mp1, MPARAM mp2 )
236{
237 if ( msg == WM_CREATE ) {
238 QPMObjectWindow *that = static_cast< QPMObjectWindow *>( mp1 );
239 if ( !that )
240 return (MRESULT) TRUE;
241 WinSetWindowPtr( hwnd, QWL_USER, that );
242 return (MRESULT) FALSE;
243 }
244
245 QPMObjectWindow *that =
246 static_cast< QPMObjectWindow * >( WinQueryWindowPtr( hwnd, QWL_USER ) );
247 Q_ASSERT( that );
248
249 // Note: before WinCreateWindow() returns to the constructor or after the
250 // destructor has been called, w is 0. We use this to determine that the
251 // object is in the unsafe state (VTBL is not yet initialized or has been
252 // already uninitialized), so message() points to never-never land.
253 if ( !that || !that->w )
254 return (MRESULT) FALSE;
255
256 return that->message( msg, mp1, mp2 );
257}
258
259/*!
260 \fn QPMObjectWindow::send( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
261
262 Synchronously sends a message \a msg with the given parameters \a mp1 and
263 \a mp2 to this window handle and returns a reply from the message() function.
264
265 \note Must be called on the same thread that cosnstructed this instance.
266*/
267
268/*!
269 \fn QPMObjectWindow::post( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
270
271 Asynchronously posts a message \a msg with the given parameters \a mp1 and
272 \a mp2 to this window handle. Returns \c TRUE on success and \c FALSE
273 otherwise.
274
275 \note Can be called on any thread.
276*/
277
278// Auxiliary object window to process WM_U_SEM_SELECT and WM_TIMER messages.
279// We need a dedicated window along with processing these messages directly in
280// QEventLoop::processEvents() to make sure they are processed even if the
281// current message loop is not run by Qt. This happens when a native modal
282// dialog is shown or when a top-level Qt widget is being moved or resized
283// using the mouse, or when a Qt-based DLL plugin is used by a non-Qt
284// application.
285
286class QPMAuxEventQueueWin : public QPMObjectWindow
287{
288public:
289 QPMAuxEventQueueWin() : QPMObjectWindow( TRUE ), eventLoop( NULL ) {}
290 void setEventLoop( QEventLoop *el ) { eventLoop = el; }
291 MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
292private:
293 QEventLoop *eventLoop;
294};
295
296MRESULT QPMAuxEventQueueWin::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
297{
298 switch ( msg ) {
299 case WM_U_SEM_SELECT: {
300 if ( eventLoop )
301 eventLoop->activateSocketNotifiers();
302 break;
303 }
304 case WM_U_SEM_ZEROTIMER: {
305 if ( numZeroTimers ) {
306 activateZeroTimers();
307 // repost the message if there are still zero timers left
308 if ( numZeroTimers )
309 WinPostMsg( hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
310 }
311 break;
312 }
313 case WM_TIMER: {
314#ifndef QT_PM_NO_DOSTIMERS
315 QApplication::sendPostedEvents( NULL, QEvent::Timer );
316 break;
317#else // ifndef QT_PM_NO_DOSTIMERS
318 USHORT id = SHORT1FROMMP( mp1 );
319 QMSG qmsg = { hwnd(), msg, mp1, mp2 };
320 dispatchTimer( (uint) id, &qmsg );
321 break;
322#endif // ifndef QT_PM_NO_DOSTIMERS
323 }
324 }
325
326 return FALSE;
327}
328
329static QPMAuxEventQueueWin qt_aux_win;
330
331
332/*****************************************************************************
333 Safe configuration (move,resize,setGeometry) mechanism to avoid
334 recursion when processing messages.
335 *****************************************************************************/
336
337#include "qptrqueue.h"
338
339struct QPMConfigRequest {
340 WId id; // widget to be configured
341 int req; // 0=move, 1=resize, 2=setGeo
342 int x, y, w, h; // request parameters
343};
344
345static QPtrQueue<QPMConfigRequest> *configRequests = 0;
346
347void qPMRequestConfig( WId id, int req, int x, int y, int w, int h )
348{
349 if ( !configRequests ) // create queue
350 configRequests = new QPtrQueue<QPMConfigRequest>;
351 QPMConfigRequest *r = new QPMConfigRequest;
352 r->id = id; // create new request
353 r->req = req;
354 r->x = x;
355 r->y = y;
356 r->w = w;
357 r->h = h;
358 configRequests->enqueue( r ); // store request in queue
359}
360
361Q_EXPORT void qPMProcessConfigRequests() // perform requests in queue
362{
363 if ( !configRequests )
364 return;
365 QPMConfigRequest *r;
366 for ( ;; ) {
367 if ( configRequests->isEmpty() )
368 break;
369 r = configRequests->dequeue();
370 QWidget *w = QWidget::find( r->id );
371 if ( w ) { // widget exists
372 if ( w->testWState(Qt::WState_ConfigPending) )
373 return; // biting our tail
374 if ( r->req == 0 )
375 w->move( r->x, r->y );
376 else if ( r->req == 1 )
377 w->resize( r->w, r->h );
378 else
379 w->setGeometry( r->x, r->y, r->w, r->h );
380 }
381 delete r;
382 }
383 delete configRequests;
384 configRequests = 0;
385}
386
387/*****************************************************************************
388 Timer handling; Our routines depend on OS/2 PM timer functions, but we
389 need some extra handling to activate objects at timeout.
390
391 There are two implemetations of Qt timers. When QT_PM_NO_DOSTIMERS is defined
392 (NOT by default), Qt uses PM timers. In this case, there are two types of
393 timer identifiers. PM timer ids (internal use) are stored in TimerInfo. Qt
394 timer ids are indexes (+1) into the timerVec vector. Note that this PM timer
395 based implementation is error-prone due to stupid PM limitations: there are
396 only several dozens of PM timers per the whole system and this number is
397 shared across all processes (e.g. if you create too many timers, other
398 applications that also need them will suck).
399
400 When QT_PM_NO_DOSTIMERS is not defined (by default), Qt uses its own timer
401 implementation based on a signle DosAsyncTimer() and a dedicated timer thread.
402 This implementation allows virtually unlimited number of Qt timers. In this
403 case, there is only one type of type identifiers: Qt timer identifiers are
404 indexes (+1) into the timeDict dictionary.
405
406 NOTE: These functions are for internal use. QObject::startTimer() and
407 QObject::killTimer() are for public use.
408 The QTimer class provides a high-level interface which translates
409 timer events into signals.
410
411 qStartTimer( interval, obj )
412 Starts a timer which will run until it is killed with qKillTimer()
413 Arguments:
414 int interval timer interval in milliseconds
415 QObject *obj where to send the timer event
416 Returns:
417 int timer identifier, or zero if not successful
418
419 qKillTimer( timerId )
420 Stops a timer specified by a timer identifier.
421 Arguments:
422 int timerId timer identifier
423 Returns:
424 bool TRUE if successful
425
426 qKillTimer( obj )
427 Stops all timers that are sent to the specified object.
428 Arguments:
429 QObject *obj object receiving timer events
430 Returns:
431 bool TRUE if successful
432 *****************************************************************************/
433
434//
435// Internal data structure for timers
436//
437
438//
439// Template to track availability of free (unused) integer values within some
440// interval. Note that take() and give() maintain spans sorted in ascending
441// order.
442//
443template <typename T>
444class QFreeValueList
445{
446public:
447 QFreeValueList( T min, T max ) : intMin( min ), intMax( max ) {
448 freeValues.push_front( Span( min, max ) );
449 }
450 T take() {
451 Q_ASSERT( !isEmpty() );
452 Span &free = freeValues.first();
453 T freeValue = free.min;
454 if ( free.min == free.max ) freeValues.pop_front();
455 else free.min ++;
456 return freeValue;
457 }
458 void give( T val ) {
459 Q_ASSERT( val >= intMin && val <= intMax );
460 // look for the span to join
461 typename FreeList::iterator it = freeValues.begin();
462 for ( ; it != freeValues.end(); ++ it ) {
463 Span &span = (*it);
464 if ( val == span.min - 1 ) {
465 // join the less end of span
466 span.min --;
467 typename FreeList::iterator it2 = it;
468 if ( it2 != freeValues.begin() && (*--it2).max + 1 == span.min ) {
469 span.min = (*it2).min;
470 freeValues.erase( it2 );
471 }
472 return;
473 } else if ( val == span.max + 1 ) {
474 // join the greater end of span
475 span.max ++;
476 typename FreeList::iterator it2 = it;
477 if ( ++it2 != freeValues.end() && (*it2).min - 1 == span.max ) {
478 span.max = (*it2).max;
479 freeValues.erase( it2 );
480 }
481 return;
482 } else if ( val < span.min ) {
483 // all the following spans are too "far" to join
484 break;
485 }
486 // span must not include val (contradicts take())
487 Q_ASSERT( val > span.max );
488 }
489 if ( it == freeValues.end() ) freeValues.push_back( Span( val, val ) );
490 else freeValues.insert( it, Span( val, val ) );
491 }
492 bool isEmpty() { return freeValues.isEmpty(); }
493 T min() const { return intMin; }
494 T max() const { return intMax; }
495private:
496 struct Span {
497 Span() : min( 0 ), max( 0 ) {}
498 Span( T mn, T mx ) : min( mn ), max( mx ) {}
499 T min;
500 T max;
501 };
502 typedef QValueList<Span> FreeList;
503 FreeList freeValues;
504 T intMin;
505 T intMax;
506};
507
508#ifndef QT_PM_NO_DOSTIMERS
509
510#include "qintdict.h"
511
512struct TimerInfo { // internal timer info
513 uint ind; // - Qt timer identifier - 1 (= index in vector)
514 ULONG interval; // - timeout interval, milliseconds
515 ULONG last; // - last shot timestamp
516 QObject *obj; // - object to receive events
517};
518
519typedef QIntDict<TimerInfo> TimerDict; // dict of TimerInfo structs
520static TimerDict *timerDict = NULL;
521
522static QMutex timerMutex; // mutex protecting timerDict
523
524typedef QFreeValueList<int> FreeQtTIDList; // list of free Qt timer IDs
525static FreeQtTIDList *freeQtTIDs = NULL;
526
527//
528// Auxiliary timer thread to wait for Dos timer events and post Qt timer events.
529//
530static class QTimerThread : public QThread
531{
532public:
533 QTimerThread();
534 virtual ~QTimerThread();
535 void run();
536 void signalQuit();
537 void ensureShot( int ival );
538private:
539 HEV hev; // OS/2 timer event semaphore
540 HTIMER htimer; // OS/2 timer
541 ULONG interval; // OS/2 timer interval
542 bool quit : 1; // quit flag
543} *timerThread = NULL;
544
545QTimerThread::QTimerThread()
546 : hev( NULLHANDLE ), htimer( NULLHANDLE )
547 , interval( 0 ), quit( false )
548{
549 APIRET rc;
550 rc = DosCreateEventSem( NULL, &hev, DC_SEM_SHARED, 0 /* reset */);
551 Q_ASSERT( rc == NO_ERROR );
552}
553
554QTimerThread::~QTimerThread()
555{
556 if ( hev != NULLHANDLE ) {
557 DosCloseEventSem( hev );
558 hev = NULLHANDLE;
559 }
560}
561
562void QTimerThread::run()
563{
564 APIRET rc;
565 ULONG now = 0;
566
567 do {
568 rc = DosWaitEventSem( hev, SEM_INDEFINITE_WAIT );
569 Q_ASSERT( rc == NO_ERROR );
570 if ( quit )
571 break;
572
573 // get current time again and calculate the interval
574 DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &now, sizeof(now) );
575
576 // go through all timers and see which were shot
577
578 QMutexLocker lock( &timerMutex );
579
580 ULONG minLeft = ULONG_MAX;
581
582 QIntDictIterator<TimerInfo> it( *timerDict );
583 for ( register TimerInfo *t; (t = it.current()); ++it ) {
584 // skip zero timers
585 if ( t->interval == 0 )
586 continue;
587 ULONG spent = now - t->last;
588 if ( spent >= t->interval ) {
589 // the timer iterval has expired, post the timer event
590 QTimerEvent *e = new QTimerEvent( t->ind + 1 );
591 QApplication::postEvent( t->obj, e );
592 // set the new last stamp
593 t->last += t->interval * (spent / t->interval);
594 }
595 // calculate minimal time to the next shot
596 minLeft = QMIN( minLeft, t->interval - (spent % t->interval) );
597 }
598
599 if ( timerDict->count() > 0 ) {
600 // post a spare WM_TIMER message to make sure timers run even when
601 // the message loop is not controlled by QEventLoop::processEvents()
602 // (e.g. when moving or resizing a window using the mouse)
603 WinPostMsg( qt_aux_win.hwnd(), WM_TIMER, 0, 0 );
604 // restart the OS/2 timer
605 interval = minLeft;
606 ULONG postCnt;
607 DosResetEventSem( hev, &postCnt );
608 // check for quit (cancelQuit() could post the semaphore just before
609 // we reset it above)
610 if ( quit )
611 break;
612 rc = DosAsyncTimer( minLeft, (HSEM) hev, &htimer );
613 Q_ASSERT( rc == NO_ERROR );
614 } else {
615 htimer = NULLHANDLE;
616 interval = 0;
617 }
618
619 } while (1);
620}
621
622void QTimerThread::signalQuit()
623{
624 quit = true;
625 DosPostEventSem( hev );
626 wait();
627}
628
629// Note: must be called from under timerMutex!
630void QTimerThread::ensureShot( int ival )
631{
632 Q_ASSERT( timerMutex.locked() );
633 Q_ASSERT( ival > 0 );
634 if ( interval == 0 || interval > (ULONG) ival ) {
635 // start another timer to make sure the new Qt timer is fired off in time
636 interval = (ULONG) ival;
637 APIRET rc = DosAsyncTimer( interval, (HSEM) hev, &htimer );
638 Q_ASSERT( rc == NO_ERROR );
639 }
640}
641
642#else // ifndef QT_PM_NO_DOSTIMERS
643
644#include "qptrvector.h"
645#include "qintdict.h"
646
647struct TimerInfo { // internal timer info
648 uint ind; // - Qt timer identifier - 1
649 ULONG id; // - PM timer identifier
650 bool zero; // - zero timing
651 QObject *obj; // - object to receive events
652};
653typedef QPtrVector<TimerInfo> TimerVec; // vector of TimerInfo structs
654typedef QIntDict<TimerInfo> TimerDict; // fast dict of timers
655
656static TimerVec *timerVec = 0; // timer vector
657static TimerDict *timerDict = 0; // timer dict
658
659typedef QFreeValueList<int> FreeQtTIDList; // list of free Qt timer IDs
660static FreeQtTIDList *freeQtTIDs = 0;
661typedef QFreeValueList<ULONG> FreePMTIDList; // list of free PM timer IDs
662static FreePMTIDList *freePMTIDs = 0;
663
664//
665// Timer activation (called from the event loop when WM_TIMER arrives)
666//
667static bool dispatchTimer( uint timerId, QMSG *msg )
668{
669 MRESULT res = NULL;
670 if ( !msg || !qApp || !qt_pmEventFilter(msg,res) )
671 {
672 if ( !timerVec ) // should never happen
673 return FALSE;
674 register TimerInfo *t = timerDict->find( timerId );
675 if ( !t ) // no such timer id
676 return FALSE;
677 QTimerEvent e( t->ind + 1 );
678 QApplication::sendEvent( t->obj, &e ); // send event
679 return TRUE; // timer event was processed
680 }
681 return TRUE;
682}
683
684#endif // ifndef QT_PM_NO_DOSTIMERS
685
686//
687// activate full-speed timers
688//
689static void activateZeroTimers()
690{
691#ifndef QT_PM_NO_DOSTIMERS
692 if ( !timerDict )
693 return;
694 QIntDictIterator<TimerInfo> it( *timerDict );
695 register TimerInfo *t = 0;
696 int n = numZeroTimers;
697 while ( n-- ) {
698 for ( ;; ) {
699 t = it();
700 Q_ASSERT( t );
701 if ( !t )
702 return;
703 if ( t->interval == 0 )
704 break;
705 }
706 QTimerEvent e( t->ind + 1 );
707 QApplication::sendEvent( t->obj, &e );
708 }
709#else
710 if ( !timerVec )
711 return;
712 uint i=0;
713 register TimerInfo *t = 0;
714 int n = numZeroTimers;
715 while ( n-- ) {
716 for ( ;; ) {
717 t = timerVec->at(i++);
718 if ( t && t->zero )
719 break;
720 else if ( i == timerVec->size() ) // should not happen
721 return;
722 }
723 QTimerEvent e( t->ind + 1 );
724 QApplication::sendEvent( t->obj, &e );
725 }
726#endif
727}
728
729
730//
731// Timer initialization and cleanup routines
732//
733
734static void initTimers() // initialize timers
735{
736#ifndef QT_PM_NO_DOSTIMERS
737 timerDict = new TimerDict( 29 );
738 Q_CHECK_PTR( timerDict );
739 timerDict->setAutoDelete( TRUE );
740 freeQtTIDs = new FreeQtTIDList( 0, 1023 ); // resonable max amount of timers
741 Q_CHECK_PTR( freeQtTIDs );
742 timerThread = new QTimerThread();
743 Q_CHECK_PTR( timerThread );
744 timerThread->start();
745#else // ifndef QT_PM_NO_DOSTIMERS
746 timerVec = new TimerVec( 128 );
747 Q_CHECK_PTR( timerVec );
748 timerVec->setAutoDelete( TRUE );
749 timerDict = new TimerDict( 29 );
750 Q_CHECK_PTR( timerDict );
751 freeQtTIDs = new FreeQtTIDList( 0, 1023 ); // resonable max amount of timers
752 Q_CHECK_PTR( freeQtTIDs );
753 freePMTIDs = new FreePMTIDList( 1, TID_USERMAX - 1 );
754 Q_CHECK_PTR( freePMTIDs );
755#endif // ifndef QT_PM_NO_DOSTIMERS
756}
757
758/// @todo (dmik) cleanupTimers() is only called by QEventLoop::cleanup() so it
759// won't be called if there is no QEventLoop instance created (for example,
760// when a Qt-based plugin DLL is used by a non-Qt application). Use atexit()?
761// Btw, the same relates to the QThread::cleanup() vs QApplication pair.
762// And to qt_aux_win (the DLL may be unloaded after the application has
763// destroyed the main event queue, so the static destructor will not be able
764// to properly destroy the window).
765
766static bool timersCleanedUp = false;
767
768static void cleanupTimers() // remove pending timers
769{
770 timersCleanedUp = true;
771
772#ifndef QT_PM_NO_DOSTIMERS
773 if ( !timerDict ) // no timers were used
774 return;
775 timerThread->signalQuit();
776 delete timerThread;
777 timerThread = NULL;
778 delete freeQtTIDs;
779 freeQtTIDs = NULL;
780 delete timerDict;
781 timerDict = NULL;
782#else // ifndef QT_PM_NO_DOSTIMERS
783 register TimerInfo *t;
784 if ( !timerVec ) // no timers were used
785 return;
786 for ( uint i=0; i<timerVec->size(); i++ ) { // kill all pending timers
787 t = timerVec->at( i );
788 if ( t && !t->zero )
789 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
790 }
791 delete freePMTIDs;
792 freePMTIDs = 0;
793 delete freeQtTIDs;
794 freeQtTIDs = 0;
795 delete timerDict;
796 timerDict = 0;
797 delete timerVec;
798 timerVec = 0;
799#endif // ifndef QT_PM_NO_DOSTIMERS
800
801 numZeroTimers = 0;
802}
803
804
805//
806// Main timer functions for starting and killing timers
807//
808
809
810int qStartTimer( int interval, QObject *obj )
811{
812 // ignore start timer requests after application termination has started
813 Q_ASSERT( !timersCleanedUp );
814 if ( timersCleanedUp )
815 return 0;
816
817 Q_ASSERT( obj );
818 if ( !obj || interval < 0 )
819 return 0;
820
821 // lazily create the auxiliary window to process WM_TIMER and
822 // WM_U_SEM_ZEROTIMER messages
823 if ( !qt_aux_win.hwnd() )
824 if ( !qt_aux_win.create() )
825 return 0;
826
827#ifndef QT_PM_NO_DOSTIMERS
828 if ( !timerDict ) // initialize timer data
829 initTimers();
830#else // ifndef QT_PM_NO_DOSTIMERS
831 if ( !timerVec ) // initialize timer data
832 initTimers();
833#endif // ifndef QT_PM_NO_DOSTIMERS
834
835 if ( freeQtTIDs->isEmpty() ) {
836#if defined(QT_CHECK_STATE)
837 qWarning( "qStartTimer: Maximum number of timers (%d) is reached.",
838 freeQtTIDs->max() - freeQtTIDs->min() + 1 );
839#endif
840 return 0;
841 }
842
843#ifdef QT_PM_NO_DOSTIMERS
844 if ( freePMTIDs->isEmpty() ) {
845#if defined(QT_CHECK_STATE)
846 qWarning( "qStartTimer: Maximum number of non-zero timers (%ld) is reached.",
847 freePMTIDs->max() - freePMTIDs->min() + 1 );
848#endif
849 return 0;
850 }
851#endif
852
853 int ind = freeQtTIDs->take(); // get free timer
854
855#ifndef QT_PM_NO_DOSTIMERS
856 register TimerInfo *t = new TimerInfo; // create timer entry
857 Q_CHECK_PTR( t );
858 t->ind = ind;
859 t->obj = obj;
860
861 t->interval = interval;
862 if ( t->interval == 0 ) { // add zero timer
863 numZeroTimers++;
864 // indicate there is a new zero timer
865 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
866 } else {
867 // set the initial last shot timestamp value to now
868 DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &t->last, sizeof(t->last) );
869 }
870
871 QMutexLocker lock( &timerMutex );
872 timerDict->insert( ind, t ); // store in timer dict
873 if ( t->interval != 0 )
874 timerThread->ensureShot( t->interval );
875#else // ifndef QT_PM_NO_DOSTIMERS
876 if ( (uint) ind >= timerVec->size() ) {
877 uint newSize = timerVec->size() * 4; // increase the size
878 if ( newSize <= (uint) ind )
879 newSize = (uint) ind + 1;
880 timerVec->resize( newSize );
881 }
882
883 register TimerInfo *t = new TimerInfo; // create timer entry
884 Q_CHECK_PTR( t );
885 t->ind = ind;
886 t->obj = obj;
887
888 t->zero = interval == 0;
889 if ( t->zero ) { // add zero timer
890 t->id = 0;
891 numZeroTimers++;
892 // indicate there is a new zero timer
893 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
894 } else {
895 ULONG freeId = freePMTIDs->take(); // get free timer ID
896 t->id = WinStartTimer( 0, qt_aux_win.hwnd(), freeId, (ULONG) interval );
897 if ( t->id == 0 ) {
898#if defined(QT_CHECK_STATE)
899 qSystemWarning( "qStartTimer: Failed to create a timer." );
900#endif
901 freePMTIDs->give( freeId ); // could not start timer
902 freeQtTIDs->give( ind );
903 delete t;
904 return 0;
905 }
906 }
907 timerVec->insert( ind, t ); // store in timer vector
908 if ( !t->zero )
909 timerDict->insert( t->id, t ); // store in dict
910#endif // ifndef QT_PM_NO_DOSTIMERS
911
912 return ind + 1; // return index in vector
913}
914
915bool qKillTimer( int ind )
916{
917#ifndef QT_PM_NO_DOSTIMERS
918 if ( !timerDict )
919 return FALSE;
920 register TimerInfo *t = timerDict->find( ind-1 );
921 if ( !t )
922 return FALSE;
923 if ( t->interval == 0 ) {
924 numZeroTimers--;
925 }
926 freeQtTIDs->give( t->ind );
927 QMutexLocker lock( &timerMutex );
928 timerDict->remove( ind-1 );
929 return TRUE;
930#else // ifndef QT_PM_NO_DOSTIMERS
931 if ( !timerVec || ind <= 0 || (uint)ind > timerVec->size() )
932 return FALSE;
933 register TimerInfo *t = timerVec->at(ind-1);
934 if ( !t )
935 return FALSE;
936 if ( t->zero ) {
937 numZeroTimers--;
938 } else {
939 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
940 freePMTIDs->give( t->id );
941 timerDict->remove( t->id );
942 }
943 freeQtTIDs->give( t->ind );
944 timerVec->remove( ind-1 );
945 return TRUE;
946#endif // ifndef QT_PM_NO_DOSTIMERS
947}
948
949bool qKillTimer( QObject *obj )
950{
951#ifndef QT_PM_NO_DOSTIMERS
952 if ( !timerDict )
953 return FALSE;
954 QIntDictIterator<TimerInfo> it ( *timerDict );
955 for ( register TimerInfo *t; (t = it.current()); ) {
956 if ( t->obj == obj ) { // object found
957 if ( t->interval == 0 ) {
958 numZeroTimers--;
959 }
960 freeQtTIDs->give( t->ind );
961 QMutexLocker lock( &timerMutex );
962 timerDict->remove( it.currentKey() );
963 } else {
964 ++it;
965 }
966 }
967 return TRUE;
968#else // ifndef QT_PM_NO_DOSTIMERS
969 if ( !timerVec )
970 return FALSE;
971 register TimerInfo *t;
972 for ( uint i=0; i<timerVec->size(); i++ ) {
973 t = timerVec->at( i );
974 if ( t && t->obj == obj ) { // object found
975 if ( t->zero ) {
976 numZeroTimers--;
977 } else {
978 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
979 freePMTIDs->give( t->id );
980 timerDict->remove( t->id );
981 }
982 freeQtTIDs->give( t->ind );
983 timerVec->remove( i );
984 }
985 }
986 return TRUE;
987#endif // ifndef QT_PM_NO_DOSTIMERS
988}
989
990/*****************************************************************************
991 Socket notifier type
992 *****************************************************************************/
993
994QSockNotType::QSockNotType()
995 : list( 0 )
996{
997 FD_ZERO( &select_fds );
998 FD_ZERO( &enabled_fds );
999 FD_ZERO( &pending_fds );
1000}
1001
1002QSockNotType::~QSockNotType()
1003{
1004 if ( list )
1005 delete list;
1006 list = 0;
1007}
1008
1009/*****************************************************************************
1010 socket select() thread
1011 *****************************************************************************/
1012
1013#if defined(QT_THREAD_SUPPORT)
1014
1015static class QSockSelectThread : public QThread
1016{
1017public:
1018 QSockSelectThread( QEventLoopPrivate *_d ) : d( _d ), exit( FALSE ) {};
1019 void run();
1020 void cancelSelectOrIdle( bool terminate = FALSE );
1021private:
1022 QEventLoopPrivate *d;
1023 bool exit;
1024} *ss_thread = 0;
1025
1026// recursive mutex to serialize access to socket notifier data
1027static QMutex ss_mutex( TRUE );
1028// flag to indicate the presence of sockets to do select() (we use QSemaphore
1029// instead of QWaitCondition because we need the "level-triggered" semantics,
1030// the "edge-triggered" semantics can produce deadlocks)
1031static QSemaphore ss_flag( 1 );
1032
1033// Note: must be called from under ss_mutex lock!
1034static inline void find_highest_fd( QEventLoopPrivate *d )
1035{
1036 d->sn_highest = -1;
1037 for ( int i = 0; i < 3; i ++ ) {
1038 QPtrList<QSockNot> *list = d->sn_vec[i].list;
1039 if ( list && !list->isEmpty() ) {
1040 QSockNot *sn = list->first();
1041 while ( sn && !FD_ISSET( sn->fd, sn->active ) )
1042 sn = list->next();
1043 if ( sn )
1044 d->sn_highest = QMAX( d->sn_highest, // list is fd-sorted
1045 sn->fd );
1046 }
1047 }
1048}
1049
1050void QSockSelectThread::run()
1051{
1052 while ( !exit ) {
1053 ss_mutex.lock();
1054 if ( d->sn_highest >= 0 ) { // has socket notifier(s)
1055 // read
1056 if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() )
1057 d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds;
1058 else
1059 FD_ZERO( &d->sn_vec[0].select_fds );
1060 // write
1061 if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() )
1062 d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds;
1063 else
1064 FD_ZERO( &d->sn_vec[1].select_fds );
1065 // except
1066 if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() )
1067 d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds;
1068 else
1069 FD_ZERO( &d->sn_vec[2].select_fds );
1070 // do select
1071 int nfds = d->sn_highest + 1;
1072 d->sn_cancel = d->sn_highest;
1073 ss_mutex.unlock();
1074 int nsel = ::select( nfds,
1075 &d->sn_vec[0].select_fds,
1076 &d->sn_vec[1].select_fds,
1077 &d->sn_vec[2].select_fds,
1078 NULL );
1079 if ( nsel > 0 ) {
1080 ss_mutex.lock();
1081 // if select says data is ready on any socket, then set
1082 // the socket notifier to pending
1083 bool activate = FALSE;
1084 for ( int i = 0; i < 3; i++ ) {
1085 if ( ! d->sn_vec[i].list )
1086 continue;
1087 QPtrList<QSockNot> *list = d->sn_vec[i].list;
1088 QSockNot *sn = list->first();
1089 while ( sn ) {
1090 if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) {
1091 // see comments in QEventLoop::setSocketNotifierPending()
1092 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1093 // queue the socket activation
1094 d->sn_pending_list.insert(
1095 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1096 sn );
1097 FD_SET( sn->fd, sn->queue );
1098 // remove from enabled sockets to prevent deadlocks
1099 // on blocking sockets (and to reduce CPU load caused
1100 // by frequent select() calls) when the event queue
1101 // is not processed fast enough to handle activation
1102 FD_CLR( sn->fd, sn->active );
1103 if ( sn->fd == d->sn_highest )
1104 find_highest_fd( d );
1105 activate = TRUE;
1106 }
1107 }
1108 sn = list->next();
1109 }
1110 }
1111 ss_mutex.unlock();
1112 // post a message to activate socket notifiers
1113 if ( activate )
1114 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1115 }
1116 } else {
1117 d->sn_cancel = -1;
1118 // no sockets to select(), go to the idle state
1119 ss_mutex.unlock();
1120 // wait for the ability to capture the flag
1121 ss_flag ++;
1122 // release the flag before termination
1123 if ( exit )
1124 ss_flag --;
1125 }
1126 }
1127}
1128
1129void QSockSelectThread::cancelSelectOrIdle( bool terminate /* = FALSE */ )
1130{
1131 ss_mutex.lock();
1132 exit = terminate;
1133 if ( d->sn_cancel >= 0 ) {
1134 // terminate select() execution
1135 ::so_cancel( d->sn_cancel );
1136 } else {
1137 // terminate the idle state by releasing the flag
1138 if ( !ss_flag.available() )
1139 ss_flag --;
1140 }
1141 ss_mutex.unlock();
1142 // wait for this thread to end if the termination is requested
1143 if ( exit )
1144 wait();
1145}
1146
1147static void ss_cleanup()
1148{
1149 ss_thread->cancelSelectOrIdle( TRUE );
1150 delete ss_thread;
1151 ss_thread = 0;
1152}
1153
1154static void ss_init( QEventLoopPrivate *d )
1155{
1156 // capture the flag initially
1157 ss_flag ++;
1158 ss_thread = new QSockSelectThread( d );
1159 ss_thread->start();
1160}
1161
1162#endif // QT_THREAD_SUPPORT
1163
1164/*****************************************************************************
1165 QEventLoop Implementation for OS/2 PM
1166 *****************************************************************************/
1167
1168void QEventLoop::init()
1169{
1170 d->sn_highest = -1;
1171 d->sn_cancel = -1;
1172
1173 qt_ensure_pm();
1174 d->hab = WinInitialize( 0 );
1175 d->hmq = WinCreateMsgQueue( d->hab, 0 );
1176 qt_gui_hab = d->hab;
1177 qt_gui_queue = d->hmq;
1178
1179 qt_aux_win.setEventLoop( this );
1180
1181#if defined(QT_THREAD_SUPPORT)
1182 // there may only be one global QEventLoop instance that manages sockets
1183 Q_ASSERT( ss_thread == 0 );
1184#else
1185#if defined(QT_CHECK_STATE)
1186 qWarning( "QEventLoop::init: socket notifiers are not supported "
1187 "for a single-threaded version of Qt." );
1188#endif
1189#endif // QT_THREAD_SUPPORT
1190}
1191
1192void QEventLoop::cleanup()
1193{
1194 // timers should have been already uninitialized in appClosingDown()
1195#ifndef QT_PM_NO_DOSTIMERS
1196 Q_ASSERT( timerDict == 0 );
1197#else
1198 Q_ASSERT( timerVec == 0 );
1199#endif
1200
1201 // ss_thread should have been already stopped in appClosingDown()
1202 Q_ASSERT( ss_thread == 0 );
1203
1204 qt_aux_win.setEventLoop( 0 );
1205
1206 // destroy all windows created by QPMObjectWindow instances
1207 for ( HWND *w = qt_objWindows.first(); w; w = qt_objWindows.next() ) {
1208 Q_ASSERT( *w );
1209 WinDestroyWindow( *w );
1210 // tell QPMObjectWindow the window has been destroyed
1211 *w = 0;
1212 }
1213
1214 WinDestroyMsgQueue( d->hmq );
1215 WinTerminate( d->hab );
1216 qt_gui_queue = 0;
1217 qt_gui_hab = 0;
1218}
1219
1220void QEventLoop::appStartingUp()
1221{
1222}
1223
1224void QEventLoop::appClosingDown()
1225{
1226 // ensure the socket thread is terminated before QApplication calls
1227 // QThread::cleanup()
1228 if ( ss_thread )
1229 ss_cleanup();
1230 // the same applies to the timer thread
1231 cleanupTimers();
1232}
1233
1234void QEventLoop::registerSocketNotifier( QSocketNotifier *notifier )
1235{
1236#if defined(QT_THREAD_SUPPORT)
1237 // there may only be one global QEventLoop instance that manages sockets
1238 Q_ASSERT( !qApp || qApp->eventloop == this );
1239 if ( qApp && qApp->eventloop != this )
1240 return;
1241
1242 // lazily create the auxiliary window to process WM_U_SEM_SELECT messages
1243 if ( !qt_aux_win.hwnd() )
1244 if ( !qt_aux_win.create() )
1245 return;
1246
1247 // lazily start the socket select thread
1248 if ( !ss_thread )
1249 ss_init( d );
1250
1251 int sockfd = -1;
1252 int type = -1;
1253 if ( notifier ) {
1254 sockfd = notifier->socket();
1255 type = notifier->type();
1256 }
1257 if ( sockfd < 0 || sockfd >= FD_SETSIZE || type < 0 || type > 2 ) {
1258#if defined(QT_CHECK_RANGE)
1259 qWarning( "QSocketNotifier: Internal error" );
1260#endif
1261 return;
1262 }
1263
1264 QMutexLocker locker( &ss_mutex );
1265 ss_thread->cancelSelectOrIdle();
1266
1267 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1268 QSockNot *sn;
1269
1270 if ( ! list ) {
1271 // create new list, the QSockNotType destructor will delete it for us
1272 list = new QPtrList<QSockNot>;
1273 Q_CHECK_PTR( list );
1274 list->setAutoDelete( TRUE );
1275 d->sn_vec[type].list = list;
1276 }
1277
1278 sn = new QSockNot;
1279 Q_CHECK_PTR( sn );
1280 sn->obj = notifier;
1281 sn->fd = sockfd;
1282 sn->queue = &d->sn_vec[type].pending_fds;
1283 sn->active = &d->sn_vec[type].enabled_fds;
1284
1285 if ( list->isEmpty() ) {
1286 list->insert( 0, sn );
1287 } else { // sort list by fd, decreasing
1288 QSockNot *p = list->first();
1289 while ( p && p->fd > sockfd )
1290 p = list->next();
1291#if defined(QT_CHECK_STATE)
1292 if ( p && p->fd == sockfd ) {
1293 static const char *t[] = { "read", "write", "exception" };
1294 qWarning( "QSocketNotifier: Multiple socket notifiers for "
1295 "same socket %d and type %s", sockfd, t[type] );
1296 }
1297#endif
1298 if ( p )
1299 list->insert( list->at(), sn );
1300 else
1301 list->append( sn );
1302 }
1303
1304 // enable the socket only if it's not already pending
1305 if ( !FD_ISSET( sockfd, sn->queue ) ) {
1306 FD_SET( sockfd, sn->active );
1307 d->sn_highest = QMAX( d->sn_highest, sockfd );
1308 }
1309#endif // QT_THREAD_SUPPORT
1310}
1311
1312void QEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier )
1313{
1314#if defined(QT_THREAD_SUPPORT)
1315 int sockfd = -1;
1316 int type = -1;
1317 if ( notifier ) {
1318 sockfd = notifier->socket();
1319 type = notifier->type();
1320 }
1321 if ( sockfd < 0 || type < 0 || type > 2 ) {
1322#if defined(QT_CHECK_RANGE)
1323 qWarning( "QSocketNotifier: Internal error" );
1324#endif
1325 return;
1326 }
1327
1328 if ( !ss_thread )
1329 return; // definitely not found
1330
1331 QMutexLocker locker( &ss_mutex );
1332 ss_thread->cancelSelectOrIdle();
1333
1334 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1335 QSockNot *sn;
1336 if ( ! list )
1337 return;
1338 sn = list->first();
1339 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1340 sn = list->next();
1341 if ( !sn ) // not found
1342 return;
1343
1344 // touch fd bitmaps only if there are no other notifiers for the same socket
1345 // (QPtrList curiously lacks getNext()/getPrev(), so play tambourine)
1346 QSockNot *next = list->next();
1347 if ( next ) list->prev();
1348 else list->last();
1349 QSockNot *prev = list->prev();
1350 if ( prev ) list->next();
1351 else list->first();
1352 bool unique = (!next || next->fd != sockfd) && (!prev || prev->fd != sockfd);
1353 if ( unique) {
1354 FD_CLR( sockfd, sn->active ); // clear fd bit
1355 FD_CLR( sockfd, sn->queue );
1356 }
1357 d->sn_pending_list.removeRef( sn ); // remove from activation list
1358 list->remove(); // remove notifier found above
1359
1360 if ( unique) {
1361 if ( d->sn_highest == sockfd )
1362 find_highest_fd( d );
1363 }
1364#endif // QT_THREAD_SUPPORT
1365}
1366
1367void QEventLoop::setSocketNotifierPending( QSocketNotifier *notifier )
1368{
1369#if defined(QT_THREAD_SUPPORT)
1370 int sockfd = -1;
1371 int type = -1;
1372 if ( notifier ) {
1373 sockfd = notifier->socket();
1374 type = notifier->type();
1375 }
1376 if ( sockfd < 0 || type < 0 || type > 2 ) {
1377#if defined(QT_CHECK_RANGE)
1378 qWarning( "QSocketNotifier: Internal error" );
1379#endif
1380 return;
1381 }
1382
1383 if ( !ss_thread )
1384 return; // definitely not found
1385
1386 QMutexLocker locker( &ss_mutex );
1387 ss_thread->cancelSelectOrIdle();
1388
1389 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1390 QSockNot *sn;
1391 if ( ! list )
1392 return;
1393 sn = list->first();
1394 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1395 sn = list->next();
1396 if ( ! sn ) { // not found
1397 return;
1398 }
1399
1400 // We choose a random activation order to be more fair under high load.
1401 // If a constant order is used and a peer early in the list can
1402 // saturate the IO, it might grab our attention completely.
1403 // Also, if we're using a straight list, the callback routines may
1404 // delete other entries from the list before those other entries are
1405 // processed.
1406 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1407 d->sn_pending_list.insert(
1408 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1409 sn
1410 );
1411 FD_SET( sockfd, sn->queue );
1412 // remove from enabled sockets, see QSockSelectThread::run()
1413 FD_CLR( sockfd, sn->active );
1414 if ( d->sn_highest == sockfd )
1415 find_highest_fd( d );
1416 }
1417#endif // QT_THREAD_SUPPORT
1418}
1419
1420bool QEventLoop::hasPendingEvents() const
1421{
1422 QMSG msg;
1423 return qGlobalPostedEventsCount() || WinPeekMsg( 0, &msg, NULL, 0, 0, PM_NOREMOVE );
1424}
1425
1426bool QEventLoop::processEvents( ProcessEventsFlags flags )
1427{
1428 QMSG msg;
1429
1430#if defined(QT_THREAD_SUPPORT)
1431 QMutexLocker locker( QApplication::qt_mutex );
1432#endif
1433
1434 QApplication::sendPostedEvents();
1435
1436#ifndef QT_PM_NO_DOSTIMERS
1437 // we've just processed all pending events above, including QEvent::Timer
1438 // events, so remove spare WM_TIMER messages posted to qt_aux_win for the
1439 // cases when the message loop is run bypassing this method
1440 if ( qt_aux_win.hwnd() )
1441 WinPeekMsg( 0, &msg, qt_aux_win.hwnd(), WM_TIMER, WM_TIMER, PM_REMOVE );
1442#endif // ifndef QT_PM_NO_DOSTIMERS
1443
1444 if ( flags & ExcludeUserInput ) {
1445 while ( WinPeekMsg( 0, &msg, 0, 0, 0, PM_NOREMOVE ) ) {
1446 if ( msg.msg == WM_CHAR ||
1447 (msg.msg >= WM_MOUSEFIRST &&
1448 msg.msg <= WM_MOUSELAST) ||
1449 (msg.msg >= WM_EXTMOUSEFIRST &&
1450 msg.msg <= WM_EXTMOUSELAST) ||
1451 msg.msg == WM_HSCROLL ||
1452 msg.msg == WM_VSCROLL
1453 ) {
1454 WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE );
1455 continue;
1456 }
1457 break;
1458 }
1459 }
1460
1461 bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
1462
1463 if ( canWait ) {
1464 // can wait if necessary
1465 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1466 emit aboutToBlock();
1467#ifdef QT_THREAD_SUPPORT
1468 locker.mutex()->unlock();
1469#endif
1470 WinGetMsg( 0, &msg, 0, 0, 0 );
1471#ifdef QT_THREAD_SUPPORT
1472 locker.mutex()->lock();
1473#endif
1474 emit awake();
1475 emit qApp->guiThreadAwake();
1476 }
1477 } else {
1478 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1479 // no pending events
1480 return FALSE;
1481 }
1482 }
1483
1484 if ( msg.msg == WM_QUIT ) {
1485 // process the quit request
1486#if !defined (QT_NO_SESSIONMANAGER)
1487 if ( qt_app_canQuit() ) {
1488#endif
1489 exit( 0 );
1490 return TRUE;
1491#if !defined (QT_NO_SESSIONMANAGER)
1492 } else {
1493 WinCancelShutdown( d->hmq, FALSE );
1494 return TRUE;
1495 }
1496#endif
1497 }
1498
1499 bool handled = FALSE;
1500 MRESULT res = 0;
1501
1502 if ( msg.msg == WM_U_SEM_SELECT && msg.hwnd == qt_aux_win.hwnd() ) {
1503 // socket select event received: prevent it from being handled by
1504 // qt_aux_win (to obey the ExcludeSocketNotifiers flag)
1505 handled = TRUE;
1506 // ignore socket notifier activation if processd by the Qt event hook
1507 if ( qt_pmEventFilter( &msg, res ) )
1508 flags |= ExcludeSocketNotifiers;
1509 }
1510
1511 if ( !handled && msg.msg && (!msg.hwnd || !QWidget::find( msg.hwnd )) ) {
1512 handled = qt_pmEventFilter( &msg, res );
1513 }
1514
1515 if ( !handled ) {
1516 // send to QtWndProc or to a native window procedure
1517 WinDispatchMsg( 0, &msg );
1518 }
1519
1520 if ( !(flags & ExcludeSocketNotifiers) )
1521 activateSocketNotifiers();
1522
1523 // any pending configs?
1524 if ( configRequests )
1525 qPMProcessConfigRequests();
1526 QApplication::sendPostedEvents();
1527
1528 return TRUE;
1529}
1530
1531void QEventLoop::wakeUp()
1532{
1533 PTIB ptib;
1534 DosGetInfoBlocks( &ptib, NULL );
1535 MQINFO mqinfo;
1536 WinQueryQueueInfo( qt_gui_queue, &mqinfo, sizeof(MQINFO) );
1537 if ( ptib->tib_ptib2->tib2_ultid != mqinfo.tid )
1538 WinPostQueueMsg( qt_gui_queue, WM_NULL, 0, 0 );
1539}
1540
1541int QEventLoop::timeToWait() const
1542{
1543 return -1;
1544}
1545
1546int QEventLoop::activateTimers()
1547{
1548 return 0;
1549}
1550
1551int QEventLoop::activateSocketNotifiers()
1552{
1553#if defined(QT_THREAD_SUPPORT)
1554 if ( d->sn_pending_list.isEmpty() )
1555 return 0;
1556
1557 if ( !ss_thread )
1558 return 0;
1559
1560 // postpone activation if ss_thread is working with the list
1561 if ( !ss_mutex.tryLock() ) {
1562 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1563 return 0;
1564 }
1565
1566 ss_thread->cancelSelectOrIdle();
1567
1568 // activate entries
1569 int n_act = 0;
1570 QEvent event( QEvent::SockAct );
1571 QPtrListIterator<QSockNot> it( d->sn_pending_list );
1572 QSockNot *sn;
1573 while ( (sn = it.current()) ) {
1574 ++it;
1575 d->sn_pending_list.removeRef( sn );
1576 if ( FD_ISSET(sn->fd, sn->queue) ) {
1577 FD_CLR( sn->fd, sn->queue );
1578 // reenable the socket to let it participate in the next select()
1579 FD_SET( sn->fd, sn->active );
1580 d->sn_highest = QMAX( d->sn_highest, sn->fd );
1581 QApplication::sendEvent( sn->obj, &event );
1582 n_act++;
1583 }
1584 }
1585
1586 ss_mutex.unlock();
1587
1588 return n_act;
1589#else
1590 return 0;
1591#endif // QT_THREAD_SUPPORT
1592}
1593
Note: See TracBrowser for help on using the repository browser.