2828#include < boost/process/search_path.hpp>
2929#include < fmt/ranges.h>
3030
31- using namespace boost ::process;
31+ namespace bp = boost::process;
3232
3333namespace Trinity
3434{
35- template <typename T>
36- static int CreateChildProcess (T waiter, std::string const & executable,
37- std::vector<std::string> const & argsVector,
38- std::string const & logger, std::string const & input,
39- bool secure)
40- {
41- #if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
42- #pragma warning(push)
43- #pragma warning(disable:4297)
44- /*
45- Silence warning with boost 1.83
46-
47- boost/process/pipe.hpp(132,5): warning C4297: 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf': function assumed not to throw an exception but does
48- boost/process/pipe.hpp(132,5): message : destructor or deallocator has a (possibly implicit) non-throwing exception specification
49- boost/process/pipe.hpp(124,6): message : while compiling class template member function 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf(void)'
50- boost/process/pipe.hpp(304,42): message : see reference to class template instantiation 'boost::process::basic_pipebuf<char,std::char_traits<char>>' being compiled
51- */
52- #endif
53- ipstream outStream;
54- ipstream errStream;
55- #if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
56- #pragma warning(pop)
57- #endif
58-
59- if (!secure)
60- {
61- TC_LOG_TRACE (logger, " Starting process \" {}\" with arguments: \" {}\" ." ,
62- executable, fmt::join (argsVector, " " ));
63- }
64-
65- // prepare file with only read permission (boost process opens with read_write)
66- auto inputFile = std::shared_ptr<FILE>(!input.empty () ? fopen (input.c_str (), " rb" ) : nullptr , [](FILE* f) { if (f) fclose (f); });
67-
68- // Start the child process
69- child c = [&]()
70- {
71- if (inputFile)
72- {
73- // With binding stdin
74- return child{
75- exe = boost::filesystem::absolute (executable).string (),
76- args = argsVector,
77- env = environment (boost::this_process::environment ()),
78- std_in = inputFile.get (),
79- std_out = outStream,
80- std_err = errStream
81- };
82- }
83- else
84- {
85- // Without binding stdin
86- return child{
87- exe = boost::filesystem::absolute (executable).string (),
88- args = argsVector,
89- env = environment (boost::this_process::environment ()),
90- std_in = boost::process::close,
91- std_out = outStream,
92- std_err = errStream
93- };
94- }
95- }();
96-
97- std::string line;
98- while (std::getline (outStream, line, ' \n ' ))
99- {
100- std::erase (line, ' \r ' );
101- if (!line.empty ())
102- TC_LOG_INFO (logger, " {}" , line);
103- }
104-
105- while (std::getline (errStream, line, ' \n ' ))
106- {
107- std::erase (line, ' \r ' );
108- if (!line.empty ())
109- TC_LOG_ERROR (logger, " {}" , line);
110- }
111-
112- // Call the waiter in the current scope to prevent
113- // the streams from closing too early on leaving the scope.
114- int const result = waiter (c);
115-
116- if (!secure)
117- {
118- TC_LOG_TRACE (logger, " >> Process \" {}\" finished with return value {}." ,
119- executable, result);
120- }
121-
122- return result;
123- }
124-
125- int StartProcess (std::string const & executable, std::vector<std::string> const & args,
126- std::string const & logger, std::string input_file, bool secure)
127- {
128- return CreateChildProcess ([](child& c) -> int
129- {
130- try
131- {
132- c.wait ();
133- return c.exit_code ();
134- }
135- catch (...)
136- {
137- return EXIT_FAILURE;
138- }
139- }, executable, args, logger, input_file, secure);
140- }
141-
14235class AsyncProcessResultImplementation
14336 : public AsyncProcessResult
14437{
@@ -150,59 +43,129 @@ class AsyncProcessResultImplementation
15043
15144 std::atomic<bool > was_terminated;
15245
153- // Workaround for missing move support in boost < 1.57
154- Optional<std::shared_ptr<std::future<int >>> result;
155- Optional<std::reference_wrapper<child>> my_child;
46+ Optional<std::future<int >> futureResult;
47+ Optional<bp::child> my_child;
15648
15749public:
15850 explicit AsyncProcessResultImplementation (std::string executable_, std::vector<std::string> args_,
15951 std::string logger_, std::string input_file_,
16052 bool secure)
16153 : executable(std::move(executable_)), args(std::move(args_)),
162- logger(std::move(logger_)), input_file(input_file_),
54+ logger(std::move(logger_)), input_file(std::move( input_file_) ),
16355 is_secure(secure), was_terminated(false ) { }
16456
16557 AsyncProcessResultImplementation (AsyncProcessResultImplementation const &) = delete ;
16658 AsyncProcessResultImplementation& operator = (AsyncProcessResultImplementation const &) = delete ;
16759 AsyncProcessResultImplementation (AsyncProcessResultImplementation&&) = delete ;
16860 AsyncProcessResultImplementation& operator = (AsyncProcessResultImplementation&&) = delete ;
16961
62+ ~AsyncProcessResultImplementation () = default ;
63+
17064 int StartProcess ()
17165 {
17266 ASSERT (!my_child, " Process started already!" );
17367
174- return CreateChildProcess ([&](child& c) -> int
68+ #if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
69+ #pragma warning(push)
70+ #pragma warning(disable:4297)
71+ /*
72+ Silence warning with boost 1.83
73+
74+ boost/process/pipe.hpp(132,5): warning C4297: 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf': function assumed not to throw an exception but does
75+ boost/process/pipe.hpp(132,5): message : destructor or deallocator has a (possibly implicit) non-throwing exception specification
76+ boost/process/pipe.hpp(124,6): message : while compiling class template member function 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf(void)'
77+ boost/process/pipe.hpp(304,42): message : see reference to class template instantiation 'boost::process::basic_pipebuf<char,std::char_traits<char>>' being compiled
78+ */
79+ #endif
80+ bp::ipstream outStream;
81+ bp::ipstream errStream;
82+ #if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
83+ #pragma warning(pop)
84+ #endif
85+
86+ if (!is_secure)
87+ {
88+ TC_LOG_TRACE (logger, " Starting process \" {}\" with arguments: \" {}\" ." ,
89+ executable, fmt::join (args, " " ));
90+ }
91+
92+ // prepare file with only read permission (boost process opens with read_write)
93+ auto inputFile = std::shared_ptr<FILE>(!input_file.empty () ? fopen (input_file.c_str (), " rb" ) : nullptr , [](FILE* f) { if (f) fclose (f); });
94+
95+ // Start the child process
96+ if (inputFile)
97+ {
98+ my_child.emplace (
99+ bp::exe = boost::filesystem::absolute (executable).string (),
100+ bp::args = args,
101+ bp::env = bp::environment (boost::this_process::environment ()),
102+ bp::std_in = inputFile.get (),
103+ bp::std_out = outStream,
104+ bp::std_err = errStream
105+ );
106+ }
107+ else
175108 {
176- int result;
177- my_child = std::reference_wrapper<child>(c);
109+ my_child.emplace (
110+ bp::exe = boost::filesystem::absolute (executable).string (),
111+ bp::args = args,
112+ bp::env = bp::environment (boost::this_process::environment ()),
113+ bp::std_in = boost::process::close,
114+ bp::std_out = outStream,
115+ bp::std_err = errStream
116+ );
117+ }
178118
179- try
119+ std::future<void > stdOutReader = std::async (std::launch::async, [&]
120+ {
121+ std::string line;
122+ while (std::getline (outStream, line, ' \n ' ))
180123 {
181- c.wait ();
182- result = c.exit_code ();
124+ std::erase (line, ' \r ' );
125+ if (!line.empty ())
126+ TC_LOG_INFO (logger, " {}" , line);
183127 }
184- catch (...)
128+ });
129+
130+ std::future<void > stdErrReader = std::async (std::launch::async, [&]
131+ {
132+ std::string line;
133+ while (std::getline (errStream, line, ' \n ' ))
185134 {
186- result = EXIT_FAILURE;
135+ std::erase (line, ' \r ' );
136+ if (!line.empty ())
137+ TC_LOG_ERROR (logger, " {}" , line);
187138 }
139+ });
188140
189- my_child.reset ();
190- return was_terminated ? EXIT_FAILURE : result;
141+ std::error_code ec;
142+ my_child->wait (ec);
143+ int32 const result = !ec && !was_terminated ? my_child->exit_code () : EXIT_FAILURE;
144+ my_child.reset ();
191145
192- }, executable, args, logger, input_file, is_secure);
146+ stdOutReader.wait ();
147+ stdErrReader.wait ();
148+
149+ if (!is_secure)
150+ {
151+ TC_LOG_TRACE (logger, " >> Process \" {}\" finished with return value {}." ,
152+ executable, result);
153+ }
154+
155+ return result;
193156 }
194157
195- void SetFuture (std::future<int > result_)
158+ void SetFuture (std::future<int32 > result_)
196159 {
197- result = std::make_shared<std::future< int >> (std::move (result_));
160+ futureResult. emplace (std::move (result_));
198161 }
199162
200163 // / Returns the future which contains the result of the process
201164 // / as soon it is finished.
202- std::future<int >& GetFutureResult () override
165+ std::future<int32 >& GetFutureResult () override
203166 {
204- ASSERT (*result , " The process wasn't started!" );
205- return **result ;
167+ ASSERT (futureResult. has_value () , " The process wasn't started!" );
168+ return *futureResult ;
206169 }
207170
208171 // / Tries to terminate the process
@@ -211,23 +174,25 @@ class AsyncProcessResultImplementation
211174 if (my_child)
212175 {
213176 was_terminated = true ;
214- try
215- {
216- my_child->get ().terminate ();
217- }
218- catch (...)
219- {
220- // Do nothing
221- }
177+ std::error_code ec;
178+ my_child->terminate (ec);
222179 }
223180 }
224181};
225182
226- std::shared_ptr<AsyncProcessResult>
227- StartAsyncProcess (std::string executable, std::vector<std::string> args,
228- std::string logger, std::string input_file, bool secure)
183+ int StartProcess (std::string executable, std::vector<std::string> args,
184+ std::string logger, std::string input_file, bool secure)
185+ {
186+ AsyncProcessResultImplementation handle (
187+ std::move (executable), std::move (args), std::move (logger), std::move (input_file), secure);
188+
189+ return handle.StartProcess ();
190+ }
191+
192+ std::shared_ptr<AsyncProcessResult> StartAsyncProcess (std::string executable, std::vector<std::string> args,
193+ std::string logger, std::string input_file, bool secure)
229194{
230- auto handle = std::make_shared<AsyncProcessResultImplementation>(
195+ std::shared_ptr<AsyncProcessResultImplementation> handle = std::make_shared<AsyncProcessResultImplementation>(
231196 std::move (executable), std::move (args), std::move (logger), std::move (input_file), secure);
232197
233198 handle->SetFuture (std::async (std::launch::async, [handle] { return handle->StartProcess (); }));
@@ -238,7 +203,7 @@ std::string SearchExecutableInPath(std::string const& filename)
238203{
239204 try
240205 {
241- return search_path (filename).string ();
206+ return bp:: search_path (filename).string ();
242207 }
243208 catch (...)
244209 {
0 commit comments