Mercurial > hg > gpsynth
view 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 |
line wrap: on
line source
// // Boost.Process // ~~~~~~~~~~~~~ // // Copyright (c) 2006, 2007 Julio M. Merino Vidal // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling // Copyright (c) 2009 Boris Schaeling // Copyright (c) 2010 Felipe Tanus, Boris Schaeling // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // /** * \file boost/process/detail/basic_status_service.hpp * * Includes the declaration of the basic status service class. */ #ifndef BOOST_PROCESS_DETAIL_BASIC_STATUS_SERVICE_HPP #define BOOST_PROCESS_DETAIL_BASIC_STATUS_SERVICE_HPP #include <boost/process/config.hpp> #if defined(BOOST_POSIX_API) # include <boost/process/operations.hpp> # include <string> # include <sys/types.h> # include <sys/wait.h> #elif defined(BOOST_WINDOWS_API) # include <windows.h> #else # error "Unsupported platform." #endif #include <boost/process/pid_type.hpp> #include <boost/process/detail/status_impl.hpp> #include <boost/asio.hpp> #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include <boost/make_shared.hpp> #include <boost/scoped_ptr.hpp> #include <boost/system/error_code.hpp> #include <boost/unordered_map.hpp> #include <vector> #include <algorithm> namespace boost { namespace process { namespace detail { /** * The basic_status_service class provides the service to wait for processes * synchronously and asynchronously. */ template <typename StatusImplementation = status_impl> class basic_status_service : public boost::asio::detail::service_base<StatusImplementation> { public: explicit basic_status_service(boost::asio::io_service &io_service) : boost::asio::detail::service_base<StatusImplementation>(io_service), #if defined(BOOST_POSIX_API) interrupt_pid_(-1), pids_(0) #elif defined(BOOST_WINDOWS_API) run_(true) #endif { #if defined(BOOST_WINDOWS_API) handles_.push_back(CreateEvent(NULL, FALSE, FALSE, NULL)); if (handles_[0] == NULL) BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateEvent() failed"); work_thread_ = boost::thread( &basic_status_service<StatusImplementation>::work_thread, this); #endif } ~basic_status_service() { #if defined(BOOST_POSIX_API) boost::unique_lock<boost::mutex> lock(work_thread_mutex_); bool worker_thread_active = (pids_ != 0); lock.unlock(); if (worker_thread_active) { stop_work_thread(); work_thread_.join(); } #elif defined(BOOST_WINDOWS_API) stop_work_thread(); work_thread_.join(); CloseHandle(handles_[0]); #endif } typedef boost::shared_ptr<StatusImplementation> implementation_type; void construct(implementation_type &impl) { impl = boost::make_shared<StatusImplementation>(); boost::unique_lock<boost::mutex> lock(work_thread_mutex_); impls_.push_back(impl); } void destroy(implementation_type &impl) { boost::unique_lock<boost::mutex> lock(work_thread_mutex_); typename std::vector<implementation_type>::iterator it = std::find(impls_.begin(), impls_.end(), impl); if (it != impls_.end()) impls_.erase(it); #if defined(BOOST_WINDOWS_API) interrupt_work_thread(); work_thread_cond_.wait(work_thread_mutex_); impl->clear(handles_); work_thread_cond_.notify_all(); #endif impl.reset(); } int wait(implementation_type &impl, pid_type pid) { boost::system::error_code ec; int status = impl->wait(pid, ec); #if defined(BOOST_POSIX_API) if (ec.value() == ECHILD) { boost::unique_lock<boost::mutex> lock(work_thread_mutex_); boost::unordered_map<pid_t, int>::iterator it = statuses_.find(pid); if (it == statuses_.end()) { work_thread_cond_.wait(work_thread_mutex_); it = statuses_.find(pid); } if (it != statuses_.end()) { status = it->second; statuses_.erase(it); ec.clear(); } } #endif boost::asio::detail::throw_error(ec); return status; } template <typename Handler> void async_wait(implementation_type &impl, pid_type pid, Handler handler) { #if defined(BOOST_POSIX_API) boost::unique_lock<boost::mutex> lock(work_thread_mutex_); if (++pids_ == 1) { work_.reset(new boost::asio::io_service::work( this->get_io_service())); work_thread_ = boost::thread( &basic_status_service<StatusImplementation>::work_thread, this); } impl->async_wait(pid, this->get_io_service().wrap(handler)); #elif defined(BOOST_WINDOWS_API) HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid); if (handle == NULL) BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("OpenProcess() failed"); boost::unique_lock<boost::mutex> lock(work_thread_mutex_); if (!work_) work_.reset(new boost::asio::io_service::work( this->get_io_service())); interrupt_work_thread(); work_thread_cond_.wait(work_thread_mutex_); handles_.push_back(handle); impl->async_wait(handle, this->get_io_service().wrap(handler)); work_thread_cond_.notify_all(); #endif } private: void shutdown_service() { #if defined(BOOST_WINDOWS_API) boost::unique_lock<boost::mutex> lock(work_thread_mutex_); work_.reset(); #endif } void work_thread() { #if defined(BOOST_POSIX_API) for (;;) { int status; pid_t pid = ::wait(&status); if (pid == -1) { if (errno != EINTR) BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("wait(2) failed"); } else if (interrupted(pid)) { // On POSIX the only reason to interrupt is to break out. break; } else { boost::unique_lock<boost::mutex> lock(work_thread_mutex_); bool regchild = false; for (typename std::vector<implementation_type>::iterator it = impls_.begin(); it != impls_.end(); ++it) regchild |= (*it)->complete(pid, status); if (regchild && --pids_ == 0) { work_.reset(); break; } else if (!regchild) { statuses_.insert(boost::unordered_map<pid_t, int>:: value_type(pid, status)); work_thread_cond_.notify_all(); } } } #elif defined(BOOST_WINDOWS_API) for (;;) { DWORD res = WaitForMultipleObjects(handles_.size(), &handles_[0], FALSE, INFINITE); if (res == WAIT_FAILED) BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( "WaitForMultipleObjects() failed"); else if (res - WAIT_OBJECT_0 == 0) { boost::unique_lock<boost::mutex> lock(work_thread_mutex_); if (!run_) break; work_thread_cond_.notify_all(); work_thread_cond_.wait(work_thread_mutex_); } else if (res - WAIT_OBJECT_0 > 0) { HANDLE handle = handles_[res - WAIT_OBJECT_0]; DWORD exit_code; if (!GetExitCodeProcess(handle, &exit_code)) BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( "GetExitCodeProcess() failed"); boost::unique_lock<boost::mutex> lock(work_thread_mutex_); for (typename std::vector<implementation_type>::iterator it = impls_.begin(); it != impls_.end(); ++it) (*it)->complete(handle, exit_code); std::vector<HANDLE>::iterator it = handles_.begin(); std::advance(it, res - WAIT_OBJECT_0); handles_.erase(it); if (handles_.size() == 1) work_.reset(); } } #endif } void interrupt_work_thread() { #if defined(BOOST_POSIX_API) // By creating a child process which immediately exits // we interrupt wait(). std::vector<std::string> args; args.push_back("-c"); args.push_back("'exit'"); interrupt_pid_ = create_child("/bin/sh", args).get_id(); #elif defined(BOOST_WINDOWS_API) // By signaling the event in the first slot WaitForMultipleObjects() // will return. The work thread won't do anything except checking if // it should continue to run. if (!SetEvent(handles_[0])) BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("SetEvent() failed"); #endif } #if defined(BOOST_POSIX_API) bool interrupted(pid_t pid) { boost::mutex::scoped_lock lock(work_thread_mutex_); return interrupt_pid_ == pid; } #endif void stop_work_thread() { boost::mutex::scoped_lock lock(work_thread_mutex_); #if defined(BOOST_WINDOWS_API) // Access to run_ must be sychronized with running(). run_ = false; #endif // Access to interrupt_pid_ must be sychronized with interrupted(). interrupt_work_thread(); } boost::scoped_ptr<boost::asio::io_service::work> work_; std::vector<implementation_type> impls_; boost::mutex work_thread_mutex_; boost::thread work_thread_; boost::condition_variable_any work_thread_cond_; #if defined(BOOST_POSIX_API) pid_t interrupt_pid_; int pids_; boost::unordered_map<pid_t, int> statuses_; #elif defined(BOOST_WINDOWS_API) bool run_; std::vector<HANDLE> handles_; #endif }; } } } #endif