Hi all, I'm currently building an application in which I needed to embed WebWare in a DLL. This Dll serves multiple purposes: it exposes the object model of the application so that PSP scripts can call into it (done as a built-in Python extension module), it acts as an adapter redirecting the web server requests to the app server and it embeds a Python interpreter used to launch and control the WebWare app server.
Here's the Python script (derived from ThreadedAppServerService) that I am controlling from the Dll: #!/usr/bin/env python """ HCAppSrv: Modified version of the original ThreadedAppServerService code (removed all SCM stuff) """ import os, sys, time, cStringIO class HCAppSrv: def __init__(self): self.server = None # Fix the current working directory -- this gets initialized incorrectly # for some reason when run as an NT service. try: os.chdir(os.path.abspath(os.path.dirname(__file__))) except: pass def Stop(self, args): # And set running to 0 in the server. If it hasn't started yet, we'll # have to wait until it does. while self.server is None: time.sleep(1.0) self.server.initiateShutdown() def Start(self, args): try: # Temporarily suck up stdout and stderr into a cStringIO if args[0] == "SERVICE": stdoutAndStderr = cStringIO.StringIO() sys.stdout = sys.stderr = stdoutAndStderr # Import the ThreadedAppServer if '' not in sys.path: sys.path = [''] + sys.path os.chdir(os.pardir) from WebKit.ThreadedAppServer import ThreadedAppServer self.server = ThreadedAppServer(self.workDir()) # Now switch the output to a logfile if args[0] == "SERVICE": sys.stdout = sys.stderr = open('EmbeddedAppServer.Log', 'a+') sys.stdout.write('-' * 68 + '\n') sys.stdout.write(stdoutAndStderr.getvalue()) del stdoutAndStderr print "AppServer is running..." self.server.mainloop() self.server._closeThread.join() print "AppServer has ended!" except Exception, e: #Need to kill the Sweeper thread somehow print e print "Exception! Exiting AppServer" if 1: #See the traceback from an exception tb = sys.exc_info() print tb[0] print tb[1] import traceback traceback.print_tb(tb[2]) if self.server: self.server.running=0 self.server.shutDown() raise def workDir(self): return None if __name__=='__main__': AppSrv = HCAppSrv() AppSrv.Start("DEBUG") In order to launch the WebWare app server without blocking the rest of the execution in the dll, a Win32 thread is dedicated to loading the module implementing the HCAppSrv class and calling HCAppSrv.Start(). Note: Start() is not supposed to return until the WebWare app server gets shutdown by a call to HCAppSrv.Stop() later. The thread launching the app server caches the PyObject pointer to the instance of HCAppSrv class that it created(m_pAppSrvInst = PyEval_CallObject(pclass, pargs) and does not call Py_DECREF on it. This is intended so that the pointer to the running instance of HCAppSrv can be reused later when HCAppSrv.Stop() needs to be called. Up to this point, everything works fine: the app server starts up and serves any requested PSP page and makes successful calls into the C++ object model exposed by the DLL. It is only when the time comes to call HCAppSrv.Stop() that things go wrong. The main thread of the application calls HCAppSrv.Stop() by using the cached PyObject* pointer to the running instance of the class and attempts to invoke Stop() which results in an exception deep down in the Python interpreter. Here's the back trace that I get when debugging under VC++: PyDict_SetItem(_object * 0x00000000, _object * 0x01d856c0, _object * 0x01d6c578) line 510 + 3 bytes PyDict_SetItemString(_object * 0x00000000, char * 0x1e136a00, _object * 0x01d6c578) line 1883 + 17 bytes PySys_SetObject(char * 0x1e136a00, _object * 0x01d6c578) line 63 + 17 bytes set_exc_info(_ts * 0x02243340, _object * 0x01d6c578, _object * 0x022448e8, _object * 0x02244c58) line 2644 + 14 bytes eval_frame(_frame * 0x02240a50) line 2284 + 30 bytes PyEval_EvalCodeEx(PyCodeObject * 0x020e9c08, _object * 0x020ee448, _object * 0x00000000, _object * * 0x022148e0, int 0, _object * * 0x022148e0, int 0, _object * * 0x00000000, int 0, _object * 0x00000000) line 2585 + 9 bytes fast_function(_object * 0x020a0ec8, _object * * * 0x01a5ec80, int 0, int 0, int 0) line 3164 + 65 bytes eval_frame(_frame * 0x02214770) line 2025 + 37 bytes PyEval_EvalCodeEx(PyCodeObject * 0x020daf50, _object * 0x020ee448, _object * 0x00000000, _object * * 0x01fd06c0, int 2, _object * * 0x01fd06c8, int 0, _object * * 0x020ede4c, int 1, _object * 0x00000000) line 2585 + 9 bytes fast_function(_object * 0x020d60c0, _object * * * 0x01a5eeac, int 2, int 2, int 0) line 3164 + 65 bytes eval_frame(_frame * 0x01fd0568) line 2025 + 37 bytes PyEval_EvalCodeEx(PyCodeObject * 0x020da3d8, _object * 0x020ee448, _object * 0x00000000, _object * * 0x01ffc078, int 1, _object * * 0x01ffc07c, int 0, _object * * 0x00000000, int 0, _object * 0x00000000) line 2585 + 9 bytes fast_function(_object * 0x020d6148, _object * * * 0x01a5f0d8, int 1, int 1, int 0) line 3164 + 65 bytes eval_frame(_frame * 0x01ffbf20) line 2025 + 37 bytes PyEval_EvalCodeEx(PyCodeObject * 0x020df1d0, _object * 0x020ee448, _object * 0x00000000, _object * * 0x02197b08, int 1, _object * * 0x02197b0c, int 0, _object * * 0x00000000, int 0, _object * 0x00000000) line 2585 + 9 bytes fast_function(_object * 0x020ef970, _object * * * 0x01a5f304, int 1, int 1, int 0) line 3164 + 65 bytes eval_frame(_frame * 0x021979b0) line 2025 + 37 bytes PyEval_EvalCodeEx(PyCodeObject * 0x01e03e60, _object * 0x01def340, _object * 0x00000000, _object * * 0x01d9b1c4, int 1, _object * * 0x01d9b1c8, int 0, _object * * 0x00000000, int 0, _object * 0x00000000) line 2585 + 9 bytes fast_function(_object * 0x01e59300, _object * * * 0x01a5f530, int 1, int 1, int 0) line 3164 + 65 bytes eval_frame(_frame * 0x01d9b068) line 2025 + 37 bytes PyEval_EvalCodeEx(PyCodeObject * 0x01d72300, _object * 0x01d97730, _object * 0x00000000, _object * * 0x01d911f4, int 2, _object * * 0x00000000, int 0, _object * * 0x00000000, int 0, _object * 0x00000000) line 2585 + 9 bytes function_call(_object * 0x01dd7bd8, _object * 0x01d911e0, _object * 0x00000000) line 379 + 64 bytes PyObject_Call(_object * 0x01dd7bd8, _object * 0x01d911e0, _object * 0x00000000) line 1684 + 15 bytes instancemethod_call(_object * 0x01dd7bd8, _object * 0x01d911e0, _object * 0x00000000) line 2276 + 17 bytes PyObject_Call(_object * 0x01fe0f78, _object * 0x01d8e238, _object * 0x00000000) line 1684 + 15 bytes PyEval_CallObjectWithKeywords(_object * 0x01fe0f78, _object * 0x01d8e238, _object * 0x00000000) line 3049 + 17 bytes CWebWareLauncher::OnStop() line 268 + 18 bytes I'm at a loss because everything "looks" right: HCAppSrv.Stop() is called exactly like HCAppSrv.Start() except for the fact that it is invoked from a different thread and references the cached instance of the app server. I can't see what I'm missing. If anyone on this list is familiar with embedding Python and Python threading or has embedded WebWare in a similar fashion before, please share your thoughts :) I'll be glad to email you the C++ source code if you think you can help with this. Regards, Fabien. _________________________________________________________________ Chat with friends online, try MSN Messenger: http://messenger.msn.com ------------------------------------------------------- This SF.NET email is sponsored by: AMD - Your access to the experts on Hammer Technology! Open Source & Linux Developers, register now for the AMD Developer Symposium. Code: EX8664 http://www.developwithamd.com/developerlab _______________________________________________ Webware-discuss mailing list [EMAIL PROTECTED] https://lists.sourceforge.net/lists/listinfo/webware-discuss