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 /** 7 * \file boost/process/group.hpp 8 * 9 * Defines a group process class. 10 * For additional information see the platform specific implementations: 11 * 12 * - [windows - job object](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684161.aspx) 13 * - [posix - process group](http://pubs.opengroup.org/onlinepubs/009695399/functions/setpgid.html) 14 * 15 */ 16 17 #ifndef BOOST_PROCESS_GROUP_HPP 18 #define BOOST_PROCESS_GROUP_HPP 19 20 #include <boost/process/detail/config.hpp> 21 #include <boost/process/child.hpp> 22 #include <chrono> 23 #include <memory> 24 25 #include <boost/none.hpp> 26 #include <atomic> 27 28 29 #if defined(BOOST_POSIX_API) 30 #include <boost/process/detail/posix/group_handle.hpp> 31 #include <boost/process/detail/posix/group_ref.hpp> 32 #include <boost/process/detail/posix/wait_group.hpp> 33 #elif defined(BOOST_WINDOWS_API) 34 #include <boost/process/detail/windows/group_handle.hpp> 35 #include <boost/process/detail/windows/group_ref.hpp> 36 #include <boost/process/detail/windows/wait_group.hpp> 37 #endif 38 39 namespace boost { 40 41 namespace process { 42 43 namespace detail { 44 struct group_builder; 45 } 46 47 /** 48 * Represents a process group. 49 * 50 * Groups are movable but non-copyable. The destructor 51 * automatically closes handles to the group process. 52 * 53 * The group will have the same interface as std::thread. 54 * 55 * \note If the destructor is called without a previous detach or wait, the group will be terminated. 56 * 57 * \attention If a default-constructed group is used before being used in a process launch, the behaviour is undefined. 58 * 59 * \attention Waiting for groups is currently broken on windows and will most likely result in a dead-lock. 60 */ 61 class group 62 { 63 ::boost::process::detail::api::group_handle _group_handle; 64 bool _attached = true; 65 public: 66 typedef ::boost::process::detail::api::group_handle group_handle; 67 ///Native representation of the handle. 68 typedef group_handle::handle_t native_handle_t; group(group_handle && ch)69 explicit group(group_handle &&ch) : _group_handle(std::move(ch)) {} 70 ///Construct the group from a native_handle group(native_handle_t & handle)71 explicit group(native_handle_t & handle) : _group_handle(handle) {}; 72 group(const group&) = delete; 73 ///Move constructor group(group && lhs)74 group(group && lhs) 75 : _group_handle(std::move(lhs._group_handle)), 76 _attached (lhs._attached) 77 { 78 lhs._attached = false; 79 } 80 ///Default constructor 81 group() = default; 82 group& operator=(const group&) = delete; 83 ///Move assign operator =(group && lhs)84 group& operator=(group && lhs) 85 { 86 _group_handle= std::move(lhs._group_handle); 87 _attached = lhs._attached; 88 89 return *this; 90 }; 91 92 ///Detach the group detach()93 void detach() {_attached = false; } 94 95 /** Join the child. This just calls wait, but that way the naming is similar to std::thread */ join()96 void join() {wait();} 97 /** Check if the child is joinable. */ joinable()98 bool joinable() {return _attached;} 99 100 /** Destructor 101 * 102 * \note If the destructor is called without a previous detach or wait, the group will be terminated. 103 * 104 */ ~group()105 ~group() 106 { 107 std::error_code ec; 108 if ( _attached && valid()) 109 terminate(ec); 110 } 111 112 ///Obtain the native handle of the group. native_handle() const113 native_handle_t native_handle() const { return _group_handle.handle(); } 114 115 ///Wait for the process group to exit. wait()116 void wait() 117 { 118 boost::process::detail::api::wait(_group_handle); 119 } 120 ///\overload void wait() wait(std::error_code & ec)121 void wait(std::error_code & ec) noexcept 122 { 123 boost::process::detail::api::wait(_group_handle, ec); 124 } 125 /** Wait for the process group to exit for period of time. 126 * \return True if all child processes exited while waiting.*/ 127 template< class Rep, class Period > wait_for(const std::chrono::duration<Rep,Period> & rel_time)128 bool wait_for (const std::chrono::duration<Rep, Period>& rel_time) 129 { 130 return boost::process::detail::api::wait_for(_group_handle, rel_time); 131 } 132 133 /** \overload bool wait_for(const std::chrono::duration<Rep, Period>& timeout_time ) */ 134 template< class Rep, class Period > wait_for(const std::chrono::duration<Rep,Period> & rel_time,std::error_code & ec)135 bool wait_for (const std::chrono::duration<Rep, Period>& rel_time, std::error_code & ec) noexcept 136 { 137 return boost::process::detail::api::wait_for(_group_handle, rel_time, ec); 138 } 139 140 /** Wait for the process group to exit until a point in time. 141 * \return True if all child processes exited while waiting.*/ 142 template< class Clock, class Duration > wait_until(const std::chrono::time_point<Clock,Duration> & timeout_time)143 bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time ) 144 { 145 return boost::process::detail::api::wait_until(_group_handle, timeout_time); 146 } 147 /** \overload bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time ) */ 148 template< class Clock, class Duration > wait_until(const std::chrono::time_point<Clock,Duration> & timeout_time,std::error_code & ec)149 bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time, std::error_code & ec) noexcept 150 { 151 return boost::process::detail::api::wait_until(_group_handle, timeout_time, ec); 152 } 153 154 ///Check if the group has a valid handle. valid() const155 bool valid() const 156 { 157 return _group_handle.valid(); 158 } 159 ///Convenience to call valid. operator bool() const160 explicit operator bool() const {return valid();} 161 162 ///Terminate the process group, i.e. all processes in the group terminate()163 void terminate() 164 { 165 ::boost::process::detail::api::terminate(_group_handle); 166 } 167 ///\overload void terminate() terminate(std::error_code & ec)168 void terminate(std::error_code & ec) noexcept 169 { 170 ::boost::process::detail::api::terminate(_group_handle, ec); 171 } 172 173 ///Assign a child process to the group add(const child & c)174 void add(const child &c) 175 { 176 _group_handle.add(c.native_handle()); 177 } 178 ///\overload void assign(const child & c) add(const child & c,std::error_code & ec)179 void add(const child &c, std::error_code & ec) noexcept 180 { 181 _group_handle.add(c.native_handle(), ec); 182 } 183 184 ///Check if the child process is in the group has(const child & c)185 bool has(const child &c) 186 { 187 return _group_handle.has(c.native_handle()); 188 } 189 ///\overload bool has(const child &) has(const child & c,std::error_code & ec)190 bool has(const child &c, std::error_code & ec) noexcept 191 { 192 return _group_handle.has(c.native_handle(), ec); 193 194 } 195 196 friend struct detail::group_builder; 197 }; 198 199 namespace detail 200 { 201 202 struct group_tag; 203 struct group_builder 204 { 205 group * group_p; 206 operator ()boost::process::detail::group_builder207 void operator()(group & grp) {this->group_p = &grp;}; 208 209 typedef api::group_ref result_type; get_initializerboost::process::detail::group_builder210 api::group_ref get_initializer() {return api::group_ref (group_p->_group_handle);}; 211 }; 212 213 template<> 214 struct initializer_tag<group> 215 { 216 typedef group_tag type; 217 }; 218 219 template<> 220 struct initializer_builder<group_tag> 221 { 222 typedef group_builder type; 223 }; 224 225 } 226 }} 227 #endif 228 229