[python-win32] unexpected behavior for threaded Windows Service

Jim Vickroy Jim.Vickroy at noaa.gov
Tue Sep 21 23:33:56 CEST 2004


For those interested, attached is an alternate approach that **does**
exhibit the desired behavior.

This alternate approach registers a Windows Service Control Handler that
responds to a "stop" request rather than periodically checking for a stop
event.

I'm still curious to know why win32event.WaitForSingleObject(...) appears to
pause both (producer and consumer) threads rather than just the consumer
thread.

-- jv

-----Original Message-----
From: python-win32-bounces at python.org
[mailto:python-win32-bounces at python.org]On Behalf Of Jim Vickroy
Sent: Tuesday, September 21, 2004 9:42 AM
To: python-win32
Subject: [python-win32] unexpected behavior for threaded Windows Service


Attached is a "short" script that demonstrates an unexpected behavior of a
threaded, producer/consumer, Windows Service.

My expectation was that the producer would be queuing events every 2
(producer_pause) seconds while the consumer would be processing queued
events every 10 (consumer_pause) seconds.  However, the log, generated by
this Service, indicates the producer is queuing events at the same frequency
as the consumer is processing them.

Could someone help me to understand why the producer is not operating
independently of the consumer?


Thanks,

-- jv


P.S.

My system:
	Python 2.3.3
	pywin build 202
	Windows XP
-------------- next part --------------
'''
A MSW  Service with a producer and a consumer thread connected by a queue.

NOTES
   o This version
      + behaves as expected (i.e., producer and consumer threads behave independently of each others
         pause interval).
      + uses servicemanager.RegisterServiceCtrlHandler(...) to designate a handler of all MSW
         Service-related events rather than the win32event.WaitForSingleObject method of polling
         for a stop request.

AUTHOR
   jim.vickroy at noaa.gov
'''


import win32serviceutil, win32service
import Queue
from   thread  import start_new_thread
from   logging import getLogger, shutdown, Logger, FileHandler, Formatter, CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET
from   time    import ctime, sleep, time




class Tester(win32serviceutil.ServiceFramework):

   _svc_name_         = 'Threaded.Services.Tester.version.2'
   _svc_display_name_ = _svc_name_.replace('.', ' ')
   _svc_deps_         = ['EventLog'] # optional -- names of services to be started before this one

   consumer_pause = 10. # seconds
   producer_pause =  2. # seconds


   def __init__(self, args):

      win32serviceutil.ServiceFramework.__init__(self, args)

      # create/configure a logger to record service activities ...
      self.log  = getLogger(self._svc_display_name_)
      filename  = '%s.log' % self._svc_display_name_
      recorder  = FileHandler(filename)
      formatter = Formatter('%(asctime)s, %(levelname)-8s, %(message)s')
      recorder.setLevel(DEBUG)
      recorder.setFormatter(formatter)
      self.log.addHandler(recorder)
      self.log.setLevel(DEBUG)

      # remaining infrastructure attributes ...
      self.events        = Queue.Queue() # events to be processed
      self.stop_received = False


   def SvcStop(self):

      self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) # send an acknowledgement to the SCM


   def SvcDoRun(self):

      start_new_thread(self.producer, ()) # no parameters to pass to the procedure

      import servicemanager # available only at runtime
      servicemanager.LogInfoMsg('%s started' % self._svc_display_name_) # record event in Microsoft Windows Application Events Log

      servicemanager.RegisterServiceCtrlHandler(self._svc_name_, self.service_events_handler)

      while not self.stop_received:
         while not self.events.empty():
            event = self.events.get()
            self.log.info('consumed "%s"' % event)
         for i in range(10):
            if self.stop_received: break
            sleep(self.consumer_pause/10)


   def producer(self):

      while not self.stop_received:
         event = ctime(time())
         try:
            self.events.put(event)
         except Exception, details:
            self.log.exception('events queuing failure')
         else:
            self.log.info('queued   "%s"' % event)
         for i in range(10):
            if self.stop_received: break
            sleep(self.producer_pause/10)


   def service_events_handler(self, operation):
      '''
      a MSW Control Handler Function for use with:
            servicemanager.RegisterServiceCtrlHandler(self._svc_name_, callback)
         as the *callback* parameter
      '''

      waitHint      = 5000
      win32ExitCode = 0

      if operation == win32service.SERVICE_CONTROL_PAUSE:
         # Do whatever it takes to pause here.
         state = win32service.SERVICE_PAUSED

      elif operation == win32service.SERVICE_CONTROL_CONTINUE:
         # Do whatever it takes to continue here.
         state = win32service.SERVICE_RUNNING

      elif operation == win32service.SERVICE_CONTROL_STOP:
         # perform all shutdown tasks
         import servicemanager # available only at runtime
         servicemanager.LogInfoMsg('%s stopped' % self._svc_display_name_) # record event in Microsoft Windows Application Events Log
         state    = win32service.SERVICE_STOPPED
         waitHint = 0
         self.ReportServiceStatus(state, waitHint, win32ExitCode)
         self.log.info('SHUTDOWN request received')
         shutdown()
         self.stop_received = True
         return

      elif operation == win32service.SERVICE_CONTROL_INTERROGATE:
         pass

      else:
         pass #> 'Unrecognized opcode %d\n' % operation

      self.ReportServiceStatus(state, waitHint, win32ExitCode)

      return




if __name__=='__main__':
   from win32serviceutil import HandleCommandLine
   HandleCommandLine(Tester)


More information about the Python-win32 mailing list