Mercurial > hg > gpsynth
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 |