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