41 const G::Path & cd ,
const std::string & exec_error_format ,
const G::Identity &
id ) ;
60 std::pair<int,std::string> wait() ;
69 void onFutureEvent()
override ;
74 void operator=(
const TaskImp & ) = delete ;
75 void operator=(
TaskImp && ) = delete ;
79 static void waitThread(
TaskImp * , FutureEvent::handle_type ) ;
87 G::threading::thread_type m_thread ;
88 static std::size_t m_zcount ;
91std::size_t GNet::TaskImp::m_zcount = 0U ;
98 const G::Path & cd ,
const std::string & exec_error_format ,
101 m_future_event(*this,es) ,
102 m_timer(*this,&
TaskImp::onTimeout,es) ,
104 m_process(commandline.exe(),commandline.args(),
120 else if( !G::threading::works() )
122 if( G::threading::using_std_thread )
123 G_WARNING_ONCE(
"GNet::TaskImp::TaskImp: multi-threading disabled: running tasks synchronously" ) ;
124 waitThread(
this , m_future_event.
handle() ) ;
128 G_ASSERT( G::threading::using_std_thread ) ;
130 m_thread = G::threading::thread_type( TaskImp::waitThread ,
this , m_future_event.
handle() ) ;
134GNet::TaskImp::~TaskImp()
139 if( m_thread.joinable() )
140 m_process.kill(
true ) ;
141 if( m_thread.joinable() )
149bool GNet::TaskImp::zombify()
152 if( m_thread.joinable() )
155 m_process.kill(
true ) ;
158 m_timer.startTimer( 1U ) ;
161 if( m_zcount == threshold )
162 G_WARNING_ONCE(
"GNet::Task::dtor: large number of threads waiting for processes to finish" ) ;
172void GNet::TaskImp::onTimeout()
174 if( m_thread.joinable() )
177 G_LOG(
"TaskImp::dtor: waiting for killed process to terminate: pid " << m_process.id() ) ;
179 m_timer.startTimer( 1U ) ;
184 G_LOG(
"TaskImp::dtor: killed process has terminated: pid " << m_process.id() ) ;
190std::pair<int,std::string> GNet::TaskImp::wait()
192 m_process.waitable().wait() ;
193 int exit_code = m_process.waitable().get() ;
194 return std::make_pair( exit_code , m_process.waitable().output() ) ;
197void GNet::TaskImp::waitThread( TaskImp * This , FutureEvent::handle_type handle )
202 This->m_process.waitable().wait() ;
211void GNet::TaskImp::onFutureEvent()
213 G_DEBUG(
"GNet::TaskImp::onFutureEvent: future event" ) ;
214 if( m_thread.joinable() )
217 int exit_code = m_process.waitable().get( std::nothrow ) ;
218 G_DEBUG(
"GNet::TaskImp::onFutureEvent: exit code " << exit_code ) ;
220 std::string pipe_output = m_process.waitable().output() ;
221 G_DEBUG(
"GNet::TaskImp::onFutureEvent: output: [" <<
G::Str::printable(pipe_output) <<
"]" ) ;
224 m_task->done( exit_code , pipe_output ) ;
230 const std::string & exec_error_format ,
const G::Identity &
id ) :
231 m_callback(callback) ,
233 m_exec_error_format(exec_error_format) ,
254 if( m_imp && m_imp->zombify() )
255 GDEF_IGNORE_RETURN m_imp.release() ;
266 G_ASSERT( !m_busy ) ;
267 m_imp = std::make_unique<TaskImp>( *
this , m_es ,
true , commandline ,
268 env , fd_stdin , fd_stdout , fd_stderr , cd ,
269 m_exec_error_format , m_id ) ;
270 return m_imp->wait() ;
276 G::NewProcess::Fd::devnull() ,
277 G::NewProcess::Fd::pipe() ,
278 G::NewProcess::Fd::devnull() ,
293 m_imp = std::make_unique<TaskImp>( *
this , m_es ,
false , commandline ,
294 env , fd_stdin , fd_stdout , fd_stderr , cd ,
295 m_exec_error_format , m_id ) ;
298void GNet::Task::done(
int exit_code ,
const std::string & output )
301 m_callback.onTaskDone( exit_code , output ) ;
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
A callback interface for GNet::FutureEvent.
A FutureEvent object can be used to send a one-shot event via the event loop to the relevant event ha...
handle_type handle() noexcept
Extracts a handle that can be passed between threads and used in send().
static bool send(handle_type handle, bool close=true) noexcept
Pokes an event into the main event loop so that the FutureEventHandler callback is called asynchronou...
An abstract interface for callbacks from GNet::Task.
A private implementation class used by GNet::Task.
A class for running an exectuable in a separate process with an asychronous completion callback.
Task(TaskCallback &, ExceptionSink es, const std::string &exec_error_format=std::string(), 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.
A timer class template in which the timeout is delivered to the specified method.
Holds a set of environment variables and also provides static methods to wrap getenv() and putenv().
static Environment minimal()
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.