1 // Copyright (c) 2016 Klemens D. Morgenstern 2 // 3 // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 #ifndef BOOST_PROCESS_WINDOWS_IO_CONTEXT_REF_HPP_ 7 #define BOOST_PROCESS_WINDOWS_IO_CONTEXT_REF_HPP_ 8 9 #include <boost/process/detail/handler_base.hpp> 10 #include <boost/process/detail/windows/async_handler.hpp> 11 #include <boost/asio/io_context.hpp> 12 #include <boost/asio/windows/object_handle.hpp> 13 #include <boost/winapi/process.hpp> 14 #include <boost/winapi/handles.hpp> 15 16 #include <boost/fusion/algorithm/iteration/for_each.hpp> 17 #include <boost/fusion/algorithm/transformation/filter_if.hpp> 18 #include <boost/fusion/algorithm/transformation/transform.hpp> 19 #include <boost/fusion/view/transform_view.hpp> 20 #include <boost/fusion/container/vector/convert.hpp> 21 22 23 #include <functional> 24 #include <type_traits> 25 #include <memory> 26 #include <atomic> 27 #include <vector> 28 29 #include <boost/type_index.hpp> 30 31 namespace boost { namespace process { namespace detail { namespace windows { 32 33 template<typename Executor> 34 struct on_exit_handler_transformer 35 { 36 Executor & exec; on_exit_handler_transformerboost::process::detail::windows::on_exit_handler_transformer37 on_exit_handler_transformer(Executor & exec) : exec(exec) {} 38 template<typename Sig> 39 struct result; 40 41 template<typename T> 42 struct result<on_exit_handler_transformer<Executor>(T&)> 43 { 44 typedef typename T::on_exit_handler_t type; 45 }; 46 47 template<typename T> operator ()boost::process::detail::windows::on_exit_handler_transformer48 auto operator()(T& t) const -> typename T::on_exit_handler_t 49 { 50 return t.on_exit_handler(exec); 51 } 52 }; 53 54 template<typename Executor> 55 struct async_handler_collector 56 { 57 Executor & exec; 58 std::vector<std::function<void(int, const std::error_code & ec)>> &handlers; 59 60 async_handler_collectorboost::process::detail::windows::async_handler_collector61 async_handler_collector(Executor & exec, 62 std::vector<std::function<void(int, const std::error_code & ec)>> &handlers) 63 : exec(exec), handlers(handlers) {} 64 65 template<typename T> operator ()boost::process::detail::windows::async_handler_collector66 void operator()(T & t) const 67 { 68 handlers.push_back(t.on_exit_handler(exec)); 69 } 70 }; 71 72 //Also set's up waiting for the exit, so it can close async stuff. 73 struct io_context_ref : boost::process::detail::handler_base 74 { 75 io_context_refboost::process::detail::windows::io_context_ref76 io_context_ref(boost::asio::io_context & ios) 77 : ios(ios) 78 { 79 } getboost::process::detail::windows::io_context_ref80 boost::asio::io_context &get() {return ios;}; 81 82 template <class Executor> on_successboost::process::detail::windows::io_context_ref83 void on_success(Executor& exec) const 84 { 85 auto asyncs = boost::fusion::filter_if< 86 is_async_handler< 87 typename std::remove_reference< boost::mpl::_ > ::type 88 >>(exec.seq); 89 90 //ok, check if there are actually any. 91 if (boost::fusion::empty(asyncs)) 92 { 93 return; 94 } 95 96 ::boost::winapi::PROCESS_INFORMATION_ & proc = exec.proc_info; 97 auto this_proc = ::boost::winapi::GetCurrentProcess(); 98 99 auto proc_in = proc.hProcess;; 100 ::boost::winapi::HANDLE_ process_handle; 101 102 if (!::boost::winapi::DuplicateHandle( 103 this_proc, proc_in, this_proc, &process_handle, 0, 104 static_cast<::boost::winapi::BOOL_>(true), 105 ::boost::winapi::DUPLICATE_SAME_ACCESS_)) 106 107 exec.set_error(::boost::process::detail::get_last_error(), 108 "Duplicate Pipe Failed"); 109 110 111 std::vector<std::function<void(int, const std::error_code & ec)>> funcs; 112 funcs.reserve(boost::fusion::size(asyncs)); 113 boost::fusion::for_each(asyncs, async_handler_collector<Executor>(exec, funcs)); 114 115 wait_handler wh(std::move(funcs), ios, process_handle, exec.exit_status); 116 117 auto handle_p = wh.handle.get(); 118 handle_p->async_wait(std::move(wh)); 119 } 120 121 122 struct wait_handler 123 { 124 std::vector<std::function<void(int, const std::error_code & ec)>> funcs; 125 std::unique_ptr<boost::asio::windows::object_handle> handle; 126 std::shared_ptr<std::atomic<int>> exit_status; 127 wait_handler(const wait_handler & ) = delete; 128 wait_handler(wait_handler && ) = default; wait_handlerboost::process::detail::windows::io_context_ref::wait_handler129 wait_handler(std::vector<std::function<void(int, const std::error_code & ec)>> && funcs, 130 boost::asio::io_context & ios, void * handle, 131 const std::shared_ptr<std::atomic<int>> &exit_status) 132 : funcs(std::move(funcs)), 133 handle(new boost::asio::windows::object_handle(ios.get_executor(), handle)), 134 exit_status(exit_status) 135 { 136 137 } operator ()boost::process::detail::windows::io_context_ref::wait_handler138 void operator()(const boost::system::error_code & ec_in) 139 { 140 std::error_code ec; 141 if (ec_in) 142 ec = std::error_code(ec_in.value(), std::system_category()); 143 144 ::boost::winapi::DWORD_ code; 145 ::boost::winapi::GetExitCodeProcess(handle->native_handle(), &code); 146 exit_status->store(code); 147 148 for (auto & func : funcs) 149 func(code, ec); 150 } 151 152 }; 153 154 private: 155 boost::asio::io_context &ios; 156 }; 157 158 }}}} 159 160 #endif /* BOOST_PROCESS_WINDOWS_IO_CONTEXT_REF_HPP_ */ 161