new interface for python, more user friendly
authorFilippo Gioachin <gioachin@illinois.edu>
Sun, 21 Nov 2004 01:05:27 +0000 (01:05 +0000)
committerFilippo Gioachin <gioachin@illinois.edu>
Sun, 21 Nov 2004 01:05:27 +0000 (01:05 +0000)
src/libs/ck-libs/pythonCCS/Makefile
src/libs/ck-libs/pythonCCS/PythonCCS-client.C [new file with mode: 0644]
src/libs/ck-libs/pythonCCS/PythonCCS-client.h [new file with mode: 0644]
src/libs/ck-libs/pythonCCS/PythonCCS.C
src/libs/ck-libs/pythonCCS/PythonCCS.ci
src/libs/ck-libs/pythonCCS/PythonCCS.h

index b7e86f907f514b5d7be66b701ed5e85b783042a1..90813151ff1d30400274921db939c272cb4fb8c0 100644 (file)
@@ -1,9 +1,9 @@
 CDIR=../../../..
 CHARMC=$(CDIR)/bin/charmc $(OPTS)
 
-HEADERS=PythonCCS.h PythonCCS.decl.h
+HEADERS=PythonCCS.h PythonCCS-client.h PythonCCS.decl.h
 HEADDEP=$(HEADERS) PythonCCS.def.h
-OBJS=PythonCCS.o
+OBJS=PythonCCS.o PythonCCS-client.o
 DEST=$(CDIR)/lib/libmodulePythonCCS.a
 
 all: $(DEST) headers
@@ -13,11 +13,15 @@ $(DEST): $(OBJS)
 
 headers: $(HEADERS)
        ln -sf ../tmp/libs/ck-libs/pythonCCS/PythonCCS.h $(CDIR)/include/
+       ln -sf ../tmp/libs/ck-libs/pythonCCS/PythonCCS-client.h $(CDIR)/include/
        ln -sf ../tmp/libs/ck-libs/pythonCCS/PythonCCS.decl.h $(CDIR)/include/
 
 PythonCCS.o: PythonCCS.C $(HEADDEP)
        $(CHARMC) -c $(FLAGS) PythonCCS.C
 
+PythonCCS-client.o: PythonCCS-client.C $(HEADDEP)
+       $(CHARMC) -c $(FLAGS) PythonCCS-client.C
+
 PythonCCS.decl.h PythonCCS.def.h: PythonCCS.ci
        $(CHARMC) PythonCCS.ci
 
