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