/src/prod/src/Common/WaitHandle.cpp

https://github.com/Microsoft/service-fabric · C++ · 253 lines · 191 code · 54 blank · 8 comment · 24 complexity · 59f218d17a5beca778db61fee1c815ba MD5 · raw file

  1. // ------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
  4. // ------------------------------------------------------------
  5. #include "stdafx.h"
  6. #include "Common/WaitHandle.h"
  7. using namespace Common;
  8. static const StringLiteral TraceType("WaitHandle");
  9. template <bool ManualReset>
  10. bool WaitHandle<ManualReset>::WaitOne(uint timeout)
  11. {
  12. return Wait(timeout).IsSuccess();
  13. }
  14. template <bool ManualReset>
  15. bool WaitHandle<ManualReset>::WaitOne(TimeSpan timeout)
  16. {
  17. return Wait(timeout).IsSuccess();
  18. }
  19. static uint TimeSpanToMilliseconds(TimeSpan timeout)
  20. {
  21. return (timeout == TimeSpan::MaxValue) ? INFINITE : (uint)timeout.TotalPositiveMilliseconds();
  22. }
  23. template <bool ManualReset>
  24. ErrorCode WaitHandle<ManualReset>::Wait(TimeSpan timeout)
  25. {
  26. return Wait(TimeSpanToMilliseconds(timeout));
  27. }
  28. template <bool ManualReset>
  29. bool WaitHandle<ManualReset>::IsSet()
  30. {
  31. return WaitOne(0);
  32. }
  33. #ifdef PLATFORM_UNIX
  34. template <bool ManualReset>
  35. WaitHandle<ManualReset>::WaitHandle(bool initialState, std::wstring eventName) : signaled_(initialState)
  36. {
  37. // @TODO - eventName isn't actually supported in linux. we need to find another way
  38. UNREFERENCED_PARAMETER(eventName);
  39. pthread_cond_init(&cond_, nullptr);
  40. pthread_mutex_init(&mutex_, nullptr);
  41. }
  42. template <bool ManualReset>
  43. WaitHandle<ManualReset>::~WaitHandle()
  44. {
  45. Close();
  46. pthread_cond_destroy(&cond_);
  47. pthread_mutex_destroy(&mutex_);
  48. }
  49. template <bool ManualReset>
  50. void WaitHandle<ManualReset>::Close()
  51. {
  52. if (closed_) return;
  53. {
  54. pthread_mutex_lock(&mutex_);
  55. KFinally([this] { pthread_mutex_unlock(&mutex_); });
  56. if (closed_) return;
  57. closed_ = true;
  58. signaled_ = true;
  59. pthread_cond_broadcast(&cond_);
  60. }
  61. OnClose();
  62. }
  63. template <bool ManualReset>
  64. ErrorCode WaitHandle<ManualReset>::Wait(uint timeout)
  65. {
  66. timeval now = {};
  67. ZeroRetValAssert(gettimeofday(&now, nullptr));
  68. timespec to =
  69. {
  70. .tv_sec = now.tv_sec + (now.tv_usec + timeout * 1e3)/1e6,
  71. .tv_nsec = ((uint64)(now.tv_usec + timeout * 1e3) % 1000000) * 1e3
  72. };
  73. WriteNoise(
  74. TraceType, "{0}({1}): wait abstime: tv_sec = {2}, tv_nsec = {3}",
  75. TextTraceThis, ManualReset, to.tv_sec, to.tv_nsec);
  76. pthread_mutex_lock(&mutex_);
  77. KFinally([this] { pthread_mutex_unlock(&mutex_); });
  78. while (!signaled_ && !closed_)
  79. {
  80. // "to" is in absolute time (CLOCK_REALTIME), Linux will convert it to
  81. // CLOCK_MONOTONIC internally, which will not be affected by clock adjustment.
  82. // The small vulnerable window is between gettimeofday and the conversion.
  83. auto retval = pthread_cond_timedwait(&cond_, &mutex_, &to);
  84. if (retval == EINTR) continue;
  85. if (retval == ETIMEDOUT)
  86. {
  87. if (signaled_ || closed_) break;
  88. return ErrorCodeValue::Timeout;
  89. }
  90. }
  91. if (!ManualReset) signaled_ = false;
  92. return ErrorCodeValue::Success;
  93. }
  94. template <bool ManualReset>
  95. void WaitHandle<ManualReset>::swap(WaitHandle &rhs) throw()
  96. {
  97. std::swap(cond_, rhs.cond_);
  98. std::swap(mutex_, rhs.mutex_);
  99. std::swap(signaled_, rhs.signaled_);
  100. std::swap(closed_, rhs.closed_);
  101. }
  102. template <bool ManualReset>
  103. ErrorCode WaitHandle<ManualReset>::Set()
  104. {
  105. WriteNoise(TraceType, "{0}({1}): set", TextTraceThis, ManualReset);
  106. pthread_mutex_lock(&mutex_);
  107. KFinally([this] { pthread_mutex_unlock(&mutex_); });
  108. signaled_ = true;
  109. OnSetEvent();
  110. if (ManualReset)
  111. {
  112. pthread_cond_broadcast(&cond_);
  113. return ErrorCodeValue::Success;
  114. }
  115. pthread_cond_signal(&cond_);
  116. return ErrorCodeValue::Success;
  117. }
  118. template <bool ManualReset>
  119. ErrorCode WaitHandle<ManualReset>::Reset()
  120. {
  121. pthread_mutex_lock(&mutex_);
  122. KFinally([this] { pthread_mutex_unlock(&mutex_); });
  123. signaled_ = false;
  124. return ErrorCodeValue::Success;
  125. }
  126. #else
  127. template <bool ManualReset>
  128. WaitHandle<ManualReset>::WaitHandle(bool initialState, std::wstring eventName)
  129. {
  130. handle_ = CreateEventW(
  131. nullptr,
  132. ManualReset ? TRUE : FALSE,
  133. initialState,
  134. eventName.empty() ? nullptr : eventName.c_str());
  135. ASSERT_IFNOT(handle_, "CreateEvent failed: {0}", ErrorCode::FromWin32Error());
  136. }
  137. template <bool ManualReset>
  138. WaitHandle<ManualReset>::~WaitHandle()
  139. {
  140. Close();
  141. }
  142. template <bool ManualReset>
  143. void WaitHandle<ManualReset>::Close()
  144. {
  145. if ( nullptr != handle_)
  146. {
  147. closed_ = true;
  148. CHK_WBOOL( ::CloseHandle( handle_ ));
  149. handle_ = nullptr;
  150. }
  151. }
  152. template <bool ManualReset>
  153. ErrorCode WaitHandle<ManualReset>::Wait(uint timeout)
  154. {
  155. if (closed_) return ErrorCodeValue::Success;
  156. Invariant(handle_);
  157. auto status = ::WaitForSingleObject(handle_, timeout);
  158. if (status == WAIT_OBJECT_0)
  159. return ErrorCodeValue::Success;
  160. if (status == WAIT_TIMEOUT)
  161. return ErrorCodeValue::Timeout;
  162. if (status == WAIT_ABANDONED)
  163. THROW(WinError(status), "object was abandoned");
  164. THROW(WinError(::GetLastError()), "WaitForSingleObject failed");
  165. }
  166. template <bool ManualReset>
  167. void WaitHandle<ManualReset>::swap(WaitHandle &rhs) throw()
  168. {
  169. std::swap(handle_, rhs.handle_);
  170. std::swap(closed_, rhs.closed_);
  171. }
  172. template <bool ManualReset>
  173. ErrorCode WaitHandle<ManualReset>::Set()
  174. {
  175. ErrorCode err;
  176. if(!::SetEvent(handle_))
  177. {
  178. err = ErrorCode::FromWin32Error();
  179. TRACE_ERROR_AND_ASSERT(TraceType, "SetEvent failed: {0}", err);
  180. }
  181. return err;
  182. }
  183. template <bool ManualReset>
  184. ErrorCode WaitHandle<ManualReset>::Reset()
  185. {
  186. ErrorCode err;
  187. if (!::ResetEvent(handle_))
  188. {
  189. err = ErrorCode::FromWin32Error();
  190. TRACE_ERROR_AND_ASSERT(TraceType, "ResetEvent failed: {0}", err);
  191. }
  192. return err;
  193. }
  194. #endif
  195. namespace Common
  196. {
  197. template class WaitHandle<false>;
  198. template class WaitHandle<true>;
  199. }