diff --git a/src/libs/ck-libs/pythonCCS/PythonCCS-client.C b/src/libs/ck-libs/pythonCCS/PythonCCS-client.C
new file mode 100644 (file)
index 0000000..82a65d8
--- /dev/null
@@ -0,0 +1,129 @@
+#include "PythonCCS-client.h"
+
+PythonExecute::PythonExecute(char *_code, bool _persistent, bool _highlevel, char _interp[4]) {
+  magic = sizeof(*this);
+  codeLength = strlen(_code);
+  code = strdup(_code);
+  methodNameLength = 0;
+  methodName = 0;
+  infoSize = 0;
+  info = 0;
+  flags = 0;
+  if (_persistent) {
+    flags |= FLAG_PERSISTENT;
+    flags |= FLAG_KEEPPRINT;
+  }
+  if (_highlevel) flags |= FLAG_HIGHLEVEL;
+  if (_interp) memcpy(interpreter, _interp, 4);
+  else memset(interpreter, 0, 4);
+}
+
+PythonExecute::PythonExecute(char *_code, char *_method, PythonIterator *_info, bool _persistent, bool _highlevel, char _interp[4]) {
+  magic = sizeof(*this);
+  codeLength = strlen(_code);
+  code = strdup(_code);
+  methodNameLength = strlen(_method);
+  methodName = strdup(_method);
+  infoSize = _info->size();
+  info = _info->pack();
+  flags = 0;
+  if (_persistent) {
+    flags |= FLAG_PERSISTENT;
+    flags |= FLAG_KEEPPRINT;
+  }
+  if (_highlevel) flags |= FLAG_HIGHLEVEL;
+  flags |= FLAG_ITERATE;
+  if (_interp) memcpy(interpreter, _interp, 4);
+  else memset(interpreter, 0, 4);
+}
+
+PythonExecute::~PythonExecute() {
+  if (code) free(code);
+  if (methodName) free(methodName);
+  if (info) free(info);
+}
+
+void PythonExecute::setCode(char *_set) {
+  codeLength = strlen(_set);
+  code = strdup(_set);
+}
+
+void PythonExecute::setMethodName(char *_set) {
+  methodNameLength = strlen(_set);
+  methodName = strdup(_set);
+}
+
+void PythonExecute::setIterator(PythonIterator *_set) {
+  infoSize = _set->size();
+  info = _set->pack();
+}
+
+void PythonExecute::setPersistent(bool _set) {
+  if (_set) flags |= FLAG_PERSISTENT;
+  else flags &= ~FLAG_PERSISTENT;
+}
+
+void PythonExecute::setIterate(bool _set) {
+  if (_set) flags |= FLAG_ITERATE;
+  else flags &= ~FLAG_ITERATE;
+}
+
+void PythonExecute::setHighLevel(bool _set) {
+  if (_set) flags |= FLAG_HIGHLEVEL;
+  else flags &= ~FLAG_HIGHLEVEL;
+}
+
+void PythonExecute::setKeepPrint(bool _set) {
+  if (_set) flags |= FLAG_KEEPPRINT;
+  else flags &= ~FLAG_KEEPPRINT;
+}
+
+int PythonExecute::size() {
+  return sizeof(PythonExecute)+codeLength+1+methodNameLength+1+infoSize;
+}
+
+char *PythonExecute::toString() {
+  void *memory = malloc(size());
+  memcpy (memory, this, sizeof(PythonExecute));
+  char *ptr = (char*)memory+sizeof(PythonExecute);
+  if (codeLength) {
+    memcpy (ptr, code, codeLength+1);
+    ptr += codeLength+1;
+  }
+  if (methodNameLength) {
+    memcpy (ptr, methodName, methodNameLength+1);
+    ptr += methodNameLength+1;
+  }
+  if (infoSize) {
+    memcpy (ptr, info, infoSize);
+  }
+  return (char*)memory;
+}
+
+void PythonExecute::unpack() {
+  if (codeLength) code = (char*)this + sizeof(PythonExecute);
+  if (methodNameLength) methodName = code + codeLength+1;
+  if (infoSize) info = (PythonIterator*) (methodName + methodNameLength+1);
+}
+
+PythonPrint::PythonPrint(char _interp[4], bool Wait) {
+  magic = sizeof(*this);
+  memcpy(interpreter, _interp, 4);
+  if (Wait) flags |= FLAG_WAIT;
+}
+
+void PythonPrint::setWait(bool _set) {
+  if (_set) flags |= FLAG_WAIT;
+  else flags &= ~FLAG_WAIT;
+}
+
+/* Testing routine */
+/*
+int main () {
+PythonExecute a("abcdefl");
+printf("size: %d %d\n",sizeof(PythonExecute),a.size());
+//a.print();
+printf("ok %s\n",a.toString()+a.size());
+return 0;
+}
+*/
diff --git a/src/libs/ck-libs/pythonCCS/PythonCCS-client.h b/src/libs/ck-libs/pythonCCS/PythonCCS-client.h
new file mode 100644 (file)
index 0000000..7c84865
--- /dev/null
@@ -0,0 +1,126 @@
+/* Security note:
+
+   The dynamic insertion of a code into a running program can generate bugs, and
+   potentially lead the parallel program to die. Moreover, a malicious client
+   can send a handcrafted message which corrupts the status of the server (i.e.
+   it can delete or modify the live data, or mess up with the interpreter
+   assigned to another user.
+
+   The current scheme does not try to resolve all conflicts and rely on the
+   correctness of the client to act honesly. In particular, the programmer
+   building the client should take care that if the current file is not included
+   (i.e. client build in a language different from c/c++) the class structure is
+   coherent with this file. Also the definition of the class inheriting from
+   PythonIterator needs to be coherent across client and server.
+*/
+
+#include "converse.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/* This class is empty, and should be reimplemented by the user */
+class PythonIterator {
+  /* _It is strongly suggested to avoid pointers_, in which case none of the
+     following methods needs to be reimplemented. Instead, if pointers are used,
+     the following three methods have to be reimplemented to pack and unpack the
+     pointered values across client and server (in the same semantics of charm
+     packing)
+*/
+ public:
+  // return the total size of the object (after packing)
+  virtual int size() {return sizeof(*this);};
+
+  // pack the message into a contiguous memory and return the pointer to this
+  // memory. the memory needs to be persistent (i.e. cannot disappear) and will
+  // be freed automatically
+  virtual PythonIterator *pack() {
+    void *memory = malloc(size());
+    memcpy (memory, this, size());
+    return (PythonIterator *)memory;
+  };
+
+  // unpack needs to reposition the pointer in the case they are used
+  virtual void unpack() {};
+};
+
+/* This class contains a single unsigned 4 bytes integer, it is needed in order
+   to distinguish between PythonExecute and PythonPrint on the server side,
+   since a byte copy does not preserve class information.
+*/
+class PythonAbstract {
+ public:
+  /* magic contains the size of the class passed, it is filled by the
+     constructors of the inheriting classes */
+  CmiUInt4 magic;
+};
+
+class PythonExecute : private PythonAbstract {
+  friend class PythonObject;
+ private:
+  int codeLength;
+  char * code;
+
+  /* the following parameters are used when the iterator mode is invoked */
+  int methodNameLength;
+  char * methodName;
+  int infoSize; /* must contain the size of the info structure passed */
+  PythonIterator *info;
+
+  char interpreter[4]; /* request for an existing interpreter */
+  char flags;
+  /* flags has the following parameters: (bit 1 is the MSB)
+     bit 1: isPersistent
+     bit 2: keepPrint
+     bit 3: isHighLevel
+     bit 4: isIterate
+     bit 5: print request
+  */
+  static const char FLAG_PERSISTENT = 0x80;
+  static const char FLAG_KEEPPRINT = 0x40;
+  static const char FLAG_HIGHLEVEL = 0x20;
+  static const char FLAG_ITERATE = 0x10;
+ public:
+
+  /* constructors */
+  /* by default, if the code is persistent, then the prints will be maintained */
+  PythonExecute(char *_code, bool _persistent=false, bool _highlevel=false, char _interp[4]=0);
+  PythonExecute(char *_code, char *_method, PythonIterator *_info, bool _persistent=false, bool _highlevel=false, char _interp[4]=0);
+  ~PythonExecute();
+
+  /* routines to set all parameters in the class */
+  void setCode(char *_set);
+  void setMethodName(char *_set);
+  void setIterator(PythonIterator *_set);
+  void setPersistent(bool _set);
+  void setIterate(bool _set);
+  void setHighLevel(bool _set);
+  void setKeepPrint(bool _set);
+  void setInterpreter(char i[4]) { memcpy(interpreter, i, 4); };
+
+  bool isPersistent() { return flags & FLAG_PERSISTENT; };
+  bool isIterate() { return flags & FLAG_ITERATE; };
+  bool isHighLevel() { return flags & FLAG_HIGHLEVEL; };
+  bool isKeepPrint() { return flags & FLAG_KEEPPRINT; };
+  void getInterpreter(char i[4]) { memcpy(i, interpreter, 4); };
+
+  int size();
+  char *toString();
+  void unpack();
+};
+
+class PythonPrint : private PythonAbstract {
+  friend class PythonObject;
+ private:
+  char interpreter[4];;
+  char flags;
+  /* flags has the following parameters: (bit 1 is the MSB)
+     bit 1: noWait
+  */
+  static const char FLAG_WAIT = 0x80;
+ public:
+  PythonPrint(char _interp[4], bool Wait=true);
+
+  void setWait(bool _set);
+  bool isWait() { return flags & FLAG_WAIT; };
+};
index e6a8e15c845888785ffcac6f39d03a7c2adde09f..5f04901c30f667dcf7f12f469d4b97ceff5e8e03 100644 (file)
@@ -2,19 +2,40 @@
 
 CsvDeclare(CmiNodeLock, pyLock);
 CsvDeclare(PythonTable *, pyWorkers);
