[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.test`_: cross-project testing tool with many advanced features | ||||||
| - `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes | - `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.path`_: path abstractions over local and subversion files  | ||||||
| - `py.code`_: dynamic code compile and traceback printing support | - `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.test`: http://pylib.org/test.html | ||||||
| .. _`py.execnet`: http://pylib.org/execnet.html | .. _`py.execnet`: http://pylib.org/execnet.html | ||||||
| .. _`py.magic.greenlet`: http://pylib.org/greenlet.html |  | ||||||
| .. _`py.path`: http://pylib.org/path.html | .. _`py.path`: http://pylib.org/path.html | ||||||
| .. _`py.code`: http://pylib.org/code.html | .. _`py.code`: http://pylib.org/code.html | ||||||
|   |   | ||||||
| """ | """ | ||||||
| from initpkg import initpkg | from initpkg import initpkg | ||||||
| 
 | 
 | ||||||
| version = "1.0.0a7" | version = "1.0.0a8" | ||||||
| 
 | 
 | ||||||
| initpkg(__name__, | initpkg(__name__, | ||||||
|     description = "pylib and py.test: agile development and test support library", |     description = "pylib and py.test: agile development and test support library", | ||||||
|     revision = int('$LastChangedRevision: 63319 $'.split(':')[1][:-1]), |     revision = int('$LastChangedRevision: 63340 $'.split(':')[1][:-1]), | ||||||
|     lastchangedate = '$LastChangedDate: 2009-03-25 12:50:57 +0100 (Wed, 25 Mar 2009) $', |     lastchangedate = '$LastChangedDate: 2009-03-26 10:33:50 +0100 (Thu, 26 Mar 2009) $', | ||||||
|     version = version,  |     version = version,  | ||||||
|     url = "http://pylib.org",  |     url = "http://pylib.org",  | ||||||
|     download_url = "http://codespeak.net/py/%s/download.html" % version, |     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.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.path`_: use path objects to transparently access local and svn filesystems. | ||||||
| 
 | 
 | ||||||
| `py.code`_: generate python code and use advanced introspection/traceback support.  | `py.code`_: generate python code and use advanced introspection/traceback support.  | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| minor support functionality  | minor support functionality  | ||||||
| --------------------------------- | --------------------------------- | ||||||
| 
 | 
 | ||||||
|  | @ -29,7 +26,6 @@ minor support functionality | ||||||
| .. _`download and installation`: download.html | .. _`download and installation`: download.html | ||||||
| .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev  | .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev  | ||||||
| .. _`py.execnet`: execnet.html  | .. _`py.execnet`: execnet.html  | ||||||
| .. _`py.magic.greenlet`: greenlet.html  |  | ||||||
| .. _`py.log`: log.html | .. _`py.log`: log.html | ||||||
| .. _`py.io`: io.html | .. _`py.io`: io.html | ||||||
| .. _`py.path`: path.html | .. _`py.path`: path.html | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue