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

Last change on this file since 116 was 116, checked in by dmik, 19 years ago

Kernel/Network: Changed the socket notifier activation logic so that sockets pending for activation are removed from select() until activation is processed by the main thread. This prevents deadlocks on blocking sockets (as a result of late socket notifications due to a race condition) and avoids repeated select() calls returning the same set of sockets when the event queue is not processed fast enough to handle activation. Note: this improvement is experimental. In case if it is rolled back for some reason, QServerSocket needs to be patched (by setting a non-blocking mode for sockets) to avoid GUI thread deadlocks.

  • Property svn:keywords set to Id
File size: 36.5 KB
Line 
1/****************************************************************************
2** $Id: qeventloop_pm.cpp 116 2006-08-11 13:01:59Z 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
43#include <sys/socket.h>
44
45#if defined(QT_THREAD_SUPPORT)
46# include "qmutex.h"
47# include "qthread.h"
48# include "qsemaphore.h"
49#endif // QT_THREAD_SUPPORT
50
51// entry point: PMWIN.305
52extern "C" HMQ APIENTRY WinQueueFromID( HAB hab, PID pid, PID tid );
53
54extern uint qGlobalPostedEventsCount();
55extern bool qt_pmEventFilter( QMSG* msg, MRESULT &result );
56
57#if !defined (QT_NO_SESSIONMANAGER)
58extern bool qt_app_canQuit(); // defined in qapplication_pm.cpp
59#endif
60
61static HAB qt_gui_hab = 0;
62static HMQ qt_gui_queue = 0;
63
64static void initTimers();
65static void cleanupTimers();
66static bool dispatchTimer( uint, QMSG * );
67static bool activateTimer( uint );
68static void activateZeroTimers();
69
70static int numZeroTimers = 0; // number of full-speed timers
71
72// a flag to disable the warning when the console process dynamically
73// switches istelf to PM. Currently used by the UIC tool.
74bool qt_suppress_morph_warning = false;
75
76// Check that the current process is in PM mode. This is called by QEventLoop
77// and QApplication initialization routines and by initTimers() to ensure
78// that we are in PM mode and therefore it is possible to create the message
79// queue. "Morphing" to PM leaves the console attahed which can be used for
80// debugging.
81void qt_ensure_pm()
82{
83 PPIB ppib;
84 DosGetInfoBlocks( NULL, &ppib );
85 if( ppib->pib_ultype != 3 ) {
86#if defined(QT_CHECK_STATE)
87 if( !qt_suppress_morph_warning )
88 qWarning( "Qt: the program has not been linked as the Presentation "
89 "Manager application\n"
90 "but it uses GUI capabilities. Switching to PM mode "
91 "dynamically." );
92#endif
93 ppib->pib_ultype = 3;
94 }
95}
96
97enum
98{
99 // socket select notification (highest priority)
100 WM_U_SEM_SELECT = WM_SEM1,
101 // zero timer notification (lowest priority)
102 WM_U_SEM_ZEROTIMER = WM_SEM4,
103};
104
105/*****************************************************************************
106 Auxiliary object window class for dedicated message processing.
107 Declared in qwindowdefs_pm.h
108 *****************************************************************************/
109
110/*!
111 \class QPMObjectWindow qwindowdefs.h
112
113 The QPMObjectWindow class is an auxiliary class for dedicated message
114 processing. Its functionality is based on PM object windows. Once an instance
115 of this class is created, PM window messages can be sent or posted to it using
116 send() or post() methods. Subclasses should override the message() method to
117 process sent or posted messages. The hwnd() method is used whenever a PM
118 window handle of this object window is necessary to be passed as a HWND
119 argument to other calls and/or messages.
120
121 Instances of this class must be created on the main GUI thread only or on a
122 thread that has created a PM message queue and runs the PM message loop
123 \b itself. If you create an instance on a thread other than main, make sure
124 you destroy it before destroying the thread's message queue.
125
126 \note WM_CREATE and WM_DESTROY messages are processed internally and not
127 delivered do the message() method. Instead, you can use the constructor and
128 the destructor of the subclasses, respectively.
129
130 \note This class is non-portable!
131*/
132
133// returns HMQ of the current thread or NULL if no event queue has been created
134static HMQ qt_WinQueryQueue( HAB hab )
135{
136 PTIB ptib;
137 PPIB ppib;
138 DosGetInfoBlocks( &ptib, &ppib );
139 return WinQueueFromID( hab, ppib->pib_ulpid, ptib->tib_ptib2->tib2_ultid );
140}
141
142static QPtrList<HWND> qt_objWindows;
143
144/*!
145 Constructs a new object window for the current thread.
146 If \a deferred is FALSE, this method calls create() to create a PM object
147 window. Otherwise, you must call create() yourself before this object
148 window is able to process messages.
149*/
150QPMObjectWindow::QPMObjectWindow( bool deferred /* = FALSE */ ) :
151 w( NULLHANDLE )
152{
153 if ( !deferred )
154 create();
155}
156
157/*!
158 Destroys this object window.
159 This method calls destroy() to free the PM object window.
160*/
161QPMObjectWindow::~QPMObjectWindow()
162{
163 destroy();
164}
165
166/*!
167 Creates a PM object window.
168 Returns \c TRUE on success or \c FALSE otherwise.
169 The method does nothing but returns \c FALSE if the window has been
170 already created. The handle of the successfully created object window can
171 be obtained using the hwnd() method.
172
173 \note Must be called on the same thread that cosnstructed this instance.
174*/
175bool QPMObjectWindow::create()
176{
177 if ( w != NULLHANDLE )
178 return FALSE;
179
180 static const char *ClassName = "QPMObjectWindow";
181 static bool classRegistered = FALSE;
182
183 if ( !classRegistered ) {
184 WinRegisterClass( 0, ClassName, windowProc, 0, QWL_USER + sizeof(PVOID) );
185 classRegistered = TRUE;
186 }
187
188 w = WinCreateWindow( HWND_OBJECT, ClassName,
189 NULL, 0, 0, 0, 0, 0, NULL,
190 HWND_BOTTOM, 0, this, NULL );
191#ifndef QT_NO_DEBUG
192 if ( w == NULLHANDLE )
193 qSystemWarning( "QPMObjectWindow: Failed to create object window" );
194#endif
195
196 if ( w != NULLHANDLE && qt_gui_queue &&
197 qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
198 // store the handle in the list for clean destruction
199 qt_objWindows.append( &w );
200 }
201
202 return w != NULLHANDLE;
203}
204
205/*!
206 Destroys the PM object window.
207 Returns \c TRUE on success or \c FALSE otherwise.
208 The method does nothing but returns \c FALSE if the window has been
209 already destroyed (or never created).
210
211 \note Must be called on the same thread that cosnstructed this instance.
212*/
213bool QPMObjectWindow::destroy()
214{
215 if ( w == NULLHANDLE )
216 return FALSE;
217
218 if ( qt_gui_queue && qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
219 // remove the handle from the 'clean destruction' list
220 qt_objWindows.removeRef( &w );
221 }
222
223 HWND h = w;
224 w = 0; // tell windowProc() we're unsafe
225 WinDestroyWindow( h );
226
227 return TRUE;
228}
229
230//static
231MRESULT EXPENTRY QPMObjectWindow::windowProc( HWND hwnd, ULONG msg,
232 MPARAM mp1, MPARAM mp2 )
233{
234 if ( msg == WM_CREATE ) {
235 QPMObjectWindow *that = static_cast< QPMObjectWindow *>( mp1 );
236 if ( !that )
237 return (MRESULT) TRUE;
238 WinSetWindowPtr( hwnd, QWL_USER, that );
239 return (MRESULT) FALSE;
240 }
241
242 QPMObjectWindow *that =
243 static_cast< QPMObjectWindow * >( WinQueryWindowPtr( hwnd, QWL_USER ) );
244 Q_ASSERT( that );
245
246 // Note: before WinCreateWindow() returns to the constructor or after the
247 // destructor has been called, w is 0. We use this to determine that the
248 // object is in the unsafe state (VTBL is not yet initialized or has been
249 // already uninitialized), so message() points to never-never land.
250 if ( !that || !that->w )
251 return (MRESULT) FALSE;
252
253 return that->message( msg, mp1, mp2 );
254}
255
256/*!
257 \fn QPMObjectWindow::send( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
258
259 Synchronously sends a message \a msg with the given parameters \a mp1 and
260 \a mp2 to this window handle and returns a reply from the message() function.
261
262 \note Must be called on the same thread that cosnstructed this instance.
263*/
264
265/*!
266 \fn QPMObjectWindow::post( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
267
268 Asynchronously posts a message \a msg with the given parameters \a mp1 and
269 \a mp2 to this window handle. Returns \c TRUE on success and \c FALSE
270 otherwise.
271
272 \note Can be called on any thread.
273*/
274
275// Auxiliary object window to process WM_U_SEM_SELECT and WM_TIMER messages.
276// We need a dedicated window along with processing these messages directly in
277// QEventLoop::processEvents() to make sure they are processed even if the
278// current message loop is not run by Qt. This happens when a native modal
279// dialog is shown or when a top-level Qt widget is being moved or resized
280// using the mouse, or when a Qt-based DLL plugin is used by a non-Qt
281// application.
282
283class QPMAuxEventQueueWin : public QPMObjectWindow
284{
285public:
286 QPMAuxEventQueueWin() : QPMObjectWindow( TRUE ), eventLoop( NULL ) {}
287 void setEventLoop( QEventLoop *el ) { eventLoop = el; }
288 MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
289private:
290 QEventLoop *eventLoop;
291};
292
293MRESULT QPMAuxEventQueueWin::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
294{
295 switch ( msg ) {
296 case WM_U_SEM_SELECT: {
297 if ( eventLoop )
298 eventLoop->activateSocketNotifiers();
299 break;
300 }
301 case WM_U_SEM_ZEROTIMER: {
302 if ( numZeroTimers ) {
303 activateZeroTimers();
304 // repost the message if there are still zero timers left
305 if ( numZeroTimers )
306 WinPostMsg( hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
307 }
308 break;
309 }
310 case WM_TIMER: {
311 USHORT id = SHORT1FROMMP( mp1 );
312 QMSG qmsg = { hwnd(), msg, mp1, mp2 };
313 dispatchTimer( (uint) id, &qmsg );
314 break;
315 }
316 }
317
318 return FALSE;
319}
320
321static QPMAuxEventQueueWin qt_aux_win;
322
323
324/*****************************************************************************
325 Safe configuration (move,resize,setGeometry) mechanism to avoid
326 recursion when processing messages.
327 *****************************************************************************/
328
329#include "qptrqueue.h"
330
331struct QPMConfigRequest {
332 WId id; // widget to be configured
333 int req; // 0=move, 1=resize, 2=setGeo
334 int x, y, w, h; // request parameters
335};
336
337static QPtrQueue<QPMConfigRequest> *configRequests = 0;
338
339void qPMRequestConfig( WId id, int req, int x, int y, int w, int h )
340{
341 if ( !configRequests ) // create queue
342 configRequests = new QPtrQueue<QPMConfigRequest>;
343 QPMConfigRequest *r = new QPMConfigRequest;
344 r->id = id; // create new request
345 r->req = req;
346 r->x = x;
347 r->y = y;
348 r->w = w;
349 r->h = h;
350 configRequests->enqueue( r ); // store request in queue
351}
352
353Q_EXPORT void qPMProcessConfigRequests() // perform requests in queue
354{
355 if ( !configRequests )
356 return;
357 QPMConfigRequest *r;
358 for ( ;; ) {
359 if ( configRequests->isEmpty() )
360 break;
361 r = configRequests->dequeue();
362 QWidget *w = QWidget::find( r->id );
363 if ( w ) { // widget exists
364 if ( w->testWState(Qt::WState_ConfigPending) )
365 return; // biting our tail
366 if ( r->req == 0 )
367 w->move( r->x, r->y );
368 else if ( r->req == 1 )
369 w->resize( r->w, r->h );
370 else
371 w->setGeometry( r->x, r->y, r->w, r->h );
372 }
373 delete r;
374 }
375 delete configRequests;
376 configRequests = 0;
377}
378
379/*****************************************************************************
380 Timer handling; Our routines depend on OS/2 PM timer functions, but we
381 need some extra handling to activate objects at timeout.
382
383 Implementation note: There are two types of timer identifiers. PM
384 timer ids (internal use) are stored in TimerInfo. Qt timer ids are
385 indexes (+1) into the timerVec vector.
386
387 NOTE: These functions are for internal use. QObject::startTimer() and
388 QObject::killTimer() are for public use.
389 The QTimer class provides a high-level interface which translates
390 timer events into signals.
391
392 qStartTimer( interval, obj )
393 Starts a timer which will run until it is killed with qKillTimer()
394 Arguments:
395 int interval timer interval in milliseconds
396 QObject *obj where to send the timer event
397 Returns:
398 int timer identifier, or zero if not successful
399
400 qKillTimer( timerId )
401 Stops a timer specified by a timer identifier.
402 Arguments:
403 int timerId timer identifier
404 Returns:
405 bool TRUE if successful
406
407 qKillTimer( obj )
408 Stops all timers that are sent to the specified object.
409 Arguments:
410 QObject *obj object receiving timer events
411 Returns:
412 bool TRUE if successful
413 *****************************************************************************/
414
415//
416// Internal data structure for timers
417//
418
419#include "qptrvector.h"
420#include "qintdict.h"
421
422struct TimerInfo { // internal timer info
423 uint ind; // - Qt timer identifier - 1
424 ULONG id; // - PM timer identifier
425 bool zero; // - zero timing
426 QObject *obj; // - object to receive events
427};
428typedef QPtrVector<TimerInfo> TimerVec; // vector of TimerInfo structs
429typedef QIntDict<TimerInfo> TimerDict; // fast dict of timers
430
431static TimerVec *timerVec = 0; // timer vector
432static TimerDict *timerDict = 0; // timer dict
433
434
435// Activate a timer, used by both event-loop based and simple timers.
436
437static bool dispatchTimer( uint timerId, QMSG *msg )
438{
439 MRESULT res = NULL;
440 if ( !msg || !qApp || !qt_pmEventFilter(msg,res) )
441 return activateTimer( timerId );
442 return TRUE;
443}
444
445
446//
447// Timer activation (called from the event loop when WM_TIMER arrives)
448//
449
450static bool activateTimer( uint id ) // activate timer
451{
452 if ( !timerVec ) // should never happen
453 return FALSE;
454 register TimerInfo *t = timerDict->find( id );
455 if ( !t ) // no such timer id
456 return FALSE;
457 QTimerEvent e( t->ind + 1 );
458 QApplication::sendEvent( t->obj, &e ); // send event
459 return TRUE; // timer event was processed
460}
461
462static void activateZeroTimers() // activate full-speed timers
463{
464 if ( !timerVec )
465 return;
466 uint i=0;
467 register TimerInfo *t = 0;
468 int n = numZeroTimers;
469 while ( n-- ) {
470 for ( ;; ) {
471 t = timerVec->at(i++);
472 if ( t && t->zero )
473 break;
474 else if ( i == timerVec->size() ) // should not happen
475 return;
476 }
477 QTimerEvent e( t->ind + 1 );
478 QApplication::sendEvent( t->obj, &e );
479 }
480}
481
482
483//
484// Timer initialization and cleanup routines
485//
486
487static void initTimers() // initialize timers
488{
489 timerVec = new TimerVec( 128 );
490 Q_CHECK_PTR( timerVec );
491 timerVec->setAutoDelete( TRUE );
492 timerDict = new TimerDict( 29 );
493 Q_CHECK_PTR( timerDict );
494}
495
496/// @todo (dmik) cleanupTimers() is only called by QEventLoop::cleanup() so it
497// won't be called if there is no QEventLoop instance created (for example,
498// when a Qt-based plugin DLL is used by a non-Qt application). Use atexit()?
499// Btw, the same relates to the QThread::cleanup() vs QApplication pair.
500// And to qt_aux_win (the DLL may be unloaded after the application has
501// destroyed the main event queue, so the static destructor will not be able
502// to properly destroy the window).
503
504static void cleanupTimers() // remove pending timers
505{
506 register TimerInfo *t;
507 if ( !timerVec ) // no timers were used
508 return;
509 for ( uint i=0; i<timerVec->size(); i++ ) { // kill all pending timers
510 t = timerVec->at( i );
511 if ( t && !t->zero )
512 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
513 }
514 delete timerDict;
515 timerDict = 0;
516 delete timerVec;
517 timerVec = 0;
518}
519
520
521//
522// Main timer functions for starting and killing timers
523//
524
525
526int qStartTimer( int interval, QObject *obj )
527{
528 Q_ASSERT( obj );
529 if ( !obj )
530 return 0;
531
532 // lazily create the auxiliary window to process WM_TIMER messages
533 if ( !qt_aux_win.hwnd() )
534 if ( !qt_aux_win.create() )
535 return 0;
536
537 register TimerInfo *t;
538 if ( !timerVec ) // initialize timer data
539 initTimers();
540
541 int ind = timerVec->findRef( 0 ); // get free timer
542 if ( ind == -1 ) {
543 ind = timerVec->size(); // increase the size
544 if ( ind >= TID_USERMAX ) {
545#if defined(QT_CHECK_STATE)
546 qWarning( "qStartTimer: Maximum number of timers (%d) is reached.",
547 TID_USERMAX );
548 return 0;
549#endif
550 }
551 timerVec->resize( ind * 4 );
552 }
553 t = new TimerInfo; // create timer entry
554 Q_CHECK_PTR( t );
555 t->ind = ind;
556 t->obj = obj;
557
558 t->zero = interval == 0;
559 if ( t->zero ) { // add zero timer
560 t->id = 0;
561 numZeroTimers++;
562 // indicate there is a new zero timer
563 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
564 } else {
565 t->id = WinStartTimer( 0, qt_aux_win.hwnd(), ind + 1, (ULONG) interval );
566 }
567 if ( !t->zero && t->id == 0 ) {
568#if defined(QT_CHECK_STATE)
569 qSystemWarning( "qStartTimer: Failed to create a timer." );
570#endif
571 delete t; // could not set timer
572 return 0;
573 }
574 timerVec->insert( ind, t ); // store in timer vector
575 if ( !t->zero )
576 timerDict->insert( t->id, t ); // store in dict
577 return ind + 1; // return index in vector
578}
579
580bool qKillTimer( int ind )
581{
582 if ( !timerVec || ind <= 0 || (uint)ind > timerVec->size() )
583 return FALSE;
584 register TimerInfo *t = timerVec->at(ind-1);
585 if ( !t )
586 return FALSE;
587 if ( t->zero ) {
588 numZeroTimers--;
589 } else {
590 WinStopTimer( 0, 0, t->id );
591 timerDict->remove( t->id );
592 }
593 timerVec->remove( ind-1 );
594 return TRUE;
595}
596
597bool qKillTimer( QObject *obj )
598{
599 if ( !timerVec )
600 return FALSE;
601 register TimerInfo *t;
602 for ( uint i=0; i<timerVec->size(); i++ ) {
603 t = timerVec->at( i );
604 if ( t && t->obj == obj ) { // object found
605 if ( t->zero ) {
606 numZeroTimers--;
607 } else {
608 WinStopTimer( 0, 0, t->id );
609 timerDict->remove( t->id );
610 }
611 timerVec->remove( i );
612 }
613 }
614 return TRUE;
615}
616
617/*****************************************************************************
618 Socket notifier type
619 *****************************************************************************/
620
621QSockNotType::QSockNotType()
622 : list( 0 )
623{
624 FD_ZERO( &select_fds );
625 FD_ZERO( &enabled_fds );
626 FD_ZERO( &pending_fds );
627}
628
629QSockNotType::~QSockNotType()
630{
631 if ( list )
632 delete list;
633 list = 0;
634}
635
636/*****************************************************************************
637 socket select() thread
638 *****************************************************************************/
639
640#if defined(QT_THREAD_SUPPORT)
641
642static class QSockSelectThread : public QThread
643{
644public:
645 QSockSelectThread( QEventLoopPrivate *_d ) : d( _d ), exit( FALSE ) {};
646 void run();
647 void cancelSelectOrIdle( bool terminate = FALSE );
648private:
649 QEventLoopPrivate *d;
650 bool exit;
651} *ss_thread = 0;
652
653// recursive mutex to serialize access to socket notifier data
654static QMutex ss_mutex( TRUE );
655// flag to indicate the presence of sockets to do select() (we use QSemaphore
656// instead of QWaitCondition because we need the "level-triggered" semantics,
657// the "edge-triggered" semantics can produce deadlocks)
658static QSemaphore ss_flag( 1 );
659
660// Note: must be called from under ss_mutex lock!
661static inline void find_highest_fd( QEventLoopPrivate *d )
662{
663 d->sn_highest = -1;
664 for ( int i = 0; i < 3; i ++ ) {
665 QPtrList<QSockNot> *list = d->sn_vec[i].list;
666 if ( list && !list->isEmpty() ) {
667 QSockNot *sn = list->first();
668 while ( sn && !FD_ISSET( sn->fd, sn->active ) )
669 sn = list->next();
670 if ( sn )
671 d->sn_highest = QMAX( d->sn_highest, // list is fd-sorted
672 sn->fd );
673 }
674 }
675}
676
677void QSockSelectThread::run()
678{
679 while ( !exit ) {
680 ss_mutex.lock();
681 if ( d->sn_highest >= 0 ) { // has socket notifier(s)
682 // read
683 if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() )
684 d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds;
685 else
686 FD_ZERO( &d->sn_vec[0].select_fds );
687 // write
688 if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() )
689 d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds;
690 else
691 FD_ZERO( &d->sn_vec[1].select_fds );
692 // except
693 if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() )
694 d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds;
695 else
696 FD_ZERO( &d->sn_vec[2].select_fds );
697 // do select
698 int nfds = d->sn_highest + 1;
699 d->sn_cancel = d->sn_highest;
700 ss_mutex.unlock();
701 int nsel = ::select( nfds,
702 &d->sn_vec[0].select_fds,
703 &d->sn_vec[1].select_fds,
704 &d->sn_vec[2].select_fds,
705 NULL );
706 if ( nsel > 0 ) {
707 ss_mutex.lock();
708 // if select says data is ready on any socket, then set
709 // the socket notifier to pending
710 bool activate = FALSE;
711 for ( int i = 0; i < 3; i++ ) {
712 if ( ! d->sn_vec[i].list )
713 continue;
714 QPtrList<QSockNot> *list = d->sn_vec[i].list;
715 QSockNot *sn = list->first();
716 while ( sn ) {
717 if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) {
718 // see comments in QEventLoop::setSocketNotifierPending()
719 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
720 // queue the socket activation
721 d->sn_pending_list.insert(
722 (rand() & 0xff) % (d->sn_pending_list.count()+1),
723 sn );
724 FD_SET( sn->fd, sn->queue );
725 // remove from enabled sockets to prevent deadlocks
726 // on blocking sockets (and to reduce CPU load caused
727 // by frequent select() calls) when the event queue
728 // is not processed fast enough to handle activation
729 FD_CLR( sn->fd, sn->active );
730 if ( sn->fd == d->sn_highest )
731 find_highest_fd( d );
732 activate = TRUE;
733 }
734 }
735 sn = list->next();
736 }
737 }
738 ss_mutex.unlock();
739 // post a message to activate socket notifiers
740 if ( activate )
741 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
742 }
743 } else {
744 d->sn_cancel = -1;
745 // no sockets to select(), go to the idle state
746 ss_mutex.unlock();
747 // wait for the ability to capture the flag
748 ss_flag ++;
749 // release the flag before termination
750 if ( exit )
751 ss_flag --;
752 }
753 }
754}
755
756void QSockSelectThread::cancelSelectOrIdle( bool terminate /* = FALSE */ )
757{
758 ss_mutex.lock();
759 exit = terminate;
760 if ( d->sn_cancel >= 0 ) {
761 // terminate select() execution
762 ::so_cancel( d->sn_cancel );
763 } else {
764 // terminate the idle state by releasing the flag
765 if ( !ss_flag.available() )
766 ss_flag --;
767 }
768 ss_mutex.unlock();
769 // wait for this thread to end if the termination is requested
770 if ( exit )
771 wait();
772}
773
774static void ss_cleanup()
775{
776 ss_thread->cancelSelectOrIdle( TRUE );
777 delete ss_thread;
778 ss_thread = 0;
779}
780
781static void ss_init( QEventLoopPrivate *d )
782{
783 // capture the flag initially
784 ss_flag ++;
785 ss_thread = new QSockSelectThread( d );
786 ss_thread->start();
787}
788
789#endif // QT_THREAD_SUPPORT
790
791/*****************************************************************************
792 QEventLoop Implementation for OS/2 PM
793 *****************************************************************************/
794
795void QEventLoop::init()
796{
797 d->sn_highest = -1;
798 d->sn_cancel = -1;
799
800 qt_ensure_pm();
801 d->hab = WinInitialize( 0 );
802 d->hmq = WinCreateMsgQueue( d->hab, 0 );
803 qt_gui_hab = d->hab;
804 qt_gui_queue = d->hmq;
805
806 qt_aux_win.setEventLoop( this );
807
808#if defined(QT_THREAD_SUPPORT)
809 // there may only be one global QEventLoop instance that manages sockets
810 Q_ASSERT( ss_thread == 0 );
811#else
812#if defined(QT_CHECK_STATE)
813 qWarning( "QEventLoop::init: socket notifiers are not supported "
814 "for a single-threaded version of Qt." );
815#endif
816#endif // QT_THREAD_SUPPORT
817}
818
819void QEventLoop::cleanup()
820{
821 cleanupTimers();
822
823 // ss_thread should have been stopped already in appClosingDown()
824 Q_ASSERT( ss_thread == 0 );
825
826 qt_aux_win.setEventLoop( 0 );
827
828 // destroy all windows created by QPMObjectWindow instances
829 for ( HWND *w = qt_objWindows.first(); w; w = qt_objWindows.next() ) {
830 Q_ASSERT( *w );
831 WinDestroyWindow( *w );
832 // tell QPMObjectWindow the window has been destroyed
833 *w = 0;
834 }
835
836 WinDestroyMsgQueue( d->hmq );
837 WinTerminate( d->hab );
838 qt_gui_queue = 0;
839 qt_gui_hab = 0;
840}
841
842void QEventLoop::appStartingUp()
843{
844}
845
846void QEventLoop::appClosingDown()
847{
848 // ensure the thread is terminated before QApplication calls
849 // QThread::cleanup()
850 if ( ss_thread )
851 ss_cleanup();
852}
853
854void QEventLoop::registerSocketNotifier( QSocketNotifier *notifier )
855{
856#if defined(QT_THREAD_SUPPORT)
857 // there may only be one global QEventLoop instance that manages sockets
858 Q_ASSERT( !qApp || qApp->eventloop == this );
859 if ( qApp && qApp->eventloop != this )
860 return;
861
862 // lazily create the auxiliary window to process WM_U_SEM_SELECT messages
863 if ( !qt_aux_win.hwnd() )
864 if ( !qt_aux_win.create() )
865 return;
866
867 // lazily start the socket select thread
868 if ( !ss_thread )
869 ss_init( d );
870
871 int sockfd = -1;
872 int type = -1;
873 if ( notifier ) {
874 sockfd = notifier->socket();
875 type = notifier->type();
876 }
877 if ( sockfd < 0 || sockfd >= FD_SETSIZE || type < 0 || type > 2 ) {
878#if defined(QT_CHECK_RANGE)
879 qWarning( "QSocketNotifier: Internal error" );
880#endif
881 return;
882 }
883
884 QMutexLocker locker( &ss_mutex );
885 ss_thread->cancelSelectOrIdle();
886
887 QPtrList<QSockNot> *list = d->sn_vec[type].list;
888 QSockNot *sn;
889
890 if ( ! list ) {
891 // create new list, the QSockNotType destructor will delete it for us
892 list = new QPtrList<QSockNot>;
893 Q_CHECK_PTR( list );
894 list->setAutoDelete( TRUE );
895 d->sn_vec[type].list = list;
896 }
897
898 sn = new QSockNot;
899 Q_CHECK_PTR( sn );
900 sn->obj = notifier;
901 sn->fd = sockfd;
902 sn->queue = &d->sn_vec[type].pending_fds;
903 sn->active = &d->sn_vec[type].enabled_fds;
904
905 if ( list->isEmpty() ) {
906 list->insert( 0, sn );
907 } else { // sort list by fd, decreasing
908 QSockNot *p = list->first();
909 while ( p && p->fd > sockfd )
910 p = list->next();
911#if defined(QT_CHECK_STATE)
912 if ( p && p->fd == sockfd ) {
913 static const char *t[] = { "read", "write", "exception" };
914 qWarning( "QSocketNotifier: Multiple socket notifiers for "
915 "same socket %d and type %s", sockfd, t[type] );
916 }
917#endif
918 if ( p )
919 list->insert( list->at(), sn );
920 else
921 list->append( sn );
922 }
923
924 // enable the socket only if it's not already pending
925 if ( !FD_ISSET( sockfd, sn->queue ) ) {
926 FD_SET( sockfd, sn->active );
927 d->sn_highest = QMAX( d->sn_highest, sockfd );
928 }
929#endif // QT_THREAD_SUPPORT
930}
931
932void QEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier )
933{
934#if defined(QT_THREAD_SUPPORT)
935 int sockfd = -1;
936 int type = -1;
937 if ( notifier ) {
938 sockfd = notifier->socket();
939 type = notifier->type();
940 }
941 if ( sockfd < 0 || type < 0 || type > 2 ) {
942#if defined(QT_CHECK_RANGE)
943 qWarning( "QSocketNotifier: Internal error" );
944#endif
945 return;
946 }
947
948 if ( !ss_thread )
949 return; // definitely not found
950
951 QMutexLocker locker( &ss_mutex );
952 ss_thread->cancelSelectOrIdle();
953
954 QPtrList<QSockNot> *list = d->sn_vec[type].list;
955 QSockNot *sn;
956 if ( ! list )
957 return;
958 sn = list->first();
959 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
960 sn = list->next();
961 if ( !sn ) // not found
962 return;
963
964 // touch fd bitmaps only if there are no other notifiers for the same socket
965 // (QPtrList curiously lacks getNext()/getPrev(), so play tambourine)
966 QSockNot *next = list->next();
967 if ( next ) list->prev();
968 else list->last();
969 QSockNot *prev = list->prev();
970 if ( prev ) list->next();
971 else list->first();
972 bool unique = (!next || next->fd != sockfd) && (!prev || prev->fd != sockfd);
973 if ( unique) {
974 FD_CLR( sockfd, sn->active ); // clear fd bit
975 FD_CLR( sockfd, sn->queue );
976 }
977 d->sn_pending_list.removeRef( sn ); // remove from activation list
978 list->remove(); // remove notifier found above
979
980 if ( unique) {
981 if ( d->sn_highest == sockfd )
982 find_highest_fd( d );
983 }
984#endif // QT_THREAD_SUPPORT
985}
986
987void QEventLoop::setSocketNotifierPending( QSocketNotifier *notifier )
988{
989#if defined(QT_THREAD_SUPPORT)
990 int sockfd = -1;
991 int type = -1;
992 if ( notifier ) {
993 sockfd = notifier->socket();
994 type = notifier->type();
995 }
996 if ( sockfd < 0 || type < 0 || type > 2 ) {
997#if defined(QT_CHECK_RANGE)
998 qWarning( "QSocketNotifier: Internal error" );
999#endif
1000 return;
1001 }
1002
1003 if ( !ss_thread )
1004 return; // definitely not found
1005
1006 QMutexLocker locker( &ss_mutex );
1007 ss_thread->cancelSelectOrIdle();
1008
1009 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1010 QSockNot *sn;
1011 if ( ! list )
1012 return;
1013 sn = list->first();
1014 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1015 sn = list->next();
1016 if ( ! sn ) { // not found
1017 return;
1018 }
1019
1020 // We choose a random activation order to be more fair under high load.
1021 // If a constant order is used and a peer early in the list can
1022 // saturate the IO, it might grab our attention completely.
1023 // Also, if we're using a straight list, the callback routines may
1024 // delete other entries from the list before those other entries are
1025 // processed.
1026 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1027 d->sn_pending_list.insert(
1028 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1029 sn
1030 );
1031 FD_SET( sockfd, sn->queue );
1032 // remove from enabled sockets, see QSockSelectThread::run()
1033 FD_CLR( sockfd, sn->active );
1034 if ( d->sn_highest == sockfd )
1035 find_highest_fd( d );
1036 }
1037#endif // QT_THREAD_SUPPORT
1038}
1039
1040bool QEventLoop::hasPendingEvents() const
1041{
1042 QMSG msg;
1043 return qGlobalPostedEventsCount() || WinPeekMsg( 0, &msg, NULL, 0, 0, PM_NOREMOVE );
1044}
1045
1046bool QEventLoop::processEvents( ProcessEventsFlags flags )
1047{
1048 QMSG msg;
1049
1050#if defined(QT_THREAD_SUPPORT)
1051 QMutexLocker locker( QApplication::qt_mutex );
1052#endif
1053
1054 QApplication::sendPostedEvents();
1055
1056 if ( flags & ExcludeUserInput ) {
1057 while ( WinPeekMsg( 0, &msg, 0, 0, 0, PM_NOREMOVE ) ) {
1058 if ( msg.msg == WM_CHAR ||
1059 (msg.msg >= WM_MOUSEFIRST &&
1060 msg.msg <= WM_MOUSELAST) ||
1061 (msg.msg >= WM_EXTMOUSEFIRST &&
1062 msg.msg <= WM_EXTMOUSELAST) ||
1063 msg.msg == WM_HSCROLL ||
1064 msg.msg == WM_VSCROLL
1065 ) {
1066 WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE );
1067 continue;
1068 }
1069 break;
1070 }
1071 }
1072
1073 bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
1074
1075 if ( canWait ) {
1076 // can wait if necessary
1077 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1078 emit aboutToBlock();
1079#ifdef QT_THREAD_SUPPORT
1080 locker.mutex()->unlock();
1081#endif
1082 WinGetMsg( 0, &msg, 0, 0, 0 );
1083#ifdef QT_THREAD_SUPPORT
1084 locker.mutex()->lock();
1085#endif
1086 emit awake();
1087 emit qApp->guiThreadAwake();
1088 }
1089 } else {
1090 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1091 // no pending events
1092 return FALSE;
1093 }
1094 }
1095
1096 if ( msg.msg == WM_QUIT ) {
1097 // process the quit request
1098#if !defined (QT_NO_SESSIONMANAGER)
1099 if ( qt_app_canQuit() ) {
1100#endif
1101 exit( 0 );
1102 return TRUE;
1103#if !defined (QT_NO_SESSIONMANAGER)
1104 } else {
1105 WinCancelShutdown( d->hmq, FALSE );
1106 return TRUE;
1107 }
1108#endif
1109 }
1110
1111 bool handled = FALSE;
1112 MRESULT res = 0;
1113
1114 if ( msg.msg == WM_U_SEM_SELECT ) {
1115 // socket select event received: prevent it from being handled by
1116 // qt_aux_win (to obey the ExcludeSocketNotifiers flag)
1117 handled = TRUE;
1118 // ignore socket notifier activation if processd by the Qt event hook
1119 if ( qt_pmEventFilter( &msg, res ) )
1120 flags |= ExcludeSocketNotifiers;
1121 }
1122
1123 if ( !handled && msg.msg && (!msg.hwnd || !QWidget::find( msg.hwnd )) ) {
1124 handled = qt_pmEventFilter( &msg, res );
1125 }
1126
1127 if ( !handled ) {
1128 // send to QtWndProc or to a native window procedure
1129 WinDispatchMsg( 0, &msg );
1130 }
1131
1132 if ( !(flags & ExcludeSocketNotifiers) )
1133 activateSocketNotifiers();
1134
1135 // any pending configs?
1136 if ( configRequests )
1137 qPMProcessConfigRequests();
1138 QApplication::sendPostedEvents();
1139
1140 return TRUE;
1141}
1142
1143void QEventLoop::wakeUp()
1144{
1145 PTIB ptib;
1146 DosGetInfoBlocks( &ptib, NULL );
1147 MQINFO mqinfo;
1148 WinQueryQueueInfo( qt_gui_queue, &mqinfo, sizeof(MQINFO) );
1149 if ( ptib->tib_ptib2->tib2_ultid != mqinfo.tid )
1150 WinPostQueueMsg( qt_gui_queue, WM_NULL, 0, 0 );
1151}
1152
1153int QEventLoop::timeToWait() const
1154{
1155 return -1;
1156}
1157
1158int QEventLoop::activateTimers()
1159{
1160 return 0;
1161}
1162
1163int QEventLoop::activateSocketNotifiers()
1164{
1165#if defined(QT_THREAD_SUPPORT)
1166 if ( d->sn_pending_list.isEmpty() )
1167 return 0;
1168
1169 if ( !ss_thread )
1170 return 0;
1171
1172 // postpone activation if ss_thread is working with the list
1173 if ( !ss_mutex.tryLock() ) {
1174 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1175 return 0;
1176 }
1177
1178 ss_thread->cancelSelectOrIdle();
1179
1180 // activate entries
1181 int n_act = 0;
1182 QEvent event( QEvent::SockAct );
1183 QPtrListIterator<QSockNot> it( d->sn_pending_list );
1184 QSockNot *sn;
1185 while ( (sn = it.current()) ) {
1186 ++it;
1187 d->sn_pending_list.removeRef( sn );
1188 if ( FD_ISSET(sn->fd, sn->queue) ) {
1189 FD_CLR( sn->fd, sn->queue );
1190 // reenable the socket to let it participate in the next select()
1191 FD_SET( sn->fd, sn->active );
1192 d->sn_highest = QMAX( d->sn_highest, sn->fd );
1193 QApplication::sendEvent( sn->obj, &event );
1194 n_act++;
1195 }
1196 }
1197
1198 ss_mutex.unlock();
1199
1200 return n_act;
1201#else
1202 return 0;
1203#endif // QT_THREAD_SUPPORT
1204}
1205
Note: See TracBrowser for help on using the repository browser.