• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "common/libs/fs/epoll.h"
18 
19 #include <sys/epoll.h>
20 
21 #include <memory>
22 #include <optional>
23 #include <set>
24 #include <shared_mutex>
25 
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/utils/result.h"
28 
29 namespace cuttlefish {
30 
Create()31 Result<Epoll> Epoll::Create() {
32   int fd = epoll_create1(EPOLL_CLOEXEC);
33   if (fd == -1) {
34     return CF_ERRNO("Failed to create epoll");
35   }
36   SharedFD shared{std::shared_ptr<FileInstance>(new FileInstance(fd, 0))};
37   return Epoll(shared);
38 }
39 
40 Epoll::Epoll() = default;
41 
Epoll(SharedFD epoll_fd)42 Epoll::Epoll(SharedFD epoll_fd) : epoll_fd_(epoll_fd) {}
43 
Epoll(Epoll && other)44 Epoll::Epoll(Epoll&& other) {
45   std::unique_lock own_watched(watched_mutex_, std::defer_lock);
46   std::unique_lock own_epoll(epoll_mutex_, std::defer_lock);
47   std::unique_lock other_epoll(other.epoll_mutex_, std::defer_lock);
48   std::unique_lock other_watched(other.watched_mutex_, std::defer_lock);
49   std::lock(own_watched, own_epoll, other_epoll, other_watched);
50 
51   epoll_fd_ = std::move(other.epoll_fd_);
52   watched_ = std::move(other.watched_);
53 }
54 
operator =(Epoll && other)55 Epoll& Epoll::operator=(Epoll&& other) {
56   std::unique_lock own_watched(watched_mutex_, std::defer_lock);
57   std::unique_lock own_epoll(epoll_mutex_, std::defer_lock);
58   std::unique_lock other_epoll(other.epoll_mutex_, std::defer_lock);
59   std::unique_lock other_watched(other.watched_mutex_, std::defer_lock);
60   std::lock(own_watched, own_epoll, other_epoll, other_watched);
61 
62   epoll_fd_ = std::move(other.epoll_fd_);
63   watched_ = std::move(other.watched_);
64   return *this;
65 }
66 
Add(SharedFD fd,uint32_t events)67 Result<void> Epoll::Add(SharedFD fd, uint32_t events) {
68   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
69   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
70   std::lock(watched_lock, epoll_lock);
71   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
72 
73   if (watched_.count(fd) != 0) {
74     return CF_ERRNO("Watched set already contains fd");
75   }
76   epoll_event event;
77   event.events = events;
78   event.data.fd = fd->fd_;
79   int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_ADD, fd->fd_, &event);
80   if (success != 0 && errno == EEXIST) {
81     // We're already tracking this fd, don't drop it from the set.
82     return CF_ERRNO("epoll_ctl: File descriptor was already present");
83   } else if (success != 0) {
84     return CF_ERRNO("epoll_ctl: Add failed");
85   }
86   watched_.insert(fd);
87   return {};
88 }
89 
AddOrModify(SharedFD fd,uint32_t events)90 Result<void> Epoll::AddOrModify(SharedFD fd, uint32_t events) {
91   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
92   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
93   std::lock(watched_lock, epoll_lock);
94   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
95 
96   epoll_event event;
97   event.events = events;
98   event.data.fd = fd->fd_;
99   int operation = watched_.count(fd) == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
100   int success = epoll_ctl(epoll_fd_->fd_, operation, fd->fd_, &event);
101   if (success != 0) {
102     std::string operation_str = operation == EPOLL_CTL_ADD ? "add" : "modify";
103     return CF_ERRNO("epoll_ctl: Operation " << operation_str << " failed");
104   }
105   watched_.insert(fd);
106   return {};
107 }
108 
Modify(SharedFD fd,uint32_t events)109 Result<void> Epoll::Modify(SharedFD fd, uint32_t events) {
110   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
111   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
112   std::lock(watched_lock, epoll_lock);
113   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
114 
115   if (watched_.count(fd) == 0) {
116     return CF_ERR("Watched set did not contain fd");
117   }
118   epoll_event event;
119   event.events = events;
120   event.data.fd = fd->fd_;
121   int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_MOD, fd->fd_, &event);
122   if (success != 0) {
123     return CF_ERRNO("epoll_ctl: Modify failed");
124   }
125   return {};
126 }
127 
Delete(SharedFD fd)128 Result<void> Epoll::Delete(SharedFD fd) {
129   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
130   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
131   std::lock(watched_lock, epoll_lock);
132   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
133 
134   if (watched_.count(fd) == 0) {
135     return CF_ERR("Watched set did not contain fd");
136   }
137   int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_DEL, fd->fd_, nullptr);
138   if (success != 0) {
139     return CF_ERRNO("epoll_ctl: Delete failed");
140   }
141   watched_.erase(fd);
142   return {};
143 }
144 
Wait()145 Result<std::optional<EpollEvent>> Epoll::Wait() {
146   epoll_event event;
147   int success;
148   {
149     std::shared_lock lock(epoll_mutex_);
150     CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
151     success = epoll_wait(epoll_fd_->fd_, &event, 1, -1);
152   }
153   if (success == -1) {
154     return CF_ERRNO("epoll_wait failed");
155   } else if (success == 0) {
156     return {};
157   } else if (success != 1) {
158     return CF_ERR("epoll_wait returned an unexpected value");
159   }
160   EpollEvent ret;
161   ret.events = event.events;
162   std::shared_lock lock(watched_mutex_);
163   for (const auto& watched : watched_) {
164     if (watched->fd_ == event.data.fd) {
165       ret.fd = watched;
166       break;
167     }
168   }
169   if (!ret.fd->IsOpen()) {
170     // Couldn't find the matching SharedFD to the file descriptor. We probably
171     // lost the race to lock watched_mutex_ against a delete call. Treat this
172     // as a spurious wakeup.
173     return {};
174   }
175   return ret;
176 }
177 
178 }  // namespace cuttlefish
179