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