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