-CsvDeclare(int, pyNumber);
+CsvDeclare(CmiUInt4, pyNumber);
 CtvDeclare(PyObject *, pythonReturnValue);
 
 // One-time per-processor setup routine
 // main interface for python to access common charm methods
 static PyObject *CkPy_printstr(PyObject *self, PyObject *args) {
   char *stringToPrint;
-  if (!PyArg_ParseTuple(args, "s:printstr", &stringToPrint))
-    return NULL;
+  if (!PyArg_ParseTuple(args, "s:printstr", &stringToPrint)) return NULL;
   CkPrintf("%s\n",stringToPrint);
   Py_INCREF(Py_None);return Py_None; //Return-nothing idiom
 }
 
+static PyObject *CkPy_print(PyObject *self, PyObject *args) {
+  char *stringToPrint;
+  if (!PyArg_ParseTuple(args, "s:print", &stringToPrint)) return NULL;
+  CmiUInt4 pyReference = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
+  CmiLock(CsvAccess(pyLock));
+  PythonObject *pyWorker = ((*CsvAccess(pyWorkers))[pyReference]).object;
+  if (((*CsvAccess(pyWorkers))[pyReference]).clientReady > 0) {
+    // return the string to the client
+    // since there is a client waiting, it means there must not be
+    // pending strings to be returned ("printed" is empty)
+    //CkPrintf("printing data to the client\n");
+    CcsDelayedReply client = ((*CsvAccess(pyWorkers))[pyReference]).client;
+    CcsSendDelayedReply(client, strlen(stringToPrint)+1, stringToPrint);
+    ((*CsvAccess(pyWorkers))[pyReference]).clientReady = 0;
+  } else {
+    // add the string to those in list to be returned
+    ((*CsvAccess(pyWorkers))[pyReference]).printed += std::string(stringToPrint);
+  }
+  CmiUnlock(CsvAccess(pyLock));
+  Py_INCREF(Py_None);return Py_None; //Return-nothing idiom
+}
+
 static PyObject *CkPy_mype(PyObject *self, PyObject *args) {
   if (!PyArg_ParseTuple(args, ":mype")) return NULL;
   return Py_BuildValue("i", CkMyPe());
@@ -28,9 +49,9 @@ static PyObject *CkPy_numpes(PyObject *self, PyObject *args) {
 /*
 static PyObject *CkPy_myindex(PyObject *self, PyObject *args) {
   if (!PyArg_ParseTuple(args, ":myindex")) return NULL;
-  int pyNumber = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
+  CmiUInt4 pyReference = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
   CmiLock(CsvAccess(pyLock));
-  ArrayElement1D *pyArray = dynamic_cast<ArrayElement1D>((*CsvAccess(pyWorkers))[pyNumber]);
+  ArrayElement1D *pyArray = dynamic_cast<ArrayElement1D>((*CsvAccess(pyWorkers))[pyReference]);
   CmiUnlock(CsvAccess(pyLock));
   if (pyArray) return Py_BuildValue("i", pyArray->thisIndex);
   else { Py_INCREF(Py_None);return Py_None;}
@@ -41,9 +62,9 @@ static PyObject *CkPy_myindex(PyObject *self, PyObject *args) {
 // method to read a variable and convert it to a python object
 static PyObject *CkPy_read(PyObject *self, PyObject *args) {
   if (!PyArg_ParseTuple(args, "O:read")) return NULL;
-  int pyNumber = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
+  CmiUInt4 pyReference = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
   CmiLock(CsvAccess(pyLock));
-  PythonObject *pyWorker = ((*CsvAccess(pyWorkers))[pyNumber]).object;
+  PythonObject *pyWorker = ((*CsvAccess(pyWorkers))[pyReference]).object;
   CmiUnlock(CsvAccess(pyLock));
   return pyWorker->read(args);
 }
@@ -52,9 +73,9 @@ static PyObject *CkPy_read(PyObject *self, PyObject *args) {
 static PyObject *CkPy_write(PyObject *self, PyObject *args) {
   PyObject *where, *what;
   if (!PyArg_ParseTuple(args, "OO:write",&where,&what)) return NULL;
-  int pyNumber = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
+  CmiUInt4 pyReference = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
   CmiLock(CsvAccess(pyLock));
-  PythonObject *pyWorker = ((*CsvAccess(pyWorkers))[pyNumber]).object;
+  PythonObject *pyWorker = ((*CsvAccess(pyWorkers))[pyReference]).object;
   CmiUnlock(CsvAccess(pyLock));
   pyWorker->write(where, what);
   Py_INCREF(Py_None);return Py_None;
@@ -65,7 +86,8 @@ PyMethodDef PythonObject::CkPy_MethodsCustom[] = {
 };
 
 PyMethodDef CkPy_MethodsDefault[] = {
-  {"printstr", CkPy_printstr , METH_VARARGS},
+  {"printstr", CkPy_printstr, METH_VARARGS},
+  {"printclient", CkPy_print, METH_VARARGS},
   {"mype", CkPy_mype, METH_VARARGS},
   {"numpes", CkPy_numpes, METH_VARARGS},
   {"read", CkPy_read, METH_VARARGS},
@@ -74,53 +96,121 @@ PyMethodDef CkPy_MethodsDefault[] = {
 };
 
 void PythonObject::execute (CkCcsRequestMsg *msg) {
+  PythonAbstract *pyAbstract = (PythonAbstract *)msg->data;
+  PythonPrint *pyPrint=0;
+  PythonExecute *pyMsg=0;
+  if (pyAbstract->magic == sizeof(PythonPrint)) {
+    pyPrint = (PythonPrint *)msg->data;
+  } else if (pyAbstract->magic == sizeof(PythonExecute)) {
+    pyMsg = (PythonExecute *)msg->data;
+  } else {
+    CkPrintf("Wrong request arrived!\n");
+    return;
+  }
 
-  // update the reference number, used to access the current chare
+  // ATTN: be sure that in all possible paths pyLock is released!
   CmiLock(CsvAccess(pyLock));
-  int pyReference = CsvAccess(pyNumber)++;
-  CsvAccess(pyNumber) &= ~(1<<31);
-  ((*CsvAccess(pyWorkers))[pyReference]).object = this;
-  CmiUnlock(CsvAccess(pyLock));
-
-  // send back this number to the client
-  CcsSendDelayedReply(msg->reply, 1, (void *)&pyReference);
+  CmiUInt4 pyReference;
+
+  // check if this is just a request for prints
+  if (pyPrint) {
+    PythonTable::iterator iter = CsvAccess(pyWorkers)->find((CmiUInt4)*pyPrint->interpreter);
+    if (iter == CsvAccess(pyWorkers)->end()) {
+      // Malformed request!
+      CkPrintf("PythonCCS: print request on invalid interpreter\n");
+      pyReference = 0;
+      CmiUnlock(CsvAccess(pyLock));
+      CcsSendDelayedReply(msg->reply, sizeof(CmiUInt4), (void *)&pyReference);
+    } else {
+      if (iter->second.printed.length() > 0) {
+       // send back to the client the string
+       const char *str = iter->second.printed.data();
+       //CkPrintf("sending data to the client\n");
+       CcsSendDelayedReply(msg->reply, strlen(str)+1, str);
+       if (iter->second.clientReady == -1) {
+         // after the client flush the printed buffer, delete the entry
+         CsvAccess(pyWorkers)->erase((CmiUInt4)*pyPrint->interpreter);
+       }
+       CmiUnlock(CsvAccess(pyLock));
+      } else {
+       // nothing printed, store the client request if it will be waiting
+       if (pyPrint->isWait()) {
+         iter->second.client = msg->reply;
+         iter->second.clientReady = 1;
+       }
+       CmiUnlock(CsvAccess(pyLock));
+      }
+    }
+    delete msg;
+    return;
+  }
 
-  // create the new interpreter
-  PyEval_AcquireLock();
-  PyThreadState *pts = Py_NewInterpreter();
+  // re-establish the pointers in the structure
+  pyMsg->unpack();
+
+  if ((CmiUInt4)*pyMsg->interpreter > 0) {
+    // the user specified an interpreter, check if it is free
+    PythonTable::iterator iter;
+    if ((iter=CsvAccess(pyWorkers)->find((CmiUInt4)*pyMsg->interpreter))!=CsvAccess(pyWorkers)->end() && !iter->second.inUse) {
+      // the interpreter already exists and it is not in use
+      //CkPrintf("interpreter present and not in use\n");
+      pyReference = (CmiUInt4)*pyMsg->interpreter;
+      iter->second.inUse = true;
+      // send back this number to the client, which is an ack
+      CcsSendDelayedReply(msg->reply, sizeof(CmiUInt4), (void *)&pyReference);
+      CmiUnlock(CsvAccess(pyLock));
+      PyEval_AcquireLock();
+    } else {
+      // ops, either the iterator does not exist or is already in use, return an
+      // error to the client, we don't want to create a new interpreter if the
+      // old is in use, because this can corrupt the semantics of the user code.
+      //if (iter!=CsvAccess(pyWorkers)->end()) CkPrintf("asked for an interpreter not present\n");
+      //else CkPrintf("interpreter already in use\n");
+      pyReference = ~0;
+      CcsSendDelayedReply(msg->reply, sizeof(CmiUInt4), (void *)&pyReference);
+      CmiUnlock(CsvAccess(pyLock));
+      return;  // stop the execution
+    }
+  } else {
+    // the user didn't specify an interpreter, create a new one
+    //CkPrintf("creating new interpreter\n");
+
+    // update the reference number, used to access the current chare
+    pyReference = CsvAccess(pyNumber)++;
+    CsvAccess(pyNumber) &= ~(1<<31);
+    ((*CsvAccess(pyWorkers))[pyReference]).object = this;
+    ((*CsvAccess(pyWorkers))[pyReference]).inUse = true;
+    CmiUnlock(CsvAccess(pyLock));
 
-  Py_InitModule("ck", CkPy_MethodsDefault);
-  Py_InitModule("charm", getMethods());
+    // send back this number to the client
+    //CkPrintf("sending interpreter to the client\n");
+    CcsSendDelayedReply(msg->reply, sizeof(CmiUInt4), (void *)&pyReference);
 
-  // insert into the dictionary a variable with the reference number
-  PyObject *mod = PyImport_AddModule("__main__");
-  PyObject *dict = PyModule_GetDict(mod);
+    // create the new interpreter
+    PyEval_AcquireLock();
+    PyThreadState *pts = Py_NewInterpreter();
 
-  PyDict_SetItemString(dict,"charmNumber",PyInt_FromLong(pyReference));
-  PyRun_String("import ck",Py_file_input,dict,dict);
-  PyRun_String("import charm",Py_file_input,dict,dict);
+    Py_InitModule("ck", CkPy_MethodsDefault);
+    if (pyMsg->isHighLevel()) Py_InitModule("charm", getMethods());
 
-  // run the program
-  //PythonArray1D *pyArray = dynamic_cast<PythonArray1D*>(this);
-  CthResume(CthCreate((CthVoidFn)_callthr_executeThread, new CkThrCallArg(msg,this), 0));
-  //executeThread(msg);
-  //CkIndex_PythonArray1D::_call_executeThread_CkCcsRequestMsg(msg,pyArray);
-  //getMyHandle()[pyArray->thisIndex].executeThread(msg);
-  //getMyHandle().executeThread(msg);
-  //PyRun_SimpleString((char *)msg->data);
+    // insert into the dictionary a variable with the reference number
+    PyObject *mod = PyImport_AddModule("__main__");
+    PyObject *dict = PyModule_GetDict(mod);
 
-  // distroy map element in pyWorkers and terminate interpreter
-  /*
-  Py_EndInterpreter(pts);
-  PyEval_ReleaseLock();
+    PyDict_SetItemString(dict,"charmNumber",PyInt_FromLong(pyReference));
+    PyRun_String("import ck",Py_file_input,dict,dict);
+    if (pyMsg->isHighLevel()) PyRun_String("import charm",Py_file_input,dict,dict);
+  }
 
-  CmiLock(CsvAccess(pyLock));
-  CsvAccess(pyWorkers)->erase(pyReference);
-  CmiUnlock(CsvAccess(pyLock));
-  delete msg;
-  */
+  // run the program
+  if (pyMsg->isHighLevel()) {
+    CthResume(CthCreate((CthVoidFn)_callthr_executeThread, new CkThrCallArg(msg,this), 0));
+  } else {
+    executeThread(msg);
+  }
 }
 
+// created in a new thread, call the executeThread method
 void PythonObject::_callthr_executeThread(CkThrCallArg *impl_arg) {
   void *impl_msg = impl_arg->msg;
   PythonObject *impl_obj = (PythonObject *) impl_arg->obj;
@@ -131,32 +221,126 @@ void PythonObject::_callthr_executeThread(CkThrCallArg *impl_arg) {
 void PythonObject::executeThread(CkCcsRequestMsg *msg) {
   // get the information about the running python thread and my reference number
   PyThreadState *mine = PyThreadState_Get();
-  int pyReference = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
+  CmiUInt4 pyReference = PyInt_AsLong(PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("__main__")),"charmNumber"));
 
-  // store the self thread for future suspention
-  CmiLock(CsvAccess(pyLock));
-  ((*CsvAccess(pyWorkers))[pyReference]).thread=CthSelf();
-  CmiUnlock(CsvAccess(pyLock));
+  PythonExecute *pyMsg = (PythonExecute *)msg->data;
 
-  PyRun_SimpleString((char *)msg->data);
+  // store the self thread for future suspention if high level execution
+  if (pyMsg->isHighLevel()) {
+    CmiLock(CsvAccess(pyLock));
+    ((*CsvAccess(pyWorkers))[pyReference]).thread=CthSelf();
+    CmiUnlock(CsvAccess(pyLock));
+  }
 
-  Py_EndInterpreter(mine);
-  PyEval_ReleaseLock();
+  // decide whether it is iterative or not
+  if (!pyMsg->isIterate()) {
+    PyRun_SimpleString(pyMsg->code);
+  } else {
+    // compile the program
+    char *userCode = pyMsg->code;
+    struct _node* programNode = PyParser_SimpleParseString(userCode, Py_file_input);
+    if (programNode==NULL) {
+      CkPrintf("Program error\n");
+      // distroy map element in pyWorkers and terminate interpreter
+      cleanup(pyMsg, mine, pyReference);
+      delete msg;
+      return;
+    }
+    PyCodeObject *program = PyNode_Compile(programNode, "");
+    if (program==NULL) {
+      CkPrintf("Program error\n");
+      PyNode_Free(programNode);
+      // distroy map element in pyWorkers and terminate interpreter
+      cleanup(pyMsg, mine, pyReference);
+      delete msg;
+      return;
+    }
+    PyObject *mod = PyImport_AddModule("__main__");
+    PyObject *dict = PyModule_GetDict(mod);
+    PyObject *code = PyEval_EvalCode(program, dict, dict);
+    if (code==NULL) {
+      CkPrintf("Program error\n");
+      PyNode_Free(programNode);
+      Py_DECREF(program);
+      // distroy map element in pyWorkers and terminate interpreter
+      cleanup(pyMsg, mine, pyReference);
+      delete msg;
+      return;
+    }
 
-  CmiLock(CsvAccess(pyLock));
-  CsvAccess(pyWorkers)->erase(pyReference);
-  CmiUnlock(CsvAccess(pyLock));
+    // load the user defined method
+    char *userMethod = userCode + strlen(userCode) + 1;
+    PyObject *item = PyDict_GetItemString(dict, userMethod);
+    if (item==NULL) {
+      CkPrintf("Method not found\n");
+      PyNode_Free(programNode);
+      Py_DECREF(program);
+      Py_DECREF(code);
+      // distroy map element in pyWorkers and terminate interpreter
+      cleanup(pyMsg, mine, pyReference);
+      delete msg;
+      return;
+    }
+
+    // create the container for the data
+    PyRun_String("class Particle:\n\tpass\n\n", Py_file_input, dict, dict);
+    PyObject *part = PyRun_String("Particle()", Py_eval_input, dict, dict);
+    PyObject *arg = PyTuple_New(1);
+    PyTuple_SetItem(arg, 0, part);
+
+    // construct the iterator calling the user defined method in the interiting class
+    void *userIterator = (void *)(userMethod + strlen(userMethod) + 1);
+    int more = buildIterator(part, userIterator);
+
+    // iterate over all the provided iterators from the user class
+    PyObject *result;
+    while (more) {
+      result = PyObject_CallObject(item, arg);
+      if (!result) {
+       CkPrintf("Python Call error\n");
+       break;
+      }
+      more = nextIteratorUpdate(part, result, userIterator);
+      Py_DECREF(result);
+    }
+
+    Py_DECREF(part);
+    Py_DECREF(arg);
+    PyNode_Free(programNode);
+    Py_DECREF(program);
+    Py_DECREF(code);
+
+  } // end decision if it is iterate or not
+
+  cleanup(pyMsg, mine, pyReference);
   delete msg;
-  // since this function should always be executed in a different thread,
-  // when it is done, destroy the current thread
-  CthFree(CthSelf());
+
 }
 
+// this function takes care of destroying the interpreter, deleting the
+// reference into the map table, depending on the flags specified
+void PythonObject::cleanup (PythonExecute *pyMsg, PyThreadState *pts, CmiUInt4 pyReference) {
+  if (!pyMsg->isPersistent()) {
+    Py_EndInterpreter(pts);
+    ((*CsvAccess(pyWorkers))[pyReference]).clientReady = -1;
+  }
+  PyEval_ReleaseLock();
+
+  if (!pyMsg->isPersistent() && !pyMsg->isKeepPrint()) {
+    // destroy the entry in the map
+    //CkPrintf("destroyed interpreter\n");
+    CmiLock(CsvAccess(pyLock));
+    CsvAccess(pyWorkers)->erase(pyReference);
+    CmiUnlock(CsvAccess(pyLock));
+  }
+}
+
+/*
 void PythonObject::iterate (CkCcsRequestMsg *msg) {
 
   // update the reference number, used to access the current chare
   CmiLock(CsvAccess(pyLock));
-  int pyReference = CsvAccess(pyNumber)++;
+  CmiUInt4 pyReference = CsvAccess(pyNumber)++;
   CsvAccess(pyNumber) &= ~(1<<31);
   ((*CsvAccess(pyWorkers))[pyReference]).object = this;
   CmiUnlock(CsvAccess(pyLock));
@@ -271,9 +455,14 @@ void PythonObject::iterate (CkCcsRequestMsg *msg) {
   CmiUnlock(CsvAccess(pyLock));
   delete msg;
 }
+*/
+
+void PythonObject::getPrint(CkCcsRequestMsg *msg) {
+
+}
 
 static void initializePythonDefault(void) {
-  CsvInitialize(int, pyNumber);
+  CsvInitialize(CmiUInt, pyNumber);
   CsvAccess(pyNumber) = 0;
   CsvInitialize(PythonTable *,pyWorkers);
   CsvAccess(pyWorkers) = new PythonTable();
index 75151e6e5fb833280ccb4f9981a237c342642151..74e913c1ee4234685624c7d102937ccae1c0be5c 100644 (file)
@@ -1,46 +1,3 @@
 module PythonCCS {
-
-       //readonly CProxy_PythonGroup python;
-/*
-       mainchare PythonMain {
-               entry PythonMain(CkArgMsg *msg);
-       }
-
-       chare PythonChare {
-               entry PythonChare(void);
-               entry void execute (CkCcsRequestMsg *msg);
-               entry void iterate (CkCcsRequestMsg *msg);
-       }
-
-       group PythonGroup {
-               entry PythonGroup(void);
-               entry void execute (CkCcsRequestMsg *msg);
-               entry void iterate (CkCcsRequestMsg *msg);
-       }
-
-       nodegroup PythonNodeGroup {
-               entry PythonNodeGroup(void);
-               entry void execute (CkCcsRequestMsg *msg);
-               entry void iterate (CkCcsRequestMsg *msg);
-       }
-
-       array [1D] PythonArray1D {
-               entry PythonArray1D(void);
-               entry void execute (CkCcsRequestMsg *msg);
-               entry void iterate (CkCcsRequestMsg *msg);
-       }
-
-       array [2D] PythonArray2D {
-               entry PythonArray2D(void);
-               entry void execute (CkCcsRequestMsg *msg);
-               entry void iterate (CkCcsRequestMsg *msg);
-       }
-
-       array [3D] PythonArray3D {
-               entry PythonArray3D(void);
-               entry void execute (CkCcsRequestMsg *msg);
-               entry void iterate (CkCcsRequestMsg *msg);
-       }
-*/
-       initnode void initializePythonDefault(void);
+   initnode void initializePythonDefault(void);
 }
index 1c7c3fa621ed1e138322c719674572be05b5bbb3..bef44c340be24b9be503da7673f2f01b824ef3f1 100644 (file)
@@ -10,6 +10,7 @@
 #include "python/eval.h"
 #include "python/node.h"
 #include "PythonCCS.decl.h"
+#include "PythonCCS-client.h"
 #include "string"
 #include "map"
 
@@ -24,7 +25,7 @@ class PythonObject {
   static PyMethodDef CkPy_MethodsCustom[];
  public:
   void execute(CkCcsRequestMsg *msg);
-  void iterate(CkCcsRequestMsg *msg);
+  void cleanup(PythonExecute *pyMsg, PyThreadState *pts, CmiUInt4 pyVal);
   void getPrint(CkCcsRequestMsg *msg);
   static void _callthr_executeThread(CkThrCallArg *impl_arg);
   void executeThread(CkCcsRequestMsg *msg);
@@ -76,18 +77,26 @@ class PythonObject {
 
 typedef struct {
   PythonObject *object; /* The c++ object running the job */
+  bool inUse;
   PyObject *arg;
   PyObject **result;
   CthThread thread;       /* The charm thread running the python code */
   PyThreadState *pythread; /* The python interpreter interpreting the code */
-  CcsDelayedReply client; /* Where to send the printed data */
-  std::string printed; /* Union of all printed string and not shipped to the client */
+  int clientReady; /* whether or not a client has sent a request for print */
+  /* meanings of clientReady:
+     1  - there is a client wainting for data
+     0  - no client waiting for data
+     -1 - no client watiing, and the current structure is alive only because of
+     "KeepPrint". it has to be deleted when a client synchonizes
+  */
+  CcsDelayedReply client; /* Where to send the printed data when ready */
+  std::string printed; /* Union of all printed string and not yet shipped to the client */
 } PythonStruct;
-typedef std::map<int,PythonStruct> PythonTable;
+typedef std::map<CmiUInt4,PythonStruct> PythonTable;
 
 CsvExtern(CmiNodeLock, pyLock);
 CsvExtern(PythonTable *, pyWorkers);
-CsvExtern(int, pyNumber);
+CsvExtern(CmiUInt4, pyNumber);
 CtvExtern(PyObject *, pythonReturnValue);
 
 #endif //__CKPYTHON_H