1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
4 # Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
5 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
25 __date__ ="$1 avr. 2010 18:12:38$"
30 # ===========================================================================
31 class Runner(threading.Thread):
33 This class provides a tool to run and drive a function in a dedicated thread.
35 def __init__(self, functionToRun=None,*argv):
36 threading.Thread.__init__( self )
37 self._stopevent = threading.Event()
38 self.setFunction(functionToRun)
39 self.setArguments(*argv)
40 self._exception = None
43 self._callbackFunction = None
44 # We create an id from the name and the time date in milliseconds
45 self._id = functionToRun.__name__ +"/"+ str(time.time())
47 def setFunction(self,functionToRun):
49 Positionne la fonction � ex�cuter. La fonction peut �tre la
50 m�thode d'un objet pass�e sous la forme 'monobjet.mamethode'.
53 self._function = functionToRun
55 def setArguments(self,*argv):
57 Positionne les arguments � passer � la fonction
64 Retourne le resultat de la fonction. En cas d'erreur, on
65 r�cup�rera l'exception lev�e au moyen de la m�thode
70 def setCallbackFunction(self, callbackFunction):
71 self._callbackFunction = callbackFunction
73 def setObserver(self, observerToNotify):
75 Permet de sp�cifier un observateur � notifier en fin
76 d'ex�cution. L'observateur est suppos� �tre un objet qui
77 impl�mente une m�thode processNotification()
80 observerToNotify.processNotification
81 except AttributeError:
82 raise DevelException("The observer should implement the method processNotification()")
84 self._observer = observerToNotify
88 Ex�cution de la fonction. Impl�mentation de la m�thode run
89 d�clench�e par l'appel � Thread.start().
91 print("##################### threadhelper.run")
92 if self._function is None: return
94 self._return = self._function(*self._argv)
95 except Exception as e:
101 # _GBO_ Maybe it's no use to have both observers and a callback function.
102 # One of the two mechanism should be sufficient
104 def notifyObserver(self):
105 if self._observer is None:
106 # Aucune notification pr�vue
109 self._observer.processNotification()
110 except AttributeError as att:
111 if str(att) == "processNotification":
112 print("L'observateur n'impl�mente pas la m�thode processNotification()")
114 print("La fonction processNotification() a lev� une exception:")
116 except Exception as e:
117 print("La fonction processNotification() a lev� une exception:")
121 if self._callbackFunction is None: return
122 self._callbackFunction()
126 Retourne true si la fonction s'est termin�e naturellement
127 (correctement ou en erreur). Utile pour les client qui
128 pratique le pooling (interrogation r�p�t�e � intervalle
131 return self._stopevent.isSet()
132 # _GBO_ On n'utilise pas isAlive() pour pouvoir ma�triser
133 # l'indicateur de stop (exemple de la fonction kill)
134 # return not self.isAlive()
136 def getException(self):
137 return self._exception
142 def wait(self,timeout=None):
144 Met fin au thread apr�s timeout seconde s'il est encore en
146 Si le compte-�-rebours est atteind sans que la fonction
147 functionToRun soit termin�e, alors le runner est laiss� dans
148 l'�tat 'not Ended', c'est-�-dire que isEnded retourne
149 false. On peut ainsi distinguer l'arr�t normal de l'arr�t �
152 threading.Thread.join(self, timeout)
155 def kill(self,message="Arr�t demand�"):
157 Cette m�thode doit �tre appeler pour interrombre le
158 thread. Cet appel d�clare le thread comme en erreur (exception
159 mise � disposition par la m�thode getException().
161 # On cr�e un exception indiquant la demande d'interruption
162 self._exception = Exception(message)
163 self._stopevent.set()
165 # Un thread python ne peut pas en r�alit� �tre int�rrompu.
166 # La fonction reste donc en ex�cution m�me si on a rejoint le
170 self._stopevent.clear()
171 self._exception = None
174 # ===========================================================================
178 class PeriodicTimer( threading.Thread ):
180 Cette classe permet d'amorcer un compte-�-rebours p�riodique pour
181 op�rer un m�canisme de pooling. On d�finit la fonction appell�e
182 p�riodiquement et la fonction terminale.
184 def __init__( self, loopdelay, initdelay=0,
185 periodic_action=None,
189 if periodic_action is None:
190 self._periodic_action = self.defaultPeriodicAction
192 self._periodic_action = periodic_action
193 self._ended_action = ended_action
194 self._loopdelay = loopdelay
195 self._initdelay = initdelay
196 self._running = CONTINUE
197 self._ended_argv = ended_argv
198 threading.Thread.__init__( self )
200 def defaultPeriodicAction():
202 Les fonctions 'periodicAction' retournent CONTINU ou STOP
203 apr�s avoir ex�cut� l'action de fin de boucle. Si STOP est
204 retourn�, le cycle est interrompu.
210 time.sleep( self._initdelay )
211 self._runtime = time.time()
212 while self._running == CONTINUE:
214 self._running = self._periodic_action()
215 self._runtime += self._loopdelay
216 time.sleep( max( 0, self._runtime - start ) )
217 if self._ended_action is not None:
218 self._ended_action( *self._ended_argv )
226 # ======================================================
227 # Fonctions de test unitaire
228 # ======================================================
231 # ======================================================
233 testfilename="/tmp/threadhelperTestFile"
234 def testIfContinue():
235 print("On examine la pr�sence du fichier ", testfilename)
236 if os.path.exists(testfilename):
244 def TEST_PeriodicTimer():
245 periodicTimer=PeriodicTimer(1,0,testIfContinue, endedAction)
246 periodicTimer.start()
249 # ======================================================
250 def function_ok(nbsteps=5):
252 Fonction qui se termine correctement
256 while ( cnt < nbsteps ):
263 def function_with_exception():
265 Fonction qui aboutie � une lev�e d'exception
274 raise Exception("erreur d'ex�cution de la fonction")
277 def infinite_function():
279 fonction de dur�e infinie (tant qu'il y a du courant �l�ctrique) pour
289 raise Exception("erreur")
293 def runWithRunner(functionToRun):
295 Ex�cute la fonction avec le runner. On illustre ici la modalit�
296 d'utilisation du Runner.
299 runner = Runner(functionToRun)
302 while ( not runner.isEnded() ):
303 print("La fonction est en cours")
305 e = runner.getException()
307 print("La fonction s'est termin�e en erreur")
309 # On peut en fait la relancer
312 print("La fonction s'est termin�e correctement")
315 def runWithTimeout(functionToRun, timeout=10):
317 Ex�cute la fonction avec le runner. On illustre ici la modalit�
318 d'utilisation du Runner.
320 print("runWithTimeout : DEBUT")
321 runner = Runner(functionToRun)
324 # On se fixe un temps au del� duquel on consid�re que la fonction
325 # est en erreur => on tue le thread (timeout)
327 print("Apr�s runner.timeout(timeout)")
328 if not runner.isEnded():
330 e = runner.getException()
332 print("La fonction s'est termin�e en erreur")
334 # On peut en fait la relancer
337 print("La fonction s'est termin�e correctement")
339 print("runWithTimeout : FIN")
345 #runWithTimeout(function_ok)
346 #runWithTimeout(function_ok,2)
347 runWithTimeout(function_with_exception)
351 runWithRunner(function_ok)
352 runWithRunner(function_with_exception)
353 #runWithRunner(infinite_function)
356 def myCallbackFunction():
357 print("myCallbackFunction: the job is ended")
360 def TEST_runWithCallback():
361 runner = Runner(function_ok,8)
362 runner.setCallbackFunction(myCallbackFunction)
365 if runner.getException() is not None:
368 runnerId = runner.getId()
369 print("A runner has been started with id="+str(runnerId))
371 while ( not runner.isEnded() ):
372 print("Waiting notification from process "+str(runner.getId())+", step n°"+str(cpt))
378 if __name__ == "__main__":
379 from . import unittester
380 #TEST_PeriodicTimer()
383 unittester.run("threadhelper","TEST_runWithCallback")