1 //
2 // detail/winrt_async_manager.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #ifndef BOOST_ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP
12 #define BOOST_ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include <boost/asio/detail/config.hpp>
19 
20 #if defined(BOOST_ASIO_WINDOWS_RUNTIME)
21 
22 #include <future>
23 #include <boost/asio/detail/atomic_count.hpp>
24 #include <boost/asio/detail/winrt_async_op.hpp>
25 #include <boost/asio/error.hpp>
26 #include <boost/asio/execution_context.hpp>
27 
28 #if defined(BOOST_ASIO_HAS_IOCP)
29 # include <boost/asio/detail/win_iocp_io_context.hpp>
30 #else // defined(BOOST_ASIO_HAS_IOCP)
31 # include <boost/asio/detail/scheduler.hpp>
32 #endif // defined(BOOST_ASIO_HAS_IOCP)
33 
34 #include <boost/asio/detail/push_options.hpp>
35 
36 namespace boost {
37 namespace asio {
38 namespace detail {
39 
40 class winrt_async_manager
41   : public execution_context_service_base<winrt_async_manager>
42 {
43 public:
44   // Constructor.
winrt_async_manager(execution_context & context)45   winrt_async_manager(execution_context& context)
46     : execution_context_service_base<winrt_async_manager>(context),
47       scheduler_(use_service<scheduler_impl>(context)),
48       outstanding_ops_(1)
49   {
50   }
51 
52   // Destructor.
~winrt_async_manager()53   ~winrt_async_manager()
54   {
55   }
56 
57   // Destroy all user-defined handler objects owned by the service.
shutdown()58   void shutdown()
59   {
60     if (--outstanding_ops_ > 0)
61     {
62       // Block until last operation is complete.
63       std::future<void> f = promise_.get_future();
64       f.wait();
65     }
66   }
67 
68   void sync(Windows::Foundation::IAsyncAction^ action,
69       boost::system::error_code& ec)
70   {
71     using namespace Windows::Foundation;
72     using Windows::Foundation::AsyncStatus;
73 
74     auto promise = std::make_shared<std::promise<boost::system::error_code>>();
75     auto future = promise->get_future();
76 
77     action->Completed = ref new AsyncActionCompletedHandler(
78       [promise](IAsyncAction^ action, AsyncStatus status)
__anona656aa600102(IAsyncAction^ action, AsyncStatus status) 79       {
80         switch (status)
81         {
82         case AsyncStatus::Canceled:
83           promise->set_value(boost::asio::error::operation_aborted);
84           break;
85         case AsyncStatus::Error:
86         case AsyncStatus::Completed:
87         default:
88           boost::system::error_code ec(
89               action->ErrorCode.Value,
90               boost::system::system_category());
91           promise->set_value(ec);
92           break;
93         }
94       });
95 
96     ec = future.get();
97   }
98 
99   template <typename TResult>
100   TResult sync(Windows::Foundation::IAsyncOperation<TResult>^ operation,
101       boost::system::error_code& ec)
102   {
103     using namespace Windows::Foundation;
104     using Windows::Foundation::AsyncStatus;
105 
106     auto promise = std::make_shared<std::promise<boost::system::error_code>>();
107     auto future = promise->get_future();
108 
109     operation->Completed = ref new AsyncOperationCompletedHandler<TResult>(
110       [promise](IAsyncOperation<TResult>^ operation, AsyncStatus status)
__anona656aa600202(IAsyncOperation<TResult>^ operation, AsyncStatus status) 111       {
112         switch (status)
113         {
114         case AsyncStatus::Canceled:
115           promise->set_value(boost::asio::error::operation_aborted);
116           break;
117         case AsyncStatus::Error:
118         case AsyncStatus::Completed:
119         default:
120           boost::system::error_code ec(
121               operation->ErrorCode.Value,
122               boost::system::system_category());
123           promise->set_value(ec);
124           break;
125         }
126       });
127 
128     ec = future.get();
129     return operation->GetResults();
130   }
131 
132   template <typename TResult, typename TProgress>
133   TResult sync(
134       Windows::Foundation::IAsyncOperationWithProgress<
135         TResult, TProgress>^ operation,
136       boost::system::error_code& ec)
137   {
138     using namespace Windows::Foundation;
139     using Windows::Foundation::AsyncStatus;
140 
141     auto promise = std::make_shared<std::promise<boost::system::error_code>>();
142     auto future = promise->get_future();
143 
144     operation->Completed
145       = ref new AsyncOperationWithProgressCompletedHandler<TResult, TProgress>(
146         [promise](IAsyncOperationWithProgress<TResult, TProgress>^ operation,
147           AsyncStatus status)
__anona656aa600302(IAsyncOperationWithProgress<TResult, TProgress>^ operation, AsyncStatus status) 148         {
149           switch (status)
150           {
151           case AsyncStatus::Canceled:
152             promise->set_value(boost::asio::error::operation_aborted);
153             break;
154           case AsyncStatus::Started:
155             break;
156           case AsyncStatus::Error:
157           case AsyncStatus::Completed:
158           default:
159             boost::system::error_code ec(
160                 operation->ErrorCode.Value,
161                 boost::system::system_category());
162             promise->set_value(ec);
163             break;
164           }
165         });
166 
167     ec = future.get();
168     return operation->GetResults();
169   }
170 
171   void async(Windows::Foundation::IAsyncAction^ action,
172       winrt_async_op<void>* handler)
173   {
174     using namespace Windows::Foundation;
175     using Windows::Foundation::AsyncStatus;
176 
177     auto on_completed = ref new AsyncActionCompletedHandler(
178       [this, handler](IAsyncAction^ action, AsyncStatus status)
__anona656aa600402(IAsyncAction^ action, AsyncStatus status) 179       {
180         switch (status)
181         {
182         case AsyncStatus::Canceled:
183           handler->ec_ = boost::asio::error::operation_aborted;
184           break;
185         case AsyncStatus::Started:
186           return;
187         case AsyncStatus::Completed:
188         case AsyncStatus::Error:
189         default:
190           handler->ec_ = boost::system::error_code(
191               action->ErrorCode.Value,
192               boost::system::system_category());
193           break;
194         }
195         scheduler_.post_deferred_completion(handler);
196         if (--outstanding_ops_ == 0)
197           promise_.set_value();
198       });
199 
200     scheduler_.work_started();
201     ++outstanding_ops_;
202     action->Completed = on_completed;
203   }
204 
205   template <typename TResult>
206   void async(Windows::Foundation::IAsyncOperation<TResult>^ operation,
207       winrt_async_op<TResult>* handler)
208   {
209     using namespace Windows::Foundation;
210     using Windows::Foundation::AsyncStatus;
211 
212     auto on_completed = ref new AsyncOperationCompletedHandler<TResult>(
213       [this, handler](IAsyncOperation<TResult>^ operation, AsyncStatus status)
__anona656aa600502(IAsyncOperation<TResult>^ operation, AsyncStatus status) 214       {
215         switch (status)
216         {
217         case AsyncStatus::Canceled:
218           handler->ec_ = boost::asio::error::operation_aborted;
219           break;
220         case AsyncStatus::Started:
221           return;
222         case AsyncStatus::Completed:
223           handler->result_ = operation->GetResults();
224           // Fall through.
225         case AsyncStatus::Error:
226         default:
227           handler->ec_ = boost::system::error_code(
228               operation->ErrorCode.Value,
229               boost::system::system_category());
230           break;
231         }
232         scheduler_.post_deferred_completion(handler);
233         if (--outstanding_ops_ == 0)
234           promise_.set_value();
235       });
236 
237     scheduler_.work_started();
238     ++outstanding_ops_;
239     operation->Completed = on_completed;
240   }
241 
242   template <typename TResult, typename TProgress>
243   void async(
244       Windows::Foundation::IAsyncOperationWithProgress<
245         TResult, TProgress>^ operation,
246       winrt_async_op<TResult>* handler)
247   {
248     using namespace Windows::Foundation;
249     using Windows::Foundation::AsyncStatus;
250 
251     auto on_completed
252       = ref new AsyncOperationWithProgressCompletedHandler<TResult, TProgress>(
253         [this, handler](IAsyncOperationWithProgress<
254           TResult, TProgress>^ operation, AsyncStatus status)
__anona656aa600602(IAsyncOperationWithProgress< TResult, TProgress>^ operation, AsyncStatus status) 255         {
256           switch (status)
257           {
258           case AsyncStatus::Canceled:
259             handler->ec_ = boost::asio::error::operation_aborted;
260             break;
261           case AsyncStatus::Started:
262             return;
263           case AsyncStatus::Completed:
264             handler->result_ = operation->GetResults();
265             // Fall through.
266           case AsyncStatus::Error:
267           default:
268             handler->ec_ = boost::system::error_code(
269                 operation->ErrorCode.Value,
270                 boost::system::system_category());
271             break;
272           }
273           scheduler_.post_deferred_completion(handler);
274           if (--outstanding_ops_ == 0)
275             promise_.set_value();
276         });
277 
278     scheduler_.work_started();
279     ++outstanding_ops_;
280     operation->Completed = on_completed;
281   }
282 
283 private:
284   // The scheduler implementation used to post completed handlers.
285 #if defined(BOOST_ASIO_HAS_IOCP)
286   typedef class win_iocp_io_context scheduler_impl;
287 #else
288   typedef class scheduler scheduler_impl;
289 #endif
290   scheduler_impl& scheduler_;
291 
292   // Count of outstanding operations.
293   atomic_count outstanding_ops_;
294 
295   // Used to keep wait for outstanding operations to complete.
296   std::promise<void> promise_;
297 };
298 
299 } // namespace detail
300 } // namespace asio
301 } // namespace boost
302 
303 #include <boost/asio/detail/pop_options.hpp>
304 
305 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME)
306 
307 #endif // BOOST_ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP
308