Skip to content

SocketReactor socket removal #5150

@aleks-f

Description

@aleks-f

SocketReactor currently only allows removing handlers, one at a time. But removing a handler does nothing to prevent data already in the dispatch queue at removal time to be passed to the just removed handler. When the removal is happening during normal client object lifetime, this can be easily dealt with in the client handler function. However, removing handlers on destruction inevitably creates a potential for UB - calling a member function of a destroyed object.

Currently, the only way to deal with this situation is not to allow handle registration to survive until destruction and be prepared to deal with some data after handler removal. Since there is a way to avoid the problem, this issue is not categorized as a bug. But it is an easy pitfall.

The solution:

  • add SocketReactor::remove(const Socket&)
    • removes socket from the PollSet
    • removes all handlers for the socket
  • add Socket SocketNotifier::socket() const
    (see below why this is needed)
  • add checks in:
    • SocketReactor::dispatch(const Socket&, SocketNotification*)
      if (!_pollSet.has(socket)) return;
    • SocketReactor::dispatch(SocketNotification*)
      if (!_pollSet.has(delegate->socket())) continue;
  • accompanying (Async)NotificationCenter modifications and optimizations

All this narrows the race window, but it does not completely close it. The race potential can never be 100% eliminated for reactor client that unregisters its handlers in destructor, but eg. an atomic mutual exclusion flag (declared first, so it is destroyed last) between destructor and handlers further narrows the possibility for UB.

Metadata

Metadata

Assignees

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions