@@ -57,6 +57,7 @@ class OutgoingRunner(Runner, BounceMixin):
5757 # Process coordination
5858 _pid_file = os .path .join (mm_cfg .LOCK_DIR , 'outgoing.pid' )
5959 _pid_lock = threading .Lock ()
60+ _running = False
6061
6162 # Shared processed messages tracking with size limits
6263 _processed_messages = set ()
@@ -131,13 +132,45 @@ def __init__(self, slice=None, numslices=1):
131132 self .__logged = False
132133 mailman_log ('debug' , 'OutgoingRunner: Initializing retry queue' )
133134 self .__retryq = Switchboard (mm_cfg .RETRYQUEUE_DIR )
135+ self ._running = True
134136 mailman_log ('debug' , 'OutgoingRunner: Initialization complete' )
135137 except Exception as e :
136138 mailman_log ('error' , 'OutgoingRunner: Initialization failed: %s' , str (e ))
137139 mailman_log ('error' , 'OutgoingRunner: Traceback: %s' , traceback .format_exc ())
138140 self ._release_pid_lock ()
139141 raise
140142
143+ def run (self ):
144+ """Run the OutgoingRunner main loop."""
145+ if not self ._running :
146+ mailman_log ('error' , 'OutgoingRunner: Not properly initialized' )
147+ return
148+
149+ mailman_log ('debug' , 'OutgoingRunner: Starting main loop' )
150+ try :
151+ while self ._running and not self ._stop :
152+ try :
153+ self ._oneloop ()
154+ # Sleep briefly to prevent CPU spinning
155+ time .sleep (1 )
156+ except Exception as e :
157+ mailman_log ('error' , 'OutgoingRunner: Error in main loop: %s' , str (e ))
158+ mailman_log ('error' , 'OutgoingRunner: Traceback: %s' , traceback .format_exc ())
159+ # Don't exit on error, just continue the loop
160+ time .sleep (5 ) # Sleep longer on error
161+ except Exception as e :
162+ mailman_log ('error' , 'OutgoingRunner: Fatal error in main loop: %s' , str (e ))
163+ mailman_log ('error' , 'OutgoingRunner: Traceback: %s' , traceback .format_exc ())
164+ finally :
165+ self ._cleanup ()
166+
167+ def stop (self ):
168+ """Stop the OutgoingRunner."""
169+ mailman_log ('debug' , 'OutgoingRunner: Stopping' )
170+ self ._running = False
171+ self ._stop = True
172+ self ._cleanup ()
173+
141174 def _acquire_pid_lock (self ):
142175 """Try to acquire the PID lock file."""
143176 try :
@@ -567,6 +600,9 @@ def _validate_bounce(self, recip, code, errmsg):
567600
568601 def _cleanup (self ):
569602 """Clean up resources."""
603+ if not self ._running :
604+ return
605+
570606 mailman_log ('debug' , 'OutgoingRunner: Starting cleanup' )
571607 try :
572608 BounceMixin ._cleanup (self )
@@ -581,6 +617,7 @@ def _cleanup(self):
581617 finally :
582618 # Always release the PID lock during cleanup
583619 self ._release_pid_lock ()
620+ self ._running = False
584621 mailman_log ('debug' , 'OutgoingRunner: Cleanup complete' )
585622
586623 _doperiodic = BounceMixin ._doperiodic
0 commit comments