From c298b01922ab03d078f33ec1ceb3e7471277f48c Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Mon, 5 Sep 2011 23:30:31 +0000 Subject: Support multithreaded execution of Python components with the HTTPD event MPM. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1165452 13f79535-47bb-0310-9956-ffa450edef68 --- sca-cpp/trunk/modules/edit/ssl-start | 1 + sca-cpp/trunk/modules/edit/start | 1 + sca-cpp/trunk/modules/python/driver.hpp | 12 +- sca-cpp/trunk/modules/python/eval.hpp | 282 ++++++++++++++++----- sca-cpp/trunk/modules/python/mod-python.cpp | 26 +- sca-cpp/trunk/modules/python/mod-python.hpp | 11 +- sca-cpp/trunk/modules/python/python-test.cpp | 178 ++++++++++++- sca-cpp/trunk/modules/python/server-test | 1 + sca-cpp/trunk/modules/python/wiring-test | 1 + sca-cpp/trunk/samples/loan-python/start | 1 + sca-cpp/trunk/samples/relay-python/start | 1 + sca-cpp/trunk/samples/store-cluster/server-conf | 1 + .../trunk/samples/store-cluster/server-ssl-conf | 1 + sca-cpp/trunk/samples/store-python/ssl-start | 1 + sca-cpp/trunk/samples/store-python/start | 1 + sca-cpp/trunk/samples/store-python/uec2-start | 1 + sca-cpp/trunk/samples/store-vhost/ssl-start | 1 + sca-cpp/trunk/samples/store-vhost/start | 1 + sca-cpp/trunk/samples/store-vhost/uec2-start | 1 + 19 files changed, 438 insertions(+), 85 deletions(-) (limited to 'sca-cpp') diff --git a/sca-cpp/trunk/modules/edit/ssl-start b/sca-cpp/trunk/modules/edit/ssl-start index 53803e4ba0..576fa39463 100755 --- a/sca-cpp/trunk/modules/edit/ssl-start +++ b/sca-cpp/trunk/modules/edit/ssl-start @@ -30,6 +30,7 @@ jsprefix=`echo "import os; print os.path.realpath('$here/../js')" | python` # 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/sca-cpp/trunk/modules/edit/start b/sca-cpp/trunk/modules/edit/start index 7d259314bf..6125ba02bf 100755 --- a/sca-cpp/trunk/modules/edit/start +++ b/sca-cpp/trunk/modules/edit/start @@ -25,6 +25,7 @@ jsprefix=`echo "import os; print os.path.realpath('$here/../js')" | python` # 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/sca-cpp/trunk/modules/python/driver.hpp b/sca-cpp/trunk/modules/python/driver.hpp index 79897b0c5e..a5d554172b 100644 --- a/sca-cpp/trunk/modules/python/driver.hpp +++ b/sca-cpp/trunk/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 output = evalScript(input, script); + const failable 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 script = readScript(moduleName(path), path, is); + failable 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/sca-cpp/trunk/modules/python/eval.hpp b/sca-cpp/trunk/modules/python/eval.hpp index f106ff1659..a1685d11ff 100644 --- a/sca-cpp/trunk/modules/python/eval.hpp +++ b/sca-cpp/trunk/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(&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(&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; }; /** @@ -82,23 +112,91 @@ const string lastError() { return ""; } +/** + * 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& v); -const list pyTupleToValues(PyObject* o); +PyObject* valueToPyObject(const value& v, PythonRuntime* py); +const value pyObjectToValue(PyObject *o, PythonRuntime* py); +PyObject* valuesToPyTuple(const list& v, PythonRuntime* py); +const list pyTupleToValues(PyObject* o, PythonRuntime* py); /** * Callable python type used to represent a lambda expression. */ typedef struct { - PyObject_HEAD - lambda&)> func; + PyObject_HEAD + lambda&)>* func; + PythonRuntime* py; } pyLambda; -PyObject *mkPyLambda(const lambda&)>& l); +PyObject *mkPyLambda(const lambda&)>& l, PythonRuntime* py); void pyLambda_dealloc(PyObject* self) { debug(self, "python::pylambda_dealloc"); @@ -112,12 +210,17 @@ const string pyRepr(PyObject* o) { return s; } +const value pyLambda_callout(const pyLambda* pyl, const list& 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 @@ PyObject* pyLambda_getattr(PyObject *self, PyObject *attrname) { 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&)>& l) { - pyLambda* pyl = NULL; - pyl = PyObject_New(pyLambda, &pyLambda_type); - if (pyl != NULL) - pyl->func = l; +PyObject *mkPyLambda(const lambda&)>& l, PythonRuntime* py) { + pyLambda* pyl = PyObject_New(pyLambda, &pyLambda_type); + if (pyl != NULL) { + pyl->func = new (gc_new&)> >()) lambda&)>(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& v) { +PyObject* valuesToPyListHelper(PyObject* l, const list& 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& v) { - PyObject* pyl = valuesToPyListHelper(PyList_New(0), v); +PyObject* valuesToPyTuple(const list& 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 @@ PyObject* valuesToPyTuple(const list& v) { /** * 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 @@ PyObject* valueToPyObject(const value& v) { * Convert a python tuple to a list of values. */ -const list pyTupleToValuesHelper(PyObject* o, const size_t i, const size_t size) { +const list pyTupleToValuesHelper(PyObject* o, const size_t i, const size_t size, PythonRuntime* py) { if (i == size) return list(); - 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 pyTupleToValues(PyObject* o) { - return pyTupleToValuesHelper(o, 0, PyTuple_Size(o)); +const list pyTupleToValues(PyObject* o, PythonRuntime* py) { + return pyTupleToValuesHelper(o, 0, PyTuple_Size(o), py); } /** @@ -243,8 +382,9 @@ const list pyTupleToValues(PyObject* o) { */ 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 @@ struct pyCallable { } const value operator()(const list& 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 @@ struct pyCallable { /** * 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 @@ const value pyObjectToValue(PyObject *o) { 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&)>(pyCallable(o)); + return lambda&)>(pyCallable(o, py)); return value(); } @@ -299,7 +440,8 @@ const string moduleName(const string& path) { /** * Evaluate an expression against a script provided as a python object. */ -const failable evalScript(const value& expr, PyObject* script) { +const failable evalScript(const value& expr, PyObject* script, PythonRuntime& py) { + PythonThreadIn pyin(&py); // Get the requested function PyObject* func = PyObject_GetAttrString(script, c_str(car(expr))); @@ -320,7 +462,7 @@ const failable evalScript(const value& expr, PyObject* script) { } // Convert args to python objects - PyObject* args = valuesToPyTuple(cdr(expr)); + PyObject* args = valuesToPyTuple(cdr(expr), &py); // Call the function PyObject* result = PyObject_CallObject(func, args); @@ -330,7 +472,7 @@ const failable evalScript(const value& expr, PyObject* script) { return mkfailure(string("Function call failed: ") + car(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 @@ const failable evalScript(const value& expr, PyObject* script) { /** * Read a python script from an input stream. */ -const failable readScript(const string& name, const string& path, istream& is) { +const failable readScript(const string& name, const string& path, istream& is, PythonRuntime& py) { + PythonThreadIn pyin(&py); + const list ls = streamList(is); ostringstream os; write(ls, os); @@ -346,19 +490,31 @@ const failable readScript(const string& name, const string& path, ist if (code == NULL) return mkfailure(string("Couldn't compile script: ") + path + " : " + lastError()); PyObject* mod = PyImport_ExecCodeModuleEx(const_cast(c_str(name)), code, const_cast(c_str(path))); - if (mod == NULL) + if (mod == NULL) { + Py_DECREF(code); return mkfailure(string("Couldn't import module: ") + path + " : " + lastError()); + } + Py_DECREF(code); return mod; } +/** + * Release a python script. + */ +const failable 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 evalScript(const value& expr, istream& is) { - failable script = readScript("script", "script.py", is); +const failable evalScript(const value& expr, istream& is, PythonRuntime& py) { + failable script = readScript("script", "script.py", is, py); if (!hasContent(script)) return mkfailure(reason(script)); - return evalScript(expr, content(script)); + return evalScript(expr, content(script), py); } } diff --git a/sca-cpp/trunk/modules/python/mod-python.cpp b/sca-cpp/trunk/modules/python/mod-python.cpp index 8561a1fbf4..24e0b3bcb0 100644 --- a/sca-cpp/trunk/modules/python/mod-python.cpp +++ b/sca-cpp/trunk/modules/python/mod-python.cpp @@ -39,23 +39,37 @@ namespace modeval { /** * Apply a lifecycle start or restart event. */ +struct pythonLifecycle { + python::PythonRuntime& py; + pythonLifecycle(python::PythonRuntime& py) : py(py) { + } + const value operator()(const list& params) const { + const value func = car(params); + if (func == "pythonRuntime") + return (gc_ptr)(value*)(void*)&py; + return lambda&)>(); + } +}; + const value applyLifecycle(unused const list& params) { // Create a Python runtime - new (gc_new()) python::PythonRuntime(); + python::PythonRuntime& py = *(new (gc_new()) python::PythonRuntime()); - // Return a nil function as we don't need to handle the stop event - return failable(lambda&)>()); + // Return the function to invoke on subsequent events + return failable(lambda&)>(pythonLifecycle(py))); } /** * Evaluate a Python component implementation and convert it to an applicable * lambda function. */ -const failable&)> > evalImplementation(const string& path, const value& impl, const list& px, unused const lambda&)>& lifecycle) { +const failable&)> > evalImplementation(const string& path, const value& impl, const list& px, const lambda&)>& 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)lifecycle(mklist("pythonRuntime")); + return modpython::evalImplementation(path, impl, px, *(python::PythonRuntime*)p); + } if (contains(itype, ".cpp")) return modcpp::evalImplementation(path, impl, px); return mkfailure&)> >(string("Unsupported implementation type: ") + itype); diff --git a/sca-cpp/trunk/modules/python/mod-python.hpp b/sca-cpp/trunk/modules/python/mod-python.hpp index 0121779530..a4d77775a5 100644 --- a/sca-cpp/trunk/modules/python/mod-python.hpp +++ b/sca-cpp/trunk/modules/python/mod-python.hpp @@ -45,12 +45,13 @@ namespace modpython { struct applyImplementation { PyObject* impl; const list px; - applyImplementation(PyObject* impl, const list& px) : impl(impl), px(px) { + python::PythonRuntime& py; + applyImplementation(PyObject* impl, const list& px, python::PythonRuntime& py) : impl(impl), px(px), py(py) { } const value operator()(const list& params) const { const value expr = append(params, px); debug(expr, "modeval::python::applyImplementation::input"); - const failable res = python::evalScript(expr, impl); + const failable res = python::evalScript(expr, impl, py); const value val = !hasContent(res)? mklist(value(), reason(res)) : mklist(content(res)); debug(val, "modeval::python::applyImplementation::result"); return val; @@ -61,16 +62,16 @@ struct applyImplementation { * Evaluate a Python component implementation and convert it to an applicable * lambda function. */ -const failable&)> > evalImplementation(const string& path, const value& impl, const list& px) { +const failable&)> > evalImplementation(const string& path, const value& impl, const list& px, python::PythonRuntime& py) { const string spath(attributeValue("script", impl)); const string fpath(path + spath); ifstream is(fpath); if (fail(is)) return mkfailure&)> >(string("Could not read implementation: ") + fpath); - const failable script = python::readScript(python::moduleName(spath), fpath, is); + const failable script = python::readScript(python::moduleName(spath), fpath, is, py); if (!hasContent(script)) return mkfailure&)> >(reason(script)); - return lambda&)>(applyImplementation(content(script), px)); + return lambda&)>(applyImplementation(content(script), px, py)); } } diff --git a/sca-cpp/trunk/modules/python/python-test.cpp b/sca-cpp/trunk/modules/python/python-test.cpp index 41889e6d0e..bc275f27c7 100644 --- a/sca-cpp/trunk/modules/python/python-test.cpp +++ b/sca-cpp/trunk/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 @@ bool testEvalExpr() { PythonRuntime py; istringstream is(testPythonAdd); - failable script = readScript("script", "script.py", is); + failable script = readScript("script", "script.py", is, py); assert(hasContent(script)); const value exp = mklist("add", 2, 3); - const failable r = evalScript(exp, content(script)); + const failable r = evalScript(exp, content(script), py); assert(hasContent(r)); assert(content(r) == value(5)); + releaseScript(content(script), py); return true; } @@ -74,7 +77,7 @@ bool testEvalLambda() { const value trl = mklist("testReturnLambda"); istringstream trlis(testReturnLambda); - const failable trlv = evalScript(trl, trlis); + const failable trlv = evalScript(trl, trlis, py); assert(hasContent(trlv)); assert(isLambda(content(trlv))); @@ -83,18 +86,179 @@ bool testEvalLambda() { istringstream tclis(testCallLambda); const value tcl = mklist("testCallLambda", content(trlv), 2, 3); - const failable tclv = evalScript(tcl, tclis); + const failable tclv = evalScript(tcl, tclis, py); assert(hasContent(tclv)); assert(content(tclv) == value(6)); istringstream tcelis(testCallLambda); const value tcel = mklist("testCallLambda", lambda&)>(mult), 3, 4); - const failable tcelv = evalScript(tcel, tcelis); + const failable 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 script = readScript("script", "script.py", is, py); + assert(hasContent(script)); + + const value exp = mklist("add", 2, 3); + const failable 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("add", 2, 3); + const failable 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 erl = lambda(testEvalReadAdd(py)); + cout << "Python read + eval test " << time(erl, 5, 10000) << " ms" << endl; + + istringstream is(testPythonAdd); + failable script = readScript("script", "script.py", is, py); + assert(hasContent(script)); + + const lambda el = lambda(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 script = readScript("script", "script.py", is, py); + assert(hasContent(script)); + + const value exp = mklist("add", 2, 3); + const failable 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("add", 2, 3); + const failable r = evalScript(exp, script, py); + assert(hasContent(r)); + assert(content(r) == value(5)); + } + return true; + } +}; + +const list > submitReadEvals(worker& w, const int max, const int i, PythonRuntime& py) { + if (i == max) + return list >(); + const lambda func = lambda(testReadEvalAddLoop(py)); + return cons(submit(w, func), submitReadEvals(w, max, i + 1, py)); +} + +const list > submitEvals(worker& w, const int max, const int i, PyObject* script, PythonRuntime& py) { + if (i == max) + return list >(); + const lambda func = lambda(testEvalAddLoop(script, py)); + return cons(submit(w, func), submitEvals(w, max, i + 1, script, py)); +} + +bool checkEvalResults(const list > 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 > 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 > 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 elr = lambda(testReadEvalThreads(w, max, py)); + cout << "Python eval + read thread test " << time(elr, 1, 1) / 10000.0 << " ms" << endl; + + istringstream is(testPythonAdd); + failable script = readScript("script", "script.py", is, py); + assert(hasContent(script)); + + const lambda el = lambda(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 @@ int main() { 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/sca-cpp/trunk/modules/python/server-test b/sca-cpp/trunk/modules/python/server-test index 0932413ffd..ecde5ca8ad 100755 --- a/sca-cpp/trunk/modules/python/server-test +++ b/sca-cpp/trunk/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 <>tmp/conf/httpd.conf <>tmp/conf/httpd.conf <>tmp/conf/httpd.conf <>tmp/conf/httpd.conf <