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