1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "base/unnamed_event.h"
31
32 #ifdef OS_WIN
33 #include <windows.h>
34 #else // OS_WIN
35 #include <errno.h>
36 #include <pthread.h>
37 #include <sys/time.h>
38 #endif // OS_WIN
39
40 #include "base/logging.h"
41 #include "base/port.h"
42
43 namespace mozc {
44
45 #ifdef OS_WIN
UnnamedEvent()46 UnnamedEvent::UnnamedEvent()
47 : handle_(::CreateEvent(NULL, FALSE, FALSE, NULL)) {
48 // Use Auto reset mode of Win32 Event. (2nd arg of CreateEvent).
49 // pthread_cond_signal: auto reset mode.
50 // pthread_cond_broadcast: manual reset mode.
51 if (NULL == handle_.get()) {
52 LOG(ERROR) << "CreateEvent failed: " << ::GetLastError();
53 }
54 }
55
~UnnamedEvent()56 UnnamedEvent::~UnnamedEvent() {}
57
IsAvailable() const58 bool UnnamedEvent::IsAvailable() const {
59 return (NULL != handle_.get());
60 }
61
Notify()62 bool UnnamedEvent::Notify() {
63 if (!IsAvailable()) {
64 LOG(WARNING) << "Event object is not available";
65 return false;
66 }
67
68 if (!::SetEvent(handle_.get())) {
69 LOG(ERROR) << "SetEvent failed: " << ::GetLastError();
70 return false;
71 }
72
73 return true;
74 }
75
Wait(int msec)76 bool UnnamedEvent::Wait(int msec) {
77 if (!IsAvailable()) {
78 return true; // assume that it is already raised
79 }
80
81 if (msec < 0) {
82 msec = INFINITE;
83 }
84
85 return WAIT_TIMEOUT !=
86 ::WaitForSingleObject(handle_.get(), msec);
87 }
88
89 #else // OS_WIN
90
91 namespace {
92 class ScopedPthreadMutexLock {
93 public:
94 explicit ScopedPthreadMutexLock(pthread_mutex_t *mutex) : mutex_(mutex) {
95 pthread_mutex_lock(mutex_);
96 }
97 ~ScopedPthreadMutexLock() {
98 pthread_mutex_unlock(mutex_);
99 }
100
101 private:
102 pthread_mutex_t *mutex_;
103 DISALLOW_COPY_AND_ASSIGN(ScopedPthreadMutexLock);
104 };
105 } // namespace
106
107 UnnamedEvent::UnnamedEvent() : notified_(false) {
108 pthread_mutex_init(&mutex_, NULL);
109 pthread_cond_init(&cond_, NULL);
110 }
111
112 // It is necessary to ensure that no threads wait for this event before the
113 // destruction.
114 UnnamedEvent::~UnnamedEvent() {
115 pthread_mutex_destroy(&mutex_);
116 pthread_cond_destroy(&cond_);
117 }
118
119 bool UnnamedEvent::IsAvailable() const {
120 return true;
121 }
122
123 bool UnnamedEvent::Notify() {
124 {
125 ScopedPthreadMutexLock lock(&mutex_);
126 notified_ = true;
127 }
128
129 // Note: Need to awake all threads waiting for this event. Otherwise
130 // some thread would start to work incorrectly in some cases.
131 // An example error scenario is:
132 // - there are four threads, A, B, C and D.
133 // - A and B are producers. C and D are consumers.
134 // - C and D start to wait this event.
135 // - Then A notifies.
136 // - A takes the lock.
137 // - set notified_ true.
138 // - A releases the lock.
139 // - Then before A sends a signal to awake a thread (either C or D),
140 // B takes the lock.
141 // - A sends a signal, but both C and D are sleeping as the lock is
142 // still taken by B.
143 // - B releases the lock. So one of the thread (let's assume C for this
144 // example) starts to run, because it can take the lock.
145 // - B also sends a signal again.
146 // - C overrides notified_ to false, and releases the lock.
147 // - At last, D starts to run wrongly.
148 // The broadcast and while (in Wait) idiom is a popular way to fix such
149 // cases.
150 pthread_cond_broadcast(&cond_);
151 return true;
152 }
153
154 bool UnnamedEvent::Wait(int msec) {
155 ScopedPthreadMutexLock lock(&mutex_);
156 if (!notified_) {
157 // Need to wait actually.
158 if (msec < 0) {
159 // Wait forever.
160 while (!notified_) {
161 pthread_cond_wait(&cond_, &mutex_);
162 }
163 } else {
164 // Wait with time out.
165 struct timeval tv;
166 if (gettimeofday(&tv, NULL) != 0) {
167 LOG(ERROR) << "Failed to take the current time: " << errno;
168 return false;
169 }
170
171 struct timespec timeout;
172 timeout.tv_sec = tv.tv_sec + msec / 1000;
173 timeout.tv_nsec = 1000 * (tv.tv_usec + 1000 * (msec % 1000));
174
175 // if tv_nsec >= 10^9, pthread_cond_timedwait may return EINVAL
176 while (timeout.tv_nsec >= 1000000000) {
177 timeout.tv_sec++;
178 timeout.tv_nsec -= 1000000000;
179 }
180
181 int result = 0;
182 while (!notified_ && result == 0) {
183 result = pthread_cond_timedwait(&cond_, &mutex_, &timeout);
184 }
185
186 if (result != 0) {
187 // Time out.
188 return false;
189 }
190 }
191 }
192 DCHECK(notified_);
193 notified_ = false;
194 return true;
195 }
196 #endif // OS_WIN
197 } // namespace mozc
198