[svn r63340] remove greenlet from py lib
--HG-- branch : trunk
This commit is contained in:
		
							parent
							
								
									92e354a486
								
							
						
					
					
						commit
						c2ee8273b0
					
				|  | @ -6,7 +6,6 @@ interacting with filesystems. | |||
| 
 | ||||
| - `py.test`_: cross-project testing tool with many advanced features | ||||
| - `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes | ||||
| - `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy | ||||
| - `py.path`_: path abstractions over local and subversion files  | ||||
| - `py.code`_: dynamic code compile and traceback printing support | ||||
| 
 | ||||
|  | @ -16,19 +15,18 @@ http://pylib.org/contact.html | |||
| 
 | ||||
| .. _`py.test`: http://pylib.org/test.html | ||||
| .. _`py.execnet`: http://pylib.org/execnet.html | ||||
| .. _`py.magic.greenlet`: http://pylib.org/greenlet.html | ||||
| .. _`py.path`: http://pylib.org/path.html | ||||
| .. _`py.code`: http://pylib.org/code.html | ||||
|   | ||||
| """ | ||||
| from initpkg import initpkg | ||||
| 
 | ||||
| version = "1.0.0a7" | ||||
| version = "1.0.0a8" | ||||
| 
 | ||||
| initpkg(__name__, | ||||
|     description = "pylib and py.test: agile development and test support library", | ||||
|     revision = int('$LastChangedRevision: 63319 $'.split(':')[1][:-1]), | ||||
|     lastchangedate = '$LastChangedDate: 2009-03-25 12:50:57 +0100 (Wed, 25 Mar 2009) $', | ||||
|     revision = int('$LastChangedRevision: 63340 $'.split(':')[1][:-1]), | ||||
|     lastchangedate = '$LastChangedDate: 2009-03-26 10:33:50 +0100 (Thu, 26 Mar 2009) $', | ||||
|     version = version,  | ||||
|     url = "http://pylib.org",  | ||||
|     download_url = "http://codespeak.net/py/%s/download.html" % version, | ||||
|  |  | |||
|  | @ -1 +0,0 @@ | |||
| # | ||||
|  | @ -1,13 +0,0 @@ | |||
| 
 | ||||
| This code is by Armin Rigo with the core pieces by Christian Tismer. | ||||
| These pieces are: | ||||
| 
 | ||||
| - slp_platformselect.h | ||||
| - switch_*.h, included from the previous one. | ||||
| 
 | ||||
| All additional credits for the general idea of stack switching also go to | ||||
| Christian.  In other words, if it works it is thanks to Christian's work, | ||||
| and if it crashes it is because of a bug of mine :-) | ||||
| 
 | ||||
| 
 | ||||
| -- Armin | ||||
|  | @ -1,148 +0,0 @@ | |||
| import thread, sys | ||||
| 
 | ||||
| __all__ = ['greenlet', 'main', 'getcurrent'] | ||||
| 
 | ||||
| 
 | ||||
| class greenlet(object): | ||||
|     __slots__ = ('run', '_controller') | ||||
| 
 | ||||
|     def __init__(self, run=None, parent=None): | ||||
|         if run is not None: | ||||
|             self.run = run | ||||
|         if parent is not None: | ||||
|             self.parent = parent | ||||
| 
 | ||||
|     def switch(self, *args): | ||||
|         global _passaround_ | ||||
|         _passaround_ = None, args, None | ||||
|         self._controller.switch(self) | ||||
|         exc, val, tb = _passaround_ | ||||
|         del _passaround_ | ||||
|         if exc is None: | ||||
|             if isinstance(val, tuple) and len(val) == 1: | ||||
|                 return val[0] | ||||
|             else: | ||||
|                 return val | ||||
|         else: | ||||
|             raise exc, val, tb | ||||
| 
 | ||||
|     def __nonzero__(self): | ||||
|         return self._controller.isactive() | ||||
| 
 | ||||
|     def __new__(cls, *args, **kwds): | ||||
|         self = object.__new__(cls) | ||||
|         self._controller = _Controller() | ||||
|         return self | ||||
| 
 | ||||
|     def __del__(self): | ||||
|         #print 'DEL:', self | ||||
|         if self._controller.parent is None: | ||||
|             return   # don't kill the main greenlet | ||||
|         while self._controller.isactive(): | ||||
|             self._controller.kill(self) | ||||
| 
 | ||||
|     def getparent(self): | ||||
|         return self._controller.parent | ||||
| 
 | ||||
|     def setparent(self, nparent): | ||||
|         if not isinstance(nparent, greenlet): | ||||
|             raise TypeError, "parent must be a greenlet" | ||||
|         p = nparent | ||||
|         while p is not None: | ||||
|             if p is self: | ||||
|                 raise ValueError, "cyclic parent chain" | ||||
|             p = p._controller.parent | ||||
|         self._controller.parent = nparent | ||||
| 
 | ||||
|     parent = property(getparent, setparent) | ||||
|     del getparent | ||||
|     del setparent | ||||
| 
 | ||||
| 
 | ||||
| class _Controller: | ||||
|     # Controllers are separated from greenlets to allow greenlets to be | ||||
|     # deallocated while running, when their last reference goes away. | ||||
|     # Care is taken to keep only references to controllers in thread's | ||||
|     # frames' local variables. | ||||
| 
 | ||||
|     # _Controller.parent: the parent greenlet. | ||||
|     # _Controller.lock: the lock used for synchronization | ||||
|     #                   it is not set before the greenlet starts | ||||
|     #                   it is None after the greenlet stops | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         self.parent = _current_ | ||||
| 
 | ||||
|     def isactive(self): | ||||
|         return getattr(self, 'lock', None) is not None | ||||
| 
 | ||||
|     def switch(self, target): | ||||
|         previous = _current_._controller | ||||
|         self.switch_no_wait(target) | ||||
|         # wait until someone releases this thread's lock | ||||
|         previous.lock.acquire() | ||||
| 
 | ||||
|     def switch_no_wait(self, target): | ||||
|         # lock tricks: each greenlet has its own lock which is almost always | ||||
|         # in 'acquired' state: | ||||
|         # * the current greenlet runs with its lock acquired | ||||
|         # * all other greenlets wait on their own lock's acquire() call | ||||
|         global _current_ | ||||
|         try: | ||||
|             while 1: | ||||
|                 _current_ = target | ||||
|                 lock = self.lock | ||||
|                 if lock is not None: | ||||
|                     break | ||||
|                 target = self.parent | ||||
|                 self = target._controller | ||||
|         except AttributeError: | ||||
|             # start the new greenlet | ||||
|             lock = self.lock = thread.allocate_lock() | ||||
|             lock.acquire() | ||||
|             thread.start_new_thread(self.run_thread, (target.run,)) | ||||
|         else: | ||||
|             # release (re-enable) the target greenlet's thread | ||||
|             lock.release() | ||||
| 
 | ||||
|     def run_thread(self, run): | ||||
|         #print 'ENTERING', self | ||||
|         global _passaround_ | ||||
|         exc, val, tb = _passaround_ | ||||
|         if exc is None: | ||||
|             try: | ||||
|                 result = run(*val) | ||||
|             except SystemExit, e: | ||||
|                 _passaround_ = None, (e,), None | ||||
|             except: | ||||
|                 _passaround_ = sys.exc_info() | ||||
|             else: | ||||
|                 _passaround_ = None, (result,), None | ||||
|         self.lock = None | ||||
|         #print 'LEAVING', self | ||||
|         self.switch_no_wait(self.parent) | ||||
| 
 | ||||
|     def kill(self, target): | ||||
|         # see comments in greenlet.c:green_dealloc() | ||||
|         global _passaround_ | ||||
|         self._parent_ = _current_ | ||||
|         _passaround_ = SystemExit, None, None | ||||
|         self.switch(target) | ||||
|         exc, val, tb = _passaround_ | ||||
|         del _passaround_ | ||||
|         if exc is not None: | ||||
|             if val is None: | ||||
|                 print >> sys.stderr, "Exception", "%s" % (exc,), | ||||
|             else: | ||||
|                 print >> sys.stderr, "Exception", "%s: %s" % (exc, val), | ||||
|             print >> sys.stderr, "in", self, "ignored" | ||||
| 
 | ||||
| 
 | ||||
| _current_ = None | ||||
| main = greenlet() | ||||
| main._controller.lock = thread.allocate_lock() | ||||
| main._controller.lock.acquire() | ||||
| _current_ = main | ||||
| 
 | ||||
| def getcurrent(): | ||||
|     return _current_ | ||||
|  | @ -1,981 +0,0 @@ | |||
| #include "greenlet.h" | ||||
| #include "structmember.h" | ||||
| 
 | ||||
| 
 | ||||
| /* XXX major open bugs:
 | ||||
|    XXX - no GC.  Unfinished greenlets won't be deallocated if they | ||||
|    XXX   contain a cycle to themselves from anywhere in their frame stack. | ||||
| */ | ||||
| 
 | ||||
| /***********************************************************
 | ||||
| 
 | ||||
| A PyGreenlet is a range of C stack addresses that must be | ||||
| saved and restored in such a way that the full range of the | ||||
| stack contains valid data when we switch to it. | ||||
| 
 | ||||
| Stack layout for a greenlet: | ||||
| 
 | ||||
|                |     ^^^       | | ||||
|                |  older data   | | ||||
|                |               | | ||||
|   stack_stop . |_______________| | ||||
|         .      |               | | ||||
|         .      | greenlet data | | ||||
|         .      |   in stack    | | ||||
|         .    * |_______________| . .  _____________  stack_copy + stack_saved | ||||
|         .      |               |     |             | | ||||
|         .      |     data      |     |greenlet data| | ||||
|         .      |   unrelated   |     |    saved    | | ||||
|         .      |      to       |     |   in heap   | | ||||
|  stack_start . |     this      | . . |_____________| stack_copy | ||||
|                |   greenlet    | | ||||
|                |               | | ||||
|                |  newer data   | | ||||
|                |     vvv       | | ||||
| 
 | ||||
| 
 | ||||
| Note that a greenlet's stack data is typically partly at its correct | ||||
| place in the stack, and partly saved away in the heap, but always in | ||||
| the above configuration: two blocks, the more recent one in the heap | ||||
| and the older one still in the stack (either block may be empty). | ||||
| 
 | ||||
| Greenlets are chained: each points to the previous greenlet, which is | ||||
| the one that owns the data currently in the C stack above my | ||||
| stack_stop.  The currently running greenlet is the first element of | ||||
| this chain.  The main (initial) greenlet is the last one.  Greenlets | ||||
| whose stack is entirely in the heap can be skipped from the chain. | ||||
| 
 | ||||
| The chain is not related to execution order, but only to the order | ||||
| in which bits of C stack happen to belong to greenlets at a particular | ||||
| point in time. | ||||
| 
 | ||||
| The main greenlet doesn't have a stack_stop: it is responsible for the | ||||
| complete rest of the C stack, and we don't know where it begins.  We | ||||
| use (char*) -1, the largest possible address. | ||||
| 
 | ||||
| States: | ||||
|   stack_stop == NULL && stack_start == NULL:  did not start yet | ||||
|   stack_stop != NULL && stack_start == NULL:  already finished | ||||
|   stack_stop != NULL && stack_start != NULL:  active | ||||
| 
 | ||||
| The running greenlet's stack_start is undefined but not NULL. | ||||
| 
 | ||||
|  ***********************************************************/ | ||||
| 
 | ||||
| /*** global state ***/ | ||||
| 
 | ||||
| /* In the presence of multithreading, this is a bit tricky:
 | ||||
| 
 | ||||
|    - ts_current always store a reference to a greenlet, but it is | ||||
|      not really the current greenlet after a thread switch occurred. | ||||
| 
 | ||||
|    - each *running* greenlet uses its run_info field to know which | ||||
|      thread it is attached to.  A greenlet can only run in the thread | ||||
|      where it was created.  This run_info is a ref to tstate->dict. | ||||
| 
 | ||||
|    - the thread state dict is used to save and restore ts_current, | ||||
|      using the dictionary key 'ts_curkey'. | ||||
| */ | ||||
| 
 | ||||
| static PyGreenlet* ts_current; | ||||
| static PyGreenlet* ts_origin; | ||||
| static PyGreenlet* ts_target; | ||||
| static PyObject* ts_passaround; | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| /* Thread-aware routines, switching global variables when needed */ | ||||
| 
 | ||||
| #define STATE_OK    (ts_current->run_info == PyThreadState_GET()->dict \ | ||||
| 			|| !green_updatecurrent()) | ||||
| 
 | ||||
| static PyObject* ts_curkey; | ||||
| static PyObject* ts_delkey; | ||||
| static PyObject* PyExc_GreenletError; | ||||
| static PyObject* PyExc_GreenletExit; | ||||
| 
 | ||||
| static PyGreenlet* green_create_main(void) | ||||
| { | ||||
| 	PyGreenlet* gmain; | ||||
| 	PyObject* dict = PyThreadState_GetDict(); | ||||
| 	if (dict == NULL) { | ||||
| 		if (!PyErr_Occurred()) | ||||
| 			PyErr_NoMemory(); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	/* create the main greenlet for this thread */ | ||||
| 	gmain = (PyGreenlet*) PyType_GenericAlloc(&PyGreen_Type, 0); | ||||
| 	if (gmain == NULL) | ||||
| 		return NULL; | ||||
| 	gmain->stack_start = (char*) 1; | ||||
| 	gmain->stack_stop = (char*) -1; | ||||
| 	gmain->run_info = dict; | ||||
| 	Py_INCREF(dict); | ||||
| 	return gmain; | ||||
| } | ||||
| 
 | ||||
| static int green_updatecurrent(void) | ||||
| { | ||||
| 	PyThreadState* tstate; | ||||
| 	PyGreenlet* next; | ||||
| 	PyGreenlet* previous; | ||||
| 	PyObject* deleteme; | ||||
| 
 | ||||
| 	/* save ts_current as the current greenlet of its own thread */ | ||||
| 	previous = ts_current; | ||||
| 	if (PyDict_SetItem(previous->run_info, ts_curkey, (PyObject*) previous)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	/* get ts_current from the active tstate */ | ||||
| 	tstate = PyThreadState_GET(); | ||||
| 	if (tstate->dict && (next = | ||||
| 	    (PyGreenlet*) PyDict_GetItem(tstate->dict, ts_curkey))) { | ||||
| 		/* found -- remove it, to avoid keeping a ref */ | ||||
| 		Py_INCREF(next); | ||||
| 		if (PyDict_SetItem(tstate->dict, ts_curkey, Py_None)) | ||||
| 			PyErr_Clear(); | ||||
| 	} | ||||
| 	else { | ||||
| 		/* first time we see this tstate */ | ||||
| 		next = green_create_main(); | ||||
| 		if (next == NULL) | ||||
| 			return -1; | ||||
| 	} | ||||
| 	ts_current = next; | ||||
| 	Py_DECREF(previous); | ||||
| 	/* green_dealloc() cannot delete greenlets from other threads, so
 | ||||
| 	   it stores them in the thread dict; delete them now. */ | ||||
| 	deleteme = PyDict_GetItem(tstate->dict, ts_delkey); | ||||
| 	if (deleteme != NULL) { | ||||
| 		PyList_SetSlice(deleteme, 0, INT_MAX, NULL); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static PyObject* green_statedict(PyGreenlet* g) | ||||
| { | ||||
| 	while (!PyGreen_STARTED(g)) | ||||
| 		g = g->parent; | ||||
| 	return g->run_info; | ||||
| } | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| 
 | ||||
| static int g_save(PyGreenlet* g, char* stop) | ||||
| { | ||||
| 	/* Save more of g's stack into the heap -- at least up to 'stop'
 | ||||
| 
 | ||||
| 	   g->stack_stop |________| | ||||
| 	                 |        | | ||||
| 			 |    __ stop       . . . . . | ||||
| 	                 |        |    ==>  .       . | ||||
| 			 |________|          _______ | ||||
| 			 |        |         |       | | ||||
| 			 |        |         |       | | ||||
| 	  g->stack_start |        |         |_______| g->stack_copy | ||||
| 
 | ||||
| 	 */ | ||||
| 	long sz1 = g->stack_saved; | ||||
| 	long sz2 = stop - g->stack_start; | ||||
| 	assert(g->stack_start != NULL); | ||||
| 	if (sz2 > sz1) { | ||||
| 		char* c = PyMem_Realloc(g->stack_copy, sz2); | ||||
| 		if (!c) { | ||||
| 			PyErr_NoMemory(); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		memcpy(c+sz1, g->stack_start+sz1, sz2-sz1); | ||||
| 		g->stack_copy = c; | ||||
| 		g->stack_saved = sz2; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void slp_restore_state(void) | ||||
| { | ||||
| 	PyGreenlet* g = ts_target; | ||||
| 	 | ||||
| 	/* Restore the heap copy back into the C stack */ | ||||
| 	if (g->stack_saved != 0) { | ||||
| 		memcpy(g->stack_start, g->stack_copy, g->stack_saved); | ||||
| 		PyMem_Free(g->stack_copy); | ||||
| 		g->stack_copy = NULL; | ||||
| 		g->stack_saved = 0; | ||||
| 	} | ||||
| 	if (ts_current->stack_stop == g->stack_stop) | ||||
| 		g->stack_prev = ts_current->stack_prev; | ||||
| 	else | ||||
| 		g->stack_prev = ts_current; | ||||
| } | ||||
| 
 | ||||
| static int slp_save_state(char* stackref) | ||||
| { | ||||
| 	/* must free all the C stack up to target_stop */ | ||||
| 	char* target_stop = ts_target->stack_stop; | ||||
| 	assert(ts_current->stack_saved == 0); | ||||
| 	if (ts_current->stack_start == NULL) | ||||
| 		ts_current = ts_current->stack_prev;  /* not saved if dying */ | ||||
| 	else | ||||
| 		ts_current->stack_start = stackref; | ||||
| 	 | ||||
| 	while (ts_current->stack_stop < target_stop) { | ||||
| 		/* ts_current is entierely within the area to free */ | ||||
| 		if (g_save(ts_current, ts_current->stack_stop)) | ||||
| 			return -1;  /* XXX */ | ||||
| 		ts_current = ts_current->stack_prev; | ||||
| 	} | ||||
| 	if (ts_current != ts_target) { | ||||
| 		if (g_save(ts_current, target_stop)) | ||||
| 			return -1;  /* XXX */ | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * the following macros are spliced into the OS/compiler | ||||
|  * specific code, in order to simplify maintenance. | ||||
|  */ | ||||
| 
 | ||||
| #define SLP_SAVE_STATE(stackref, stsizediff)		\ | ||||
|   stackref += STACK_MAGIC;				\ | ||||
|   if (slp_save_state((char*)stackref)) return -1;	\ | ||||
|   if (!PyGreen_ACTIVE(ts_target)) return 1;		\ | ||||
|   stsizediff = ts_target->stack_start - (char*)stackref | ||||
| 
 | ||||
| #define SLP_RESTORE_STATE()			\ | ||||
|   slp_restore_state() | ||||
| 
 | ||||
| 
 | ||||
| #define SLP_EVAL | ||||
| #include "slp_platformselect.h" | ||||
| 
 | ||||
| #ifndef STACK_MAGIC | ||||
| #error "greenlet needs to be ported to this platform,\ | ||||
|  or teached how to detect your compiler properly." | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /* This is a trick to prevent the compiler from inlining or
 | ||||
|    removing the frames */ | ||||
| int (*_PyGreen_slp_switch) (void); | ||||
| int (*_PyGreen_switchstack) (void); | ||||
| void (*_PyGreen_initialstub) (void*); | ||||
| 
 | ||||
| static int g_switchstack(void) | ||||
| { | ||||
| 	/* perform a stack switch according to some global variables
 | ||||
| 	   that must be set before: | ||||
| 	   - ts_current: current greenlet (holds a reference) | ||||
| 	   - ts_target: greenlet to switch to | ||||
| 	   - ts_passaround: NULL if PyErr_Occurred(), | ||||
| 	             else a tuple of args sent to ts_target (holds a reference) | ||||
| 	*/ | ||||
| 	int err; | ||||
| 	{   /* save state */ | ||||
| 		PyThreadState* tstate = PyThreadState_GET(); | ||||
| 		ts_current->recursion_depth = tstate->recursion_depth; | ||||
| 		ts_current->top_frame = tstate->frame; | ||||
| 	} | ||||
| 	ts_origin = ts_current; | ||||
| 	err = _PyGreen_slp_switch(); | ||||
| 	if (err < 0) {   /* error */ | ||||
| 		Py_XDECREF(ts_passaround); | ||||
| 		ts_passaround = NULL; | ||||
| 	} | ||||
| 	else { | ||||
| 		PyThreadState* tstate = PyThreadState_GET(); | ||||
| 		tstate->recursion_depth = ts_target->recursion_depth; | ||||
| 		tstate->frame = ts_target->top_frame; | ||||
| 		ts_target->top_frame = NULL; | ||||
| 		ts_current = ts_target; | ||||
| 		Py_INCREF(ts_target); | ||||
| 		Py_DECREF(ts_origin); | ||||
| 	} | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static PyObject* g_switch(PyGreenlet* target, PyObject* args) | ||||
| { | ||||
| 	/* _consumes_ a reference to the args tuple,
 | ||||
| 	   and return a new tuple reference */ | ||||
| 
 | ||||
| 	/* check ts_current */ | ||||
| 	if (!STATE_OK) { | ||||
| 		Py_DECREF(args); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (green_statedict(target) != ts_current->run_info) { | ||||
| 		PyErr_SetString(PyExc_GreenletError, | ||||
| 				"cannot switch to a different thread"); | ||||
| 		Py_DECREF(args); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	ts_passaround = args; | ||||
| 
 | ||||
| 	/* find the real target by ignoring dead greenlets,
 | ||||
| 	   and if necessary starting a greenlet. */ | ||||
| 	while (1) { | ||||
| 		if (PyGreen_ACTIVE(target)) { | ||||
| 			ts_target = target; | ||||
| 			_PyGreen_switchstack(); | ||||
| 			return ts_passaround; | ||||
| 		} | ||||
| 		if (!PyGreen_STARTED(target)) { | ||||
| 			void* dummymarker; | ||||
| 			ts_target = target; | ||||
| 			_PyGreen_initialstub(&dummymarker); | ||||
| 			return ts_passaround; | ||||
| 		} | ||||
| 		target = target->parent; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static PyObject *g_handle_exit(PyObject *result) | ||||
| { | ||||
| 	if (result == NULL && | ||||
| 	    PyErr_ExceptionMatches(PyExc_GreenletExit)) { | ||||
| 		/* catch and ignore GreenletExit */ | ||||
| 		PyObject *exc, *val, *tb; | ||||
| 		PyErr_Fetch(&exc, &val, &tb); | ||||
| 		if (val == NULL) { | ||||
| 			Py_INCREF(Py_None); | ||||
| 			val = Py_None; | ||||
| 		} | ||||
| 		result = val; | ||||
| 		Py_DECREF(exc); | ||||
| 		Py_XDECREF(tb); | ||||
| 	} | ||||
| 	if (result != NULL) { | ||||
| 		/* package the result into a 1-tuple */ | ||||
| 		PyObject* r = result; | ||||
| 		result = PyTuple_New(1); | ||||
| 		if (result) | ||||
| 			PyTuple_SET_ITEM(result, 0, r); | ||||
| 		else | ||||
| 			Py_DECREF(r); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| static void g_initialstub(void* mark) | ||||
| { | ||||
| 	int err; | ||||
| 	PyObject* o; | ||||
| 
 | ||||
| 	/* ts_target.run is the object to call in the new greenlet */ | ||||
| 	PyObject* run = PyObject_GetAttrString((PyObject*) ts_target, "run"); | ||||
| 	if (run == NULL) { | ||||
| 		Py_XDECREF(ts_passaround); | ||||
| 		ts_passaround = NULL; | ||||
| 		return; | ||||
| 	} | ||||
| 	/* now use run_info to store the statedict */ | ||||
| 	o = ts_target->run_info; | ||||
| 	ts_target->run_info = green_statedict(ts_target->parent); | ||||
| 	Py_INCREF(ts_target->run_info); | ||||
| 	Py_XDECREF(o); | ||||
| 
 | ||||
| 	/* start the greenlet */ | ||||
| 	ts_target->stack_start = NULL; | ||||
| 	ts_target->stack_stop = (char*) mark; | ||||
| 	if (ts_current->stack_start == NULL)    /* ts_current is dying */ | ||||
| 		ts_target->stack_prev = ts_current->stack_prev; | ||||
| 	else | ||||
| 		ts_target->stack_prev = ts_current; | ||||
| 	ts_target->top_frame = NULL; | ||||
| 	ts_target->recursion_depth = PyThreadState_GET()->recursion_depth; | ||||
| 	err = _PyGreen_switchstack(); | ||||
| 	/* returns twice!
 | ||||
| 	   The 1st time with err=1: we are in the new greenlet | ||||
| 	   The 2nd time with err=0: back in the caller's greenlet | ||||
| 	*/ | ||||
| 	if (err == 1) { | ||||
| 		/* in the new greenlet */ | ||||
| 		PyObject* args; | ||||
| 		PyObject* result; | ||||
| 		PyGreenlet* ts_self = ts_current; | ||||
| 		ts_self->stack_start = (char*) 1;  /* running */ | ||||
| 
 | ||||
| 		args = ts_passaround; | ||||
| 		if (args == NULL)    /* pending exception */ | ||||
| 			result = NULL; | ||||
| 		else { | ||||
| 			/* call g.run(*args) */ | ||||
| 			result = PyEval_CallObject(run, args); | ||||
| 			Py_DECREF(args); | ||||
| 		} | ||||
| 		Py_DECREF(run); | ||||
| 		result = g_handle_exit(result); | ||||
| 		/* jump back to parent */ | ||||
| 		ts_self->stack_start = NULL;  /* dead */ | ||||
| 		g_switch(ts_self->parent, result); | ||||
| 		/* must not return from here! */ | ||||
| 		PyErr_WriteUnraisable((PyObject*) ts_self); | ||||
| 		Py_FatalError("greenlets cannot continue"); | ||||
| 	} | ||||
| 	/* back in the parent */ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| static PyObject* green_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||
| { | ||||
| 	PyObject* o; | ||||
| 	if (!STATE_OK) | ||||
| 		return NULL; | ||||
| 	 | ||||
| 	o = type->tp_alloc(type, 0); | ||||
| 	if (o != NULL) { | ||||
| 		Py_INCREF(ts_current); | ||||
| 		((PyGreenlet*) o)->parent = ts_current; | ||||
| 	} | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| static int green_setrun(PyGreenlet* self, PyObject* nparent, void* c); | ||||
| static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c); | ||||
| 
 | ||||
| static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwds) | ||||
| { | ||||
| 	PyObject *run = NULL; | ||||
| 	PyObject* nparent = NULL; | ||||
| 	static char *kwlist[] = {"run", "parent", 0}; | ||||
| 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:green", kwlist, | ||||
| 					 &run, &nparent)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (run != NULL) { | ||||
| 		if (green_setrun(self, run, NULL)) | ||||
| 			return -1; | ||||
| 	} | ||||
| 	if (nparent != NULL) | ||||
| 		return green_setparent(self, nparent, NULL); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int kill_greenlet(PyGreenlet* self) | ||||
| { | ||||
| 	/* Cannot raise an exception to kill the greenlet if
 | ||||
| 	   it is not running in the same thread! */ | ||||
| 	if (self->run_info == PyThreadState_GET()->dict) { | ||||
| 		/* The dying greenlet cannot be a parent of ts_current
 | ||||
| 		   because the 'parent' field chain would hold a | ||||
| 		   reference */ | ||||
| 		PyObject* result; | ||||
| 		if (!STATE_OK) | ||||
| 			return -1; | ||||
| 		Py_INCREF(ts_current); | ||||
| 		self->parent = ts_current; | ||||
| 		/* Send the greenlet a GreenletExit exception. */ | ||||
| 		PyErr_SetNone(PyExc_GreenletExit); | ||||
| 		result = g_switch(self, NULL); | ||||
| 		if (result == NULL) | ||||
| 			return -1; | ||||
| 		Py_DECREF(result); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	else { | ||||
| 		/* Not the same thread! Temporarily save the greenlet
 | ||||
| 		   into its thread's ts_delkey list. */ | ||||
| 		PyObject* lst; | ||||
| 		lst = PyDict_GetItem(self->run_info, ts_delkey); | ||||
| 		if (lst == NULL) { | ||||
| 			lst = PyList_New(0); | ||||
| 			if (lst == NULL || PyDict_SetItem(self->run_info, | ||||
| 							  ts_delkey, lst) < 0) | ||||
| 				return -1; | ||||
| 		} | ||||
| 		if (PyList_Append(lst, (PyObject*) self) < 0) | ||||
| 			return -1; | ||||
| 		if (!STATE_OK)  /* to force ts_delkey to be reconsidered */ | ||||
| 			return -1; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void green_dealloc(PyGreenlet* self) | ||||
| { | ||||
| 	PyObject *error_type, *error_value, *error_traceback; | ||||
| 
 | ||||
| 	Py_XDECREF(self->parent); | ||||
| 	self->parent = NULL; | ||||
| 	if (PyGreen_ACTIVE(self)) { | ||||
| 		/* Hacks hacks hacks copied from instance_dealloc() */ | ||||
| 		/* Temporarily resurrect the greenlet. */ | ||||
| 		assert(self->ob_refcnt == 0); | ||||
| 		self->ob_refcnt = 1; | ||||
| 		/* Save the current exception, if any. */ | ||||
| 		PyErr_Fetch(&error_type, &error_value, &error_traceback); | ||||
| 		if (kill_greenlet(self) < 0) { | ||||
| 			PyErr_WriteUnraisable((PyObject*) self); | ||||
| 			/* XXX what else should we do? */ | ||||
| 		} | ||||
| 		/* Restore the saved exception. */ | ||||
| 		PyErr_Restore(error_type, error_value, error_traceback); | ||||
| 		/* Undo the temporary resurrection; can't use DECREF here,
 | ||||
| 		 * it would cause a recursive call. | ||||
| 		 */ | ||||
| 		assert(self->ob_refcnt > 0); | ||||
| 		--self->ob_refcnt; | ||||
| 		if (self->ob_refcnt == 0 && PyGreen_ACTIVE(self)) { | ||||
| 			/* Not resurrected, but still not dead!
 | ||||
| 			   XXX what else should we do? we complain. */ | ||||
| 			PyObject* f = PySys_GetObject("stderr"); | ||||
| 			if (f != NULL) { | ||||
| 				PyFile_WriteString("GreenletExit did not kill ", | ||||
| 						   f); | ||||
| 				PyFile_WriteObject((PyObject*) self, f, 0); | ||||
| 				PyFile_WriteString("\n", f); | ||||
| 			} | ||||
| 			Py_INCREF(self);   /* leak! */ | ||||
| 		} | ||||
| 		if (self->ob_refcnt != 0) { | ||||
| 			/* Resurrected! */ | ||||
| 			int refcnt = self->ob_refcnt; | ||||
| 			_Py_NewReference((PyObject*) self); | ||||
| 			self->ob_refcnt = refcnt; | ||||
| #ifdef COUNT_ALLOCS | ||||
| 			--self->ob_type->tp_frees; | ||||
| 			--self->ob_type->tp_allocs; | ||||
| #endif | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	if (self->weakreflist != NULL) | ||||
| 		PyObject_ClearWeakRefs((PyObject *) self); | ||||
| 	Py_XDECREF(self->run_info); | ||||
| 	self->ob_type->tp_free((PyObject*) self); | ||||
| } | ||||
| 
 | ||||
| static PyObject* single_result(PyObject* results) | ||||
| { | ||||
| 	if (results != NULL && PyTuple_Check(results) && | ||||
| 	    PyTuple_GET_SIZE(results) == 1) { | ||||
| 		PyObject *result = PyTuple_GET_ITEM(results, 0); | ||||
| 		Py_INCREF(result); | ||||
| 		Py_DECREF(results); | ||||
| 		return result; | ||||
| 	} | ||||
| 	else | ||||
| 		return results; | ||||
| } | ||||
| 
 | ||||
| static PyObject * | ||||
| throw_greenlet(PyGreenlet* self, PyObject* typ, PyObject* val, PyObject* tb) | ||||
| { | ||||
| 	/* Note: _consumes_ a reference to typ, val, tb */ | ||||
| 	PyObject *result = NULL; | ||||
| 	PyErr_Restore(typ, val, tb); | ||||
| 	if (PyGreen_STARTED(self) && !PyGreen_ACTIVE(self)) { | ||||
| 		/* dead greenlet: turn GreenletExit into a regular return */ | ||||
| 		result = g_handle_exit(result); | ||||
| 	} | ||||
| 	return single_result(g_switch(self, result)); | ||||
| } | ||||
| 
 | ||||
| PyDoc_STRVAR(switch_doc, | ||||
| "switch([val]) -> switch execution to greenlet optionally passing a value, " | ||||
| "return value passed when switching back"); | ||||
| 
 | ||||
| static PyObject* green_switch(PyGreenlet* self, PyObject* args) | ||||
| { | ||||
| 	Py_INCREF(args); | ||||
| 	return single_result(g_switch(self, args)); | ||||
| } | ||||
| 
 | ||||
| #ifndef PyExceptionClass_Check      /* Python < 2.5 */ | ||||
| # define PyExceptionClass_Check     PyClass_Check | ||||
| #endif | ||||
| #ifndef PyExceptionInstance_Check   /* Python < 2.5 */ | ||||
| # define PyExceptionInstance_Check  PyInstance_Check | ||||
| #endif | ||||
| #ifndef PyExceptionInstance_Class   /* Python < 2.5 */ | ||||
| # define PyExceptionInstance_Class(x)				\ | ||||
| 		((PyObject*)((PyInstanceObject*)(x))->in_class) | ||||
| #endif | ||||
| 
 | ||||
| PyDoc_STRVAR(throw_doc, | ||||
| "throw(typ[,val[,tb]]) -> raise exception in greenlet, return value passed " | ||||
| "when switching back"); | ||||
| 
 | ||||
| static PyObject* green_throw(PyGreenlet* self, PyObject* args) | ||||
| { | ||||
| 	PyObject *typ = PyExc_GreenletExit; | ||||
| 	PyObject *val = NULL; | ||||
| 	PyObject *tb = NULL; | ||||
| 	 | ||||
| 	if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	/* First, check the traceback argument, replacing None with
 | ||||
| 	   NULL. */ | ||||
| 	if (tb == Py_None) | ||||
| 		tb = NULL; | ||||
| 	else if (tb != NULL && !PyTraceBack_Check(tb)) { | ||||
| 		PyErr_SetString(PyExc_TypeError, | ||||
| 			"throw() third argument must be a traceback object"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	Py_INCREF(typ); | ||||
| 	Py_XINCREF(val); | ||||
| 	Py_XINCREF(tb); | ||||
| 
 | ||||
| 	if (PyExceptionClass_Check(typ)) { | ||||
| 		PyErr_NormalizeException(&typ, &val, &tb); | ||||
| 	} | ||||
| 
 | ||||
| 	else if (PyExceptionInstance_Check(typ)) { | ||||
| 		/* Raising an instance.  The value should be a dummy. */ | ||||
| 		if (val && val != Py_None) { | ||||
| 			PyErr_SetString(PyExc_TypeError, | ||||
| 			  "instance exception may not have a separate value"); | ||||
| 			goto failed_throw; | ||||
| 		} | ||||
| 		else { | ||||
| 			/* Normalize to raise <class>, <instance> */ | ||||
| 			Py_XDECREF(val); | ||||
| 			val = typ; | ||||
| 			typ = PyExceptionInstance_Class(typ); | ||||
| 			Py_INCREF(typ); | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		/* Not something you can raise.  throw() fails. */ | ||||
| 		PyErr_Format(PyExc_TypeError, | ||||
| 			     "exceptions must be classes, or instances, not %s", | ||||
| 			     typ->ob_type->tp_name); | ||||
| 		goto failed_throw; | ||||
| 	} | ||||
| 
 | ||||
| 	return throw_greenlet(self, typ, val, tb); | ||||
| 
 | ||||
| failed_throw: | ||||
| 	/* Didn't use our arguments, so restore their original refcounts */ | ||||
| 	Py_DECREF(typ); | ||||
| 	Py_XDECREF(val); | ||||
| 	Py_XDECREF(tb); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static int green_nonzero(PyGreenlet* self) | ||||
| { | ||||
| 	return PyGreen_ACTIVE(self); | ||||
| } | ||||
| 
 | ||||
| static PyObject* green_getdead(PyGreenlet* self, void* c) | ||||
| { | ||||
| 	PyObject* res; | ||||
| 	if (PyGreen_ACTIVE(self) || !PyGreen_STARTED(self)) | ||||
| 		res = Py_False; | ||||
| 	else | ||||
| 		res = Py_True; | ||||
| 	Py_INCREF(res); | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| static PyObject* green_getrun(PyGreenlet* self, void* c) | ||||
| { | ||||
| 	if (PyGreen_STARTED(self) || self->run_info == NULL) { | ||||
| 		PyErr_SetString(PyExc_AttributeError, "run"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	Py_INCREF(self->run_info); | ||||
| 	return self->run_info; | ||||
| } | ||||
| 
 | ||||
| static int green_setrun(PyGreenlet* self, PyObject* nrun, void* c) | ||||
| { | ||||
| 	PyObject* o; | ||||
| 	if (PyGreen_STARTED(self)) { | ||||
| 		PyErr_SetString(PyExc_AttributeError, | ||||
| 				"run cannot be set " | ||||
| 				"after the start of the greenlet"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	o = self->run_info; | ||||
| 	self->run_info = nrun; | ||||
| 	Py_XINCREF(nrun); | ||||
| 	Py_XDECREF(o); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static PyObject* green_getparent(PyGreenlet* self, void* c) | ||||
| { | ||||
| 	PyObject* result = self->parent ? (PyObject*) self->parent : Py_None; | ||||
| 	Py_INCREF(result); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c) | ||||
| { | ||||
| 	PyGreenlet* p; | ||||
| 	if (nparent == NULL) { | ||||
| 		PyErr_SetString(PyExc_AttributeError, "can't delete attribute"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!PyGreen_Check(nparent)) { | ||||
| 		PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	for (p=(PyGreenlet*) nparent; p; p=p->parent) { | ||||
| 		if (p == self) { | ||||
| 			PyErr_SetString(PyExc_ValueError, "cyclic parent chain"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 	p = self->parent; | ||||
| 	self->parent = (PyGreenlet*) nparent; | ||||
| 	Py_INCREF(nparent); | ||||
| 	Py_DECREF(p); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static PyObject* green_getframe(PyGreenlet* self, void* c) | ||||
| { | ||||
| 	PyObject* result = self->top_frame ? (PyObject*) self->top_frame : Py_None; | ||||
| 	Py_INCREF(result); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| /* C interface */ | ||||
| 
 | ||||
| PyObject* PyGreen_New(PyObject* run, PyObject* parent) | ||||
| { | ||||
| 	PyGreenlet* o; | ||||
| 	if (!PyGreen_Check(parent)) { | ||||
| 		PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	o = (PyGreenlet*) PyType_GenericAlloc(&PyGreen_Type, 0); | ||||
| 	if (o == NULL) | ||||
| 		return NULL; | ||||
| 	Py_INCREF(run); | ||||
| 	o->run_info = run; | ||||
| 	Py_INCREF(parent); | ||||
| 	o->parent = (PyGreenlet*) parent; | ||||
| 	return (PyObject*) o; | ||||
| } | ||||
| 
 | ||||
| PyObject* PyGreen_Current(void) | ||||
| { | ||||
| 	if (!STATE_OK) | ||||
| 		return NULL; | ||||
| 	return (PyObject*) ts_current; | ||||
| } | ||||
| 
 | ||||
| PyObject* PyGreen_Switch(PyObject* g, PyObject* value) | ||||
| { | ||||
| 	PyGreenlet *self; | ||||
| 	if (!PyGreen_Check(g)) { | ||||
| 		PyErr_BadInternalCall(); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	self = (PyGreenlet*) g; | ||||
| 	Py_XINCREF(value); | ||||
| 	if (PyGreen_STARTED(self) && !PyGreen_ACTIVE(self)) | ||||
| 		value = g_handle_exit(value); | ||||
| 	return single_result(g_switch(self, value)); | ||||
| } | ||||
| 
 | ||||
| int PyGreen_SetParent(PyObject* g, PyObject* nparent) | ||||
| { | ||||
| 	if (!PyGreen_Check(g)) { | ||||
| 		PyErr_BadInternalCall(); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return green_setparent((PyGreenlet*) g, nparent, NULL); | ||||
| } | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| static PyMethodDef green_methods[] = { | ||||
| 	{"switch", (PyCFunction)green_switch, METH_VARARGS, switch_doc}, | ||||
| 	{"throw",  (PyCFunction)green_throw,  METH_VARARGS, throw_doc}, | ||||
| 	{NULL,     NULL}		/* sentinel */ | ||||
| }; | ||||
| 
 | ||||
| static PyGetSetDef green_getsets[] = { | ||||
| 	{"run",    (getter)green_getrun, | ||||
| 		   (setter)green_setrun, /*XXX*/ NULL}, | ||||
| 	{"parent", (getter)green_getparent, | ||||
| 		   (setter)green_setparent, /*XXX*/ NULL}, | ||||
| 	{"gr_frame", (getter)green_getframe, | ||||
| 	             NULL, /*XXX*/ NULL}, | ||||
| 	{"dead",   (getter)green_getdead, | ||||
| 	             NULL, /*XXX*/ NULL}, | ||||
| 	{NULL} | ||||
| }; | ||||
| 
 | ||||
| static PyNumberMethods green_as_number = { | ||||
| 	NULL,		/* nb_add */ | ||||
| 	NULL,		/* nb_subtract */ | ||||
| 	NULL,		/* nb_multiply */ | ||||
| 	NULL,		/* nb_divide */ | ||||
| 	NULL,		/* nb_remainder */ | ||||
| 	NULL,		/* nb_divmod */ | ||||
| 	NULL,		/* nb_power */ | ||||
| 	NULL,		/* nb_negative */ | ||||
| 	NULL,		/* nb_positive */ | ||||
| 	NULL,		/* nb_absolute */ | ||||
| 	(inquiry)green_nonzero,	/* nb_nonzero */ | ||||
| }; | ||||
| 
 | ||||
| PyTypeObject PyGreen_Type = { | ||||
| 	PyObject_HEAD_INIT(NULL) | ||||
| 	0, | ||||
| 	"greenlet.greenlet", | ||||
| 	sizeof(PyGreenlet), | ||||
| 	0, | ||||
| 	(destructor)green_dealloc,		/* tp_dealloc */ | ||||
| 	0,					/* tp_print */ | ||||
| 	0,					/* tp_getattr */ | ||||
| 	0,					/* tp_setattr */ | ||||
| 	0,					/* tp_compare */ | ||||
| 	0,					/* tp_repr */ | ||||
| 	&green_as_number,			/* tp_as _number*/ | ||||
| 	0,					/* tp_as _sequence*/ | ||||
| 	0,					/* tp_as _mapping*/ | ||||
| 	0, 					/* tp_hash */ | ||||
| 	0,					/* tp_call */ | ||||
| 	0,					/* tp_str */ | ||||
| 	0,					/* tp_getattro */ | ||||
| 	0,					/* tp_setattro */ | ||||
| 	0,					/* tp_as_buffer*/ | ||||
| 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */ | ||||
| 	"greenlet(run=None, parent=None)\n\
 | ||||
| Create a new greenlet object (without running it).  \"run\" is the\n\
 | ||||
| callable to invoke, and \"parent\" is the parent greenlet, which\n\
 | ||||
| defaults to the current greenlet.",		/* tp_doc */ | ||||
|  	0,					/* tp_traverse */ | ||||
| 	0,					/* tp_clear */ | ||||
| 	0,					/* tp_richcompare */ | ||||
| 	offsetof(PyGreenlet, weakreflist),	/* tp_weaklistoffset */ | ||||
| 	0,					/* tp_iter */ | ||||
| 	0,					/* tp_iternext */ | ||||
| 	green_methods,				/* tp_methods */ | ||||
| 	0,					/* tp_members */ | ||||
| 	green_getsets,				/* tp_getset */ | ||||
| 	0,					/* tp_base */ | ||||
| 	0,					/* tp_dict */ | ||||
| 	0,					/* tp_descr_get */ | ||||
| 	0,					/* tp_descr_set */ | ||||
| 	0,					/* tp_dictoffset */ | ||||
| 	(initproc)green_init,			/* tp_init */ | ||||
| 	0,					/* tp_alloc */ | ||||
| 	green_new,				/* tp_new */ | ||||
| }; | ||||
| /* XXX need GC support */ | ||||
| 
 | ||||
| 
 | ||||
| static PyObject* mod_getcurrent(PyObject* self) | ||||
| { | ||||
| 	if (!STATE_OK) | ||||
| 		return NULL; | ||||
| 	Py_INCREF(ts_current); | ||||
| 	return (PyObject*) ts_current; | ||||
| } | ||||
| 
 | ||||
| static PyMethodDef GreenMethods[] = { | ||||
| 	{"getcurrent", (PyCFunction)mod_getcurrent, METH_NOARGS, | ||||
| 	"greenlet.getcurrent()\n\
 | ||||
| Returns the current greenlet (i.e. the one which called this\n\ | ||||
| function)."}, | ||||
| 	{NULL,     NULL}	/* Sentinel */ | ||||
| }; | ||||
| 
 | ||||
| static char* copy_on_greentype[] = { | ||||
| 	"getcurrent", "error", "GreenletExit", NULL | ||||
| }; | ||||
| 
 | ||||
| void initgreenlet(void) | ||||
| { | ||||
| 	PyObject* m; | ||||
| 	PyObject* greenletexit_doc; | ||||
| 	PyObject* greenletexit_dict; | ||||
| 	PyObject* greenleterror_doc; | ||||
| 	PyObject* greenleterror_dict; | ||||
| 	int error; | ||||
| 	char** p; | ||||
| 	_PyGreen_switchstack = g_switchstack; | ||||
| 	_PyGreen_slp_switch = slp_switch; | ||||
| 	_PyGreen_initialstub = g_initialstub; | ||||
| 	m = Py_InitModule("greenlet", GreenMethods); | ||||
| 
 | ||||
| 	ts_curkey = PyString_InternFromString("__greenlet_ts_curkey"); | ||||
| 	ts_delkey = PyString_InternFromString("__greenlet_ts_delkey"); | ||||
| 	if (ts_curkey == NULL || ts_delkey == NULL) | ||||
| 		return; | ||||
| 	if (PyType_Ready(&PyGreen_Type) < 0) | ||||
| 		return; | ||||
| 
 | ||||
|         greenleterror_dict = PyDict_New(); | ||||
| 	if (greenleterror_dict == NULL) | ||||
| 		return; | ||||
| 	greenleterror_doc = PyString_FromString("internal greenlet error"); | ||||
| 	if (greenleterror_doc == NULL) { | ||||
|                 Py_DECREF(greenleterror_dict); | ||||
| 		return; | ||||
|         } | ||||
| 
 | ||||
|         error = PyDict_SetItemString(greenleterror_dict, "__doc__", greenleterror_doc); | ||||
| 	Py_DECREF(greenleterror_doc); | ||||
| 	if (error == -1) { | ||||
|                 Py_DECREF(greenleterror_dict); | ||||
| 		return; | ||||
|         } | ||||
| 
 | ||||
| 	PyExc_GreenletError = PyErr_NewException("py.magic.greenlet.error", NULL, greenleterror_dict); | ||||
|         Py_DECREF(greenleterror_dict); | ||||
| 	if (PyExc_GreenletError == NULL) | ||||
| 		return; | ||||
| 
 | ||||
|         greenletexit_dict = PyDict_New(); | ||||
| 	if (greenletexit_dict == NULL) | ||||
| 		return; | ||||
| 	greenletexit_doc = PyString_FromString("greenlet.GreenletExit\n\
 | ||||
| This special exception does not propagate to the parent greenlet; it\n\ | ||||
| can be used to kill a single greenlet.\n"); | ||||
| 	if (greenletexit_doc == NULL) { | ||||
|                 Py_DECREF(greenletexit_dict); | ||||
| 		return; | ||||
|         } | ||||
| 
 | ||||
|         error = PyDict_SetItemString(greenletexit_dict, "__doc__", greenletexit_doc); | ||||
| 	Py_DECREF(greenletexit_doc); | ||||
| 	if (error == -1) { | ||||
|                 Py_DECREF(greenletexit_dict); | ||||
| 		return; | ||||
|         } | ||||
| 
 | ||||
| 	PyExc_GreenletExit = PyErr_NewException("py.magic.greenlet.GreenletExit", | ||||
| 						NULL, greenletexit_dict); | ||||
|         Py_DECREF(greenletexit_dict); | ||||
| 	if (PyExc_GreenletExit == NULL) | ||||
| 		return; | ||||
| 
 | ||||
| 	ts_current = green_create_main(); | ||||
| 	if (ts_current == NULL) | ||||
| 		return; | ||||
| 
 | ||||
| 	Py_INCREF(&PyGreen_Type); | ||||
| 	PyModule_AddObject(m, "greenlet", (PyObject*) &PyGreen_Type); | ||||
| 	Py_INCREF(PyExc_GreenletError); | ||||
| 	PyModule_AddObject(m, "error", PyExc_GreenletError); | ||||
| 	Py_INCREF(PyExc_GreenletExit); | ||||
| 	PyModule_AddObject(m, "GreenletExit", PyExc_GreenletExit); | ||||
| 
 | ||||
| 	/* also publish module-level data as attributes of the greentype. */ | ||||
| 	for (p=copy_on_greentype; *p; p++) { | ||||
| 		PyObject* o = PyObject_GetAttrString(m, *p); | ||||
| 		if (!o) continue; | ||||
| 		PyDict_SetItemString(PyGreen_Type.tp_dict, *p, o); | ||||
| 		Py_DECREF(o); | ||||
| 	} | ||||
| } | ||||
|  | @ -1,41 +0,0 @@ | |||
| 
 | ||||
| /* Greenlet object interface */ | ||||
| 
 | ||||
| #ifndef Py_GREENLETOBJECT_H | ||||
| #define Py_GREENLETOBJECT_H | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| #include <Python.h> | ||||
| 
 | ||||
| typedef struct _greenlet { | ||||
| 	PyObject_HEAD | ||||
| 	char* stack_start; | ||||
| 	char* stack_stop; | ||||
| 	char* stack_copy; | ||||
| 	long stack_saved; | ||||
| 	struct _greenlet* stack_prev; | ||||
| 	struct _greenlet* parent; | ||||
| 	PyObject* run_info; | ||||
| 	struct _frame* top_frame; | ||||
| 	int recursion_depth; | ||||
| 	PyObject* weakreflist; | ||||
| } PyGreenlet; | ||||
| 
 | ||||
| extern PyTypeObject PyGreen_Type; | ||||
| 
 | ||||
| #define PyGreen_Check(op)      PyObject_TypeCheck(op, &PyGreen_Type) | ||||
| #define PyGreen_STARTED(op)    (((PyGreenlet*)(op))->stack_stop != NULL) | ||||
| #define PyGreen_ACTIVE(op)     (((PyGreenlet*)(op))->stack_start != NULL) | ||||
| #define PyGreen_GET_PARENT(op) (((PyGreenlet*)(op))->parent) | ||||
| 
 | ||||
| PyObject* PyGreen_New(PyObject* run, PyObject* parent); | ||||
| PyObject* PyGreen_Current(void); | ||||
| PyObject* PyGreen_Switch(PyObject* g, PyObject* args);  /* g.switch(*args) */ | ||||
| int PyGreen_SetParent(PyObject* g, PyObject* nparent);  /* g.parent = ... */ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| #endif /* !Py_GREENLETOBJECT_H */ | ||||
|  | @ -1,8 +0,0 @@ | |||
| from distutils.core import setup | ||||
| from distutils.extension import Extension | ||||
| 
 | ||||
| setup (	name             = "greenlet", | ||||
|       	version          = "0.1", | ||||
|       	ext_modules=[Extension(name = 'greenlet', | ||||
|                                sources = ['greenlet.c'])] | ||||
|         ) | ||||
|  | @ -1,23 +0,0 @@ | |||
| /*
 | ||||
|  * Platform Selection for Stackless Python | ||||
|  */ | ||||
| 
 | ||||
| #if   defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) | ||||
| #include "switch_x86_msvc.h" /* MS Visual Studio on X86 */ | ||||
| #elif defined(__GNUC__) && defined(__amd64__) | ||||
| #include "switch_amd64_unix.h" /* gcc on amd64 */ | ||||
| #elif defined(__GNUC__) && defined(__i386__) | ||||
| #include "switch_x86_unix.h" /* gcc on X86 */ | ||||
| #elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__) | ||||
| #include "switch_ppc_unix.h" /* gcc on PowerPC */ | ||||
| #elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) | ||||
| #include "switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */ | ||||
| #elif defined(__GNUC__) && defined(sparc) && defined(sun) | ||||
| #include "switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ | ||||
| #elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) | ||||
| #include "switch_s390_unix.h"	/* Linux/S390 */ | ||||
| #elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) | ||||
| #include "switch_s390_unix.h"	/* Linux/S390 zSeries (identical) */ | ||||
| #elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) | ||||
| #include "switch_mips_unix.h"	/* Linux/MIPS */ | ||||
| #endif | ||||
|  | @ -1,66 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 01-Apr-04  Hye-Shik Chang    <perky@FreeBSD.org> | ||||
|  *      Ported from i386 to amd64. | ||||
|  * 24-Nov-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      needed to add another magic constant to insure | ||||
|  *      that f in slp_eval_frame(PyFrameObject *f) | ||||
|  *      STACK_REFPLUS will probably be 1 in most cases. | ||||
|  *      gets included into the saved stack area. | ||||
|  * 17-Sep-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      after virtualizing stack save/restore, the | ||||
|  *      stack size shrunk a bit. Needed to introduce | ||||
|  *      an adjustment STACK_MAGIC per platform. | ||||
|  * 15-Sep-02  Gerd Woetzel       <gerd.woetzel@GMD.DE> | ||||
|  *      slightly changed framework for spark | ||||
|  * 31-Avr-02  Armin Rigo         <arigo@ulb.ac.be> | ||||
|  *      Added ebx, esi and edi register-saves. | ||||
|  * 01-Mar-02  Samual M. Rushing  <rushing@ironport.com> | ||||
|  *      Ported from i386. | ||||
|  */ | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| /* #define STACK_MAGIC 3 */ | ||||
| /* the above works fine with gcc 2.96, but 2.95.3 wants this */ | ||||
| #define STACK_MAGIC 0 | ||||
| 
 | ||||
| #define REGS_TO_SAVE "rdx", "rbx", "r12", "r13", "r14", "r15" | ||||
| 
 | ||||
| 
 | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register long *stackref, stsizediff; | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     __asm__ ("movq %%rsp, %0" : "=g" (stackref)); | ||||
|     { | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
|         __asm__ volatile ( | ||||
|             "addq %0, %%rsp\n" | ||||
|             "addq %0, %%rbp\n" | ||||
|             : | ||||
|             : "r" (stsizediff) | ||||
|             ); | ||||
|         SLP_RESTORE_STATE(); | ||||
|     } | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * if you want to add self-inspection tools, place them | ||||
|  * here. See the x86_msvc for the necessary defines. | ||||
|  * These features are highly experimental und not | ||||
|  * essential yet. | ||||
|  */ | ||||
|  | @ -1,56 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 05-Jan-08 Thiemo Seufer  <ths@debian.org> | ||||
|  *      Ported from ppc. | ||||
|  */ | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| #define STACK_MAGIC 0 | ||||
| 
 | ||||
| #ifdef __mips64 | ||||
| #define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ | ||||
|        "$23", "$28", "$30" | ||||
| #else | ||||
| #define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ | ||||
|        "$23", "$30" | ||||
| #endif | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register int *stackref, stsizediff; | ||||
|     __asm__ __volatile__ ("" : : : REGS_TO_SAVE); | ||||
|     __asm__ ("move %0, $29" : "=r" (stackref) : ); | ||||
|     { | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
|         __asm__ __volatile__ ( | ||||
| #ifdef __mips64 | ||||
|             "daddu $29, %0\n" | ||||
| #else | ||||
|             "addu $29, %0\n" | ||||
| #endif | ||||
|             : /* no outputs */ | ||||
|             : "r" (stsizediff) | ||||
|             ); | ||||
|         SLP_RESTORE_STATE(); | ||||
|     } | ||||
|     __asm__ __volatile__ ("" : : : REGS_TO_SAVE); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * if you want to add self-inspection tools, place them | ||||
|  * here. See the x86_msvc for the necessary defines. | ||||
|  * These features are highly experimental und not | ||||
|  * essential yet. | ||||
|  */ | ||||
|  | @ -1,80 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 07-Sep-05 (py-dev mailing list discussion) | ||||
|  *      removed 'r31' from the register-saved.  !!!! WARNING !!!! | ||||
|  *      It means that this file can no longer be compiled statically! | ||||
|  *      It is now only suitable as part of a dynamic library! | ||||
|  * 14-Jan-04  Bob Ippolito <bob@redivi.com> | ||||
|  *      added cr2-cr4 to the registers to be saved. | ||||
|  *      Open questions: Should we save FP registers? | ||||
|  *      What about vector registers? | ||||
|  *      Differences between darwin and unix? | ||||
|  * 24-Nov-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      needed to add another magic constant to insure | ||||
|  *      that f in slp_eval_frame(PyFrameObject *f) | ||||
|  *      STACK_REFPLUS will probably be 1 in most cases. | ||||
|  *      gets included into the saved stack area. | ||||
|  * 17-Sep-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      after virtualizing stack save/restore, the | ||||
|  *      stack size shrunk a bit. Needed to introduce | ||||
|  *      an adjustment STACK_MAGIC per platform. | ||||
|  * 15-Sep-02  Gerd Woetzel       <gerd.woetzel@GMD.DE> | ||||
|  *      slightly changed framework for sparc | ||||
|  * 29-Jun-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      Added register 13-29, 31 saves. The same way as | ||||
|  *      Armin Rigo did for the x86_unix version. | ||||
|  *      This seems to be now fully functional! | ||||
|  * 04-Mar-02  Hye-Shik Chang  <perky@fallin.lv> | ||||
|  *      Ported from i386. | ||||
|  */ | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| #define STACK_MAGIC 3 | ||||
| 
 | ||||
| /* !!!!WARNING!!!! need to add "r31" in the next line if this header file
 | ||||
|  * is meant to be compiled non-dynamically! | ||||
|  */ | ||||
| #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ | ||||
|        "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ | ||||
|        "cr2", "cr3", "cr4" | ||||
| 
 | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register int *stackref, stsizediff; | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); | ||||
|     { | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
|         __asm__ volatile ( | ||||
|             "; asm block 3\n" | ||||
|             "\tmr r11, %0\n" | ||||
|             "\tadd r1, r1, r11\n" | ||||
|             "\tadd r30, r30, r11\n" | ||||
|             : /* no outputs */ | ||||
|             : "g" (stsizediff) | ||||
|             : "r11" | ||||
|             ); | ||||
|         SLP_RESTORE_STATE(); | ||||
|     } | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * if you want to add self-inspection tools, place them | ||||
|  * here. See the x86_msvc for the necessary defines. | ||||
|  * These features are highly experimental und not | ||||
|  * essential yet. | ||||
|  */ | ||||
|  | @ -1,80 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 07-Sep-05 (py-dev mailing list discussion) | ||||
|  *      removed 'r31' from the register-saved.  !!!! WARNING !!!! | ||||
|  *      It means that this file can no longer be compiled statically! | ||||
|  *      It is now only suitable as part of a dynamic library! | ||||
|  * 14-Jan-04  Bob Ippolito <bob@redivi.com> | ||||
|  *      added cr2-cr4 to the registers to be saved. | ||||
|  *      Open questions: Should we save FP registers? | ||||
|  *      What about vector registers? | ||||
|  *      Differences between darwin and unix? | ||||
|  * 24-Nov-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      needed to add another magic constant to insure | ||||
|  *      that f in slp_eval_frame(PyFrameObject *f) | ||||
|  *      STACK_REFPLUS will probably be 1 in most cases. | ||||
|  *      gets included into the saved stack area. | ||||
|  * 04-Oct-02  Gustavo Niemeyer <niemeyer@conectiva.com> | ||||
|  *      Ported from MacOS version. | ||||
|  * 17-Sep-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      after virtualizing stack save/restore, the | ||||
|  *      stack size shrunk a bit. Needed to introduce | ||||
|  *      an adjustment STACK_MAGIC per platform. | ||||
|  * 15-Sep-02  Gerd Woetzel       <gerd.woetzel@GMD.DE> | ||||
|  *      slightly changed framework for sparc | ||||
|  * 29-Jun-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      Added register 13-29, 31 saves. The same way as | ||||
|  *      Armin Rigo did for the x86_unix version. | ||||
|  *      This seems to be now fully functional! | ||||
|  * 04-Mar-02  Hye-Shik Chang  <perky@fallin.lv> | ||||
|  *      Ported from i386. | ||||
|  */ | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| #define STACK_MAGIC 3 | ||||
| 
 | ||||
| /* !!!!WARNING!!!! need to add "r31" in the next line if this header file
 | ||||
|  * is meant to be compiled non-dynamically! | ||||
|  */ | ||||
| #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ | ||||
|        "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ | ||||
|        "cr2", "cr3", "cr4" | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register int *stackref, stsizediff; | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     __asm__ ("mr %0, 1" : "=g" (stackref) : ); | ||||
|     { | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
|         __asm__ volatile ( | ||||
|             "mr 11, %0\n" | ||||
|             "add 1, 1, 11\n" | ||||
|             "add 30, 30, 11\n" | ||||
|             : /* no outputs */ | ||||
|             : "g" (stsizediff) | ||||
|             : "11" | ||||
|             ); | ||||
|         SLP_RESTORE_STATE(); | ||||
|     } | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * if you want to add self-inspection tools, place them | ||||
|  * here. See the x86_msvc for the necessary defines. | ||||
|  * These features are highly experimental und not | ||||
|  * essential yet. | ||||
|  */ | ||||
|  | @ -1,54 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 24-Nov-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      needed to add another magic constant to insure | ||||
|  *      that f in slp_eval_frame(PyFrameObject *f) | ||||
|  *      STACK_REFPLUS will probably be 1 in most cases. | ||||
|  *      gets included into the saved stack area. | ||||
|  * 06-Oct-02  Gustavo Niemeyer <niemeyer@conectiva.com> | ||||
|  *      Ported to Linux/S390. | ||||
|  */ | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| #define STACK_MAGIC 0 | ||||
| 
 | ||||
| #define REGS_TO_SAVE "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r14", \ | ||||
| 		     "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ | ||||
| 		     "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" | ||||
| 
 | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register int *stackref, stsizediff; | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     __asm__ ("lr %0, 15" : "=g" (stackref) : ); | ||||
|     { | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
|         __asm__ volatile ( | ||||
|             "ar 15, %0" | ||||
|             : /* no outputs */ | ||||
|             : "g" (stsizediff) | ||||
|             ); | ||||
|         SLP_RESTORE_STATE(); | ||||
|     } | ||||
|     __asm__ volatile ("" : : : REGS_TO_SAVE); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * if you want to add self-inspection tools, place them | ||||
|  * here. See the x86_msvc for the necessary defines. | ||||
|  * These features are highly experimental und not | ||||
|  * essential yet. | ||||
|  */ | ||||
|  | @ -1,85 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 24-Nov-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      needed to add another magic constant to insure | ||||
|  *      that f in slp_eval_frame(PyFrameObject *f) | ||||
|  *      STACK_REFPLUS will probably be 1 in most cases. | ||||
|  *      gets included into the saved stack area. | ||||
|  * 17-Sep-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      after virtualizing stack save/restore, the | ||||
|  *      stack size shrunk a bit. Needed to introduce | ||||
|  *      an adjustment STACK_MAGIC per platform. | ||||
|  * 15-Sep-02  Gerd Woetzel       <gerd.woetzel@GMD.DE> | ||||
|  *      added support for SunOS sparc with gcc | ||||
|  */ | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| #include <sys/trap.h> | ||||
| 
 | ||||
| #define STACK_MAGIC 0 | ||||
| 
 | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register int *stackref, stsizediff; | ||||
| 
 | ||||
|     /* Put the stack pointer into stackref */ | ||||
| 
 | ||||
|     /* Sparc special: at first, flush register windows
 | ||||
|      */ | ||||
|     __asm__ volatile ( | ||||
|         "ta %1\n\t" | ||||
|         "mov %%sp, %0" | ||||
|         : "=r" (stackref) :  "i" (ST_FLUSH_WINDOWS)); | ||||
| 
 | ||||
|     {   /* You shalt put SLP_SAVE_STATE into a local block */ | ||||
| 
 | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
| 
 | ||||
|         /* Increment stack and frame pointer by stsizediff */ | ||||
| 
 | ||||
|         /* Sparc special: at first load new return address.
 | ||||
|            This cannot be done later, because the stack | ||||
|            might be overwritten again just after SLP_RESTORE_STATE | ||||
|            has finished. BTW: All other registers (l0-l7 and i0-i5) | ||||
|            might be clobbered too.  | ||||
|          */ | ||||
|         __asm__ volatile ( | ||||
|         "ld [%0+60], %%i7\n\t" | ||||
|         "add %1, %%sp, %%sp\n\t" | ||||
|         "add %1, %%fp, %%fp" | ||||
|         : : "r" (_cst->stack), "r" (stsizediff) | ||||
|         : "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", | ||||
|           "%i0", "%i1", "%i2", "%i3", "%i4", "%i5"); | ||||
| 
 | ||||
|         SLP_RESTORE_STATE(); | ||||
| 
 | ||||
|         /* Run far away as fast as possible, don't look back at the sins.
 | ||||
|          * The LORD rained down burning sulfur on Sodom and Gomorra ... | ||||
|          */ | ||||
| 
 | ||||
|         /* Sparc special: Must make it *very* clear to the CPU that
 | ||||
|            it shouldn't look back into the register windows | ||||
|          */ | ||||
|         __asm__ volatile ( "ta %0" : : "i" (ST_CLEAN_WINDOWS)); | ||||
|         return 0; | ||||
|     }  | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * if you want to add self-inspection tools, place them | ||||
|  * here. See the x86_msvc for the necessary defines. | ||||
|  * These features are highly experimental und not | ||||
|  * essential yet. | ||||
|  */ | ||||
|  | @ -1,73 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 24-Nov-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      needed to add another magic constant to insure | ||||
|  *      that f in slp_eval_frame(PyFrameObject *f) | ||||
|  *      STACK_REFPLUS will probably be 1 in most cases. | ||||
|  *      gets included into the saved stack area. | ||||
|  * 26-Sep-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      again as a result of virtualized stack access, | ||||
|  *      the compiler used less registers. Needed to | ||||
|  *      explicit mention registers in order to get them saved. | ||||
|  *      Thanks to Jeff Senn for pointing this out and help. | ||||
|  * 17-Sep-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      after virtualizing stack save/restore, the | ||||
|  *      stack size shrunk a bit. Needed to introduce | ||||
|  *      an adjustment STACK_MAGIC per platform. | ||||
|  * 15-Sep-02  Gerd Woetzel       <gerd.woetzel@GMD.DE> | ||||
|  *      slightly changed framework for sparc | ||||
|  * 01-Mar-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      Initial final version after lots of iterations for i386. | ||||
|  */ | ||||
| 
 | ||||
| #define alloca _alloca | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| #define STACK_MAGIC 0 | ||||
| 
 | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register int *stackref, stsizediff; | ||||
|     __asm mov stackref, esp; | ||||
| 	/* modify EBX, ESI and EDI in order to get them preserved */ | ||||
| 	__asm mov ebx, ebx; | ||||
| 	__asm xchg esi, edi; | ||||
|     { | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
|         __asm { | ||||
|             mov     eax, stsizediff | ||||
|             add     esp, eax | ||||
|             add     ebp, eax | ||||
|         } | ||||
|         SLP_RESTORE_STATE(); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /* we have IsBadReadPtr available, so we can peek at objects */ | ||||
| #define STACKLESS_SPY | ||||
| 
 | ||||
| #ifdef IMPLEMENT_STACKLESSMODULE | ||||
| #include "Windows.h" | ||||
| #define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) | ||||
| 
 | ||||
| static int IS_ON_STACK(void*p) | ||||
| { | ||||
|     int stackref; | ||||
|     int stackbase = ((int)&stackref) & 0xfffff000; | ||||
|     return (int)p >= stackbase && (int)p < stackbase + 0x00100000; | ||||
| }  | ||||
| 
 | ||||
| #endif | ||||
|  | @ -1,69 +0,0 @@ | |||
| /*
 | ||||
|  * this is the internal transfer function. | ||||
|  * | ||||
|  * HISTORY | ||||
|  * 07-Sep-05 (py-dev mailing list discussion) | ||||
|  *      removed 'ebx' from the register-saved.  !!!! WARNING !!!! | ||||
|  *      It means that this file can no longer be compiled statically! | ||||
|  *      It is now only suitable as part of a dynamic library! | ||||
|  * 24-Nov-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      needed to add another magic constant to insure | ||||
|  *      that f in slp_eval_frame(PyFrameObject *f) | ||||
|  *      STACK_REFPLUS will probably be 1 in most cases. | ||||
|  *      gets included into the saved stack area. | ||||
|  * 17-Sep-02  Christian Tismer  <tismer@tismer.com> | ||||
|  *      after virtualizing stack save/restore, the | ||||
|  *      stack size shrunk a bit. Needed to introduce | ||||
|  *      an adjustment STACK_MAGIC per platform. | ||||
|  * 15-Sep-02  Gerd Woetzel       <gerd.woetzel@GMD.DE> | ||||
|  *      slightly changed framework for spark | ||||
|  * 31-Avr-02  Armin Rigo         <arigo@ulb.ac.be> | ||||
|  *      Added ebx, esi and edi register-saves. | ||||
|  * 01-Mar-02  Samual M. Rushing  <rushing@ironport.com> | ||||
|  *      Ported from i386. | ||||
|  */ | ||||
| 
 | ||||
| #define STACK_REFPLUS 1 | ||||
| 
 | ||||
| #ifdef SLP_EVAL | ||||
| 
 | ||||
| /* #define STACK_MAGIC 3 */ | ||||
| /* the above works fine with gcc 2.96, but 2.95.3 wants this */ | ||||
| #define STACK_MAGIC 0 | ||||
| 
 | ||||
| static int | ||||
| slp_switch(void) | ||||
| { | ||||
|     register int *stackref, stsizediff; | ||||
|     /* !!!!WARNING!!!! need to add "ebx" in the next line, as well as in the
 | ||||
|      * last line of this function, if this header file is meant to be compiled | ||||
|      * non-dynamically! | ||||
|      */ | ||||
|     __asm__ volatile ("" : : : "esi", "edi"); | ||||
|     __asm__ ("movl %%esp, %0" : "=g" (stackref)); | ||||
|     { | ||||
|         SLP_SAVE_STATE(stackref, stsizediff); | ||||
|         __asm__ volatile ( | ||||
|             "addl %0, %%esp\n" | ||||
|             "addl %0, %%ebp\n" | ||||
|             : | ||||
|             : "r" (stsizediff) | ||||
|             ); | ||||
|         SLP_RESTORE_STATE(); | ||||
|     } | ||||
|     __asm__ volatile ("" : : : "esi", "edi"); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * further self-processing support | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * if you want to add self-inspection tools, place them | ||||
|  * here. See the x86_msvc for the necessary defines. | ||||
|  * These features are highly experimental und not | ||||
|  * essential yet. | ||||
|  */ | ||||
|  | @ -1,58 +0,0 @@ | |||
| import py | ||||
| try: | ||||
|     from py.magic import greenlet | ||||
| except (ImportError, RuntimeError), e: | ||||
|     py.test.skip(str(e)) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class genlet(greenlet): | ||||
| 
 | ||||
|     def __init__(self, *args, **kwds): | ||||
|         self.args = args | ||||
|         self.kwds = kwds | ||||
| 
 | ||||
|     def run(self): | ||||
|         fn, = self.fn | ||||
|         fn(*self.args, **self.kwds) | ||||
| 
 | ||||
|     def __iter__(self): | ||||
|         return self | ||||
| 
 | ||||
|     def next(self): | ||||
|         self.parent = greenlet.getcurrent() | ||||
|         result = self.switch() | ||||
|         if self: | ||||
|             return result | ||||
|         else: | ||||
|             raise StopIteration | ||||
| 
 | ||||
| def Yield(value): | ||||
|     g = greenlet.getcurrent() | ||||
|     while not isinstance(g, genlet): | ||||
|         if g is None: | ||||
|             raise RuntimeError, 'yield outside a genlet' | ||||
|         g = g.parent | ||||
|     g.parent.switch(value) | ||||
| 
 | ||||
| def generator(func): | ||||
|     class generator(genlet): | ||||
|         fn = (func,) | ||||
|     return generator | ||||
| 
 | ||||
| # ____________________________________________________________ | ||||
| 
 | ||||
| def test_generator(): | ||||
|     seen = [] | ||||
| 
 | ||||
|     def g(n): | ||||
|         for i in range(n): | ||||
|             seen.append(i) | ||||
|             Yield(i) | ||||
|     g = generator(g) | ||||
| 
 | ||||
|     for k in range(3): | ||||
|         for j in g(5): | ||||
|             seen.append(j) | ||||
| 
 | ||||
|     assert seen == 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4] | ||||
|  | @ -1,161 +0,0 @@ | |||
| from __future__ import generators | ||||
| import py | ||||
| try: | ||||
|     from py.magic import greenlet | ||||
| except (ImportError, RuntimeError), e: | ||||
|     py.test.skip(str(e)) | ||||
| 
 | ||||
| class genlet(greenlet): | ||||
| 
 | ||||
|     def __init__(self, *args, **kwds): | ||||
|         self.args = args | ||||
|         self.kwds = kwds | ||||
|         self.child = None | ||||
|          | ||||
|     def run(self): | ||||
|         fn, = self.fn | ||||
|         fn(*self.args, **self.kwds) | ||||
| 
 | ||||
|     def __iter__(self): | ||||
|         return self | ||||
| 
 | ||||
|     def set_child(self, child): | ||||
|         self.child = child | ||||
| 
 | ||||
|     def next(self): | ||||
|         if self.child: | ||||
|             child = self.child | ||||
|             while child.child: | ||||
|                 tmp = child | ||||
|                 child = child.child | ||||
|                 tmp.child = None | ||||
| 
 | ||||
|             result = child.switch() | ||||
|         else: | ||||
|             self.parent = greenlet.getcurrent()             | ||||
|             result = self.switch() | ||||
|          | ||||
|         if self: | ||||
|             return result | ||||
|         else: | ||||
|             raise StopIteration | ||||
| 
 | ||||
| def Yield(value, level = 1): | ||||
|     g = greenlet.getcurrent() | ||||
|      | ||||
|     while level != 0: | ||||
|         if not isinstance(g, genlet): | ||||
|             raise RuntimeError, 'yield outside a genlet' | ||||
|         if level > 1: | ||||
|             g.parent.set_child(g) | ||||
|         g = g.parent | ||||
|         level -= 1 | ||||
| 
 | ||||
|     g.switch(value) | ||||
|      | ||||
| def Genlet(func): | ||||
|     class Genlet(genlet): | ||||
|         fn = (func,) | ||||
|     return Genlet | ||||
| 
 | ||||
| # ____________________________________________________________ | ||||
| 
 | ||||
| def g1(n, seen): | ||||
|     for i in range(n): | ||||
|         seen.append(i+1) | ||||
|         yield i | ||||
| 
 | ||||
| def g2(n, seen): | ||||
|     for i in range(n): | ||||
|         seen.append(i+1) | ||||
|         Yield(i) | ||||
| 
 | ||||
| g2 = Genlet(g2) | ||||
| 
 | ||||
| def nested(i): | ||||
|     Yield(i) | ||||
| 
 | ||||
| def g3(n, seen): | ||||
|     for i in range(n): | ||||
|         seen.append(i+1) | ||||
|         nested(i) | ||||
| g3 = Genlet(g3) | ||||
| 
 | ||||
| def test_genlet_simple(): | ||||
| 
 | ||||
|     for g in [g1, g2, g3]: | ||||
|         seen = [] | ||||
|         for k in range(3): | ||||
|             for j in g(5, seen): | ||||
|                 seen.append(j) | ||||
|                  | ||||
|         assert seen == 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4] | ||||
| 
 | ||||
| def test_genlet_bad(): | ||||
|     try: | ||||
|         Yield(10) | ||||
|     except RuntimeError: | ||||
|         pass | ||||
|      | ||||
| test_genlet_bad() | ||||
| test_genlet_simple() | ||||
| test_genlet_bad() | ||||
| 
 | ||||
| def a(n): | ||||
|     if n == 0: | ||||
|         return | ||||
|     for ii in ax(n-1): | ||||
|         Yield(ii) | ||||
|     Yield(n) | ||||
| ax = Genlet(a) | ||||
| 
 | ||||
| def test_nested_genlets(): | ||||
|     seen = [] | ||||
|     for ii in ax(5): | ||||
|         seen.append(ii) | ||||
| 
 | ||||
| test_nested_genlets() | ||||
| 
 | ||||
| def perms(l): | ||||
|     if len(l) > 1: | ||||
|         for e in l: | ||||
|             # No syntactical sugar for generator expressions | ||||
|             [Yield([e] + p) for p in perms([x for x in l if x!=e])] | ||||
|     else: | ||||
|         Yield(l) | ||||
| 
 | ||||
| perms = Genlet(perms) | ||||
| 
 | ||||
| def test_perms(): | ||||
|     gen_perms = perms(range(4)) | ||||
|     permutations = list(gen_perms) | ||||
|     assert len(permutations) == 4*3*2*1 | ||||
|     assert [0,1,2,3] in permutations | ||||
|     assert [3,2,1,0] in permutations | ||||
|     res = [] | ||||
|     for ii in zip(perms(range(4)), perms(range(3))): | ||||
|         res.append(ii) | ||||
|     # XXX Test to make sure we are working as a generator expression | ||||
| test_perms() | ||||
| 
 | ||||
| 
 | ||||
| def gr1(n): | ||||
|     for ii in range(1, n): | ||||
|         Yield(ii) | ||||
|         Yield(ii * ii, 2) | ||||
| 
 | ||||
| gr1 = Genlet(gr1) | ||||
| 
 | ||||
| def gr2(n, seen): | ||||
|     for ii in gr1(n): | ||||
|         seen.append(ii) | ||||
| 
 | ||||
| gr2 = Genlet(gr2) | ||||
| 
 | ||||
| def test_layered_genlets(): | ||||
|     seen = [] | ||||
|     for ii in gr2(5, seen): | ||||
|         seen.append(ii) | ||||
|     assert seen == [1, 1, 2, 4, 3, 9, 4, 16] | ||||
| 
 | ||||
| test_layered_genlets() | ||||
|  | @ -1,158 +0,0 @@ | |||
| import py | ||||
| try: | ||||
|     from py.magic import greenlet | ||||
| except (ImportError, RuntimeError), e: | ||||
|     py.test.skip(str(e)) | ||||
| 
 | ||||
| import sys, gc | ||||
| from py.test import raises | ||||
| try: | ||||
|     import thread, threading | ||||
| except ImportError: | ||||
|     thread = None | ||||
| 
 | ||||
| def test_simple(): | ||||
|     lst = [] | ||||
|     def f(): | ||||
|         lst.append(1) | ||||
|         greenlet.getcurrent().parent.switch() | ||||
|         lst.append(3) | ||||
|     g = greenlet(f) | ||||
|     lst.append(0) | ||||
|     g.switch() | ||||
|     lst.append(2) | ||||
|     g.switch() | ||||
|     lst.append(4) | ||||
|     assert lst == range(5) | ||||
| 
 | ||||
| def test_threads(): | ||||
|     if not thread: | ||||
|         py.test.skip("this is a test about thread") | ||||
|     success = [] | ||||
|     def f(): | ||||
|         test_simple() | ||||
|         success.append(True) | ||||
|     ths = [threading.Thread(target=f) for i in range(10)] | ||||
|     for th in ths: | ||||
|         th.start() | ||||
|     for th in ths: | ||||
|         th.join() | ||||
|     assert len(success) == len(ths) | ||||
| 
 | ||||
| 
 | ||||
| class SomeError(Exception): | ||||
|     pass | ||||
| 
 | ||||
| def fmain(seen): | ||||
|     try: | ||||
|         greenlet.getcurrent().parent.switch() | ||||
|     except: | ||||
|         seen.append(sys.exc_info()[0]) | ||||
|         raise | ||||
|     raise SomeError | ||||
| 
 | ||||
| def test_exception(): | ||||
|     seen = [] | ||||
|     g1 = greenlet(fmain) | ||||
|     g2 = greenlet(fmain) | ||||
|     g1.switch(seen) | ||||
|     g2.switch(seen) | ||||
|     g2.parent = g1 | ||||
|     assert seen == [] | ||||
|     raises(SomeError, g2.switch) | ||||
|     assert seen == [SomeError] | ||||
|     g2.switch() | ||||
|     assert seen == [SomeError] | ||||
| 
 | ||||
| def send_exception(g, exc): | ||||
|     # note: send_exception(g, exc)  can be now done with  g.throw(exc). | ||||
|     # the purpose of this test is to explicitely check the propagation rules. | ||||
|     def crasher(exc): | ||||
|         raise exc | ||||
|     g1 = greenlet(crasher, parent=g) | ||||
|     g1.switch(exc) | ||||
| 
 | ||||
| def test_send_exception(): | ||||
|     seen = [] | ||||
|     g1 = greenlet(fmain) | ||||
|     g1.switch(seen) | ||||
|     raises(KeyError, "send_exception(g1, KeyError)") | ||||
|     assert seen == [KeyError] | ||||
| 
 | ||||
| def test_dealloc(): | ||||
|     seen = [] | ||||
|     g1 = greenlet(fmain) | ||||
|     g2 = greenlet(fmain) | ||||
|     g1.switch(seen) | ||||
|     g2.switch(seen) | ||||
|     assert seen == [] | ||||
|     del g1 | ||||
|     gc.collect() | ||||
|     assert seen == [greenlet.GreenletExit] | ||||
|     del g2 | ||||
|     gc.collect() | ||||
|     assert seen == [greenlet.GreenletExit, greenlet.GreenletExit] | ||||
| 
 | ||||
| def test_dealloc_other_thread(): | ||||
|     if not thread: | ||||
|         py.test.skip("this is a test about thread") | ||||
|     seen = [] | ||||
|     someref = [] | ||||
|     lock = thread.allocate_lock() | ||||
|     lock.acquire() | ||||
|     lock2 = thread.allocate_lock() | ||||
|     lock2.acquire() | ||||
|     def f(): | ||||
|         g1 = greenlet(fmain) | ||||
|         g1.switch(seen) | ||||
|         someref.append(g1) | ||||
|         del g1 | ||||
|         gc.collect() | ||||
|         lock.release() | ||||
|         lock2.acquire() | ||||
|         greenlet()   # trigger release | ||||
|         lock.release() | ||||
|         lock2.acquire() | ||||
|     t = threading.Thread(target=f) | ||||
|     t.start() | ||||
|     lock.acquire() | ||||
|     assert seen == [] | ||||
|     assert len(someref) == 1 | ||||
|     del someref[:] | ||||
|     gc.collect() | ||||
|     # g1 is not released immediately because it's from another thread | ||||
|     assert seen == [] | ||||
|     lock2.release() | ||||
|     lock.acquire() | ||||
|     assert seen == [greenlet.GreenletExit] | ||||
|     lock2.release() | ||||
|     t.join() | ||||
| 
 | ||||
| def test_frame(): | ||||
|     def f1(): | ||||
|         f = sys._getframe(0) | ||||
| 	assert f.f_back is None | ||||
| 	greenlet.getcurrent().parent.switch(f) | ||||
| 	return "meaning of life" | ||||
|     g = greenlet(f1) | ||||
|     frame = g.switch() | ||||
|     assert frame is g.gr_frame | ||||
|     assert g | ||||
|     next = g.switch() | ||||
|     assert not g | ||||
|     assert next == "meaning of life" | ||||
|     assert g.gr_frame is None | ||||
| 
 | ||||
| def test_thread_bug(): | ||||
|     if not thread: | ||||
|         py.test.skip("this is a test about thread") | ||||
|     import time | ||||
|     def runner(x): | ||||
|         g = greenlet(lambda: time.sleep(x)) | ||||
|         g.switch() | ||||
|     t1 = threading.Thread(target=runner, args=(0.2,)) | ||||
|     t2 = threading.Thread(target=runner, args=(0.3,)) | ||||
|     t1.start() | ||||
|     t2.start() | ||||
|     t1.join() | ||||
|     t2.join() | ||||
|  | @ -1,66 +0,0 @@ | |||
| import py | ||||
| try: | ||||
|     from py.magic import greenlet | ||||
| except (ImportError, RuntimeError), e: | ||||
|     py.test.skip(str(e)) | ||||
| 
 | ||||
| 
 | ||||
| class RGreenletBunch: | ||||
| 
 | ||||
|     def __init__(self, gateway): | ||||
|         self.channel = gateway.remote_exec(''' | ||||
|             from py.magic import greenlet | ||||
|             glob = {"greenlet": greenlet} | ||||
|             gids = {} | ||||
|             while True: | ||||
|                 key, code, args = channel.receive() | ||||
|                 if args is not None: | ||||
|                     if code is not None: | ||||
|                         def run(code=code): | ||||
|                             exec code in glob, {} | ||||
|                         gids[key] = greenlet(run) | ||||
|                     result = gids[key].switch(*args) | ||||
|                     channel.send(result) | ||||
|                 else: | ||||
|                     del gids[key] | ||||
|         ''') | ||||
| 
 | ||||
|     def greenlet(self, code): | ||||
|         return RGreenlet(self, code) | ||||
| 
 | ||||
| 
 | ||||
| class RGreenlet: | ||||
| 
 | ||||
|     def __init__(self, bunch, code): | ||||
|         self.channel = bunch.channel | ||||
|         self.code    = str(py.code.Source(code)) | ||||
| 
 | ||||
|     def switch(self, *args): | ||||
|         self.channel.send((id(self), self.code, args)) | ||||
|         self.code = None     # only send over the code the first time | ||||
|         return self.channel.receive() | ||||
| 
 | ||||
|     def __del__(self): | ||||
|         if self.code is None: | ||||
|             self.channel.send((id(self), None, None)) | ||||
| 
 | ||||
| 
 | ||||
| def test_rgreenlet(): | ||||
|     gw = py.execnet.PopenGateway() | ||||
|     bunch = RGreenletBunch(gw) | ||||
|     g = bunch.greenlet(''' | ||||
|         x = greenlet.getcurrent().parent.switch(42) | ||||
|         y = greenlet.getcurrent().parent.switch(x+1) | ||||
|         greenlet.getcurrent().parent.switch(y+2) | ||||
|         import os | ||||
|         greenlet.getcurrent().parent.switch(os.getpid()) | ||||
|     ''') | ||||
|     result = g.switch() | ||||
|     assert result == 42 | ||||
|     result = g.switch(102) | ||||
|     assert result == 103 | ||||
|     result = g.switch(-93) | ||||
|     assert result == -91 | ||||
|     import os | ||||
|     result = g.switch() | ||||
|     assert result != os.getpid() | ||||
|  | @ -1,97 +0,0 @@ | |||
| import py | ||||
| try: | ||||
|     from py.magic import greenlet | ||||
| except (ImportError, RuntimeError), e: | ||||
|     py.test.skip(str(e)) | ||||
| 
 | ||||
| def switch(*args): | ||||
|     return greenlet.getcurrent().parent.switch(*args) | ||||
| 
 | ||||
| def test_class(): | ||||
|     def f(): | ||||
|         try: | ||||
|             switch("ok") | ||||
|         except RuntimeError: | ||||
|             switch("ok") | ||||
|             return | ||||
|         switch("fail") | ||||
| 
 | ||||
|     g = greenlet(f) | ||||
|     res = g.switch() | ||||
|     assert res == "ok" | ||||
|     res = g.throw(RuntimeError) | ||||
|     assert res == "ok" | ||||
| 
 | ||||
| def test_val(): | ||||
|     def f(): | ||||
|         try: | ||||
|             switch("ok") | ||||
|         except RuntimeError, val: | ||||
|             if str(val) == "ciao": | ||||
|                 switch("ok") | ||||
|                 return | ||||
|         switch("fail") | ||||
| 
 | ||||
|     g = greenlet(f) | ||||
|     res = g.switch() | ||||
|     assert res == "ok" | ||||
|     res = g.throw(RuntimeError("ciao")) | ||||
|     assert res == "ok" | ||||
| 
 | ||||
|     g = greenlet(f) | ||||
|     res = g.switch() | ||||
|     assert res == "ok" | ||||
|     res = g.throw(RuntimeError, "ciao") | ||||
|     assert res == "ok" | ||||
| 
 | ||||
| def test_kill(): | ||||
|     def f(): | ||||
|         switch("ok") | ||||
|         switch("fail") | ||||
| 
 | ||||
|     g = greenlet(f) | ||||
|     res = g.switch() | ||||
|     assert res == "ok" | ||||
|     res = g.throw() | ||||
|     assert isinstance(res, greenlet.GreenletExit) | ||||
|     assert g.dead | ||||
|     res = g.throw()    # immediately eaten by the already-dead greenlet | ||||
|     assert isinstance(res, greenlet.GreenletExit) | ||||
| 
 | ||||
| def test_throw_goes_to_original_parent(): | ||||
|     main = greenlet.getcurrent() | ||||
|     def f1(): | ||||
|         try: | ||||
|             main.switch("f1 ready to catch") | ||||
|         except IndexError: | ||||
|             return "caught" | ||||
|         else: | ||||
|             return "normal exit" | ||||
|     def f2(): | ||||
|         main.switch("from f2") | ||||
| 
 | ||||
|     g1 = greenlet(f1) | ||||
|     g2 = greenlet(f2, parent=g1) | ||||
|     py.test.raises(IndexError, g2.throw, IndexError) | ||||
|     assert g2.dead | ||||
|     assert g1.dead | ||||
| 
 | ||||
|     g1 = greenlet(f1) | ||||
|     g2 = greenlet(f2, parent=g1) | ||||
|     res = g1.switch() | ||||
|     assert res == "f1 ready to catch" | ||||
|     res = g2.throw(IndexError) | ||||
|     assert res == "caught" | ||||
|     assert g2.dead | ||||
|     assert g1.dead | ||||
| 
 | ||||
|     g1 = greenlet(f1) | ||||
|     g2 = greenlet(f2, parent=g1) | ||||
|     res = g1.switch() | ||||
|     assert res == "f1 ready to catch" | ||||
|     res = g2.switch() | ||||
|     assert res == "from f2" | ||||
|     res = g2.throw(IndexError) | ||||
|     assert res == "caught" | ||||
|     assert g2.dead | ||||
|     assert g1.dead | ||||
|  | @ -1,315 +0,0 @@ | |||
| ===================================================== | ||||
| py.magic.greenlet: Lightweight concurrent programming | ||||
| ===================================================== | ||||
| 
 | ||||
| .. contents:: | ||||
| .. sectnum:: | ||||
| 
 | ||||
| Motivation | ||||
| ========== | ||||
| 
 | ||||
| The "greenlet" package is a spin-off of `Stackless`_, a version of CPython | ||||
| that supports micro-threads called "tasklets".  Tasklets run | ||||
| pseudo-concurrently (typically in a single or a few OS-level threads) and | ||||
| are synchronized with data exchanges on "channels". | ||||
| 
 | ||||
| A "greenlet", on the other hand, is a still more primitive notion of | ||||
| micro-thread with no implicit scheduling; coroutines, in other words.   | ||||
| This is useful when you want to | ||||
| control exactly when your code runs.  You can build custom scheduled | ||||
| micro-threads on top of greenlet; however, it seems that greenlets are | ||||
| useful on their own as a way to make advanced control flow structures.   | ||||
| For example, we can recreate generators; the difference with Python's own | ||||
| generators is that our generators can call nested functions and the nested | ||||
| functions can yield values too.  (Additionally, you don't need a "yield" | ||||
| keyword.  See the example in :source:`py/c-extension/greenlet/test_generator.py`).  | ||||
| 
 | ||||
| Greenlets are provided as a C extension module for the regular unmodified | ||||
| interpreter. | ||||
| 
 | ||||
| .. _`Stackless`: http://www.stackless.com | ||||
| 
 | ||||
| Example | ||||
| ------- | ||||
| 
 | ||||
| Let's consider a system controlled by a terminal-like console, where the user | ||||
| types commands.  Assume that the input comes character by character.  In such | ||||
| a system, there will typically be a loop like the following one:: | ||||
| 
 | ||||
|     def process_commands(*args): | ||||
|         while True: | ||||
|             line = '' | ||||
|             while not line.endswith('\n'): | ||||
|                 line += read_next_char() | ||||
|             if line == 'quit\n': | ||||
|                 print "are you sure?" | ||||
|                 if read_next_char() != 'y': | ||||
|                     continue    # ignore the command | ||||
|             process_command(line) | ||||
| 
 | ||||
| Now assume that you want to plug this program into a GUI.  Most GUI toolkits | ||||
| are event-based.  They will invoke a call-back for each character the user | ||||
| presses.  [Replace "GUI" with "XML expat parser" if that rings more bells to | ||||
| you ``:-)``]  In this setting, it is difficult to implement the | ||||
| read_next_char() function needed by the code above.  We have two incompatible | ||||
| functions:: | ||||
| 
 | ||||
|     def event_keydown(key): | ||||
|         ??  | ||||
| 
 | ||||
|     def read_next_char(): | ||||
|         ?? should wait for the next event_keydown() call | ||||
| 
 | ||||
| You might consider doing that with threads.  Greenlets are an alternate | ||||
| solution that don't have the related locking and shutdown problems.  You | ||||
| start the process_commands() function in its own, separate greenlet, and | ||||
| then you exchange the keypresses with it as follows:: | ||||
| 
 | ||||
|     def event_keydown(key): | ||||
|              # jump into g_processor, sending it the key | ||||
|         g_processor.switch(key) | ||||
| 
 | ||||
|     def read_next_char(): | ||||
|             # g_self is g_processor in this simple example | ||||
|         g_self = greenlet.getcurrent() | ||||
|             # jump to the parent (main) greenlet, waiting for the next key | ||||
|         next_char = g_self.parent.switch() | ||||
|         return next_char | ||||
| 
 | ||||
|     g_processor = greenlet(process_commands) | ||||
|     g_processor.switch(*args)   # input arguments to process_commands() | ||||
| 
 | ||||
|     gui.mainloop() | ||||
| 
 | ||||
| In this example, the execution flow is: when read_next_char() is called, it | ||||
| is part of the g_processor greenlet, so when it switches to its parent | ||||
| greenlet, it resumes execution in the top-level main loop (the GUI).  When | ||||
| the GUI calls event_keydown(), it switches to g_processor, which means that | ||||
| the execution jumps back wherever it was suspended in that greenlet -- in | ||||
| this case, to the switch() instruction in read_next_char() -- and the ``key`` | ||||
| argument in event_keydown() is passed as the return value of the switch() in | ||||
| read_next_char(). | ||||
| 
 | ||||
| Note that read_next_char() will be suspended and resumed with its call stack | ||||
| preserved, so that it will itself return to different positions in | ||||
| process_commands() depending on where it was originally called from.  This | ||||
| allows the logic of the program to be kept in a nice control-flow way; we | ||||
| don't have to completely rewrite process_commands() to turn it into a state | ||||
| machine. | ||||
| 
 | ||||
| 
 | ||||
| Usage | ||||
| ===== | ||||
| 
 | ||||
| Introduction | ||||
| ------------ | ||||
| 
 | ||||
| A "greenlet" is a small independent pseudo-thread.  Think about it as a | ||||
| small stack of frames; the outermost (bottom) frame is the initial | ||||
| function you called, and the innermost frame is the one in which the | ||||
| greenlet is currently paused.  You work with greenlets by creating a | ||||
| number of such stacks and jumping execution between them.  Jumps are never | ||||
| implicit: a greenlet must choose to jump to another greenlet, which will | ||||
| cause the former to suspend and the latter to resume where it was | ||||
| suspended.  Jumping between greenlets is called "switching". | ||||
| 
 | ||||
| When you create a greenlet, it gets an initially empty stack; when you | ||||
| first switch to it, it starts the run a specified function, which may call | ||||
| other functions, switch out of the greenlet, etc.  When eventually the | ||||
| outermost function finishes its execution, the greenlet's stack becomes | ||||
| empty again and the greenlet is "dead".  Greenlets can also die of an | ||||
| uncaught exception. | ||||
| 
 | ||||
| For example:: | ||||
| 
 | ||||
|     from py.magic import greenlet | ||||
| 
 | ||||
|     def test1(): | ||||
|         print 12 | ||||
|         gr2.switch() | ||||
|         print 34 | ||||
| 
 | ||||
|     def test2(): | ||||
|         print 56 | ||||
|         gr1.switch() | ||||
|         print 78 | ||||
| 
 | ||||
|     gr1 = greenlet(test1) | ||||
|     gr2 = greenlet(test2) | ||||
|     gr1.switch() | ||||
| 
 | ||||
| The last line jumps to test1, which prints 12, jumps to test2, prints 56, | ||||
| jumps back into test1, prints 34; and then test1 finishes and gr1 dies.   | ||||
| At this point, the execution comes back to the original ``gr1.switch()`` | ||||
| call.  Note that 78 is never printed. | ||||
| 
 | ||||
| Parents | ||||
| ------- | ||||
| 
 | ||||
| Let's see where execution goes when a greenlet dies.  Every greenlet has a | ||||
| "parent" greenlet.  The parent greenlet is initially the one in which the | ||||
| greenlet was created (this can be changed at any time).  The parent is | ||||
| where execution continues when a greenlet dies.  This way, greenlets are | ||||
| organized in a tree.  Top-level code that doesn't run in a user-created | ||||
| greenlet runs in the implicit "main" greenlet, which is the root of the | ||||
| tree. | ||||
| 
 | ||||
| In the above example, both gr1 and gr2 have the main greenlet as a parent.   | ||||
| Whenever one of them dies, the execution comes back to "main". | ||||
| 
 | ||||
| Uncaught exceptions are propagated into the parent, too.  For example, if | ||||
| the above test2() contained a typo, it would generate a NameError that | ||||
| would kill gr2, and the exception would go back directly into "main".   | ||||
| The traceback would show test2, but not test1.  Remember, switches are not | ||||
| calls, but transfer of execution between parallel "stack containers", and | ||||
| the "parent" defines which stack logically comes "below" the current one. | ||||
| 
 | ||||
| Instantiation | ||||
| ------------- | ||||
| 
 | ||||
| ``py.magic.greenlet`` is the greenlet type, which supports the following | ||||
| operations: | ||||
| 
 | ||||
| ``greenlet(run=None, parent=None)`` | ||||
|     Create a new greenlet object (without running it).  ``run`` is the | ||||
|     callable to invoke, and ``parent`` is the parent greenlet, which | ||||
|     defaults to the current greenlet. | ||||
| 
 | ||||
| ``greenlet.getcurrent()`` | ||||
|     Returns the current greenlet (i.e. the one which called this | ||||
|     function). | ||||
| 
 | ||||
| ``greenlet.GreenletExit`` | ||||
|     This special exception does not propagate to the parent greenlet; it | ||||
|     can be used to kill a single greenlet. | ||||
| 
 | ||||
| The ``greenlet`` type can be subclassed, too.  A greenlet runs by calling | ||||
| its ``run`` attribute, which is normally set when the greenlet is | ||||
| created; but for subclasses it also makes sense to define a ``run`` method | ||||
| instead of giving a ``run`` argument to the constructor. | ||||
| 
 | ||||
| Switching | ||||
| --------- | ||||
| 
 | ||||
| Switches between greenlets occur when the method switch() of a greenlet is | ||||
| called, in which case execution jumps to the greenlet whose switch() is | ||||
| called, or when a greenlet dies, in which case execution jumps to the | ||||
| parent greenlet.  During a switch, an object or an exception is "sent" to | ||||
| the target greenlet; this can be used as a convenient way to pass | ||||
| information between greenlets.  For example:: | ||||
| 
 | ||||
|     def test1(x, y): | ||||
|         z = gr2.switch(x+y) | ||||
|         print z | ||||
| 
 | ||||
|     def test2(u): | ||||
|         print u | ||||
|         gr1.switch(42) | ||||
| 
 | ||||
|     gr1 = greenlet(test1) | ||||
|     gr2 = greenlet(test2) | ||||
|     gr1.switch("hello", " world") | ||||
| 
 | ||||
| This prints "hello world" and 42, with the same order of execution as the | ||||
| previous example.  Note that the arguments of test1() and test2() are not | ||||
| provided when the greenlet is created, but only the first time someone | ||||
| switches to it. | ||||
| 
 | ||||
| Here are the precise rules for sending objects around: | ||||
| 
 | ||||
| ``g.switch(obj=None or *args)`` | ||||
|     Switches execution to the greenlet ``g``, sending it the given | ||||
|     ``obj``.  As a special case, if ``g`` did not start yet, then it will | ||||
|     start to run now; in this case, any number of arguments can be | ||||
|     provided, and ``g.run(*args)`` is called. | ||||
| 
 | ||||
| Dying greenlet | ||||
|     If a greenlet's ``run()`` finishes, its return value is the object  | ||||
|     sent to its parent.  If ``run()`` terminates with an exception, the | ||||
|     exception is propagated to its parent (unless it is a | ||||
|     ``greenlet.GreenletExit`` exception, in which case the exception | ||||
|     object is caught and *returned* to the parent). | ||||
| 
 | ||||
| Apart from the cases described above, the target greenlet normally | ||||
| receives the object as the return value of the call to ``switch()`` in | ||||
| which it was previously suspended.  Indeed, although a call to | ||||
| ``switch()`` does not return immediately, it will still return at some | ||||
| point in the future, when some other greenlet switches back.  When this | ||||
| occurs, then execution resumes just after the ``switch()`` where it was | ||||
| suspended, and the ``switch()`` itself appears to return the object that | ||||
| was just sent.  This means that ``x = g.switch(y)`` will send the object | ||||
| ``y`` to ``g``, and will later put the (unrelated) object that some | ||||
| (unrelated) greenlet passes back to us into ``x``. | ||||
| 
 | ||||
| Note that any attempt to switch to a dead greenlet actually goes to the | ||||
| dead greenlet's parent, or its parent's parent, and so on.  (The final | ||||
| parent is the "main" greenlet, which is never dead.) | ||||
| 
 | ||||
| Methods and attributes of greenlets | ||||
| ----------------------------------- | ||||
| 
 | ||||
| ``g.switch(obj=None or *args)`` | ||||
|     Switches execution to the greenlet ``g``.  See above. | ||||
| 
 | ||||
| ``g.run`` | ||||
|     The callable that ``g`` will run when it starts.  After ``g`` started, | ||||
|     this attribute no longer exists. | ||||
| 
 | ||||
| ``g.parent`` | ||||
|     The parent greenlet.  This is writeable, but it is not allowed to | ||||
|     create cycles of parents. | ||||
| 
 | ||||
| ``g.gr_frame`` | ||||
|     The current top frame, or None. | ||||
| 
 | ||||
| ``g.dead`` | ||||
|     True if ``g`` is dead (i.e. it finished its execution). | ||||
| 
 | ||||
| ``bool(g)`` | ||||
|     True if ``g`` is active, False if it is dead or not yet started. | ||||
| 
 | ||||
| ``g.throw([typ, [val, [tb]]])`` | ||||
|     Switches execution to the greenlet ``g``, but immediately raises the | ||||
|     given exception in ``g``.  If no argument is provided, the exception | ||||
|     defaults to ``greenlet.GreenletExit``.  The normal exception | ||||
|     propagation rules apply, as described above.  Note that calling this | ||||
|     method is almost equivalent to the following:: | ||||
| 
 | ||||
|         def raiser(): | ||||
|             raise typ, val, tb | ||||
|         g_raiser = greenlet(raiser, parent=g) | ||||
|         g_raiser.switch() | ||||
| 
 | ||||
|     except that this trick does not work for the | ||||
|     ``greenlet.GreenletExit`` exception, which would not propagate | ||||
|     from ``g_raiser`` to ``g``. | ||||
| 
 | ||||
| Greenlets and Python threads | ||||
| ---------------------------- | ||||
| 
 | ||||
| Greenlets can be combined with Python threads; in this case, each thread | ||||
| contains an independent "main" greenlet with a tree of sub-greenlets.  It | ||||
| is not possible to mix or switch between greenlets belonging to different | ||||
| threads. | ||||
| 
 | ||||
| Garbage-collecting live greenlets | ||||
| --------------------------------- | ||||
| 
 | ||||
| If all the references to a greenlet object go away (including the | ||||
| references from the parent attribute of other greenlets), then there is no | ||||
| way to ever switch back to this greenlet.  In this case, a GreenletExit | ||||
| exception is generated into the greenlet.  This is the only case where a | ||||
| greenlet receives the execution asynchronously.  This gives | ||||
| ``try:finally:`` blocks a chance to clean up resources held by the | ||||
| greenlet.  This feature also enables a programming style in which | ||||
| greenlets are infinite loops waiting for data and processing it.  Such | ||||
| loops are automatically interrupted when the last reference to the | ||||
| greenlet goes away. | ||||
| 
 | ||||
| The greenlet is expected to either die or be resurrected by having a new | ||||
| reference to it stored somewhere; just catching and ignoring the | ||||
| GreenletExit is likely to lead to an infinite loop. | ||||
| 
 | ||||
| Greenlets do not participate in garbage collection; cycles involving data | ||||
| that is present in a greenlet's frames will not be detected.  Storing | ||||
| references to other greenlets cyclically may lead to leaks. | ||||
|  | @ -5,13 +5,10 @@ py lib: Main tools and APIs | |||
| 
 | ||||
| `py.execnet`_ rapidly deploy local or remote processes from your program.  | ||||
| 
 | ||||
| `py.magic.greenlet`_: instantiate thousands of micro-threads from your program.  | ||||
| 
 | ||||
| `py.path`_: use path objects to transparently access local and svn filesystems. | ||||
| 
 | ||||
| `py.code`_: generate python code and use advanced introspection/traceback support.  | ||||
| 
 | ||||
| 
 | ||||
| minor support functionality  | ||||
| --------------------------------- | ||||
| 
 | ||||
|  | @ -29,7 +26,6 @@ minor support functionality | |||
| .. _`download and installation`: download.html | ||||
| .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev  | ||||
| .. _`py.execnet`: execnet.html  | ||||
| .. _`py.magic.greenlet`: greenlet.html  | ||||
| .. _`py.log`: log.html | ||||
| .. _`py.io`: io.html | ||||
| .. _`py.path`: path.html | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue