diff options
Diffstat (limited to 'ace/Dev_Poll_Reactor.cpp')
-rw-r--r-- | ace/Dev_Poll_Reactor.cpp | 132 |
1 files changed, 100 insertions, 32 deletions
diff --git a/ace/Dev_Poll_Reactor.cpp b/ace/Dev_Poll_Reactor.cpp index 75b88255e23..66a0a1ef19c 100644 --- a/ace/Dev_Poll_Reactor.cpp +++ b/ace/Dev_Poll_Reactor.cpp @@ -492,11 +492,23 @@ ACE_Dev_Poll_Reactor::Handler_Repository::unbind_all (void) { ACE_TRACE ("ACE_Dev_Poll_Reactor::Handler_Repository::unbind_all"); - // Unbind all of the event handlers. + // Unbind all of the event handlers; similar to remove_handler() on all. for (int handle = 0; handle < this->max_size_; ++handle) - this->unbind (handle); + { + Event_Tuple *entry = this->find (handle); + if (entry == 0) + continue; + + // Check for ref counting now - handle_close () may delete eh. + bool const requires_reference_counting = + entry->event_handler->reference_counting_policy ().value () == + ACE_Event_Handler::Reference_Counting_Policy::ENABLED; + + (void) entry->event_handler->handle_close (handle, entry->mask); + this->unbind (handle, requires_reference_counting); + } return 0; } @@ -597,6 +609,9 @@ ACE_Dev_Poll_Reactor::ACE_Dev_Poll_Reactor (ACE_Sig_Handler *sh, , poll_fd_ (ACE_INVALID_HANDLE) , size_ (0) // , ready_set_ () +#if defined (ACE_HAS_EVENT_POLL) + , epoll_wait_in_progress_ (false) +#endif /* ACE_HAS_EVENT_POLL */ #if defined (ACE_HAS_DEV_POLL) , dp_fds_ (0) , start_pfds_ (0) @@ -864,7 +879,7 @@ ACE_Dev_Poll_Reactor::close (void) this->delete_signal_handler_ = false; } - (void) this->handler_rep_.close (); + (void) this->handler_rep_.close (); if (this->delete_timer_queue_) { @@ -953,11 +968,33 @@ ACE_Dev_Poll_Reactor::work_pending_i (ACE_Time_Value * max_wait_time) #if defined (ACE_HAS_EVENT_POLL) - // Wait for an event. - int const nfds = ::epoll_wait (this->poll_fd_, - &this->event_, - 1, - static_cast<int> (timeout)); + // See if there are handlers that have to be resumed before waiting. + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, grd, this->to_be_resumed_lock_, -1); + this->epoll_wait_in_progress_ = true; + for (Resume_Map::iterator i = this->to_be_resumed_.begin (); + i != this->to_be_resumed_.end (); + ++i) + { + // Make sure that 1) the handle is still registered, + // 2) the registered handler is the one we're waiting to resume. + Event_Tuple *info = this->handler_rep_.find (i->first); + if (info != 0 && info->event_handler == i->second) + { + this->resume_handler_i (i->first); + } + } + this->to_be_resumed_.clear (); + } + + // Wait for an event. + int const nfds = ::epoll_wait (this->poll_fd_, + &this->event_, + 1, + static_cast<int> (timeout)); + // Count on this being an atomic update; at worst, we may get an + // extraneous notify() from dispatch_io_event. + this->epoll_wait_in_progress_ = false; #else @@ -1269,13 +1306,21 @@ ACE_Dev_Poll_Reactor::dispatch_io_event (Token_Guard &guard) // With epoll, events are registered with oneshot, so the handle is // effectively suspended; future calls to epoll_wait() will select // the next event, so they're not managed here. - // The hitch to this is that the notify handler must always be resumed - // immediately, before letting go of the guard. Else it's possible to - // get into a state where all handles, including the notify pipe, are - // suspended and that means the wait thread can't be interrupted. - info->suspended = true; - if (eh == this->notify_handler_) - this->resume_handler_i (handle); + // The hitch to this is that the notify handler is always registered + // WITHOUT oneshot and is never suspended/resumed. This avoids endless + // notify loops caused by the notify handler requiring a resumption + // which requires the token, which requires a notify, etc. described + // in Bugzilla 3714. So, never suspend the notify handler. + + bool reactor_resumes_eh = false; + if (eh != this->notify_handler_) + { + info->suspended = true; + + reactor_resumes_eh = + eh->resume_handler () == + ACE_Event_Handler::ACE_REACTOR_RESUMES_HANDLER; + } #endif /* ACE_HAS_DEV_POLL */ int status = 0; // gets callback status, below. @@ -1294,6 +1339,39 @@ ACE_Dev_Poll_Reactor::dispatch_io_event (Token_Guard &guard) // if callback returns > 0. We come back with either 0 or < 0. status = this->upcall (eh, callback, handle); + if (eh == this->notify_handler_) + return status == 0 ? 1 : -1; + + // If the callback returned 0, epoll-based needs to resume the + // suspended handler but dev/poll doesn't. + // The epoll case is optimized to not acquire the token in order + // to resume the handler; the handler is added to a list of those + // that need to be resumed and is handled by the next leader + // that does an epoll_wait(). + // In both epoll and dev/poll cases, if the callback returns <0, + // the token needs to be acquired and the handler checked and + // removed if it hasn't already been. + if (status == 0) + { +#ifdef ACE_HAS_EVENT_POLL + // epoll-based effectively suspends handlers around the upcall. + // If the handler must be resumed, add it to the list. + if (reactor_resumes_eh) + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, + grd, + this->to_be_resumed_lock_, + -1); + bool map_was_empty = this->to_be_resumed_.empty(); + this->to_be_resumed_.insert + (Resume_Map::value_type (handle, eh)); + if (this->epoll_wait_in_progress_ && map_was_empty) + this->notify(); + } +#endif /* ACE_HAS_EVENT_POLL */ + return 1; + } + // All state in the handler repository may have changed during the // upcall while other threads had the token. Thus, reacquire the // token and evaluate what's needed. If the upcalled handler is still @@ -1305,15 +1383,6 @@ ACE_Dev_Poll_Reactor::dispatch_io_event (Token_Guard &guard) { if (status < 0) this->remove_handler_i (handle, disp_mask); - -#ifdef ACE_HAS_EVENT_POLL - // epoll-based effectively suspends handlers around the upcall. - // If the handler must be resumed here, do it now. - if (info->suspended && - (eh->resume_handler () == - ACE_Event_Handler::ACE_REACTOR_RESUMES_HANDLER)) - this->resume_handler_i (handle); -#endif /* ACE_HAS_EVENT_POLL */ } } // Scope close handles eh ref count decrement, if needed. @@ -1416,8 +1485,13 @@ ACE_Dev_Poll_Reactor::register_handler_i (ACE_HANDLE handle, ACE_OS::memset (&epev, 0, sizeof (epev)); static const int op = EPOLL_CTL_ADD; - epev.events = this->reactor_mask_to_poll_event (mask) | EPOLLONESHOT; epev.data.fd = handle; + epev.events = this->reactor_mask_to_poll_event (mask); + // All but the notify handler get registered with oneshot to facilitate + // auto suspend before the upcall. See dispatch_io_event for more + // information. + if (event_handler != this->notify_handler_) + epev.events |= EPOLLONESHOT; if (::epoll_ctl (this->poll_fd_, op, handle, &epev) == -1) { @@ -1575,7 +1649,7 @@ ACE_Dev_Poll_Reactor::remove_handler_i (ACE_HANDLE handle, // If registered event handler not the same as eh, don't mess with // the mask, but do the proper callback and refcount when needed. bool handle_reg_changed = true; - Event_Tuple *info = this->handler_rep_.find (handle); + Event_Tuple *info = this->handler_rep_.find (handle); if (info == 0 && eh == 0) // Nothing to work with return -1; if (info != 0 && (eh == 0 || info->event_handler == eh)) @@ -1599,12 +1673,6 @@ ACE_Dev_Poll_Reactor::remove_handler_i (ACE_HANDLE handle, if (!handle_reg_changed && info->mask == ACE_Event_Handler::NULL_MASK) this->handler_rep_.unbind (handle, requires_reference_counting); - // Note the fact that we've changed the state of the wait_set, - // i.e. the "interest set," which is used by the dispatching loop to - // determine whether it can keep going or if it needs to reconsult - // /dev/poll or /dev/epoll. - // this->state_changed_ = 1; - return 0; } |