E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gtask.cpp
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gtask.cpp
19 //
20
21 #include "gdef.h"
22 #include "gtask.h"
23 #include "gfutureevent.h"
24 #include "gnewprocess.h"
25 #include "gstr.h"
26 #include "glog.h"
27
28 /// \class GNet::TaskImp
29 /// A private implementation class used by GNet::Task.
30 ///
31 class GNet::TaskImp : private FutureEventHandler
32 {
33 public:
34 TaskImp( Task & , ExceptionHandler & eh , const G::ExecutableCommand & ,
35 const std::string & exec_error_format , const G::Identity & id ) ;
36 // Constructor. Spawns the child processes and starts the
37 // associated wait() thread.
38 //
39 // The GNet::FutureEvent class is used to send the completion
40 // message from the wait() thread to the main thread via
41 // the event-loop.
42 //
43 // In a single-threaded build, or if multi-threading is broken,
44 // this contructor runs the task, waits for it to complete
45 // and posts the completion message to the event-loop
46 // before this constructor returns.
47
48 static std::pair<int,std::string> run( const G::ExecutableCommand & ,
49 const std::string & exec_error_format , const G::Identity & id ) ;
50 // Runs the task synchronously.
51
52 virtual ~TaskImp() ;
53 // Destructor.
54
55 void kill() ;
56 // Kills the task.
57
58 private:
59 TaskImp( const TaskImp & ) ;
60 void operator=( const TaskImp & ) ;
61 virtual void onFutureEvent() override ; // GNet::FutureEventHandler
62 static void wait( TaskImp * , FutureEvent::handle_type ) ; // thread function
63
64 private:
65 Task & m_task ;
66 FutureEvent m_future_event ;
67 G::NewProcess m_process ;
68 G::threading::thread_type m_thread ;
69 } ;
70
71 // ==
72
73 GNet::TaskImp::TaskImp( Task & task , ExceptionHandler & eh , const G::ExecutableCommand & commandline ,
74 const std::string & exec_error_format , const G::Identity & id ) :
75 m_task(task) ,
76 m_future_event(*this,eh) ,
77 m_process(commandline.exe(),commandline.args(),1,true,true,id,true,127,exec_error_format)
78 {
79 if( ! G::threading::works() ) // grr
80 {
81 if( G::threading::using_std_thread )
82 G_WARNING_ONCE( "GNet::TaskImp::TaskImp: multi-threading disabled: running tasks synchronously" ) ;
83 wait( this , m_future_event.handle() ) ;
84 }
85 else
86 {
87 m_thread = G::threading::thread_type( TaskImp::wait , this , m_future_event.handle() ) ;
88 }
89 }
90
91 GNet::TaskImp::~TaskImp()
92 {
93 if( m_thread.joinable() )
94 {
95 m_process.kill() ;
96 G_WARNING( "TaskImp::dtor: blocked waiting for process " << m_process.id() << " to terminate" ) ;
97 m_thread.join() ;
98 G_LOG( "TaskImp::dtor: process " << m_process.id() << " terminated" ) ;
99 }
100 }
101
102 void GNet::TaskImp::wait( TaskImp * This , FutureEvent::handle_type handle )
103 {
104 // worker-thread -- keep it simple
105 try
106 {
107 This->m_process.wait().run() ;
108 FutureEvent::send( handle ) ;
109 }
110 catch(...) // worker thread outer function
111 {
112 FutureEvent::send( handle ) ; // nothrow
113 }
114 }
115
116 void GNet::TaskImp::onFutureEvent()
117 {
118 try
119 {
120 G_DEBUG( "GNet::TaskImp::onFutureEvent: future event" ) ;
121 if( G::threading::works() )
122 {
123 m_thread.join() ;
124 G_DEBUG( "GNet::TaskImp::onFutureEvent: thread joined" ) ;
125 }
126
127 int exit_code = m_process.wait().get() ;
128 G_DEBUG( "GNet::TaskImp::onFutureEvent: exit code " << exit_code ) ;
129
130 std::string output = m_process.wait().output() ;
131 G_DEBUG( "GNet::TaskImp::onFutureEvent: output: [" << G::Str::printable(output) << "]" ) ;
132
133 m_task.done( exit_code , output ) ; // last
134 }
135 catch( std::exception & e )
136 {
137 throw Task::Failed( e.what() ) ;
138 }
139 }
140
141 void GNet::TaskImp::kill()
142 {
143 m_process.kill() ;
144 }
145
146 // ==
147
148 GNet::Task::Task( TaskCallback & callback , ExceptionHandler & eh ,
149 const std::string & exec_error_format , const G::Identity & id ) :
150 m_callback(callback) ,
151 m_eh(eh) ,
152 m_exec_error_format(exec_error_format) ,
153 m_id(id) ,
154 m_busy(false)
155 {
156 }
157
158 GNet::Task::~Task()
159 {
160 }
161
162 void GNet::Task::start( const G::ExecutableCommand & commandline )
163 {
164 if( m_busy )
165 throw Busy() ;
166
167 m_busy = true ;
168 m_imp.reset( new TaskImp( *this , m_eh , commandline , m_exec_error_format , m_id ) ) ;
169 }
170
171 void GNet::Task::stop()
172 {
173 // best effort - may block
174 m_busy = false ;
175 m_imp.reset() ;
176 }
177
178 void GNet::Task::done( int exit_code , std::string output )
179 {
180 m_busy = false ;
181 m_callback.onTaskDone( exit_code , output ) ;
182 }
183
184 // ==
185
186 GNet::TaskCallback::~TaskCallback()
187 {
188 }
189
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:701
GNet::FutureEventHandler
A callback interface for GNet::FutureEvent.
Definition: gfutureevent.h:100
GNet::FutureEvent
An object that hooks into the event loop and calls back to the client code.
Definition: gfutureevent.h:62
glog.h
GNet::Task::Task
Task(TaskCallback &, ExceptionHandler &eh, const std::string &exec_error_format=std::string(), const G::Identity &=G::Identity::invalid())
Constructor for a start()able object.
Definition: gtask.cpp:148
G::Identity
A combination of user-id and group-id, with a very low-level interface to the get/set/e/uid/gid funct...
Definition: gidentity.h:42
GNet::Task::start
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...
Definition: gtask.cpp:162
GNet::TaskImp
A private implementation class used by GNet::Task.
Definition: gtask.cpp:31
GNet::ExceptionHandler
An abstract interface for handling exceptions thrown out of event-loop callbacks (socket events...
Definition: gexceptionhandler.h:44
GNet::Task::stop
void stop()
Attempts to kill the spawned process and waits for it to terminate.
Definition: gtask.cpp:171
gstr.h
GNet::Task
A class for running an exectuable in a separate process with an asychronous completion callback...
Definition: gtask.h:42
gnewprocess.h
G::ExecutableCommand
A structure representing an external program, holding a path and a set of arguments.
Definition: gexecutablecommand.h:43
GNet::FutureEvent::send
static bool send(handle_type handle) g__noexcept
Pokes an event into the main event loop so that the FutureEventHandler callback is called asynchronou...
Definition: gfutureevent_unix.cpp:146
gtask.h
GNet::FutureEvent::handle
handle_type handle()
Returns a handle that can be passed between threads and used in send().
Definition: gfutureevent_unix.cpp:151
GNet::TaskCallback
An abstract interface for callbacks from GNet::Task.
Definition: gtask.h:89
GNet::Task::~Task
~Task()
Destructor.
Definition: gtask.cpp:158
gfutureevent.h
G::NewProcess
A class for creating new processes.
Definition: gnewprocess.h:56
GNet::TaskCallback::~TaskCallback
virtual ~TaskCallback()
Destructor.
Definition: gtask.cpp:186