win_object_handle_service.ipp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. //
  2. // detail/impl/win_object_handle_service.ipp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. // Copyright (c) 2011 Boris Schaeling (boris@highscore.de)
  7. //
  8. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. #ifndef ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP
  12. #define ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP
  13. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  14. # pragma once
  15. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  16. #include "asio/detail/config.hpp"
  17. #if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE)
  18. #include "asio/detail/win_object_handle_service.hpp"
  19. #include "asio/detail/push_options.hpp"
  20. namespace asio {
  21. namespace detail {
  22. win_object_handle_service::win_object_handle_service(execution_context& context)
  23. : execution_context_service_base<win_object_handle_service>(context),
  24. scheduler_(asio::use_service<scheduler_impl>(context)),
  25. mutex_(),
  26. impl_list_(0),
  27. shutdown_(false)
  28. {
  29. }
  30. void win_object_handle_service::shutdown()
  31. {
  32. mutex::scoped_lock lock(mutex_);
  33. // Setting this flag to true prevents new objects from being registered, and
  34. // new asynchronous wait operations from being started. We only need to worry
  35. // about cleaning up the operations that are currently in progress.
  36. shutdown_ = true;
  37. op_queue<operation> ops;
  38. for (implementation_type* impl = impl_list_; impl; impl = impl->next_)
  39. ops.push(impl->op_queue_);
  40. lock.unlock();
  41. scheduler_.abandon_operations(ops);
  42. }
  43. void win_object_handle_service::construct(
  44. win_object_handle_service::implementation_type& impl)
  45. {
  46. impl.handle_ = INVALID_HANDLE_VALUE;
  47. impl.wait_handle_ = INVALID_HANDLE_VALUE;
  48. impl.owner_ = this;
  49. // Insert implementation into linked list of all implementations.
  50. mutex::scoped_lock lock(mutex_);
  51. if (!shutdown_)
  52. {
  53. impl.next_ = impl_list_;
  54. impl.prev_ = 0;
  55. if (impl_list_)
  56. impl_list_->prev_ = &impl;
  57. impl_list_ = &impl;
  58. }
  59. }
  60. void win_object_handle_service::move_construct(
  61. win_object_handle_service::implementation_type& impl,
  62. win_object_handle_service::implementation_type& other_impl)
  63. {
  64. mutex::scoped_lock lock(mutex_);
  65. // Insert implementation into linked list of all implementations.
  66. if (!shutdown_)
  67. {
  68. impl.next_ = impl_list_;
  69. impl.prev_ = 0;
  70. if (impl_list_)
  71. impl_list_->prev_ = &impl;
  72. impl_list_ = &impl;
  73. }
  74. impl.handle_ = other_impl.handle_;
  75. other_impl.handle_ = INVALID_HANDLE_VALUE;
  76. impl.wait_handle_ = other_impl.wait_handle_;
  77. other_impl.wait_handle_ = INVALID_HANDLE_VALUE;
  78. impl.op_queue_.push(other_impl.op_queue_);
  79. impl.owner_ = this;
  80. // We must not hold the lock while calling UnregisterWaitEx. This is because
  81. // the registered callback function might be invoked while we are waiting for
  82. // UnregisterWaitEx to complete.
  83. lock.unlock();
  84. if (impl.wait_handle_ != INVALID_HANDLE_VALUE)
  85. ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE);
  86. if (!impl.op_queue_.empty())
  87. register_wait_callback(impl, lock);
  88. }
  89. void win_object_handle_service::move_assign(
  90. win_object_handle_service::implementation_type& impl,
  91. win_object_handle_service& other_service,
  92. win_object_handle_service::implementation_type& other_impl)
  93. {
  94. asio::error_code ignored_ec;
  95. close(impl, ignored_ec);
  96. mutex::scoped_lock lock(mutex_);
  97. if (this != &other_service)
  98. {
  99. // Remove implementation from linked list of all implementations.
  100. if (impl_list_ == &impl)
  101. impl_list_ = impl.next_;
  102. if (impl.prev_)
  103. impl.prev_->next_ = impl.next_;
  104. if (impl.next_)
  105. impl.next_->prev_= impl.prev_;
  106. impl.next_ = 0;
  107. impl.prev_ = 0;
  108. }
  109. impl.handle_ = other_impl.handle_;
  110. other_impl.handle_ = INVALID_HANDLE_VALUE;
  111. impl.wait_handle_ = other_impl.wait_handle_;
  112. other_impl.wait_handle_ = INVALID_HANDLE_VALUE;
  113. impl.op_queue_.push(other_impl.op_queue_);
  114. impl.owner_ = this;
  115. if (this != &other_service)
  116. {
  117. // Insert implementation into linked list of all implementations.
  118. impl.next_ = other_service.impl_list_;
  119. impl.prev_ = 0;
  120. if (other_service.impl_list_)
  121. other_service.impl_list_->prev_ = &impl;
  122. other_service.impl_list_ = &impl;
  123. }
  124. // We must not hold the lock while calling UnregisterWaitEx. This is because
  125. // the registered callback function might be invoked while we are waiting for
  126. // UnregisterWaitEx to complete.
  127. lock.unlock();
  128. if (impl.wait_handle_ != INVALID_HANDLE_VALUE)
  129. ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE);
  130. if (!impl.op_queue_.empty())
  131. register_wait_callback(impl, lock);
  132. }
  133. void win_object_handle_service::destroy(
  134. win_object_handle_service::implementation_type& impl)
  135. {
  136. mutex::scoped_lock lock(mutex_);
  137. // Remove implementation from linked list of all implementations.
  138. if (impl_list_ == &impl)
  139. impl_list_ = impl.next_;
  140. if (impl.prev_)
  141. impl.prev_->next_ = impl.next_;
  142. if (impl.next_)
  143. impl.next_->prev_= impl.prev_;
  144. impl.next_ = 0;
  145. impl.prev_ = 0;
  146. if (is_open(impl))
  147. {
  148. ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
  149. &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "close"));
  150. HANDLE wait_handle = impl.wait_handle_;
  151. impl.wait_handle_ = INVALID_HANDLE_VALUE;
  152. op_queue<operation> ops;
  153. while (wait_op* op = impl.op_queue_.front())
  154. {
  155. op->ec_ = asio::error::operation_aborted;
  156. impl.op_queue_.pop();
  157. ops.push(op);
  158. }
  159. // We must not hold the lock while calling UnregisterWaitEx. This is
  160. // because the registered callback function might be invoked while we are
  161. // waiting for UnregisterWaitEx to complete.
  162. lock.unlock();
  163. if (wait_handle != INVALID_HANDLE_VALUE)
  164. ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE);
  165. ::CloseHandle(impl.handle_);
  166. impl.handle_ = INVALID_HANDLE_VALUE;
  167. scheduler_.post_deferred_completions(ops);
  168. }
  169. }
  170. asio::error_code win_object_handle_service::assign(
  171. win_object_handle_service::implementation_type& impl,
  172. const native_handle_type& handle, asio::error_code& ec)
  173. {
  174. if (is_open(impl))
  175. {
  176. ec = asio::error::already_open;
  177. ASIO_ERROR_LOCATION(ec);
  178. return ec;
  179. }
  180. impl.handle_ = handle;
  181. ec = asio::error_code();
  182. return ec;
  183. }
  184. asio::error_code win_object_handle_service::close(
  185. win_object_handle_service::implementation_type& impl,
  186. asio::error_code& ec)
  187. {
  188. if (is_open(impl))
  189. {
  190. ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
  191. &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "close"));
  192. mutex::scoped_lock lock(mutex_);
  193. HANDLE wait_handle = impl.wait_handle_;
  194. impl.wait_handle_ = INVALID_HANDLE_VALUE;
  195. op_queue<operation> completed_ops;
  196. while (wait_op* op = impl.op_queue_.front())
  197. {
  198. impl.op_queue_.pop();
  199. op->ec_ = asio::error::operation_aborted;
  200. completed_ops.push(op);
  201. }
  202. // We must not hold the lock while calling UnregisterWaitEx. This is
  203. // because the registered callback function might be invoked while we are
  204. // waiting for UnregisterWaitEx to complete.
  205. lock.unlock();
  206. if (wait_handle != INVALID_HANDLE_VALUE)
  207. ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE);
  208. if (::CloseHandle(impl.handle_))
  209. {
  210. impl.handle_ = INVALID_HANDLE_VALUE;
  211. ec = asio::error_code();
  212. }
  213. else
  214. {
  215. DWORD last_error = ::GetLastError();
  216. ec = asio::error_code(last_error,
  217. asio::error::get_system_category());
  218. }
  219. scheduler_.post_deferred_completions(completed_ops);
  220. }
  221. else
  222. {
  223. ec = asio::error_code();
  224. }
  225. ASIO_ERROR_LOCATION(ec);
  226. return ec;
  227. }
  228. asio::error_code win_object_handle_service::cancel(
  229. win_object_handle_service::implementation_type& impl,
  230. asio::error_code& ec)
  231. {
  232. if (is_open(impl))
  233. {
  234. ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
  235. &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "cancel"));
  236. mutex::scoped_lock lock(mutex_);
  237. HANDLE wait_handle = impl.wait_handle_;
  238. impl.wait_handle_ = INVALID_HANDLE_VALUE;
  239. op_queue<operation> completed_ops;
  240. while (wait_op* op = impl.op_queue_.front())
  241. {
  242. op->ec_ = asio::error::operation_aborted;
  243. impl.op_queue_.pop();
  244. completed_ops.push(op);
  245. }
  246. // We must not hold the lock while calling UnregisterWaitEx. This is
  247. // because the registered callback function might be invoked while we are
  248. // waiting for UnregisterWaitEx to complete.
  249. lock.unlock();
  250. if (wait_handle != INVALID_HANDLE_VALUE)
  251. ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE);
  252. ec = asio::error_code();
  253. scheduler_.post_deferred_completions(completed_ops);
  254. }
  255. else
  256. {
  257. ec = asio::error::bad_descriptor;
  258. }
  259. ASIO_ERROR_LOCATION(ec);
  260. return ec;
  261. }
  262. void win_object_handle_service::wait(
  263. win_object_handle_service::implementation_type& impl,
  264. asio::error_code& ec)
  265. {
  266. switch (::WaitForSingleObject(impl.handle_, INFINITE))
  267. {
  268. case WAIT_FAILED:
  269. {
  270. DWORD last_error = ::GetLastError();
  271. ec = asio::error_code(last_error,
  272. asio::error::get_system_category());
  273. ASIO_ERROR_LOCATION(ec);
  274. break;
  275. }
  276. case WAIT_OBJECT_0:
  277. case WAIT_ABANDONED:
  278. default:
  279. ec = asio::error_code();
  280. break;
  281. }
  282. }
  283. void win_object_handle_service::start_wait_op(
  284. win_object_handle_service::implementation_type& impl, wait_op* op)
  285. {
  286. scheduler_.work_started();
  287. if (is_open(impl))
  288. {
  289. mutex::scoped_lock lock(mutex_);
  290. if (!shutdown_)
  291. {
  292. impl.op_queue_.push(op);
  293. // Only the first operation to be queued gets to register a wait callback.
  294. // Subsequent operations have to wait for the first to finish.
  295. if (impl.op_queue_.front() == op)
  296. register_wait_callback(impl, lock);
  297. }
  298. else
  299. {
  300. lock.unlock();
  301. scheduler_.post_deferred_completion(op);
  302. }
  303. }
  304. else
  305. {
  306. op->ec_ = asio::error::bad_descriptor;
  307. scheduler_.post_deferred_completion(op);
  308. }
  309. }
  310. void win_object_handle_service::register_wait_callback(
  311. win_object_handle_service::implementation_type& impl,
  312. mutex::scoped_lock& lock)
  313. {
  314. lock.lock();
  315. if (!RegisterWaitForSingleObject(&impl.wait_handle_,
  316. impl.handle_, &win_object_handle_service::wait_callback,
  317. &impl, INFINITE, WT_EXECUTEONLYONCE))
  318. {
  319. DWORD last_error = ::GetLastError();
  320. asio::error_code ec(last_error,
  321. asio::error::get_system_category());
  322. op_queue<operation> completed_ops;
  323. while (wait_op* op = impl.op_queue_.front())
  324. {
  325. op->ec_ = ec;
  326. impl.op_queue_.pop();
  327. completed_ops.push(op);
  328. }
  329. lock.unlock();
  330. scheduler_.post_deferred_completions(completed_ops);
  331. }
  332. }
  333. void win_object_handle_service::wait_callback(PVOID param, BOOLEAN)
  334. {
  335. implementation_type* impl = static_cast<implementation_type*>(param);
  336. mutex::scoped_lock lock(impl->owner_->mutex_);
  337. if (impl->wait_handle_ != INVALID_HANDLE_VALUE)
  338. {
  339. ::UnregisterWaitEx(impl->wait_handle_, NULL);
  340. impl->wait_handle_ = INVALID_HANDLE_VALUE;
  341. }
  342. if (wait_op* op = impl->op_queue_.front())
  343. {
  344. op_queue<operation> completed_ops;
  345. op->ec_ = asio::error_code();
  346. impl->op_queue_.pop();
  347. completed_ops.push(op);
  348. if (!impl->op_queue_.empty())
  349. {
  350. if (!RegisterWaitForSingleObject(&impl->wait_handle_,
  351. impl->handle_, &win_object_handle_service::wait_callback,
  352. param, INFINITE, WT_EXECUTEONLYONCE))
  353. {
  354. DWORD last_error = ::GetLastError();
  355. asio::error_code ec(last_error,
  356. asio::error::get_system_category());
  357. while ((op = impl->op_queue_.front()) != 0)
  358. {
  359. op->ec_ = ec;
  360. impl->op_queue_.pop();
  361. completed_ops.push(op);
  362. }
  363. }
  364. }
  365. scheduler_impl& sched = impl->owner_->scheduler_;
  366. lock.unlock();
  367. sched.post_deferred_completions(completed_ops);
  368. }
  369. }
  370. } // namespace detail
  371. } // namespace asio
  372. #include "asio/detail/pop_options.hpp"
  373. #endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE)
  374. #endif // ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP