Mercurial > hg > gpsynth
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/detail/basic_status_service.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,318 @@ +// +// 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