• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <pdx/service_dispatcher.h>
2 
3 #include <errno.h>
4 #include <log/log.h>
5 #include <sys/epoll.h>
6 #include <sys/eventfd.h>
7 
8 #include <pdx/service.h>
9 #include <pdx/service_endpoint.h>
10 
11 static const int kMaxEventsPerLoop = 128;
12 
13 namespace android {
14 namespace pdx {
15 
Create()16 std::unique_ptr<ServiceDispatcher> ServiceDispatcher::Create() {
17   std::unique_ptr<ServiceDispatcher> dispatcher{new ServiceDispatcher()};
18   if (!dispatcher->epoll_fd_ || !dispatcher->event_fd_) {
19     dispatcher.reset();
20   }
21 
22   return dispatcher;
23 }
24 
ServiceDispatcher()25 ServiceDispatcher::ServiceDispatcher() {
26   event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
27   if (!event_fd_) {
28     ALOGE("Failed to create event fd because: %s\n", strerror(errno));
29     return;
30   }
31 
32   epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
33   if (!epoll_fd_) {
34     ALOGE("Failed to create epoll fd because: %s\n", strerror(errno));
35     return;
36   }
37 
38   // Use "this" as a unique pointer to distinguish the event fd from all
39   // the other entries that point to instances of Service.
40   epoll_event event;
41   event.events = EPOLLIN;
42   event.data.ptr = this;
43 
44   if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
45     ALOGE("Failed to add event fd to epoll fd because: %s\n", strerror(errno));
46 
47     // Close the fds here and signal failure to the factory method.
48     event_fd_.Close();
49     epoll_fd_.Close();
50   }
51 }
52 
~ServiceDispatcher()53 ServiceDispatcher::~ServiceDispatcher() { SetCanceled(true); }
54 
ThreadEnter()55 int ServiceDispatcher::ThreadEnter() {
56   std::lock_guard<std::mutex> autolock(mutex_);
57 
58   if (canceled_)
59     return -EBUSY;
60 
61   thread_count_++;
62   return 0;
63 }
64 
ThreadExit()65 void ServiceDispatcher::ThreadExit() {
66   std::lock_guard<std::mutex> autolock(mutex_);
67   thread_count_--;
68   condition_.notify_one();
69 }
70 
AddService(const std::shared_ptr<Service> & service)71 int ServiceDispatcher::AddService(const std::shared_ptr<Service>& service) {
72   std::lock_guard<std::mutex> autolock(mutex_);
73 
74   epoll_event event;
75   event.events = EPOLLIN;
76   event.data.ptr = service.get();
77 
78   if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, service->endpoint()->epoll_fd(),
79                 &event) < 0) {
80     ALOGE("Failed to add service to dispatcher because: %s\n", strerror(errno));
81     return -errno;
82   }
83 
84   services_.push_back(service);
85   return 0;
86 }
87 
RemoveService(const std::shared_ptr<Service> & service)88 int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) {
89   std::lock_guard<std::mutex> autolock(mutex_);
90 
91   // It's dangerous to remove a service while other threads may be using it.
92   if (thread_count_ > 0)
93     return -EBUSY;
94 
95   epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
96   if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, service->endpoint()->epoll_fd(),
97                 &dummy) < 0) {
98     ALOGE("Failed to remove service from dispatcher because: %s\n",
99           strerror(errno));
100     return -errno;
101   }
102 
103   services_.erase(std::remove(services_.begin(), services_.end(), service),
104                   services_.end());
105   return 0;
106 }
107 
ReceiveAndDispatch()108 int ServiceDispatcher::ReceiveAndDispatch() { return ReceiveAndDispatch(-1); }
109 
ReceiveAndDispatch(int timeout)110 int ServiceDispatcher::ReceiveAndDispatch(int timeout) {
111   int ret = ThreadEnter();
112   if (ret < 0)
113     return ret;
114 
115   epoll_event events[kMaxEventsPerLoop];
116 
117   int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, timeout);
118   if (count <= 0) {
119     ALOGE_IF(count < 0, "Failed to wait for epoll events because: %s\n",
120              strerror(errno));
121     ThreadExit();
122     return count < 0 ? -errno : -ETIMEDOUT;
123   }
124 
125   for (int i = 0; i < count; i++) {
126     if (events[i].data.ptr == this) {
127       ThreadExit();
128       return -EBUSY;
129     } else {
130       Service* service = static_cast<Service*>(events[i].data.ptr);
131 
132       ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
133                service->endpoint()->epoll_fd());
134       service->ReceiveAndDispatch();
135     }
136   }
137 
138   ThreadExit();
139   return 0;
140 }
141 
EnterDispatchLoop()142 int ServiceDispatcher::EnterDispatchLoop() {
143   int ret = ThreadEnter();
144   if (ret < 0)
145     return ret;
146 
147   epoll_event events[kMaxEventsPerLoop];
148 
149   while (!IsCanceled()) {
150     int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, -1);
151     if (count < 0 && errno != EINTR) {
152       ALOGE("Failed to wait for epoll events because: %s\n", strerror(errno));
153       ThreadExit();
154       return -errno;
155     }
156 
157     for (int i = 0; i < count; i++) {
158       if (events[i].data.ptr == this) {
159         ThreadExit();
160         return -EBUSY;
161       } else {
162         Service* service = static_cast<Service*>(events[i].data.ptr);
163 
164         ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
165                  service->endpoint()->epoll_fd());
166         service->ReceiveAndDispatch();
167       }
168     }
169   }
170 
171   ThreadExit();
172   return 0;
173 }
174 
SetCanceled(bool cancel)175 void ServiceDispatcher::SetCanceled(bool cancel) {
176   std::unique_lock<std::mutex> lock(mutex_);
177   canceled_ = cancel;
178 
179   if (canceled_ && thread_count_ > 0) {
180     eventfd_write(event_fd_.Get(), 1);  // Signal threads to quit.
181 
182     condition_.wait(lock, [this] { return !(canceled_ && thread_count_ > 0); });
183 
184     eventfd_t value;
185     eventfd_read(event_fd_.Get(), &value);  // Unsignal.
186   }
187 }
188 
IsCanceled() const189 bool ServiceDispatcher::IsCanceled() const { return canceled_; }
190 
191 }  // namespace pdx
192 }  // namespace android
193