Changeset 116


Ignore:
Timestamp:
Aug 11, 2006, 3:01:59 PM (19 years ago)
Author:
dmik
Message:

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.

Location:
trunk/src/kernel
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kernel/qeventloop_p.h

    r8 r116  
    7070    int fd;
    7171    fd_set *queue;
     72#if defined(Q_WS_PM)
     73    fd_set *active;
     74#endif
    7275};
    7376
     
    132135    // highest fd for all socket notifiers
    133136    int sn_highest;
     137#if defined(Q_WS_PM)
     138    // fd for cancelling select()
     139    int sn_cancel;
     140#endif
    134141    // 3 socket notifier types - read, write and exception
    135142    QSockNotType sn_vec[3];
  • trunk/src/kernel/qeventloop_pm.cpp

    r113 r116  
    658658static QSemaphore ss_flag( 1 );
    659659
     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
    660677void QSockSelectThread::run()
    661678{
     
    680697            // do select
    681698            int nfds = d->sn_highest + 1;
     699            d->sn_cancel = d->sn_highest;
    682700            ss_mutex.unlock();
    683701            int nsel = ::select( nfds,
     
    690708                // if select says data is ready on any socket, then set
    691709                // the socket notifier to pending
    692                 int i;
    693                 for ( i=0; i<3; i++ ) {
     710                bool activate = FALSE;
     711                for ( int i = 0; i < 3; i++ ) {
    694712                    if ( ! d->sn_vec[i].list )
    695713                        continue;
     
    698716                    while ( sn ) {
    699717                        if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) {
    700                             // see comments inside QEventLoop::setSocketNotifierPending()
     718                            // see comments in QEventLoop::setSocketNotifierPending()
    701719                            if ( !FD_ISSET( sn->fd, sn->queue ) ) {
     720                                // queue the socket activation
    702721                                d->sn_pending_list.insert(
    703722                                    (rand() & 0xff) % (d->sn_pending_list.count()+1),
    704                                     sn
    705                                 );
     723                                    sn );
    706724                                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;
    707733                            }
    708734                        }
     
    711737                }
    712738                ss_mutex.unlock();
    713                 // post a message to activate socket notifiers
    714                 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
     739                // post a message to activate socket notifiers
     740                if ( activate )
     741                    qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
    715742            }
    716743        } else {
     744            d->sn_cancel = -1;
    717745            // no sockets to select(), go to the idle state
    718746            ss_mutex.unlock();
     
    730758    ss_mutex.lock();
    731759    exit = terminate;
    732     if ( d->sn_highest >= 0 ) {
     760    if ( d->sn_cancel >= 0 ) {
    733761        // terminate select() execution
    734         ::so_cancel( d->sn_highest );
     762        ::so_cancel( d->sn_cancel );
    735763    } else {
    736764        // terminate the idle state by releasing the flag
     
    768796{
    769797    d->sn_highest = -1;
     798    d->sn_cancel = -1;
    770799
    771800    qt_ensure_pm();
     
    855884    QMutexLocker locker( &ss_mutex );
    856885    ss_thread->cancelSelectOrIdle();
    857 
     886   
    858887    QPtrList<QSockNot>  *list = d->sn_vec[type].list;
    859     fd_set *fds  = &d->sn_vec[type].enabled_fds;
    860888    QSockNot *sn;
    861889
     
    873901    sn->fd = sockfd;
    874902    sn->queue = &d->sn_vec[type].pending_fds;
     903    sn->active = &d->sn_vec[type].enabled_fds;
    875904
    876905    if ( list->isEmpty() ) {
     
    893922    }
    894923
    895     FD_SET( sockfd, fds );
    896     d->sn_highest = QMAX( d->sn_highest, sockfd );
     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    }
    897929#endif // QT_THREAD_SUPPORT
    898930}
     
    921953
    922954    QPtrList<QSockNot> *list = d->sn_vec[type].list;
    923     fd_set *fds  =  &d->sn_vec[type].enabled_fds;
    924955    QSockNot *sn;
    925956    if ( ! list )
     
    931962        return;
    932963
    933     FD_CLR( sockfd, fds );                      // clear fd bit
    934     FD_CLR( sockfd, sn->queue );
     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    }
    935977    d->sn_pending_list.removeRef( sn );         // remove from activation list
    936978    list->remove();                             // remove notifier found above
    937979
    938     if ( d->sn_highest == sockfd ) {            // find highest fd
    939         d->sn_highest = -1;
    940         for ( int i=0; i<3; i++ ) {
    941             if ( d->sn_vec[i].list && ! d->sn_vec[i].list->isEmpty() )
    942                 d->sn_highest = QMAX( d->sn_highest,  // list is fd-sorted
    943                                       d->sn_vec[i].list->getFirst()->fd );
    944         }
     980    if ( unique) {
     981        if ( d->sn_highest == sockfd )
     982            find_highest_fd( d );
    945983    }
    946984#endif // QT_THREAD_SUPPORT
     
    9671005   
    9681006    QMutexLocker locker( &ss_mutex );
     1007    ss_thread->cancelSelectOrIdle();
    9691008
    9701009    QPtrList<QSockNot> *list = d->sn_vec[type].list;
     
    9901029            sn
    9911030        );
    992         FD_SET( sn->fd, sn->queue );
     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 );
    9931036    }
    9941037#endif // QT_THREAD_SUPPORT
     
    11281171   
    11291172    // postpone activation if ss_thread is working with the list
    1130     if ( !ss_mutex.tryLock() )
     1173    if ( !ss_mutex.tryLock() ) {
     1174        qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
    11311175        return 0;
     1176    }
     1177
     1178    ss_thread->cancelSelectOrIdle();
    11321179
    11331180    // activate entries
     
    11411188        if ( FD_ISSET(sn->fd, sn->queue) ) {
    11421189            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 );
    11431193            QApplication::sendEvent( sn->obj, &event );
    11441194            n_act++;
Note: See TracChangeset for help on using the changeset viewer.