[Ace-users] [ace-users] ACE_Asynch_Acceptor: Cancellation Bug

Paul Daugherty paul at nextalk.com
Mon Feb 25 17:21:49 CST 2008


    ACE VERSION: 

        5.5

    HOST MACHINE and OPERATING SYSTEM:

        Windows

    TARGET MACHINE and OPERATING SYSTEM, if different from HOST:
    COMPILER NAME AND VERSION (AND PATCHLEVEL):

        N/A

    THE $ACE_ROOT/ace/config.h FILE [if you use a link to a platform-
    specific file, simply state which one]:
    
        #if defined (WIN32)
        #  define ACE_HAS_MFC 1
        #  if defined (NXI_USE_SSL)
        #    define ACE_SSL_HAS_DLL 1
        #    define ACE_SSL_BUILD_DLL
        #  endif // NXI_USE_SSL
        #  include "ace/config-win32.h"
        #  if defined (ACE_HAS_WCHAR)
        #    undef ACE_HAS_WCHAR
        #  endif // ACE_HAS_WCHAR
        #elif defined(linux) // WIN32
        #  define ACE_HAS_LINUX_EPOLL
        #  include "config-linux.h"
        #endif // WIN32

    AREA/CLASS/EXAMPLE AFFECTED:

        N/A

    DOES THE PROBLEM AFFECT:

        The problem is with the ACE_Asynch_Acceptor interface and will
affect execution on Windows when shutting down an ACE_Asynch_Acceptor.  

    SYNOPSIS:

        Cancelling an acceptor from a different thread from which open()
was called is troublesome due to the following:

        1) The ACE_Asynch_Acceptor interface for the handle() methods
are as follows:

        /// Return the listen handle.
        ACE_HANDLE handle (void) const;
        /// Set the listen handle.
        void handle (ACE_HANDLE h);	

        However, after inspecting the implemenation for the handle
setter, it appears that the setter method does not set the listen
handle, instead it calls ACE_Handler::handle(ACE_HANDLE).

        2) The ACE_Asynch_Acceptor handle() methods are protected and
should be public.

    DESCRIPTION:

        On Win32, it is well known that the ACE_Asynch_Acceptor::cancel
method will only cancel outstanding accepts if cancel is called from the
same thread that accepts were called from.  Because of this, the only
way to cancel the outstanding reads (accepts) on the acceptor from a
different thread is to call ACE_OS::closesocket on the acceptor's
listen_handle_.  After all i/o operations complete, the acceptor can be
safely destroyed.

        When closesocket is called on the acceptor's listen_handle_, the
acceptor's listen handle must be assigned ACE_INVALID_HANDLE or the same
handle will be closed in the acceptor's dtor which could 'pull the rug
out from under' another newly created and unrelated socket.

        The current interface provides a set_handle() method which does
assign a value to listen_handle_ but this method allocates a new
implementation which will cause a memory leak of the old implementation
allocation.

        So, unless somebody enlightens me to a better way, the way to
cancel/shutdown an ACE_Asynch_Acceptor from a different thread is as
follows:

        // closesocket req'd since we're closing from different thread
than 
        // thread where i/o was issued.
        1) ACE_OS::closesocket(acceptor->handle()); // or
acceptor->get_handle()
        2) acceptor->handle(ACE_INVALID_HANDLE);
        3) Wait for all outstanding i/o to complete
        4) Delete the acceptor

        The acceptor does have a public cancel() method but as stated
above, it does not work when called from a different thread than the
thread cancel() is called in.

        ACE_Asynch_Acceptor::handle(ACE_HANDLE h) has a comment stating
that '/// Set the listen handle' but it does not set the listen handle,
it calls "ACE_Handler::handle(h)."

        The ACE_Asynch_Acceptor::handle() methods must have protected
access but need to have public access since these methods are part of an
acceptor's cancellation due to the inter-thread ineffectiveness of the
cancel() method.

    REPEAT BY:

        N/A

    SAMPLE FIX/WORKAROUND:

        1) Change the 'void handle(ACE_HANDLE h)' method as follows:

            template <class HANDLER> void
            ACE_Asynch_Acceptor<HANDLER>::handle (ACE_HANDLE h)
              {
                --ACE_Handler::handle (h);
                ++this->listen_handle_ = h;
              }

        2) Change access to the handle() methods from protected to
public.



More information about the Ace-users mailing list