1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <errno.h>
6 #include <poll.h>
7 #include <pthread.h>
8 #include <stdio.h>
9 #include <sys/time.h>
10
11 #include "nacl_io/error.h"
12 #include "nacl_io/event_listener.h"
13 #include "nacl_io/kernel_wrap.h"
14 #include "nacl_io/osstat.h"
15 #include "nacl_io/ostime.h"
16 #include "nacl_io/osunistd.h"
17
18 #include "sdk_util/auto_lock.h"
19
20 #if defined(WIN32)
21
22 #define USECS_FROM_WIN_TO_TO_UNIX_EPOCH 11644473600000LL
usec_since_epoch()23 static uint64_t usec_since_epoch() {
24 FILETIME ft;
25 ULARGE_INTEGER ularge;
26 GetSystemTimeAsFileTime(&ft);
27
28 ularge.LowPart = ft.dwLowDateTime;
29 ularge.HighPart = ft.dwHighDateTime;
30
31 // Truncate to usec resolution.
32 return ularge.QuadPart / 10;
33 }
34
35 #else
36
usec_since_epoch()37 static uint64_t usec_since_epoch() {
38 struct timeval tv;
39 gettimeofday(&tv, NULL);
40 return tv.tv_usec + (tv.tv_sec * 1000000);
41 }
42
43 #endif
44
45 namespace nacl_io {
46
EventListener()47 EventListener::EventListener() {
48 pthread_cond_init(&signal_cond_, NULL);
49 }
50
~EventListener()51 EventListener::~EventListener() {
52 pthread_cond_destroy(&signal_cond_);
53 }
54
AbsoluteFromDeltaMS(struct timespec * timeout,int ms_timeout)55 static void AbsoluteFromDeltaMS(struct timespec* timeout, int ms_timeout) {
56 if (ms_timeout >= 0) {
57 uint64_t usec = usec_since_epoch();
58 usec += ((int64_t)ms_timeout * 1000);
59
60 timeout->tv_nsec = (usec % 1000000) * 1000;
61 timeout->tv_sec = (usec / 1000000);
62 } else {
63 timeout->tv_sec = 0;
64 timeout->tv_nsec = 0;
65 }
66 }
67
EventListenerLock(EventEmitter * emitter)68 EventListenerLock::EventListenerLock(EventEmitter* emitter)
69 : EventListener(),
70 emitter_(emitter),
71 lock_(new sdk_util::AutoLock(emitter->GetLock())),
72 events_(0) {
73 }
74
~EventListenerLock()75 EventListenerLock::~EventListenerLock() {
76 delete lock_;
77 }
78
ReceiveEvents(EventEmitter * emitter,uint32_t events)79 void EventListenerLock::ReceiveEvents(EventEmitter* emitter, uint32_t events) {
80 // We are using the emitter's mutex, which is already locked.
81 pthread_cond_signal(&signal_cond_);
82 }
83
WaitOnEvent(uint32_t events,int ms_timeout)84 Error EventListenerLock::WaitOnEvent(uint32_t events, int ms_timeout) {
85 struct timespec timeout;
86 AbsoluteFromDeltaMS(&timeout, ms_timeout);
87
88 emitter_->RegisterListener_Locked(this, events);
89 while ((emitter_->GetEventStatus_Locked() & events) == 0) {
90 int return_code;
91 if (ms_timeout >= 0) {
92 return_code = pthread_cond_timedwait(&signal_cond_,
93 emitter_->GetLock().mutex(),
94 &timeout);
95 } else {
96 return_code = pthread_cond_wait(&signal_cond_,
97 emitter_->GetLock().mutex());
98 }
99
100 if (emitter_->GetEventStatus_Locked() & POLLERR)
101 return_code = EINTR;
102
103 // Return the failure, unlocked
104 if (return_code != 0) {
105 emitter_->UnregisterListener_Locked(this);
106 return Error(return_code);
107 }
108 }
109
110 emitter_->UnregisterListener_Locked(this);
111 return 0;
112 }
113
ReceiveEvents(EventEmitter * emitter,uint32_t events)114 void EventListenerPoll::ReceiveEvents(EventEmitter* emitter, uint32_t events) {
115 AUTO_LOCK(signal_lock_);
116 emitters_[emitter]->events |= events;
117 signaled_++;
118 pthread_cond_signal(&signal_cond_);
119 }
120
WaitOnAny(EventRequest * requests,size_t cnt,int ms_timeout)121 Error EventListenerPoll::WaitOnAny(EventRequest* requests,
122 size_t cnt,
123 int ms_timeout) {
124 signaled_ = 0;
125
126 // Build a map of request emitters to request data before
127 // emitters can access them.
128 for (size_t index = 0; index < cnt; index++) {
129 EventRequest* request = requests + index;
130 emitters_[request->emitter.get()] = request;
131 request->events = 0;
132 }
133
134 // Emitters can now accessed the unlocked set, since each emitter is
135 // responsible for it's own request.
136 for (size_t index = 0; index < cnt; index++) {
137 EventRequest* request = requests + index;
138 request->emitter->RegisterListener(this, request->filter);
139 uint32_t events = request->emitter->GetEventStatus() & request->filter;
140
141 if (events) {
142 AUTO_LOCK(signal_lock_);
143 request->events |= events;
144 signaled_++;
145 }
146 }
147
148 struct timespec timeout;
149 AbsoluteFromDeltaMS(&timeout, ms_timeout);
150 int return_code = 0;
151
152 {
153 AUTO_LOCK(signal_lock_)
154 while (0 == signaled_) {
155 if (ms_timeout >= 0) {
156 return_code = pthread_cond_timedwait(&signal_cond_,
157 signal_lock_.mutex(),
158 &timeout);
159 } else {
160 return_code = pthread_cond_wait(&signal_cond_,
161 signal_lock_.mutex());
162 }
163
164 if (return_code != 0)
165 signaled_++;
166 }
167 }
168
169 // Unregister first to prevent emitters from modifying the set any further
170 for (size_t index = 0; index < cnt; index++) {
171 EventRequest* request = requests + index;
172 request->emitter->UnregisterListener(this);
173
174 if (request->events & POLLERR)
175 return_code = EINTR;
176 }
177
178 // We can now release the map.
179 emitters_.clear();
180
181 return Error(return_code);
182 }
183
184 } // namespace nacl_io
185