35class GNet::TaskImp :
private FutureEventHandler ,
private ExceptionHandler
38 TaskImp( Task & , EventState ,
bool sync ,
41 const G::Path & cd ,
const std::string & exec_error_format ,
const G::Identity &
id ) ;
60 std::pair<int,std::string> wait() ;
70 void onFutureEvent()
override ;
71 void onException( ExceptionSource * , std::exception & ,
bool )
override ;
74 TaskImp(
const TaskImp & ) = delete ;
75 TaskImp( TaskImp && ) = delete ;
76 TaskImp & operator=(
const TaskImp & ) = delete ;
77 TaskImp & operator=( TaskImp && ) = delete ;
81 static void waitThread( TaskImp * , HANDLE ) ;
86 FutureEvent m_future_event ;
87 Timer<TaskImp> m_timer ;
88 bool m_logged {
false} ;
90 G::threading::thread_type m_thread ;
91 static std::size_t m_zcount ;
94std::size_t GNet::TaskImp::m_zcount = 0U ;
98GNet::TaskImp::TaskImp( Task & task , EventState ,
bool sync ,
101 const G::Path & cd ,
const std::string & exec_error_format ,
104 m_es(EventState::create(
std::nothrow)) ,
105 m_future_event(*this,m_es) ,
106 m_timer(*this,&TaskImp::onTimeout,m_es) ,
107 m_process( commandline.exe() , commandline.args() ,
108 G::NewProcess::Config()
111 .set_stdout(fd_stdout)
112 .set_stderr(fd_stderr)
114 .set_strict_exe(true)
117 .set_exec_error_exit(127)
118 .set_exec_error_format(exec_error_format) )
124 else if( !G::threading::works() )
126 if( G::threading::using_std_thread )
127 G_WARNING_ONCE(
"GNet::TaskImp::TaskImp: multi-threading disabled: running tasks synchronously" ) ;
128 waitThread(
this , m_future_event.handle() ) ;
132 G_ASSERT( G::threading::using_std_thread ) ;
134 m_thread = G::threading::thread_type( TaskImp::waitThread ,
this , m_future_event.handle() ) ;
138GNet::TaskImp::~TaskImp()
143 if( m_thread.joinable() )
144 m_process.kill(
true ) ;
145 if( m_thread.joinable() )
153void GNet::TaskImp::onException( ExceptionSource * , std::exception & e ,
bool done )
158 G_LOG(
"GNet::TaskImp: exception: " << e.what() ) ;
161bool GNet::TaskImp::zombify()
163 G_ASSERT( m_es.esrc() ==
nullptr ) ;
164 G_ASSERT( m_es.logging() ==
nullptr ) ;
170 if( m_thread.joinable() )
173 m_process.kill(
true ) ;
176 m_timer.startTimer( 1U ) ;
178 static constexpr std::size_t warning_threshold = 30U ;
179 if( m_zcount == warning_threshold )
180 G_WARNING_ONCE(
"GNet::Task::dtor: large number of threads waiting for processes to finish" ) ;
190void GNet::TaskImp::onTimeout()
192 G_ASSERT( m_task ==
nullptr ) ;
193 if( m_thread.joinable() )
196 G_LOG(
"TaskImp::dtor: waiting for killed process to terminate: pid " << m_process.id() ) ;
198 m_timer.startTimer( 1U ) ;
203 G_LOG(
"TaskImp::dtor: killed process has terminated: pid " << m_process.id() ) ;
209std::pair<int,std::string> GNet::TaskImp::wait()
211 m_process.waitable().wait() ;
212 int exit_code = m_process.waitable().get() ;
213 return { exit_code , m_process.waitable().output() } ;
216void GNet::TaskImp::waitThread( TaskImp * This , HANDLE handle )
221 This->m_process.waitable().wait() ;
222 FutureEvent::send( handle ) ;
226 static_assert(
noexcept(FutureEvent::send(handle)) ,
"" ) ;
227 FutureEvent::send( handle ) ;
231void GNet::TaskImp::onFutureEvent()
233 G_DEBUG(
"GNet::TaskImp::onFutureEvent: future event" ) ;
234 if( m_thread.joinable() )
237 int exit_code = m_process.waitable().get( std::nothrow ) ;
238 G_DEBUG(
"GNet::TaskImp::onFutureEvent: exit code " << exit_code ) ;
240 std::string pipe_output = m_process.waitable().output() ;
241 G_LOG_MORE(
"GNet::TaskImp::onFutureEvent: executable output: [" <<
G::Str::printable(pipe_output) <<
"]" ) ;
244 m_task->done( exit_code , pipe_output ) ;
250 const std::string & exec_error_format ,
const G::Identity &
id ) :
251 m_callback(callback) ,
253 m_exec_error_format(exec_error_format) ,
273 if( m_imp && m_imp->zombify() )
276 GDEF_IGNORE_RETURN m_imp.release() ;
289 G_ASSERT( !m_busy ) ;
290 m_imp = std::make_unique<TaskImp>( *
this ,
291 m_es ,
true , commandline , env ,
292 fd_stdin , fd_stdout , fd_stderr , cd ,
293 m_exec_error_format , m_id ) ;
294 return m_imp->wait() ;
301 G::NewProcess::Fd::devnull() ,
302 G::NewProcess::Fd::pipe() ,
303 G::NewProcess::Fd::devnull() ,
318 m_imp = std::make_unique<TaskImp>( *
this , m_es ,
false , commandline ,
319 env , fd_stdin , fd_stdout , fd_stderr , cd ,
320 m_exec_error_format , m_id ) ;
323void GNet::Task::done(
int exit_code ,
const std::string & output )
326 m_callback.onTaskDone( exit_code , output ) ;
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
An abstract interface for callbacks from GNet::Task.
Task(TaskCallback &, EventState es, const std::string &exec_error_format={}, const G::Identity &=G::Identity::invalid())
Constructor for an object that can be start()ed or run().
void stop()
Attempts to kill the spawned process.
void start(const G::ExecutableCommand &commandline)
Starts the task by spawning a new process with the given command-line and also starting a thread to w...
std::pair< int, std::string > run(const G::ExecutableCommand &commandline, const G::Environment &env, G::NewProcess::Fd fd_stdin=G::NewProcess::Fd::devnull(), G::NewProcess::Fd fd_stdout=G::NewProcess::Fd::pipe(), G::NewProcess::Fd fd_stderr=G::NewProcess::Fd::devnull(), const G::Path &cd=G::Path())
Runs the task synchronously and returns the exit code and pipe output.
Holds a set of environment variables and also provides static methods to wrap getenv() and putenv().
static Environment minimal(bool sbin=false)
Returns a minimal, safe set of environment variables.
A structure representing an external program, holding a path and a set of arguments.
A combination of user-id and group-id, with a very low-level interface to the get/set/e/uid/gid funct...
A class for creating new processes.
A Path object represents a file system path.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static bool enabled() noexcept
Returns true if test features are enabled.
A RAII class to temporarily block signal delivery.
Wraps up a file descriptor for passing to G::NewProcess.