COM server threads/unloading
Uncle Enzo
uncleenzo at hushmail.com
Wed Jun 26 19:29:34 EDT 2002
Hello,
I have noticed that my multithreaded COM servers are not unloading
when their clients have unloaded.
To reproduce this in a simple case, I am using the py2exe COM server
recipe. Running the server example with the client code below results
in the testserver.exe loading after the dispatch call, and unloading
after the server reference is set to None.
Then I added the googlelogger import statement and the two lines
initializing and calling the log method. The logger that has two
threads. Using the same client code results in the testserver.exe
loading after the dispatch call, but it does not unload after the
server reference is set to None, nor does it unload after the client
terminates.
What is the proper way to get a multithreaded python COM server to
unload when there are no external references to it?
Thanks in advance,
Enzo
ps, comments about my logger design are welcome, but I'm mostly
interested in the threading issues :)
-----------------------------
Python 2.2.1 (#34, Apr 9 2002, 19:34:33) [MSC 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import win32com.client
>>> server = win32com.client.Dispatch("Python.TestServer")
>>> print server.Hello('enzo')
Hello enzo
>>> server = None
>>> ^Z
----------------------------
file: testcomserver.py
import googlelogger
import sys
import pythoncom
if hasattr(sys, 'importers'):
# we are running as py2exe-packed executable
pythoncom.frozen = 1
class HelloWorld:
_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
if hasattr(sys, 'importers'):
# In the py2exe-packed version, specify the module.class
# to use. In the python script version, python is able
# to figure it out itself.
_reg_class_spec_ = "__main__.HelloWorld"
_reg_clsid_ = "{592C15CE-894D-11D6-9A35-0050DA05A582}"
_reg_desc_ = "Python Test COM Server"
_reg_progid_ = "Python.TestServer"
_public_methods_ = ['Hello']
_public_attrs_ = ['softspace', 'noCalls']
_readonly_attrs_ = ['noCalls']
def __init__(self):
self.softspace = 1
self.noCalls = 0
self.mylogger = googlelogger.getLogger()
# __init__()
def Hello(self, who):
self.noCalls = self.noCalls + 1
# insert "softspace" number of spaces
self.mylogger.log('caller', str(who))
return "Hello" + " " * self.softspace + str(who)
if __name__ == '__main__':
if hasattr(sys, 'importers'):
# running as packed executable.
if '--register' in sys.argv[1:] or '--unregister' in
sys.argv[1:]:
# --register and --unregister work as usual
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)
else:
# start the server.
from win32com.server import localserver
localserver.main()
else:
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)
----------------------
file: setup.py
from distutils.core import setup
import py2exe
setup(name='testCOMserver',
scripts=['testCOMserver.py'])
------------------------
file: googlelogger.py
import threading
import Queue
import sys
import pavlnchcfg
import time
import win32com.client
# This thread waits for ("name", "value") items to appear in the
queue.
# It then checks for a log named "name", creates it if it doesn't
exist,
# and appends the message to that log, with a timestamp.
class Writer(threading.Thread):
def setLogger(self, logger):
#print "setting self._logger to ", logger
self._logger = logger
def run(self):
print "starting Writer.run thread"
while 1 :
# blocks until there are items in the queue
event = self._logger._logeventqueue.get(1)
#get returns a tuple, (name, message)
if event[0] == 'QUIT': #magic quit event
print "logger.Writer.run: got magic event: returning"
return
logname = event[0]
message = event[1]
try:
filename = self._logger._dir + '\\' + logname + ".txt"
logfile = open(filename, 'a')
logfile.write(time.asctime() + " " + message + '\n')
logfile.flush()
logfile.close()
print "logger.Writer.run: logged ", message, " to ",
logname
except:
print "logger.Writer.run: with to log: ", logname, "
failed with message: ", message
class Logger:
# class variables
_logeventqueue = Queue.Queue() #one thread safe multiple user
queue
_dir = ""
def __init__(self):
try:
Logger._dir="C:\\" # for our example
print "logger.Logger.init: got log directory",
Logger._dir, "from config"
except:
Logger._dir = "C:\\"
print "logger.Logger.init: No log directory specified in
config, _dir set to C:\\"
writer = Writer()
writer.setName("writerthread")
writer.setLogger(self)
writer.start()
def log(self, logname, message):
event = logname, message #making the event tuple
# event tuple is tuple[0] = name, tuple[1] = event
Logger._logeventqueue.put(event)
# factory pattern as module instance
_loggerInstance = None
def getLogger():
import logger
if logger._loggerInstance == None:
logger._loggerInstance = Logger()
return logger._loggerInstance
else:
return logger._loggerInstance
if __name__ == '__main__': #test main
import logger
print "starting main"
l = getLogger()
print l
l.log("DEBUG", "l, debug message 1")
l.log("USAGE", "l, usagestat 1")
l.log("DEBUG", "l, debug message 2")
lg = getLogger()
print lg
lg.log("DEBUG", "lg, debug message 1")
lg.log("USAGE", "lg, usagestat 1")
lg.log("DEBUG", "lg, debug message 2")
l.log("QUIT", "QUIT")
print "done"
More information about the Python-list
mailing list