Support multithreaded execution of Python components with the HTTPD event MPM.

git-svn-id: https://svn.apache.org/repos/asf/tuscany/sca-cpp/trunk@1165452 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/modules/edit/ssl-start b/modules/edit/ssl-start
index 53803e4..576fa39 100755
--- a/modules/edit/ssl-start
+++ b/modules/edit/ssl-start
@@ -30,6 +30,7 @@
 
 # Configure server with virtual hosting
 ../../modules/http/httpd-conf tmp sca-store.com 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/http/vhost-conf tmp apps htdocs
 ../../modules/http/httpd-ssl-conf tmp 8453
 ../../modules/http/vhost-ssl-conf tmp
diff --git a/modules/edit/start b/modules/edit/start
index 7d25931..6125ba0 100755
--- a/modules/edit/start
+++ b/modules/edit/start
@@ -25,6 +25,7 @@
 
 # Configure server with virtual hosting
 ../../modules/http/httpd-conf tmp sca-store.com 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/http/vhost-conf tmp apps htdocs
 
 # Configure Python component support
diff --git a/modules/python/driver.hpp b/modules/python/driver.hpp
index 79897b0..a5d5541 100644
--- a/modules/python/driver.hpp
+++ b/modules/python/driver.hpp
@@ -35,26 +35,26 @@
 namespace tuscany {
 namespace python {
 
-const value evalDriverLoop(PyObject* script, istream& in, ostream& out) {
+const value evalDriverLoop(PyObject* script, istream& in, ostream& out, PythonRuntime& py) {
     scheme::promptForInput(scheme::evalInputPrompt, out);
     value input = scheme::readValue(in);
     if (isNil(input))
         return input;
-    const failable<value> output = evalScript(input, script);
+    const failable<value> output = evalScript(input, script, py);
     scheme::announceOutput(scheme::evalOutputPrompt, out);
     scheme::userPrint(content(output), out);
-    return evalDriverLoop(script, in, out);
+    return evalDriverLoop(script, in, out, py);
 }
 
 const bool evalDriverRun(const char* path, istream& in, ostream& out) {
     PythonRuntime py;
     scheme::setupDisplay(out);
     ifstream is(path);
-    failable<PyObject*> script = readScript(moduleName(path), path, is);
+    failable<PyObject*> script = readScript(moduleName(path), path, is, py);
     if (!hasContent(script))
         return true;
-    evalDriverLoop(content(script), in, out);
-    Py_DECREF(content(script));
+    evalDriverLoop(content(script), in, out, py);
+    releaseScript(content(script), py);
     return true;
 }
 
diff --git a/modules/python/eval.hpp b/modules/python/eval.hpp
index f106ff1..a1685d1 100644
--- a/modules/python/eval.hpp
+++ b/modules/python/eval.hpp
@@ -37,23 +37,53 @@
 namespace tuscany {
 namespace python {
 
+class PythonThreadIn;
+class PythonThreadOut;
+class PythonRuntimeLock;
+
 /**
  * Represent a Python runtime.
  */
+
 class PythonRuntime {
 public:
     PythonRuntime() {
         debug("python::pythonruntime");
-        if (Py_IsInitialized())
-            return;
-        Py_InitializeEx(0);
-        const char* arg0 = "";
-        PySys_SetArgv(0, const_cast<char**>(&arg0));
+        pthread_mutex_init(&modulemx, NULL);
+
+        // Initialize the Python interpreter
+        if (!Py_IsInitialized()) {
+            debug("python::pythonruntime::initialize");
+            Py_InitializeEx(0);
+
+            // Set default interpreter args
+            const char* arg0 = "";
+            PySys_SetArgv(0, const_cast<char**>(&arg0));
+
+#ifdef WANT_THREADS
+            // Initialize the Python thread support
+            PyEval_InitThreads();
+
+            // Release Python lock
+            PyEval_ReleaseLock();
+#endif
+        }
+
     }
 
     ~PythonRuntime() {
         debug("python::~pythonruntime");
     }
+
+private:
+
+#ifdef WANT_THREADS
+    pthread_mutex_t modulemx;
+#endif
+
+    friend class PythonThreadIn;
+    friend class PythonThreadOut;
+    friend class PythonRuntimeLock;
 };
 
 /**
@@ -83,22 +113,90 @@
 }
 
 /**
+ * Represent a lock on the Python runtime.
+ */
+class PythonRuntimeLock {
+public:
+    PythonRuntimeLock(PythonRuntime* py) : py(py) {
+#ifdef WANT_THREADS
+        pthread_mutex_lock(&py->modulemx);
+#endif
+    }
+
+    ~PythonRuntimeLock() {
+#ifdef WANT_THREADS
+        pthread_mutex_unlock(&py->modulemx);
+#endif
+    }
+
+private:
+    PythonRuntime* py;
+};
+
+/**
+ * Represent a thread calling into the Python interpreter.
+ */
+class PythonThreadIn {
+public:
+    PythonThreadIn(PythonRuntime* py) : py(py) {
+#ifdef WANT_THREADS
+        //debug("python::gil::ensure");
+        gstate = PyGILState_Ensure();
+#endif
+    }
+
+    ~PythonThreadIn() {
+#ifdef WANT_THREADS
+        //debug("python::gil::release");
+        PyGILState_Release(gstate);
+#endif
+    }
+
+private:
+    PythonRuntime* py;
+    PyGILState_STATE gstate;
+};
+
+/**
+ * Represent a thread calling out of the Python interpreter.
+ */
+class PythonThreadOut {
+public:
+    PythonThreadOut(PythonRuntime* py) : py(py) {
+#ifdef WANT_THREADS
+        //tstate = PyEval_SaveThread();
+#endif
+    }
+
+    ~PythonThreadOut() {
+#ifdef WANT_THREADS
+        //PyEval_RestoreThread(tstate);
+#endif
+    }
+
+private:
+    PythonRuntime* py;
+    PyThreadState* tstate;
+};
+
+/**
  * Declare conversion functions.
  */
-PyObject* valueToPyObject(const value& v);
-const value pyObjectToValue(PyObject *o);
-PyObject* valuesToPyTuple(const list<value>& v);
-const list<value> pyTupleToValues(PyObject* o);
+PyObject* valueToPyObject(const value& v, PythonRuntime* py);
+const value pyObjectToValue(PyObject *o, PythonRuntime* py);
+PyObject* valuesToPyTuple(const list<value>& v, PythonRuntime* py);
+const list<value> pyTupleToValues(PyObject* o, PythonRuntime* py);
 
 /**
  * Callable python type used to represent a lambda expression.
  */
 typedef struct {
-  PyObject_HEAD
-  lambda<value(const list<value>&)> func;
+    PyObject_HEAD
+    lambda<value(const list<value>&)>* func;
+    PythonRuntime* py;
 } pyLambda;
 
-PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l);
+PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l, PythonRuntime* py);
 
 void pyLambda_dealloc(PyObject* self) {
     debug(self, "python::pylambda_dealloc");
@@ -112,12 +210,17 @@
     return s;
 }
 
+const value pyLambda_callout(const pyLambda* pyl, const list<value>& args, PythonRuntime* py) {
+    PythonThreadOut pyout(py);
+    return (*(pyl->func))(args);
+}
+
 PyObject* pyLambda_call(PyObject* self, PyObject* args, unused PyObject* kwds) {
     debug("python::call");
-    const pyLambda* pyl = (pyLambda*)self;
-    const value result = pyl->func(pyTupleToValues(args));
+    const pyLambda* pyl = (const pyLambda*)self;
+    const value result =  pyLambda_callout(pyl, pyTupleToValues(args, pyl->py), pyl->py);
     debug(result, "python::call::result");
-    PyObject *pyr = valueToPyObject(result);
+    PyObject *pyr = valueToPyObject(result, pyl->py);
     return pyr;
 }
 
@@ -148,53 +251,89 @@
 
     const pyLambda* pyl = (pyLambda*)self;
     debug(name, "python::getattr::name");
-    PyObject* pyr = mkPyLambda(pyProxy(name, pyl->func));
+    PyObject* pyr = mkPyLambda(pyProxy(name, *(pyl->func)), pyl->py);
     return pyr;
 }
 
-PyTypeObject pyLambda_type = {
-    PyObject_HEAD_INIT(0)
-    0,
-    "lambda",
-    sizeof(pyLambda),
-    0,
-    (destructor)pyLambda_dealloc,
-    0, 0, 0, 0, 0, 0, 0, 0, 0,
-    (ternaryfunc)pyLambda_call,
-    0, 
-    (binaryfunc)pyLambda_getattr,
-    0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0
+ PyTypeObject pyLambda_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "lambda",                     /*tp_name*/
+    sizeof(pyLambda),             /*tp_basicsize*/
+    0,                            /*tp_itemsize*/
+    /* methods */
+    (destructor)pyLambda_dealloc, /*tp_dealloc*/
+    0,                            /*tp_print*/
+    0,                            /*tp_getattr*/
+    0,                            /*tp_setattr*/
+    0,                            /*tp_compare*/
+    0,                            /*tp_repr*/
+    0,                            /*tp_as_number*/
+    0,                            /*tp_as_sequence*/
+    0,                            /*tp_as_mapping*/
+    0,                            /*tp_hash*/
+    (ternaryfunc)pyLambda_call,   /*tp_call*/
+    0,                            /*tp_str*/
+    (binaryfunc)pyLambda_getattr, /*tp_getattro*/
+    0,                            /*tp_setattro*/
+    0,                            /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT,           /*tp_flags*/
+    0,                            /*tp_doc*/
+    0,                            /*tp_traverse*/
+    0,                            /*tp_clear*/
+    0,                            /*tp_richcompare*/
+    0,                            /*tp_weaklistoffset*/
+    0,                            /*tp_iter*/
+    0,                            /*tp_iternext*/
+    0,                            /*tp_methods*/
+    0,                            /*tp_members*/
+    0,                            /*tp_getset*/
+    0,                            /*tp_base*/
+    0,                            /*tp_dict*/
+    0,                            /*tp_descr_get*/
+    0,                            /*tp_descr_set*/
+    0,                            /*tp_dictoffset*/
+    0,                            /*tp_init*/
+    0,                            /*tp_alloc*/
+    0,                            /*tp_new*/
+    0,                            /*tp_free*/
+    0,                            /*tp_is_gc*/
+    0,                            /*tp_bases*/
+    0,                            /*tp_mro*/
+    0,                            /*tp_cache*/
+    0,                            /*tp_subclasses*/
+    0,                            /*tp_weaklist*/
+    0,                            /*tp_del*/
+    0                             /*tp_version_tag*/
 };
 
+
 /**
  * Create a new python object representing a lambda expression.
  */
-PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l) {
-    pyLambda* pyl = NULL;
-    pyl = PyObject_New(pyLambda, &pyLambda_type);
-    if (pyl != NULL)
-      pyl->func = l;
+PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l, PythonRuntime* py) {
+    pyLambda* pyl = PyObject_New(pyLambda, &pyLambda_type);
+    if (pyl != NULL) {
+        pyl->func = new (gc_new<lambda<value(const list<value>&)> >()) lambda<value(const list<value>&)>(l);
+        pyl->py = py;
+    }
     debug(pyl, "python::mkpylambda");
-    return (PyObject *)pyl;
+    return (PyObject*)pyl;
 }
 
 /**
  * Convert a list of values to a python list.
  */
-PyObject* valuesToPyListHelper(PyObject* l, const list<value>& v) {
+PyObject* valuesToPyListHelper(PyObject* l, const list<value>& v, PythonRuntime* py) {
     if (isNil(v))
         return l;
-    PyObject* pyv = valueToPyObject(car(v));
+    PyObject* pyv = valueToPyObject(car(v), py);
     PyList_Append(l, pyv);
     Py_DECREF(pyv);
-    return valuesToPyListHelper(l, cdr(v));
+    return valuesToPyListHelper(l, cdr(v), py);
 }
 
-PyObject* valuesToPyTuple(const list<value>& v) {
-    PyObject* pyl = valuesToPyListHelper(PyList_New(0), v);
+PyObject* valuesToPyTuple(const list<value>& v, PythonRuntime* py) {
+    PyObject* pyl = valuesToPyListHelper(PyList_New(0), v, py);
     PyObject* pyt = PyList_AsTuple(pyl);
     Py_DECREF(pyl);
     return pyt;
@@ -203,12 +342,12 @@
 /**
  * Convert a value to a python object.
  */
-PyObject* valueToPyObject(const value& v) {
+PyObject* valueToPyObject(const value& v, PythonRuntime* py) {
     switch (type(v)) {
     case value::List:
-        return valuesToPyTuple(v);
+        return valuesToPyTuple(v, py);
     case value::Lambda:
-        return mkPyLambda(v);
+        return mkPyLambda(v, py);
     case value::Symbol:
         return PyString_FromString(c_str(string("'") + v));
     case value::String: {
@@ -228,14 +367,14 @@
  * Convert a python tuple to a list of values.
  */
 
-const list<value> pyTupleToValuesHelper(PyObject* o, const size_t i, const size_t size) {
+const list<value> pyTupleToValuesHelper(PyObject* o, const size_t i, const size_t size, PythonRuntime* py) {
     if (i == size)
         return list<value>();
-    return cons(pyObjectToValue(PyTuple_GetItem(o, i)), pyTupleToValuesHelper(o, i + 1, size));
+    return cons(pyObjectToValue(PyTuple_GetItem(o, i), py), pyTupleToValuesHelper(o, i + 1, size, py));
 }
 
-const list<value> pyTupleToValues(PyObject* o) {
-    return pyTupleToValuesHelper(o, 0, PyTuple_Size(o));
+const list<value> pyTupleToValues(PyObject* o, PythonRuntime* py) {
+    return pyTupleToValuesHelper(o, 0, PyTuple_Size(o), py);
 }
 
 /**
@@ -243,8 +382,9 @@
  */
 struct pyCallable {
     PyObject* func;
+    PythonRuntime* py;
 
-    pyCallable(PyObject* func) : func(func) {
+    pyCallable(PyObject* func, PythonRuntime* py) : func(func), py(py) {
         Py_INCREF(func);
     }
 
@@ -253,9 +393,10 @@
     }
 
     const value operator()(const list<value>& args) const {
-        PyObject* pyargs = valuesToPyTuple(args);
+        PythonThreadIn pyin(py);
+        PyObject* pyargs = valuesToPyTuple(args, py);
         PyObject* result = PyObject_CallObject(func, pyargs);
-        const value v = pyObjectToValue(result);
+        const value v = pyObjectToValue(result, py);
         Py_DECREF(pyargs);
         Py_DECREF(result);
         return v;
@@ -265,7 +406,7 @@
 /**
  * Convert a python object to a value.
  */
-const value pyObjectToValue(PyObject *o) {
+const value pyObjectToValue(PyObject *o, PythonRuntime* py) {
     if (PyString_Check(o)) {
         char* s = NULL;
         Py_ssize_t l = 0;
@@ -283,9 +424,9 @@
     if (PyFloat_Check(o))
         return value((double)PyFloat_AsDouble(o));
     if (PyTuple_Check(o))
-        return pyTupleToValues(o);
+        return pyTupleToValues(o, py);
     if (PyCallable_Check(o))
-        return lambda<value(const list<value>&)>(pyCallable(o));
+        return lambda<value(const list<value>&)>(pyCallable(o, py));
     return value();
 }
 
@@ -299,7 +440,8 @@
 /**
  * Evaluate an expression against a script provided as a python object.
  */
-const failable<value> evalScript(const value& expr, PyObject* script) {
+const failable<value> evalScript(const value& expr, PyObject* script, PythonRuntime& py) {
+    PythonThreadIn pyin(&py);
 
     // Get the requested function
     PyObject* func = PyObject_GetAttrString(script, c_str(car<value>(expr)));
@@ -320,7 +462,7 @@
     }
 
     // Convert args to python objects
-    PyObject* args = valuesToPyTuple(cdr<value>(expr));
+    PyObject* args = valuesToPyTuple(cdr<value>(expr), &py);
 
     // Call the function
     PyObject* result = PyObject_CallObject(func, args);
@@ -330,7 +472,7 @@
         return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastError());
 
     // Convert python result to a value
-    const value v = pyObjectToValue(result);
+    const value v = pyObjectToValue(result, &py);
     Py_DECREF(result);
     return v;
 }
@@ -338,7 +480,9 @@
 /**
  * Read a python script from an input stream.
  */
-const failable<PyObject*> readScript(const string& name, const string& path, istream& is) {
+const failable<PyObject*> readScript(const string& name, const string& path, istream& is, PythonRuntime& py) {
+    PythonThreadIn pyin(&py);
+
     const list<string> ls = streamList(is);
     ostringstream os;
     write(ls, os);
@@ -346,19 +490,31 @@
     if (code == NULL)
         return mkfailure<PyObject*>(string("Couldn't compile script: ") + path + " : " + lastError());
     PyObject* mod = PyImport_ExecCodeModuleEx(const_cast<char*>(c_str(name)), code, const_cast<char*>(c_str(path)));
-    if (mod == NULL)
+    if (mod == NULL) {
+        Py_DECREF(code);
         return mkfailure<PyObject*>(string("Couldn't import module: ") + path + " : " + lastError());
+    }
+    Py_DECREF(code);
     return mod;
 }
 
 /**
+ * Release a python script.
+ */
+const failable<bool> releaseScript(PyObject* script, PythonRuntime& py) {
+    PythonThreadIn pyin(&py);
+    Py_DECREF(script);
+    return true;
+}
+
+/**
  * Evaluate an expression against a script provided as an input stream.
  */
-const failable<value> evalScript(const value& expr, istream& is) {
-    failable<PyObject*> script = readScript("script", "script.py", is);
+const failable<value> evalScript(const value& expr, istream& is, PythonRuntime& py) {
+    failable<PyObject*> script = readScript("script", "script.py", is, py);
     if (!hasContent(script))
         return mkfailure<value>(reason(script));
-    return evalScript(expr, content(script));
+    return evalScript(expr, content(script), py);
 }
 
 }
diff --git a/modules/python/mod-python.cpp b/modules/python/mod-python.cpp
index 8561a1f..24e0b3b 100644
--- a/modules/python/mod-python.cpp
+++ b/modules/python/mod-python.cpp
@@ -39,23 +39,37 @@
 /**
  * Apply a lifecycle start or restart event.
  */
+struct pythonLifecycle {
+    python::PythonRuntime& py;
+    pythonLifecycle(python::PythonRuntime& py) : py(py) {
+    }
+    const value operator()(const list<value>& params) const {
+        const value func = car(params);
+        if (func == "pythonRuntime")
+            return (gc_ptr<value>)(value*)(void*)&py;
+        return lambda<value(const list<value>&)>();
+    }
+};
+
 const value applyLifecycle(unused const list<value>& params) {
 
     // Create a Python runtime
-    new (gc_new<python::PythonRuntime>()) python::PythonRuntime();
+    python::PythonRuntime& py = *(new (gc_new<python::PythonRuntime>()) python::PythonRuntime());
 
-    // Return a nil function as we don't need to handle the stop event
-    return failable<value>(lambda<value(const list<value>&)>());
+    // Return the function to invoke on subsequent events
+    return failable<value>(lambda<value(const list<value>&)>(pythonLifecycle(py)));
 }
 
 /**
  * Evaluate a Python component implementation and convert it to an applicable
  * lambda function.
  */
-const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, unused const lambda<value(const list<value>&)>& lifecycle) {
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle) {
     const string itype(elementName(impl));
-    if (contains(itype, ".python"))
-        return modpython::evalImplementation(path, impl, px);
+    if (contains(itype, ".python")) {
+        const void* p = (gc_ptr<value>)lifecycle(mklist<value>("pythonRuntime"));
+        return modpython::evalImplementation(path, impl, px, *(python::PythonRuntime*)p);
+    }
     if (contains(itype, ".cpp"))
         return modcpp::evalImplementation(path, impl, px);
     return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype);
diff --git a/modules/python/mod-python.hpp b/modules/python/mod-python.hpp
index 0121779..a4d7777 100644
--- a/modules/python/mod-python.hpp
+++ b/modules/python/mod-python.hpp
@@ -45,12 +45,13 @@
 struct applyImplementation {
     PyObject* impl;
     const list<value> px;
-    applyImplementation(PyObject* impl, const list<value>& px) : impl(impl), px(px) {
+    python::PythonRuntime& py;
+    applyImplementation(PyObject* impl, const list<value>& px, python::PythonRuntime& py) : impl(impl), px(px), py(py) {
     }
     const value operator()(const list<value>& params) const {
         const value expr = append<value>(params, px);
         debug(expr, "modeval::python::applyImplementation::input");
-        const failable<value> res = python::evalScript(expr, impl);
+        const failable<value> res = python::evalScript(expr, impl, py);
         const value val = !hasContent(res)? mklist<value>(value(), reason(res)) : mklist<value>(content(res));
         debug(val, "modeval::python::applyImplementation::result");
         return val;
@@ -61,16 +62,16 @@
  * Evaluate a Python component implementation and convert it to an applicable
  * lambda function.
  */
-const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) {
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, python::PythonRuntime& py) {
     const string spath(attributeValue("script", impl));
     const string fpath(path + spath);
     ifstream is(fpath);
     if (fail(is))
         return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath);
-    const failable<PyObject*> script = python::readScript(python::moduleName(spath), fpath, is);
+    const failable<PyObject*> script = python::readScript(python::moduleName(spath), fpath, is, py);
     if (!hasContent(script))
         return mkfailure<lambda<value(const list<value>&)> >(reason(script));
-    return lambda<value(const list<value>&)>(applyImplementation(content(script), px));
+    return lambda<value(const list<value>&)>(applyImplementation(content(script), px, py));
 }
 
 }
diff --git a/modules/python/python-test.cpp b/modules/python/python-test.cpp
index 41889e6..bc275f2 100644
--- a/modules/python/python-test.cpp
+++ b/modules/python/python-test.cpp
@@ -27,6 +27,8 @@
 #include "stream.hpp"
 #include "string.hpp"
 #include "driver.hpp"
+#include "parallel.hpp"
+#include "perf.hpp"
 
 namespace tuscany {
 namespace python {
@@ -40,14 +42,15 @@
     PythonRuntime py;
 
     istringstream is(testPythonAdd);
-    failable<PyObject*> script = readScript("script", "script.py", is);
+    failable<PyObject*> script = readScript("script", "script.py", is, py);
     assert(hasContent(script));
 
     const value exp = mklist<value>("add", 2, 3);
-    const failable<value> r = evalScript(exp, content(script));
+    const failable<value> r = evalScript(exp, content(script), py);
     assert(hasContent(r));
     assert(content(r) == value(5));
 
+    releaseScript(content(script), py);
     return true;
 }
 
@@ -74,7 +77,7 @@
 
     const value trl = mklist<value>("testReturnLambda");
     istringstream trlis(testReturnLambda);
-    const failable<value> trlv = evalScript(trl, trlis);
+    const failable<value> trlv = evalScript(trl, trlis, py);
 
     assert(hasContent(trlv));
     assert(isLambda(content(trlv)));
@@ -83,18 +86,179 @@
 
     istringstream tclis(testCallLambda);
     const value tcl = mklist<value>("testCallLambda", content(trlv), 2, 3);
-    const failable<value> tclv = evalScript(tcl, tclis);
+    const failable<value> tclv = evalScript(tcl, tclis, py);
     assert(hasContent(tclv));
     assert(content(tclv) == value(6));
 
     istringstream tcelis(testCallLambda);
     const value tcel = mklist<value>("testCallLambda", lambda<value(const list<value>&)>(mult), 3, 4);
-    const failable<value> tcelv = evalScript(tcel, tcelis);
+    const failable<value> tcelv = evalScript(tcel, tcelis, py);
     assert(hasContent(tcelv));
     assert(content(tcelv) == value(12));
     return true;
 }
 
+struct testEvalReadAdd {
+    PythonRuntime& py;
+    testEvalReadAdd(PythonRuntime& py) : py(py) {
+    }
+    const bool operator()() const {
+        istringstream is(testPythonAdd);
+        failable<PyObject*> script = readScript("script", "script.py", is, py);
+        assert(hasContent(script));
+
+        const value exp = mklist<value>("add", 2, 3);
+        const failable<value> r = evalScript(exp, content(script), py);
+        assert(hasContent(r));
+        assert(content(r) == value(5));
+
+        releaseScript(content(script), py);
+        return true;
+    }
+};
+
+struct testEvalAdd {
+    PyObject* script;
+    PythonRuntime& py;
+    testEvalAdd(PyObject* script, PythonRuntime& py) : script(script), py(py) {
+    }
+    const bool operator()() const {
+        const value exp = mklist<value>("add", 2, 3);
+        const failable<value> r = evalScript(exp, script, py);
+        assert(hasContent(r));
+        assert(content(r) == value(5));
+        return true;
+    }
+};
+
+bool testEvalPerf() {
+    gc_scoped_pool pool;
+    PythonRuntime py;
+
+    const lambda<bool()> erl = lambda<bool()>(testEvalReadAdd(py));
+    cout << "Python read + eval test " << time(erl, 5, 10000) << " ms" << endl;
+
+    istringstream is(testPythonAdd);
+    failable<PyObject*> script = readScript("script", "script.py", is, py);
+    assert(hasContent(script));
+
+    const lambda<bool()> el = lambda<bool()>(testEvalAdd(content(script), py));
+    cout << "Python eval test " << time(el, 5, 10000) << " ms" << endl;
+
+    releaseScript(content(script), py);
+    return true;
+}
+
+#ifdef WANT_THREADS
+
+struct testReadEvalAddLoop {
+    PythonRuntime& py;
+    testReadEvalAddLoop(PythonRuntime& py) : py(py) {
+    }
+    const bool operator()() const {
+        for (int i = 0; i < 100; i++) {
+            istringstream is(testPythonAdd);
+            failable<PyObject*> script = readScript("script", "script.py", is, py);
+            assert(hasContent(script));
+
+            const value exp = mklist<value>("add", 2, 3);
+            const failable<value> r = evalScript(exp, content(script), py);
+            assert(hasContent(r));
+            assert(content(r) == value(5));
+
+            releaseScript(content(script), py);
+        }
+        return true;
+    }
+};
+
+struct testEvalAddLoop {
+    PyObject* script;
+    PythonRuntime& py;
+    testEvalAddLoop(PyObject* script, PythonRuntime& py) : script(script), py(py) {
+    }
+    const bool operator()() const {
+        for (int i = 0; i < 100; i++) {
+            const value exp = mklist<value>("add", 2, 3);
+            const failable<value> r = evalScript(exp, script, py);
+            assert(hasContent(r));
+            assert(content(r) == value(5));
+        }
+        return true;
+    }
+};
+
+const list<future<bool> > submitReadEvals(worker& w, const int max, const int i, PythonRuntime& py) {
+    if (i == max)
+        return list<future<bool> >();
+    const lambda<bool()> func = lambda<bool()>(testReadEvalAddLoop(py));
+    return cons(submit(w, func), submitReadEvals(w, max, i + 1, py));
+}
+
+const list<future<bool> > submitEvals(worker& w, const int max, const int i, PyObject* script, PythonRuntime& py) {
+    if (i == max)
+        return list<future<bool> >();
+    const lambda<bool()> func = lambda<bool()>(testEvalAddLoop(script, py));
+    return cons(submit(w, func), submitEvals(w, max, i + 1, script, py));
+}
+
+bool checkEvalResults(const list<future<bool> > r) {
+    if (isNil(r))
+        return true;
+    assert(car(r) == true);
+    return checkEvalResults(cdr(r));
+}
+
+struct testReadEvalThreads {
+    worker& w;
+    const int max;
+    PythonRuntime& py;
+    testReadEvalThreads(worker& w, const int max, PythonRuntime& py) : w(w), max(max), py(py) {
+    }
+    const bool operator()() const {
+        const list<future<bool> > r(submitReadEvals(w, max, 0, py));
+        checkEvalResults(r);
+        return true;
+    }
+};
+
+struct testEvalThreads {
+    worker& w;
+    const int max;
+    PyObject* script;
+    PythonRuntime& py;
+    testEvalThreads(worker& w, const int max, PyObject* script, PythonRuntime& py) : w(w), max(max), script(script), py(py) {
+    }
+    const bool operator()() const {
+        const list<future<bool> > r(submitEvals(w, max, 0, script, py));
+        checkEvalResults(r);
+        return true;
+    }
+};
+
+bool testThreads() {
+    gc_scoped_pool pool;
+    PythonRuntime py;
+
+    const int max = 100;
+    worker w(max);
+
+    const lambda<bool()> elr = lambda<bool()>(testReadEvalThreads(w, max, py));
+    cout << "Python eval + read thread test " << time(elr, 1, 1) / 10000.0 << " ms" << endl;
+
+    istringstream is(testPythonAdd);
+    failable<PyObject*> script = readScript("script", "script.py", is, py);
+    assert(hasContent(script));
+
+    const lambda<bool()> el = lambda<bool()>(testEvalThreads(w, max, content(script), py));
+    cout << "Python eval thread test " << time(el, 1, 1) / 10000.0 << " ms" << endl;
+
+    releaseScript(content(script), py);
+    return true;
+}
+
+#endif
+
 }
 }
 
@@ -103,6 +267,10 @@
 
     tuscany::python::testEvalExpr();
     tuscany::python::testEvalLambda();
+    tuscany::python::testEvalPerf();
+#ifdef WANT_THREADS
+    tuscany::python::testThreads();
+#endif
 
     tuscany::cout << "OK" << tuscany::endl;
     return 0;
diff --git a/modules/python/server-test b/modules/python/server-test
index 0932413..ecde5ca 100755
--- a/modules/python/server-test
+++ b/modules/python/server-test
@@ -19,6 +19,7 @@
 
 # Setup
 ../http/httpd-conf tmp localhost 8090 ../server/htdocs
+../http/httpd-event-conf tmp
 ../server/server-conf tmp
 ./python-conf tmp
 cat >>tmp/conf/httpd.conf <<EOF
diff --git a/modules/python/wiring-test b/modules/python/wiring-test
index 22666c3..4dd3ca7 100755
--- a/modules/python/wiring-test
+++ b/modules/python/wiring-test
@@ -23,6 +23,7 @@
 
 # Setup
 ../http/httpd-conf tmp localhost 8090 ../server/htdocs
+../http/httpd-event-conf tmp
 ../server/server-conf tmp
 ./python-conf tmp
 cat >>tmp/conf/httpd.conf <<EOF
diff --git a/samples/loan-python/start b/samples/loan-python/start
index e7aa7d8..499a48c 100755
--- a/samples/loan-python/start
+++ b/samples/loan-python/start
@@ -18,6 +18,7 @@
 #  under the License.
 
 ../../modules/http/httpd-conf tmp localhost 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/server/server-conf tmp
 ../../modules/python/python-conf tmp
 cat >>tmp/conf/httpd.conf <<EOF
diff --git a/samples/relay-python/start b/samples/relay-python/start
index c156e43..e9d9d2d 100755
--- a/samples/relay-python/start
+++ b/samples/relay-python/start
@@ -18,6 +18,7 @@
 #  under the License.
 
 ../../modules/http/httpd-conf tmp localhost 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/server/server-conf tmp
 ../../modules/python/python-conf tmp
 cat >>tmp/conf/httpd.conf <<EOF
diff --git a/samples/store-cluster/server-conf b/samples/store-cluster/server-conf
index babf2f2..f65ba37 100755
--- a/samples/store-cluster/server-conf
+++ b/samples/store-cluster/server-conf
@@ -24,6 +24,7 @@
 
 # Configure an app server
 ../../modules/http/httpd-conf $root sca-store.com $port/80 htdocs
+../../modules/http/httpd-event-conf $root
 ../../modules/http/vhost-conf $root domains htdocs
 ../../modules/server/server-conf $root
 ../../modules/python/python-conf $root
diff --git a/samples/store-cluster/server-ssl-conf b/samples/store-cluster/server-ssl-conf
index 56ca5ed..83628bb 100755
--- a/samples/store-cluster/server-ssl-conf
+++ b/samples/store-cluster/server-ssl-conf
@@ -25,6 +25,7 @@
 
 # Configure an SSL-enabled app server
 ../../modules/http/httpd-conf $root sca-store.com $port htdocs
+../../modules/http/httpd-event-conf $root
 ../../modules/http/vhost-conf $root domains htdocs
 
 tar -C tmp/ssl -c `../../modules/http/ssl-cert-find tmp/ssl` | tar -C $root -x
diff --git a/samples/store-python/ssl-start b/samples/store-python/ssl-start
index 60b9bb5..e639b53 100755
--- a/samples/store-python/ssl-start
+++ b/samples/store-python/ssl-start
@@ -20,6 +20,7 @@
 ../../modules/http/ssl-ca-conf tmp localhost
 ../../modules/http/ssl-cert-conf tmp localhost
 ../../modules/http/httpd-conf tmp localhost 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/http/httpd-ssl-conf tmp 8453
 ../../modules/http/open-auth-conf tmp
 ../../modules/http/passwd-auth-conf tmp foo foo
diff --git a/samples/store-python/start b/samples/store-python/start
index 8df7875..b4ca146 100755
--- a/samples/store-python/start
+++ b/samples/store-python/start
@@ -18,6 +18,7 @@
 #  under the License.
 
 ../../modules/http/httpd-conf tmp localhost 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/server/server-conf tmp
 ../../modules/python/python-conf tmp
 cat >>tmp/conf/httpd.conf <<EOF
diff --git a/samples/store-python/uec2-start b/samples/store-python/uec2-start
index 84c170e..53428be 100755
--- a/samples/store-python/uec2-start
+++ b/samples/store-python/uec2-start
@@ -31,6 +31,7 @@
 ../../modules/http/ssl-ca-conf tmp $host
 ../../modules/http/ssl-cert-conf tmp $host
 ../../modules/http/httpd-conf tmp $host 8090/80 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/http/httpd-ssl-conf tmp 8453/443
 ../../modules/server/server-conf tmp
 ../../modules/python/python-conf tmp
diff --git a/samples/store-vhost/ssl-start b/samples/store-vhost/ssl-start
index ddff91e..3a6bb82 100755
--- a/samples/store-vhost/ssl-start
+++ b/samples/store-vhost/ssl-start
@@ -24,6 +24,7 @@
 ../../modules/http/ssl-cert-conf tmp sca-store.com server
 ../../modules/http/ssl-cert-conf tmp *.sca-store.com vhost
 ../../modules/http/httpd-conf tmp sca-store.com 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/http/vhost-conf tmp domains htdocs
 ../../modules/http/httpd-ssl-conf tmp 8453
 ../../modules/http/vhost-ssl-conf tmp
diff --git a/samples/store-vhost/start b/samples/store-vhost/start
index c8bb685..38661e7 100755
--- a/samples/store-vhost/start
+++ b/samples/store-vhost/start
@@ -18,6 +18,7 @@
 #  under the License.
 
 ../../modules/http/httpd-conf tmp sca-store.com 8090 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/http/vhost-conf tmp domains htdocs
 ../../modules/server/server-conf tmp
 ../../modules/python/python-conf tmp
diff --git a/samples/store-vhost/uec2-start b/samples/store-vhost/uec2-start
index e8b30af..f7208b7 100755
--- a/samples/store-vhost/uec2-start
+++ b/samples/store-vhost/uec2-start
@@ -33,6 +33,7 @@
 ../../modules/http/ssl-cert-conf tmp $host server
 ../../modules/http/ssl-cert-conf tmp "*.$host" vhost
 ../../modules/http/httpd-conf tmp $host 8090/80 htdocs
+../../modules/http/httpd-event-conf tmp
 ../../modules/http/vhost-conf tmp domains htdocs
 ../../modules/http/httpd-ssl-conf tmp 8453/443
 ../../modules/http/vhost-ssl-conf tmp