LCOV - code coverage report
Current view: top level - source/common/event - file_event_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 76 102 74.5 %
Date: 2024-01-05 06:35:25 Functions: 8 10 80.0 %

          Line data    Source code
       1             : #include "source/common/event/file_event_impl.h"
       2             : 
       3             : #include <cstdint>
       4             : 
       5             : #include "source/common/common/assert.h"
       6             : #include "source/common/event/dispatcher_impl.h"
       7             : 
       8             : #include "event2/event.h"
       9             : 
      10             : namespace Envoy {
      11             : namespace Event {
      12             : 
      13             : FileEventImpl::FileEventImpl(DispatcherImpl& dispatcher, os_fd_t fd, FileReadyCb cb,
      14             :                              FileTriggerType trigger, uint32_t events)
      15             :     : dispatcher_(dispatcher), cb_(cb), fd_(fd), trigger_(trigger), enabled_events_(events),
      16        2891 :       activation_cb_(dispatcher.createSchedulableCallback([this]() {
      17        2891 :         ASSERT(injected_activation_events_ != 0);
      18        2891 :         mergeInjectedEventsAndRunCb(0);
      19        4306 :       })) {
      20             :   // Treat the lack of a valid fd (which in practice should only happen if we run out of FDs) as
      21             :   // an OOM condition and just crash.
      22        3411 :   RELEASE_ASSERT(SOCKET_VALID(fd), "");
      23             : #ifdef WIN32
      24             :   ASSERT(trigger_ != FileTriggerType::Edge, "libevent does not support edge triggers on Windows");
      25             : #endif
      26        3411 :   if constexpr (PlatformDefaultTriggerType != FileTriggerType::EmulatedEdge) {
      27        3411 :     ASSERT(trigger_ != FileTriggerType::EmulatedEdge,
      28        3411 :            "Cannot use EmulatedEdge events if they are not the default platform type");
      29        3411 :   }
      30             : 
      31        3411 :   assignEvents(events, &dispatcher.base());
      32        3411 :   event_add(&raw_event_, nullptr);
      33        3411 : }
      34             : 
      35        7016 : void FileEventImpl::activate(uint32_t events) {
      36        7016 :   ASSERT(dispatcher_.isThreadSafe());
      37             : 
      38             :   // events is not empty.
      39        7016 :   ASSERT(events != 0);
      40             :   // Only supported event types are set.
      41        7016 :   ASSERT((events & (FileReadyType::Read | FileReadyType::Write | FileReadyType::Closed)) == events);
      42             : 
      43             :   // Schedule the activation callback so it runs as part of the next loop iteration if it is not
      44             :   // already scheduled.
      45        7016 :   if (injected_activation_events_ == 0) {
      46        4194 :     ASSERT(!activation_cb_->enabled());
      47        4194 :     activation_cb_->scheduleCallbackNextIteration();
      48        4194 :   }
      49        7016 :   ASSERT(activation_cb_->enabled());
      50             : 
      51             :   // Merge new events with pending injected events.
      52        7016 :   injected_activation_events_ |= events;
      53        7016 : }
      54             : 
      55        4081 : void FileEventImpl::assignEvents(uint32_t events, event_base* base) {
      56        4081 :   ASSERT(dispatcher_.isThreadSafe());
      57        4081 :   ASSERT(base != nullptr);
      58             : 
      59        4081 :   enabled_events_ = events;
      60        4081 :   event_assign(
      61        4081 :       &raw_event_, base, fd_,
      62        4081 :       EV_PERSIST | (trigger_ == FileTriggerType::Edge ? EV_ET : 0) |
      63        4081 :           (events & FileReadyType::Read ? EV_READ : 0) |
      64        4081 :           (events & FileReadyType::Write ? EV_WRITE : 0) |
      65        4081 :           (events & FileReadyType::Closed ? EV_CLOSED : 0),
      66        5729 :       [](evutil_socket_t, short what, void* arg) -> void {
      67        5722 :         auto* event = static_cast<FileEventImpl*>(arg);
      68        5722 :         uint32_t events = 0;
      69        5722 :         if (what & EV_READ) {
      70        4088 :           events |= FileReadyType::Read;
      71        4088 :         }
      72             : 
      73        5722 :         if (what & EV_WRITE) {
      74        4119 :           events |= FileReadyType::Write;
      75        4119 :         }
      76             : 
      77        5722 :         if (what & EV_CLOSED) {
      78         341 :           events |= FileReadyType::Closed;
      79         341 :         }
      80             : 
      81        5722 :         ASSERT(events != 0);
      82        5722 :         event->mergeInjectedEventsAndRunCb(events);
      83        5722 :       },
      84        4081 :       this);
      85        4081 : }
      86             : 
      87         670 : void FileEventImpl::updateEvents(uint32_t events) {
      88         670 :   ASSERT(dispatcher_.isThreadSafe());
      89             :   // The update can be skipped in cases where the old and new event mask are the same if the fd is
      90             :   // using Level or EmulatedEdge trigger modes, but not Edge trigger mode. When the fd is registered
      91             :   // in edge trigger mode, re-registering the fd will force re-computation of the readable/writable
      92             :   // state even in cases where the event mask is not changing. See
      93             :   // https://github.com/envoyproxy/envoy/pull/16389 for more details.
      94             :   // TODO(antoniovicente) Consider ways to optimize away event registration updates in edge trigger
      95             :   // mode once setEnabled stops clearing injected_activation_events_ before calling updateEvents
      96             :   // and/or implement optimizations at the Network::ConnectionImpl level to reduce the number of
      97             :   // calls to setEnabled.
      98         670 :   if (events == enabled_events_ && trigger_ != FileTriggerType::Edge) {
      99           0 :     return;
     100           0 :   }
     101         670 :   auto* base = event_get_base(&raw_event_);
     102         670 :   event_del(&raw_event_);
     103         670 :   assignEvents(events, base);
     104         670 :   event_add(&raw_event_, nullptr);
     105         670 : }
     106             : 
     107         670 : void FileEventImpl::setEnabled(uint32_t events) {
     108         670 :   ASSERT(dispatcher_.isThreadSafe());
     109         670 :   if (injected_activation_events_ != 0) {
     110             :     // Clear pending events on updates to the fd event mask to avoid delivering events that are no
     111             :     // longer relevant. Updating the event mask will reset the fd edge trigger state so the proxy
     112             :     // will be able to determine the fd read/write state without need for the injected activation
     113             :     // events.
     114         302 :     injected_activation_events_ = 0;
     115         302 :     activation_cb_->cancel();
     116         302 :   }
     117         670 :   updateEvents(events);
     118         670 : }
     119             : 
     120           0 : void FileEventImpl::unregisterEventIfEmulatedEdge(uint32_t event) {
     121           0 :   ASSERT(dispatcher_.isThreadSafe());
     122             :   // This constexpr if allows the compiler to optimize away the function on POSIX
     123           0 :   if constexpr (PlatformDefaultTriggerType == FileTriggerType::EmulatedEdge) {
     124           0 :     if (trigger_ == FileTriggerType::EmulatedEdge) {
     125           0 :       auto new_event_mask = enabled_events_ & ~event;
     126           0 :       updateEvents(new_event_mask);
     127           0 :     }
     128           0 :   }
     129           0 : }
     130             : 
     131           0 : void FileEventImpl::registerEventIfEmulatedEdge(uint32_t event) {
     132           0 :   ASSERT(dispatcher_.isThreadSafe());
     133             :   // This constexpr if allows the compiler to optimize away the function on POSIX
     134           0 :   if constexpr (PlatformDefaultTriggerType == FileTriggerType::EmulatedEdge) {
     135           0 :     ASSERT((event & (FileReadyType::Read | FileReadyType::Write)) == event);
     136           0 :     if (trigger_ == FileTriggerType::EmulatedEdge) {
     137           0 :       auto new_event_mask = enabled_events_ | event;
     138           0 :       updateEvents(new_event_mask);
     139           0 :     }
     140           0 :   }
     141           0 : }
     142             : 
     143        8614 : void FileEventImpl::mergeInjectedEventsAndRunCb(uint32_t events) {
     144        8614 :   ASSERT(dispatcher_.isThreadSafe());
     145        8614 :   if (injected_activation_events_ != 0) {
     146        3529 :     events |= injected_activation_events_;
     147        3529 :     injected_activation_events_ = 0;
     148        3529 :     activation_cb_->cancel();
     149        3529 :   }
     150             : 
     151             :   // TODO(davinci26): This can be optimized further in (w)epoll backends using the `EPOLLONESHOT`
     152             :   // flag. With this flag `EPOLLIN`/`EPOLLOUT` are automatically disabled when the event is
     153             :   // activated.
     154        8614 :   if constexpr (PlatformDefaultTriggerType == FileTriggerType::EmulatedEdge) {
     155           0 :     if (trigger_ == FileTriggerType::EmulatedEdge) {
     156           0 :       unregisterEventIfEmulatedEdge(events &
     157           0 :                                     (Event::FileReadyType::Write | Event::FileReadyType::Read));
     158           0 :     }
     159           0 :   }
     160             : 
     161        8614 :   cb_(events);
     162        8614 : }
     163             : 
     164             : } // namespace Event
     165             : } // namespace Envoy

Generated by: LCOV version 1.15