xref: /aosp_15_r20/external/pigweed/pw_channel/epoll_channel.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_channel/epoll_channel.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <fcntl.h>
18*61c4878aSAndroid Build Coastguard Worker #include <unistd.h>
19*61c4878aSAndroid Build Coastguard Worker 
20*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
22*61c4878aSAndroid Build Coastguard Worker 
23*61c4878aSAndroid Build Coastguard Worker namespace pw::channel {
24*61c4878aSAndroid Build Coastguard Worker 
Register()25*61c4878aSAndroid Build Coastguard Worker void EpollChannel::Register() {
26*61c4878aSAndroid Build Coastguard Worker   if (fcntl(channel_fd_, F_SETFL, O_NONBLOCK) != 0) {
27*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to make channel file descriptor nonblocking: %s",
28*61c4878aSAndroid Build Coastguard Worker                  std::strerror(errno));
29*61c4878aSAndroid Build Coastguard Worker     set_closed();
30*61c4878aSAndroid Build Coastguard Worker     return;
31*61c4878aSAndroid Build Coastguard Worker   }
32*61c4878aSAndroid Build Coastguard Worker 
33*61c4878aSAndroid Build Coastguard Worker   if (!dispatcher_->native()
34*61c4878aSAndroid Build Coastguard Worker            .NativeRegisterFileDescriptor(channel_fd_,
35*61c4878aSAndroid Build Coastguard Worker                                          async2::backend::NativeDispatcher::
36*61c4878aSAndroid Build Coastguard Worker                                              FileDescriptorType::kReadWrite)
37*61c4878aSAndroid Build Coastguard Worker            .ok()) {
38*61c4878aSAndroid Build Coastguard Worker     set_closed();
39*61c4878aSAndroid Build Coastguard Worker     return;
40*61c4878aSAndroid Build Coastguard Worker   }
41*61c4878aSAndroid Build Coastguard Worker 
42*61c4878aSAndroid Build Coastguard Worker   ready_to_write_ = true;
43*61c4878aSAndroid Build Coastguard Worker }
44*61c4878aSAndroid Build Coastguard Worker 
DoPendRead(async2::Context & cx)45*61c4878aSAndroid Build Coastguard Worker async2::Poll<Result<multibuf::MultiBuf>> EpollChannel::DoPendRead(
46*61c4878aSAndroid Build Coastguard Worker     async2::Context& cx) {
47*61c4878aSAndroid Build Coastguard Worker   write_alloc_future_.SetDesiredSizes(
48*61c4878aSAndroid Build Coastguard Worker       kMinimumReadSize, kDesiredReadSize, pw::multibuf::kNeedsContiguous);
49*61c4878aSAndroid Build Coastguard Worker   async2::Poll<std::optional<multibuf::MultiBuf>> maybe_multibuf =
50*61c4878aSAndroid Build Coastguard Worker       write_alloc_future_.Pend(cx);
51*61c4878aSAndroid Build Coastguard Worker   if (maybe_multibuf.IsPending()) {
52*61c4878aSAndroid Build Coastguard Worker     return async2::Pending();
53*61c4878aSAndroid Build Coastguard Worker   }
54*61c4878aSAndroid Build Coastguard Worker 
55*61c4878aSAndroid Build Coastguard Worker   if (!maybe_multibuf->has_value()) {
56*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to allocate multibuf for reading");
57*61c4878aSAndroid Build Coastguard Worker     return Status::ResourceExhausted();
58*61c4878aSAndroid Build Coastguard Worker   }
59*61c4878aSAndroid Build Coastguard Worker 
60*61c4878aSAndroid Build Coastguard Worker   multibuf::MultiBuf buf = std::move(**maybe_multibuf);
61*61c4878aSAndroid Build Coastguard Worker   multibuf::Chunk& chunk = *buf.Chunks().begin();
62*61c4878aSAndroid Build Coastguard Worker 
63*61c4878aSAndroid Build Coastguard Worker   int bytes_read = read(channel_fd_, chunk.data(), chunk.size());
64*61c4878aSAndroid Build Coastguard Worker   if (bytes_read >= 0) {
65*61c4878aSAndroid Build Coastguard Worker     buf.Truncate(bytes_read);
66*61c4878aSAndroid Build Coastguard Worker     return async2::Ready(std::move(buf));
67*61c4878aSAndroid Build Coastguard Worker   }
68*61c4878aSAndroid Build Coastguard Worker 
69*61c4878aSAndroid Build Coastguard Worker   if (errno == EAGAIN) {
70*61c4878aSAndroid Build Coastguard Worker     // EAGAIN on a non-blocking read indicates that there is no data available.
71*61c4878aSAndroid Build Coastguard Worker     // Put the task to sleep until the dispatcher is notified that the file
72*61c4878aSAndroid Build Coastguard Worker     // descriptor is active.
73*61c4878aSAndroid Build Coastguard Worker     PW_ASYNC_STORE_WAKER(
74*61c4878aSAndroid Build Coastguard Worker         cx,
75*61c4878aSAndroid Build Coastguard Worker         cx.dispatcher().native().NativeAddReadWakerForFileDescriptor(
76*61c4878aSAndroid Build Coastguard Worker             channel_fd_),
77*61c4878aSAndroid Build Coastguard Worker         "EpollChannel is waiting on a file descriptor read");
78*61c4878aSAndroid Build Coastguard Worker     return async2::Pending();
79*61c4878aSAndroid Build Coastguard Worker   }
80*61c4878aSAndroid Build Coastguard Worker 
81*61c4878aSAndroid Build Coastguard Worker   return Status::Internal();
82*61c4878aSAndroid Build Coastguard Worker }
83*61c4878aSAndroid Build Coastguard Worker 
DoPendReadyToWrite(async2::Context & cx)84*61c4878aSAndroid Build Coastguard Worker async2::Poll<Status> EpollChannel::DoPendReadyToWrite(async2::Context& cx) {
85*61c4878aSAndroid Build Coastguard Worker   if (ready_to_write_) {
86*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
87*61c4878aSAndroid Build Coastguard Worker   }
88*61c4878aSAndroid Build Coastguard Worker   // The previous write operation failed. Block the task until the dispatcher
89*61c4878aSAndroid Build Coastguard Worker   // receives a notification for the channel's file descriptor.
90*61c4878aSAndroid Build Coastguard Worker   ready_to_write_ = true;
91*61c4878aSAndroid Build Coastguard Worker   PW_ASYNC_STORE_WAKER(
92*61c4878aSAndroid Build Coastguard Worker       cx,
93*61c4878aSAndroid Build Coastguard Worker       cx.dispatcher().native().NativeAddWriteWakerForFileDescriptor(
94*61c4878aSAndroid Build Coastguard Worker           channel_fd_),
95*61c4878aSAndroid Build Coastguard Worker       "EpollChannel is waiting on a file descriptor write");
96*61c4878aSAndroid Build Coastguard Worker   return async2::Pending();
97*61c4878aSAndroid Build Coastguard Worker }
98*61c4878aSAndroid Build Coastguard Worker 
DoStageWrite(multibuf::MultiBuf && data)99*61c4878aSAndroid Build Coastguard Worker Status EpollChannel::DoStageWrite(multibuf::MultiBuf&& data) {
100*61c4878aSAndroid Build Coastguard Worker   for (multibuf::Chunk& chunk : data.Chunks()) {
101*61c4878aSAndroid Build Coastguard Worker     if (write(channel_fd_, chunk.data(), chunk.size()) < 0) {
102*61c4878aSAndroid Build Coastguard Worker       if (errno == EAGAIN || errno == EWOULDBLOCK) {
103*61c4878aSAndroid Build Coastguard Worker         // The file descriptor is not currently available. The next call to
104*61c4878aSAndroid Build Coastguard Worker         // `PendReadyToWrite` will put the task to sleep until it is writable
105*61c4878aSAndroid Build Coastguard Worker         // again.
106*61c4878aSAndroid Build Coastguard Worker         ready_to_write_ = false;
107*61c4878aSAndroid Build Coastguard Worker         return Status::Unavailable();
108*61c4878aSAndroid Build Coastguard Worker       }
109*61c4878aSAndroid Build Coastguard Worker 
110*61c4878aSAndroid Build Coastguard Worker       PW_LOG_ERROR("Epoll channel write failed: %s", std::strerror(errno));
111*61c4878aSAndroid Build Coastguard Worker       return Status::Internal();
112*61c4878aSAndroid Build Coastguard Worker     }
113*61c4878aSAndroid Build Coastguard Worker   }
114*61c4878aSAndroid Build Coastguard Worker 
115*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker 
Cleanup()118*61c4878aSAndroid Build Coastguard Worker void EpollChannel::Cleanup() {
119*61c4878aSAndroid Build Coastguard Worker   if (is_read_or_write_open()) {
120*61c4878aSAndroid Build Coastguard Worker     dispatcher_->native()
121*61c4878aSAndroid Build Coastguard Worker         .NativeUnregisterFileDescriptor(channel_fd_)
122*61c4878aSAndroid Build Coastguard Worker         .IgnoreError();
123*61c4878aSAndroid Build Coastguard Worker     set_closed();
124*61c4878aSAndroid Build Coastguard Worker   }
125*61c4878aSAndroid Build Coastguard Worker   close(channel_fd_);
126*61c4878aSAndroid Build Coastguard Worker }
127*61c4878aSAndroid Build Coastguard Worker 
128*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::channel
129