annotate third_party/boost/process/detail/basic_status_service.hpp @ 0:add35537fdbb tip

Initial import
author irh <ian.r.hobson@gmail.com>
date Thu, 25 Aug 2011 11:05:55 +0100
parents
children
rev   line source
ian@0 1 //
ian@0 2 // Boost.Process
ian@0 3 // ~~~~~~~~~~~~~
ian@0 4 //
ian@0 5 // Copyright (c) 2006, 2007 Julio M. Merino Vidal
ian@0 6 // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
ian@0 7 // Copyright (c) 2009 Boris Schaeling
ian@0 8 // Copyright (c) 2010 Felipe Tanus, Boris Schaeling
ian@0 9 //
ian@0 10 // Distributed under the Boost Software License, Version 1.0. (See accompanying
ian@0 11 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
ian@0 12 //
ian@0 13
ian@0 14 /**
ian@0 15 * \file boost/process/detail/basic_status_service.hpp
ian@0 16 *
ian@0 17 * Includes the declaration of the basic status service class.
ian@0 18 */
ian@0 19
ian@0 20 #ifndef BOOST_PROCESS_DETAIL_BASIC_STATUS_SERVICE_HPP
ian@0 21 #define BOOST_PROCESS_DETAIL_BASIC_STATUS_SERVICE_HPP
ian@0 22
ian@0 23 #include <boost/process/config.hpp>
ian@0 24
ian@0 25 #if defined(BOOST_POSIX_API)
ian@0 26 # include <boost/process/operations.hpp>
ian@0 27 # include <string>
ian@0 28 # include <sys/types.h>
ian@0 29 # include <sys/wait.h>
ian@0 30 #elif defined(BOOST_WINDOWS_API)
ian@0 31 # include <windows.h>
ian@0 32 #else
ian@0 33 # error "Unsupported platform."
ian@0 34 #endif
ian@0 35
ian@0 36 #include <boost/process/pid_type.hpp>
ian@0 37 #include <boost/process/detail/status_impl.hpp>
ian@0 38 #include <boost/asio.hpp>
ian@0 39 #include <boost/thread.hpp>
ian@0 40 #include <boost/shared_ptr.hpp>
ian@0 41 #include <boost/make_shared.hpp>
ian@0 42 #include <boost/scoped_ptr.hpp>
ian@0 43 #include <boost/system/error_code.hpp>
ian@0 44 #include <boost/unordered_map.hpp>
ian@0 45 #include <vector>
ian@0 46 #include <algorithm>
ian@0 47
ian@0 48 namespace boost {
ian@0 49 namespace process {
ian@0 50 namespace detail {
ian@0 51
ian@0 52 /**
ian@0 53 * The basic_status_service class provides the service to wait for processes
ian@0 54 * synchronously and asynchronously.
ian@0 55 */
ian@0 56 template <typename StatusImplementation = status_impl>
ian@0 57 class basic_status_service
ian@0 58 : public boost::asio::detail::service_base<StatusImplementation>
ian@0 59 {
ian@0 60 public:
ian@0 61 explicit basic_status_service(boost::asio::io_service &io_service)
ian@0 62 : boost::asio::detail::service_base<StatusImplementation>(io_service),
ian@0 63 #if defined(BOOST_POSIX_API)
ian@0 64 interrupt_pid_(-1),
ian@0 65 pids_(0)
ian@0 66 #elif defined(BOOST_WINDOWS_API)
ian@0 67 run_(true)
ian@0 68 #endif
ian@0 69 {
ian@0 70 #if defined(BOOST_WINDOWS_API)
ian@0 71 handles_.push_back(CreateEvent(NULL, FALSE, FALSE, NULL));
ian@0 72 if (handles_[0] == NULL)
ian@0 73 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateEvent() failed");
ian@0 74 work_thread_ = boost::thread(
ian@0 75 &basic_status_service<StatusImplementation>::work_thread, this);
ian@0 76 #endif
ian@0 77 }
ian@0 78
ian@0 79 ~basic_status_service()
ian@0 80 {
ian@0 81 #if defined(BOOST_POSIX_API)
ian@0 82 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 83 bool worker_thread_active = (pids_ != 0);
ian@0 84 lock.unlock();
ian@0 85 if (worker_thread_active)
ian@0 86 {
ian@0 87 stop_work_thread();
ian@0 88 work_thread_.join();
ian@0 89 }
ian@0 90 #elif defined(BOOST_WINDOWS_API)
ian@0 91 stop_work_thread();
ian@0 92 work_thread_.join();
ian@0 93 CloseHandle(handles_[0]);
ian@0 94 #endif
ian@0 95 }
ian@0 96
ian@0 97 typedef boost::shared_ptr<StatusImplementation> implementation_type;
ian@0 98
ian@0 99 void construct(implementation_type &impl)
ian@0 100 {
ian@0 101 impl = boost::make_shared<StatusImplementation>();
ian@0 102 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 103 impls_.push_back(impl);
ian@0 104 }
ian@0 105
ian@0 106 void destroy(implementation_type &impl)
ian@0 107 {
ian@0 108 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 109 typename std::vector<implementation_type>::iterator it =
ian@0 110 std::find(impls_.begin(), impls_.end(), impl);
ian@0 111 if (it != impls_.end())
ian@0 112 impls_.erase(it);
ian@0 113 #if defined(BOOST_WINDOWS_API)
ian@0 114 interrupt_work_thread();
ian@0 115 work_thread_cond_.wait(work_thread_mutex_);
ian@0 116 impl->clear(handles_);
ian@0 117 work_thread_cond_.notify_all();
ian@0 118 #endif
ian@0 119 impl.reset();
ian@0 120 }
ian@0 121
ian@0 122 int wait(implementation_type &impl, pid_type pid)
ian@0 123 {
ian@0 124 boost::system::error_code ec;
ian@0 125 int status = impl->wait(pid, ec);
ian@0 126 #if defined(BOOST_POSIX_API)
ian@0 127 if (ec.value() == ECHILD)
ian@0 128 {
ian@0 129 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 130 boost::unordered_map<pid_t, int>::iterator it = statuses_.find(pid);
ian@0 131 if (it == statuses_.end())
ian@0 132 {
ian@0 133 work_thread_cond_.wait(work_thread_mutex_);
ian@0 134 it = statuses_.find(pid);
ian@0 135 }
ian@0 136 if (it != statuses_.end())
ian@0 137 {
ian@0 138 status = it->second;
ian@0 139 statuses_.erase(it);
ian@0 140 ec.clear();
ian@0 141 }
ian@0 142 }
ian@0 143 #endif
ian@0 144 boost::asio::detail::throw_error(ec);
ian@0 145 return status;
ian@0 146 }
ian@0 147
ian@0 148 template <typename Handler>
ian@0 149 void async_wait(implementation_type &impl, pid_type pid, Handler handler)
ian@0 150 {
ian@0 151 #if defined(BOOST_POSIX_API)
ian@0 152 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 153 if (++pids_ == 1)
ian@0 154 {
ian@0 155 work_.reset(new boost::asio::io_service::work(
ian@0 156 this->get_io_service()));
ian@0 157 work_thread_ = boost::thread(
ian@0 158 &basic_status_service<StatusImplementation>::work_thread,
ian@0 159 this);
ian@0 160 }
ian@0 161 impl->async_wait(pid, this->get_io_service().wrap(handler));
ian@0 162 #elif defined(BOOST_WINDOWS_API)
ian@0 163 HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
ian@0 164 FALSE, pid);
ian@0 165 if (handle == NULL)
ian@0 166 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("OpenProcess() failed");
ian@0 167 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 168 if (!work_)
ian@0 169 work_.reset(new boost::asio::io_service::work(
ian@0 170 this->get_io_service()));
ian@0 171 interrupt_work_thread();
ian@0 172 work_thread_cond_.wait(work_thread_mutex_);
ian@0 173 handles_.push_back(handle);
ian@0 174 impl->async_wait(handle, this->get_io_service().wrap(handler));
ian@0 175 work_thread_cond_.notify_all();
ian@0 176 #endif
ian@0 177 }
ian@0 178
ian@0 179 private:
ian@0 180 void shutdown_service()
ian@0 181 {
ian@0 182 #if defined(BOOST_WINDOWS_API)
ian@0 183 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 184 work_.reset();
ian@0 185 #endif
ian@0 186 }
ian@0 187
ian@0 188 void work_thread()
ian@0 189 {
ian@0 190 #if defined(BOOST_POSIX_API)
ian@0 191 for (;;)
ian@0 192 {
ian@0 193 int status;
ian@0 194 pid_t pid = ::wait(&status);
ian@0 195 if (pid == -1)
ian@0 196 {
ian@0 197 if (errno != EINTR)
ian@0 198 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("wait(2) failed");
ian@0 199 }
ian@0 200 else if (interrupted(pid))
ian@0 201 {
ian@0 202 // On POSIX the only reason to interrupt is to break out.
ian@0 203 break;
ian@0 204 }
ian@0 205 else
ian@0 206 {
ian@0 207 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 208 bool regchild = false;
ian@0 209 for (typename std::vector<implementation_type>::iterator it =
ian@0 210 impls_.begin(); it != impls_.end(); ++it)
ian@0 211 regchild |= (*it)->complete(pid, status);
ian@0 212 if (regchild && --pids_ == 0)
ian@0 213 {
ian@0 214 work_.reset();
ian@0 215 break;
ian@0 216 }
ian@0 217 else if (!regchild)
ian@0 218 {
ian@0 219 statuses_.insert(boost::unordered_map<pid_t, int>::
ian@0 220 value_type(pid, status));
ian@0 221 work_thread_cond_.notify_all();
ian@0 222 }
ian@0 223 }
ian@0 224 }
ian@0 225 #elif defined(BOOST_WINDOWS_API)
ian@0 226 for (;;)
ian@0 227 {
ian@0 228 DWORD res = WaitForMultipleObjects(handles_.size(), &handles_[0],
ian@0 229 FALSE, INFINITE);
ian@0 230 if (res == WAIT_FAILED)
ian@0 231 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
ian@0 232 "WaitForMultipleObjects() failed");
ian@0 233 else if (res - WAIT_OBJECT_0 == 0)
ian@0 234 {
ian@0 235 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 236 if (!run_)
ian@0 237 break;
ian@0 238 work_thread_cond_.notify_all();
ian@0 239 work_thread_cond_.wait(work_thread_mutex_);
ian@0 240 }
ian@0 241 else if (res - WAIT_OBJECT_0 > 0)
ian@0 242 {
ian@0 243 HANDLE handle = handles_[res - WAIT_OBJECT_0];
ian@0 244 DWORD exit_code;
ian@0 245 if (!GetExitCodeProcess(handle, &exit_code))
ian@0 246 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
ian@0 247 "GetExitCodeProcess() failed");
ian@0 248 boost::unique_lock<boost::mutex> lock(work_thread_mutex_);
ian@0 249 for (typename std::vector<implementation_type>::iterator it =
ian@0 250 impls_.begin(); it != impls_.end(); ++it)
ian@0 251 (*it)->complete(handle, exit_code);
ian@0 252 std::vector<HANDLE>::iterator it = handles_.begin();
ian@0 253 std::advance(it, res - WAIT_OBJECT_0);
ian@0 254 handles_.erase(it);
ian@0 255 if (handles_.size() == 1)
ian@0 256 work_.reset();
ian@0 257 }
ian@0 258 }
ian@0 259 #endif
ian@0 260 }
ian@0 261
ian@0 262 void interrupt_work_thread()
ian@0 263 {
ian@0 264 #if defined(BOOST_POSIX_API)
ian@0 265 // By creating a child process which immediately exits
ian@0 266 // we interrupt wait().
ian@0 267 std::vector<std::string> args;
ian@0 268 args.push_back("-c");
ian@0 269 args.push_back("'exit'");
ian@0 270 interrupt_pid_ = create_child("/bin/sh", args).get_id();
ian@0 271 #elif defined(BOOST_WINDOWS_API)
ian@0 272 // By signaling the event in the first slot WaitForMultipleObjects()
ian@0 273 // will return. The work thread won't do anything except checking if
ian@0 274 // it should continue to run.
ian@0 275 if (!SetEvent(handles_[0]))
ian@0 276 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("SetEvent() failed");
ian@0 277 #endif
ian@0 278 }
ian@0 279
ian@0 280 #if defined(BOOST_POSIX_API)
ian@0 281 bool interrupted(pid_t pid)
ian@0 282 {
ian@0 283 boost::mutex::scoped_lock lock(work_thread_mutex_);
ian@0 284 return interrupt_pid_ == pid;
ian@0 285 }
ian@0 286 #endif
ian@0 287
ian@0 288 void stop_work_thread()
ian@0 289 {
ian@0 290 boost::mutex::scoped_lock lock(work_thread_mutex_);
ian@0 291 #if defined(BOOST_WINDOWS_API)
ian@0 292 // Access to run_ must be sychronized with running().
ian@0 293 run_ = false;
ian@0 294 #endif
ian@0 295 // Access to interrupt_pid_ must be sychronized with interrupted().
ian@0 296 interrupt_work_thread();
ian@0 297 }
ian@0 298
ian@0 299 boost::scoped_ptr<boost::asio::io_service::work> work_;
ian@0 300 std::vector<implementation_type> impls_;
ian@0 301 boost::mutex work_thread_mutex_;
ian@0 302 boost::thread work_thread_;
ian@0 303 boost::condition_variable_any work_thread_cond_;
ian@0 304 #if defined(BOOST_POSIX_API)
ian@0 305 pid_t interrupt_pid_;
ian@0 306 int pids_;
ian@0 307 boost::unordered_map<pid_t, int> statuses_;
ian@0 308 #elif defined(BOOST_WINDOWS_API)
ian@0 309 bool run_;
ian@0 310 std::vector<HANDLE> handles_;
ian@0 311 #endif
ian@0 312 };
ian@0 313
ian@0 314 }
ian@0 315 }
ian@0 316 }
ian@0 317
ian@0 318 #endif