peerplays-fc/include/boost/process/detail/basic_status_service.hpp
2012-12-19 12:23:12 -05:00

318 lines
10 KiB
C++

//
// 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