mCRL2
Loading...
Searching...
No Matches
child_process.cpp
Go to the documentation of this file.
1// Author(s): Thomas Neele
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8//
9/// \file child_process.cpp
10
11#include "mcrl2/smt/child_process.h"
12#include "mcrl2/utilities/exception.h"
13#include "mcrl2/utilities/platform.h"
14
15#ifdef MCRL2_PLATFORM_WINDOWS
16 #include <windows.h>
17 #include <tchar.h>
18 #include <cstdio>
19 #include <strsafe.h>
20#else
21 #include <unistd.h>
22 #include <sys/select.h>
23 #include <sys/wait.h>
24#endif // MCRL2_PLATFORM_WINDOWS
25
26#include <cstring>
27#include <csignal>
28#include <cerrno>
29
30
31namespace mcrl2
32{
33namespace smt
34{
35
36#ifdef MCRL2_PLATFORM_WINDOWS
37
39{
44};
45
46void child_process::write(const std::string& command) const
47{
50
52 if(!bSuccess || dwWritten != command.size())
53 {
54 throw mcrl2::runtime_error("Failed to write SMT problem to the solver.\n" + command);
55 }
56}
57
58
60{
62 CHAR output[512];
64
65 totalRead = 0;
66 do
67 {
70 } while(bSuccess && output[totalRead-1] != '\n');
71 if(!bSuccess || totalRead == 0 )
72 {
73 throw mcrl2::runtime_error("Reading from child process " + m_name + " failed.");
74 }
75 return std::string(output, totalRead);
76}
77
79{
80 // Not implemented on Windows
81 return read();
82}
83
84void child_process::send_sigint() const
85{
86 // Not implemented on Windows
87}
88
90{
93 // Set the bInheritHandle flag so pipe handles are inherited.
97
98 // Create a pipe for the child process's STDOUT.
100 {
101 throw mcrl2::runtime_error("Could not connect to SMT solver: failed to create pipe Stdout");
102 }
103 // Ensure the read handle to the pipe for STDOUT is not inherited.
105 {
106 throw mcrl2::runtime_error("Could not modify SMT solver handle: SetHandleInformation Stdout");
107 }
108 // Create a pipe for the child process's STDIN.
110 {
111 throw mcrl2::runtime_error("Could not connect to SMT solver: failed to create pipe Stdin");
112 }
113 // Ensure the write handle to the pipe for STDIN is not inherited.
115 {
116 throw mcrl2::runtime_error("Could not modify SMT solver handle: SetHandleInformation Stdin");
117 }
118 // Create the child process.
119 TCHAR szCmdline[] = TEXT("z3.exe -smt2 -in");
123
124 // Set up members of the PROCESS_INFORMATION structure.
126
127 // Set up members of the STARTUPINFO structure.
128 // This structure specifies the STDIN and STDOUT handles for redirection.
130 siStartInfo.cb = sizeof(STARTUPINFO);
135
136 // Create the child process.
138 szCmdline, // command line
139 NULL, // process security attributes
140 NULL, // primary thread security attributes
141 TRUE, // handles are inherited
142 0, // creation flags
143 NULL, // use parent's environment
144 NULL, // use parent's current directory
145 &siStartInfo, // STARTUPINFO pointer
146 &piProcInfo); // receives PROCESS_INFORMATION
147
148 // If an error occurs, exit the application.
149 if(!bSuccess)
150 {
151 throw mcrl2::runtime_error("Problem occurred while creating SMT solver process.\nIs " + m_name + " in your path?");
152 }
153 else
154 {
155 // Close handles to the child process and its primary thread.
156 // Some applications might keep these handles to monitor the status
157 // of the child process, for example.
160 }
161}
162
164{
165 // Clean up the child process
166 // It terminates when we close its stdin
168}
169
170
171#else // MCRL2_PLATFORM_WINDOWS
172
174{
179};
180
181void child_process::write(const std::string& command) const
182{
183 if(::write(m_pimpl->pipe_stdin[1], command.c_str(), command.size()) < 0)
184 {
185 throw mcrl2::runtime_error("Failed to write to " + m_name + ".\n" + command);
186 }
187}
188
189std::string child_process::read() const
190{
191 char output[512];
192 // check return value
193 int count = ::read(m_pimpl->pipe_stdout[0], output, 512);
194 if(count > 0)
195 {
196 return std::string(output, count);
197 }
198 else
199 {
200 std::string message;
201
202 while(int i = ::read(m_pimpl->pipe_stderr[0], output, 512))
203 {
204 message.append(output, 0, i);
205 }
206
207 throw mcrl2::runtime_error("Reading from child process " + m_name + " failed: " + message);
208 }
209}
210
211std::string child_process::read(const std::chrono::microseconds& timeout) const
212{
213 struct timeval tv;
214 tv.tv_sec = 0;
215 tv.tv_usec = timeout.count();
216
217 fd_set readfds;
218 FD_ZERO(&readfds);
219 FD_SET(m_pimpl->pipe_stdout[0], &readfds);
220
221 int result = ::select(m_pimpl->pipe_stdout[0] + 1, &readfds, NULL, NULL, &tv);
222
223 if(result == -1)
224 {
225 throw mcrl2::runtime_error("Error while waiting for input from " + m_name);
226 }
227 else if(result == 0)
228 {
230 }
231 return read();
232}
233
235{
236 ::kill(m_pimpl->child_pid, SIGINT);
237}
238
240{
241 signal(SIGPIPE, SIG_IGN);
242 m_pimpl = std::make_shared<child_process::platform_impl>();
243 // Create pipes (two pairs r/w)
244 if (::pipe(&m_pimpl->pipe_stdin[0]) < 0)
245 {
246 throw mcrl2::runtime_error("failed to create pipe");
247 }
248
249 if (::pipe(&m_pimpl->pipe_stdout[0]) < 0)
250 {
251 throw mcrl2::runtime_error("failed to create pipe");
252 }
253
254 if (::pipe(&m_pimpl->pipe_stderr[0]) < 0)
255 {
256 throw mcrl2::runtime_error("failed to create pipe");
257 }
258
259 // fork process
260 pid_t pid = ::fork();
261 if (pid == 0)
262 {
263 // Forked process keeps waiting for input
264 ::dup2(m_pimpl->pipe_stdin[0], STDIN_FILENO);
265 ::dup2(m_pimpl->pipe_stdout[1], STDOUT_FILENO);
266 ::dup2(m_pimpl->pipe_stderr[1], STDERR_FILENO);
267
268 ::close(m_pimpl->pipe_stdin[1]);
269 ::close(m_pimpl->pipe_stdout[0]);
270 ::close(m_pimpl->pipe_stderr[0]);
271
272 ::execlp("z3", "z3", "-smt2", "-in", (char*) nullptr);
273
274 ::_exit(errno);
275 }
276 else if (pid < 0)
277 {
278 ::close(m_pimpl->pipe_stdin[0]);
279 ::close(m_pimpl->pipe_stdin[1]);
280 ::close(m_pimpl->pipe_stdout[0]);
281 ::close(m_pimpl->pipe_stdout[1]);
282 ::close(m_pimpl->pipe_stderr[0]);
283 ::close(m_pimpl->pipe_stderr[1]);
284
285 throw mcrl2::runtime_error("Problem occurred while forking SMT solver process: " + std::string(strerror(errno)) + ".\nIs " + m_name + " in your path?");
286 }
287 else
288 {
289 m_pimpl->child_pid = pid;
290 ::close(m_pimpl->pipe_stdin[0]);
291 ::close(m_pimpl->pipe_stdout[1]);
292 ::close(m_pimpl->pipe_stderr[1]);
293 }
294}
295
297{
298 // Clean up the child process
299 // It terminates when we close its stdin
300 ::close(m_pimpl->pipe_stdin[1]);
301 ::close(m_pimpl->pipe_stdout[0]);
302 ::close(m_pimpl->pipe_stderr[0]);
303
304 int return_status;
305 ::wait(&return_status);
306}
307
308#endif // MCRL2_PLATFORM_WINDOWS
309
310
311} // namespace smt
312} // namespace mcrl2
Standard exception class for reporting runtime errors.
Definition exception.h:27
std::string read() const
Read output from the child process. This is a blocking call.
void send_sigint() const
Send the SIGINT signal to the child.
std::string read(const std::chrono::microseconds &timeout) const
Read output from the child process. If no output is available before the timeout happens,...
void write(const std::string &s) const
std::size_t operator()(const atermpp::detail::reference_aterm< T > &t) const