Ignore:
Timestamp:
Mar 22, 2006 11:49:19 AM (18 years ago)
Author:
bennylp
Message:

Fixed bug in ioqueue with IO Completion Port backend, where events may still be called after key is unregistered

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/src/pj/ioqueue_winnt.c

    r83 r349  
    100100}; 
    101101 
     102enum { POST_QUIT_LEN = 0xFFFFDEADUL }; 
     103 
    102104/* 
    103105 * Structure for individual socket. 
     
    113115#endif 
    114116    pj_ioqueue_callback cb; 
     117    pj_bool_t           has_quit_signal; 
    115118}; 
    116119 
     
    393396} 
    394397 
    395 /* 
    396  * pj_ioqueue_unregister() 
    397  */ 
    398 PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) 
    399 { 
    400     PJ_ASSERT_RETURN(key, PJ_EINVAL); 
    401  
    402 #if PJ_HAS_TCP 
    403     if (key->connecting) { 
    404         unsigned pos; 
    405         pj_ioqueue_t *ioqueue; 
    406  
    407         ioqueue = key->ioqueue; 
    408  
    409         /* Erase from connecting_handles */ 
    410         pj_lock_acquire(ioqueue->lock); 
    411         for (pos=0; pos < ioqueue->connecting_count; ++pos) { 
    412             if (ioqueue->connecting_keys[pos] == key) { 
    413                 erase_connecting_socket(ioqueue, pos); 
    414                 break; 
    415             } 
    416         } 
    417         key->connecting = 0; 
    418         pj_lock_release(ioqueue->lock); 
    419     } 
    420 #endif 
    421     if (key->hnd_type == HND_IS_FILE) { 
    422         CloseHandle(key->hnd); 
    423     } 
    424     return PJ_SUCCESS; 
    425 } 
    426398 
    427399/* 
     
    450422} 
    451423 
    452 /* 
    453  * pj_ioqueue_poll() 
    454  * 
    455  * Poll for events. 
    456  */ 
    457 PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) 
    458 { 
    459     DWORD dwMsec, dwBytesTransfered, dwKey; 
     424 
     425 
     426/* 
     427 * Internal function to poll the I/O Completion Port, execute callback,  
     428 * and return the key and bytes transfered of the last operation. 
     429 */ 
     430static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout,  
     431                            pj_ssize_t *p_bytes, pj_ioqueue_key_t **p_key ) 
     432{ 
     433    DWORD dwBytesTransfered, dwKey; 
    460434    generic_overlapped *pOv; 
    461435    pj_ioqueue_key_t *key; 
    462     int connect_count; 
    463436    pj_ssize_t size_status = -1; 
    464     BOOL rcGetQueued;; 
    465  
    466     PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL); 
    467  
    468     /* Check the connecting array. */ 
    469 #if PJ_HAS_TCP 
    470     connect_count = check_connecting(ioqueue); 
    471 #endif 
    472  
    473     /* Calculate miliseconds timeout for GetQueuedCompletionStatus */ 
    474     dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE; 
     437    BOOL rcGetQueued; 
    475438 
    476439    /* Poll for completion status. */ 
    477     rcGetQueued = GetQueuedCompletionStatus(ioqueue->iocp, &dwBytesTransfered, 
     440    rcGetQueued = GetQueuedCompletionStatus(hIocp, &dwBytesTransfered, 
    478441                                            &dwKey, (OVERLAPPED**)&pOv,  
    479                                             dwMsec); 
     442                                            dwTimeout); 
    480443 
    481444    /* The return value is: 
     
    488451        key = (pj_ioqueue_key_t*)dwKey; 
    489452        size_status = dwBytesTransfered; 
     453 
     454        /* Report to caller regardless */ 
     455        if (p_bytes) 
     456            *p_bytes = size_status; 
     457        if (p_key) 
     458            *p_key = key; 
     459 
     460        /* If size_status is POST_QUIT_LEN, mark the key as quitting */ 
     461        if (size_status == POST_QUIT_LEN) { 
     462            key->has_quit_signal = 1; 
     463            return PJ_TRUE; 
     464        } 
     465 
     466        /* We shouldn't call callbacks if key is quitting.  
     467         * But this should have been taken care by unregister function 
     468         * (the unregister function should have cleared out the callbacks) 
     469         */ 
     470 
     471        /* Carry out the callback */ 
    490472        switch (pOv->operation) { 
    491473        case PJ_IOQUEUE_OP_READ: 
     
    523505            break; 
    524506        } 
    525         return connect_count+1; 
     507        return PJ_TRUE; 
    526508    } 
    527509 
    528510    /* No event was queued. */ 
    529     return connect_count; 
     511    return PJ_FALSE; 
     512} 
     513 
     514/* 
     515 * pj_ioqueue_unregister() 
     516 */ 
     517PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) 
     518{ 
     519    pj_ssize_t polled_len; 
     520    pj_ioqueue_key_t *polled_key; 
     521    generic_overlapped ov; 
     522    BOOL rc; 
     523 
     524    PJ_ASSERT_RETURN(key, PJ_EINVAL); 
     525 
     526#if PJ_HAS_TCP 
     527    if (key->connecting) { 
     528        unsigned pos; 
     529        pj_ioqueue_t *ioqueue; 
     530 
     531        ioqueue = key->ioqueue; 
     532 
     533        /* Erase from connecting_handles */ 
     534        pj_lock_acquire(ioqueue->lock); 
     535        for (pos=0; pos < ioqueue->connecting_count; ++pos) { 
     536            if (ioqueue->connecting_keys[pos] == key) { 
     537                erase_connecting_socket(ioqueue, pos); 
     538                break; 
     539            } 
     540        } 
     541        key->connecting = 0; 
     542        pj_lock_release(ioqueue->lock); 
     543    } 
     544#endif 
     545 
     546 
     547    /* Unregistering handle from IOCP is pretty tricky. 
     548     * 
     549     * Even after the socket has been closed, GetQueuedCompletionStatus 
     550     * may still return events for the handle. This will likely to 
     551     * cause crash in pjlib, because the key associated with the handle 
     552     * most likely will have been destroyed. 
     553     * 
     554     * The solution is to poll the IOCP until we're sure that there are 
     555     * no further events for the handle. 
     556     */ 
     557 
     558    /* Clear up callbacks for the key.  
     559     * We don't want the callback to be called for this key. 
     560     */ 
     561    key->cb.on_read_complete = NULL; 
     562    key->cb.on_write_complete = NULL; 
     563    key->cb.on_accept_complete = NULL; 
     564    key->cb.on_connect_complete = NULL; 
     565 
     566    /* Init overlapped struct */ 
     567    pj_memset(&ov, 0, sizeof(ov)); 
     568    ov.operation = PJ_IOQUEUE_OP_READ; 
     569 
     570    /* Post queued completion status with a special length. */ 
     571    rc = PostQueuedCompletionStatus( key->ioqueue->iocp, (DWORD)POST_QUIT_LEN, 
     572                                     (DWORD)key, &ov.overlapped); 
     573 
     574    /* Poll IOCP until has_quit_signal is set in the key. 
     575     * The has_quit_signal flag is set in poll_iocp() when POST_QUIT_LEN 
     576     * is detected. We need to have this flag because POST_QUIT_LEN may be 
     577     * detected by other threads. 
     578     */ 
     579    do { 
     580        polled_len = 0; 
     581        polled_key = NULL; 
     582 
     583        rc = poll_iocp(key->ioqueue->iocp, 0, &polled_len, &polled_key); 
     584 
     585    } while (rc && !key->has_quit_signal); 
     586 
     587 
     588    /* Close handle if this is a file. */ 
     589    if (key->hnd_type == HND_IS_FILE) { 
     590        CloseHandle(key->hnd); 
     591    } 
     592 
     593    return PJ_SUCCESS; 
     594} 
     595 
     596/* 
     597 * pj_ioqueue_poll() 
     598 * 
     599 * Poll for events. 
     600 */ 
     601PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) 
     602{ 
     603    DWORD dwMsec; 
     604    int connect_count = 0; 
     605    pj_bool_t has_event; 
     606 
     607    PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL); 
     608 
     609    /* Check the connecting array. */ 
     610#if PJ_HAS_TCP 
     611    connect_count = check_connecting(ioqueue); 
     612#endif 
     613 
     614    /* Calculate miliseconds timeout for GetQueuedCompletionStatus */ 
     615    dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE; 
     616 
     617    /* Poll for completion status. */ 
     618    has_event = poll_iocp(ioqueue->iocp, dwMsec, NULL, NULL); 
     619 
     620    /* Return number of events. */ 
     621    return connect_count + has_event; 
    530622} 
    531623 
Note: See TracChangeset for help on using the changeset viewer.