[Stackless-checkins] CVS: slpdev/src/2.3/dev/Stackless/core stackless_impl.h, 1.85, 1.86 stackless_structs.h, 1.61, 1.62 stackless_tstate.h, 1.49, 1.50 stackless_util.c, 1.17, 1.18 stacklesseval.c, 1.146, 1.147

Christian Tismer tismer at centera.de
Wed May 12 17:33:58 CEST 2004


Update of /home/cvs/slpdev/src/2.3/dev/Stackless/core
In directory centera.de:/tmp/cvs-serv16151/dev/Stackless/core

Modified Files:
	stackless_impl.h stackless_structs.h stackless_tstate.h 
	stackless_util.c stacklesseval.c 
Log Message:
This is a major update.
Channels got a complete overhaul. They have garbage collection,
they are soft-switched, and they are simplified very much.
Exception handling has changed completely and is much simpler now.
Instead of taking care of exceptions immediately, they are now tuned
into "bomb" objects, which can be passed around. A tasklet that
hold as bomb when getting activated will explode the bomb as an exception.
This also made send_exception much cleaner. We just send a bomb.
Also cured a bug with tasklet destruction and hard-switching.

The enable_softswitch() function now controls completely whether
attempts for stackless calls are made. This is besides speed comparison
a nice way for testing, since you can "switch the cstack on" for debugging.

Soon to come:
channel transfer (coroutine-like)
channel pickling
getting rid of the tasklet runner (internal stuff no longer needed)
new "caller" property for tasklets which defines where to return to
after tasklet.run etc.
Exposing new properties for scheduling control on channels and tasklets.
Re-introducing stack saving, because stackless behavior can be disabled.


Index: stackless_impl.h
===================================================================
RCS file: /home/cvs/slpdev/src/2.3/dev/Stackless/core/stackless_impl.h,v
retrieving revision 1.85
retrieving revision 1.86
diff -C2 -d -r1.85 -r1.86
*** stackless_impl.h	1 May 2004 19:01:06 -0000	1.85
--- stackless_impl.h	12 May 2004 15:33:56 -0000	1.86
***************
*** 174,183 ****
  #define STACKLESS_PROMOTE_ALL() (slp_try_stackless = stackless, NULL)
  
! #define STACKLESS_PROPOSE(func) {int stackless = 1; STACKLESS_PROMOTE(func);}
  
! #define STACKLESS_PROPOSE_FLAG(flag) {int stackless = 1; \
  				      STACKLESS_PROMOTE_FLAG(flag);}
  
! #define STACKLESS_PROPOSE_ALL() slp_try_stackless = 1;
  
  #define STACKLESS_RETRACT() slp_try_stackless = 0;
--- 174,184 ----
  #define STACKLESS_PROMOTE_ALL() (slp_try_stackless = stackless, NULL)
  
! #define STACKLESS_PROPOSE(func) {int stackless = slp_enable_softswitch; \
! 				 STACKLESS_PROMOTE(func);}
  
! #define STACKLESS_PROPOSE_FLAG(flag) {int stackless = slp_enable_softswitch; \
  				      STACKLESS_PROMOTE_FLAG(flag);}
  
! #define STACKLESS_PROPOSE_ALL() slp_try_stackless = slp_enable_softswitch;
  
  #define STACKLESS_RETRACT() slp_try_stackless = 0;
***************
*** 254,260 ****
   ********************************************************************/
  
- PyAPI_DATA(PyTypeObject) PyBaseFrame_Type;
- PyAPI_DATA(PyTypeObject) PyCFrame_Type;
- 
  /* generic ops for chained objects */
  
--- 255,258 ----
***************
*** 265,270 ****
  { \
  	__objtype *l, *r; \
! 		assert((__task)->__next == NULL); \
! 		assert((__task)->__prev == NULL); \
  	if (*(__chain) == NULL) { \
  		(__task)->__next = (__task)->__prev = (__task); \
--- 263,268 ----
  { \
  	__objtype *l, *r; \
! 	assert((__task)->__next == NULL); \
! 	assert((__task)->__prev == NULL); \
  	if (*(__chain) == NULL) { \
  		(__task)->__next = (__task)->__prev = (__task); \
***************
*** 302,305 ****
--- 300,327 ----
  }
  
+ /* these versions operate on an embedded head, which channels use now */
+ 
+ #define SLP_HEADCHAIN_INSERT(__objtype, __chan, __task, __next, __prev) \
+ { \
+ 	__objtype *__head = (__objtype *) __chan; \
+ 	assert((__task)->__next == NULL); \
+ 	assert((__task)->__prev == NULL); \
+ 	/* insert at end */ \
+ 	(__task)->__prev = (__head)->__prev; \
+ 	(__task)->__next = (__head); \
+ 	(__head)->__prev->next = (__task); \
+ 	(__head)->__prev = (__task); \
+ }
+ 
+ #define SLP_HEADCHAIN_REMOVE(__task, __next, __prev) \
+ { \
+ 	assert((__task)->__next != NULL); \
+ 	assert((__task)->__prev != NULL); \
+ 	/* remove at front */ \
+ 	(__task)->__next->__prev = (__task)->prev; \
+ 	(__task)->__prev->__next = (__task)->next; \
+ 	(__task)->__next = (__task)->__prev = NULL; \
+ }
+ 
  /* operations on chains */
  
***************
*** 314,319 ****
  				    PyChannelObject *channel,
  				    int dir, PyTaskletObject *task);
  
- /* tasklet operations */
  PyAPI_FUNC(PyObject *) slp_tasklet_new(PyTypeObject *type, PyObject *args,
  				       PyObject *kwds);
--- 336,343 ----
  				    PyChannelObject *channel,
  				    int dir, PyTaskletObject *task);
+ PyAPI_FUNC(PyTaskletObject) * slp_channel_remove_slow(PyTaskletObject *task);
+ 
+ /* tasklet/scheduling operations */
  
  PyAPI_FUNC(PyObject *) slp_tasklet_new(PyTypeObject *type, PyObject *args,
  				       PyObject *kwds);
***************
*** 321,325 ****
  PyAPI_FUNC(PyObject *) slp_schedule_task(PyTaskletObject *prev,
  					 PyTaskletObject *next,
! 					 int stackless, PyObject *retval);
  
  PyAPI_FUNC(PyObject *) slp_run_tasklet(PyFrameObject *f);
--- 345,391 ----
  PyAPI_FUNC(PyObject *) slp_schedule_task(PyTaskletObject *prev,
  					 PyTaskletObject *next,
! 					 int stackless);
! 
! /* setting the tasklet's tempval */
! 
! #define TASKLET_SETVAL(task, val) \
! 	if ((task)->tempval != (PyObject *) val) { \
! 		PyObject *hold = (task)->tempval; \
! 		Py_INCREF(val); \
! 		(task)->tempval = (PyObject *) val; \
! 		Py_DECREF(hold); \
! 	}
! 
! /* ditto, without incref */
! 
! #define TASKLET_SETVAL_OWN(task, val) \
! 	if ((task)->tempval != (PyObject *) val) { \
! 		PyObject *hold = (task)->tempval; \
! 		assert(val != NULL); \
! 		(task)->tempval = (PyObject *) val; \
! 		Py_DECREF(hold); \
! 	} \
! 	else \
! 		Py_DECREF(val);
! 
! /* exchanging values with safety check */
! 
! #define TASKLET_SWAPVAL(prev, next) \
! 	{ \
! 		PyObject *hold = (prev)->tempval; \
! 		assert((prev)->tempval != NULL); \
! 		assert((next)->tempval != NULL); \
! 		(prev)->tempval = (next)->tempval; \
! 		(next)->tempval = hold; \
! 	}
! 
! 
! /* exception handling */
! 
! PyAPI_FUNC(PyObject *) slp_make_bomb(PyObject *klass, PyObject *args, char *msg);
! PyAPI_FUNC(PyObject *) slp_curexc_to_bomb(void);
! PyAPI_FUNC(PyObject *) slp_bomb_explode(PyTaskletObject *task);
! 
! /* tasklet startup */
  
  PyAPI_FUNC(PyObject *) slp_run_tasklet(PyFrameObject *f);
***************
*** 358,362 ****
  
  PyAPI_FUNC(PyFrameObject *) slp_get_frame(PyTaskletObject *task);
- PyAPI_FUNC(PyTaskletFlagStruc *) slp_get_flags(PyTaskletObject *task);
  PyAPI_FUNC(void) slp_check_pending_irq(void);
  PyAPI_FUNC(int) slp_return_wrapper(PyObject *retval);
--- 424,427 ----
***************
*** 364,367 ****
--- 429,434 ----
  PyAPI_FUNC(int) slp_current_wrapper(int(*func)(PyTaskletObject*),
  				    PyTaskletObject *task);
+ PyAPI_FUNC(int) slp_resurrect_and_kill(PyObject *self, 
+ 				       void(*killer)(PyObject *));
  PyAPI_FUNC(int) slp_revive_main(void);
  

Index: stackless_structs.h
===================================================================
RCS file: /home/cvs/slpdev/src/2.3/dev/Stackless/core/stackless_structs.h,v
retrieving revision 1.61
retrieving revision 1.62
diff -C2 -d -r1.61 -r1.62
*** stackless_structs.h	24 Apr 2004 23:29:39 -0000	1.61
--- stackless_structs.h	12 May 2004 15:33:56 -0000	1.62
***************
*** 16,33 ****
      -----------------------
  
!     active:	    This tasklets is currently running. The frame
! 		    and flags attributes of the tasklet are invalid
! 		    and mapped to tstate. Maintenance of these fields
! 		    is done during switching.
! 		    This is now a computed attribute.
  
      atomic:	    If true, schedulers will never switch. Driven by
  		    the code object or dynamically, see below.
  
!     scheduled:	    The tasklet likes to be auto-scheduled. User driven.
  
!     blocked:	    The tasklet is either waiting in a channel for
! 		    writing (1) or reading (-1) or not blocked(0).
! 		    Maintained by the channel logic. Do not change.
  
      block_trap:     Debugging aid. Whenever the tasklet would be
--- 16,30 ----
      -----------------------
  
!     blocked:	    The tasklet is either waiting in a channel for
! 		    writing (1) or reading (-1) or not blocked(0).
! 		    Maintained by the channel logic. Do not change.
  
      atomic:	    If true, schedulers will never switch. Driven by
  		    the code object or dynamically, see below.
  
!     ignore_nesting: Allows auto-scheduling, even if nesting_level
! 		    is not zero.
  
!     autoschedule:   The tasklet likes to be auto-scheduled. User driven.
  
      block_trap:     Debugging aid. Whenever the tasklet would be
***************
*** 39,48 ****
  
  
!   Policy for atomic/schedule and switching:
!     -----------------------------------------
      A tasklet switch can always be done explicitly by calling schedule().
      Atomic and schedule are concerned with automatic features.
  
!     atomic  scheduled
  
  	1	any	Neither a scheduler nor a watchdog will
--- 36,45 ----
  
  
!     Policy for atomic/autoschedule and switching:
!     ---------------------------------------------
      A tasklet switch can always be done explicitly by calling schedule().
      Atomic and schedule are concerned with automatic features.
  
!     atomic  autoschedule
  
  	1	any	Neither a scheduler nor a watchdog will
***************
*** 56,74 ****
      Default settings:
      -----------------
!     The default value of scheduled is taken from the global variable
!     enable_scheduling in stacklessmodule. It can be set or cleared
!     for every tasklet at any time.
!     The value of atomic is normally calculated from the executed code
!     object. Unless the code object has a special flag set, atomic
!     will be saved on entry of the code object, set to true, and reset
!     on exit. This protects you from crashing any unknown Python code.
!     For known code objects, this mechanism can be turned off, and the
!     atomic flag is under your control.
  
   ***************************************************************************/
  
! /* 
!  * note: the tasklet flags are living in stackless_tstate.h
!  */
  
  typedef struct _tasklet {
--- 53,70 ----
      Default settings:
      -----------------
!     All flags are zero by default.
  
   ***************************************************************************/
  
! typedef struct _tasklet_flags {
! 	int blocked: 2;
! 	unsigned int atomic: 1;
! 	unsigned int ignore_nesting: 1;
! 	unsigned int autoschedule: 1;
! 	unsigned int block_trap: 1;
! 	unsigned int is_zombie: 1;
! 	unsigned int pending_irq: 1;
! } PyTaskletFlagStruc;
! 
  
  typedef struct _tasklet {
***************
*** 112,121 ****
  
  
  /*** important structures: channel ***/
  
  typedef struct _channel {
  	PyObject_HEAD
! 	struct _tasklet *queue;
  	int balance;
  	PyObject *chan_weakreflist;
  } PyChannelObject;
--- 108,158 ----
  
  
+ /*** important structures: bomb ***/
+ 
+ typedef struct _bomb {
+ 	PyObject_HEAD
+ 	PyObject *curexc_type;
+ 	PyObject *curexc_value;
+ 	PyObject *curexc_traceback;
+ } PyBombObject;
+ 
  /*** important structures: channel ***/
  
+ /***************************************************************************
+ 
+     Channel Flag Definition
+     -----------------------
+ 
+ 
+     closing:        When the closing flag is set, the channel does not
+ 		    accept to be extended. The computed attribute
+ 		    'closed' is true when closing is set and the
+ 		    channel is empty.
+ 
+     preference:	    0    no preference, caller will continue
+ 		    1    sender will be inserted after receiver and run
+ 		    -1   receiver will be inserted after sender and run
+ 
+     schedule_all:   ignore preference and always schedule the next task
+ 
+     Default settings:
+     -----------------
+     All flags are zero by default.
+ 
+  ***************************************************************************/
+ 
+ typedef struct _channel_flags {
+ 	unsigned int closing: 1;
+ 	int preference: 2;
+ 	unsigned int schedule_all:1;
+ } PyChannelFlagStruc;
+ 
  typedef struct _channel {
  	PyObject_HEAD
! 	/* make sure that these fit tasklet's next/prev */
! 	struct _tasklet *head;
! 	struct _tasklet *tail;
  	int balance;
+ 	struct _channel_flags flags;
  	PyObject *chan_weakreflist;
  } PyChannelObject;
***************
*** 161,168 ****
  /*** associated type objects ***/
  
! /* PyCFrame_Type in stackless_frame.h */
! 
  PyAPI_DATA(PyTypeObject) PyCStack_Type;
  
  PyAPI_DATA(PyTypeObject*) PyTasklet_TypePtr;
  #define PyTasklet_Type (*PyTasklet_TypePtr)
--- 198,208 ----
  /*** associated type objects ***/
  
! PyAPI_DATA(PyTypeObject) PyBaseFrame_Type;
! PyAPI_DATA(PyTypeObject) PyCFrame_Type;
  PyAPI_DATA(PyTypeObject) PyCStack_Type;
  
+ PyAPI_DATA(PyTypeObject) PyBomb_Type;
+ #define PyBomb_Check(op) ((op)->ob_type == &PyBomb_Type)
+ 
  PyAPI_DATA(PyTypeObject*) PyTasklet_TypePtr;
  #define PyTasklet_Type (*PyTasklet_TypePtr)

Index: stackless_tstate.h
===================================================================
RCS file: /home/cvs/slpdev/src/2.3/dev/Stackless/core/stackless_tstate.h,v
retrieving revision 1.49
retrieving revision 1.50
diff -C2 -d -r1.49 -r1.50
*** stackless_tstate.h	24 Apr 2004 23:29:39 -0000	1.49
--- stackless_tstate.h	12 May 2004 15:33:56 -0000	1.50
***************
*** 1,13 ****
  /*** addition to tstate ***/
  
- typedef struct _tasklet_flags {
- 	unsigned int atomic: 1;
- 	unsigned int ignore_nesting: 1;
- 	int blocked: 2;
- 	unsigned int block_trap: 1;
- 	unsigned int is_zombie: 1;
- 	unsigned int pending_irq: 1;
- } PyTaskletFlagStruc;
- 
  typedef struct _sts {
  	/* the blueprint for new stacks */
--- 1,4 ----
***************
*** 25,30 ****
  	/* the base address for hijacking stacks */
  	int *cstack_base;
- 	/* flags of the running tasklet */
- 	struct _tasklet_flags flags;
  	/* main tasklet */
  	struct _tasklet *main;
--- 16,19 ----
***************
*** 56,60 ****
  	tstate->st.cstack_chain = NULL; \
  	tstate->st.cstack_base = NULL; \
- 	*(int*)&tstate->st.flags = 0; \
  	tstate->st.ticker = 0; \
  	tstate->st.interval = 0; \
--- 45,48 ----

Index: stackless_util.c
===================================================================
RCS file: /home/cvs/slpdev/src/2.3/dev/Stackless/core/stackless_util.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -C2 -d -r1.17 -r1.18
*** stackless_util.c	24 Apr 2004 23:29:39 -0000	1.17
--- stackless_util.c	12 May 2004 15:33:56 -0000	1.18
***************
*** 49,70 ****
  }
  
- PyTaskletFlagStruc *
- slp_get_flags(PyTaskletObject *task)
- {
- 	PyThreadState *ts;
- 
- 	assert(task->cstate != NULL);
- 	ts = task->cstate->tstate;
- 	return ts->st.current == task ? &ts->st.flags : &task->flags;
- }
- 
  void slp_check_pending_irq()
  {
  	PyThreadState *ts = PyThreadState_GET();
  
! 	if (ts->st.flags.pending_irq) {
! 		if (ts->st.flags.atomic)
  			return;
! 		if (ts->st.nesting_level && !ts->st.flags.ignore_nesting)
  			return;
  		/* trigger interrupt */
--- 49,61 ----
  }
  
  void slp_check_pending_irq()
  {
  	PyThreadState *ts = PyThreadState_GET();
+ 	PyTaskletObject *current = ts->st.current;
  
! 	if (current->flags.pending_irq) {
! 		if (current->flags.atomic)
  			return;
! 		if (ts->st.nesting_level && !current->flags.ignore_nesting)
  			return;
  		/* trigger interrupt */
***************
*** 72,76 ****
  			_Py_Ticker = 0;
  		ts->st.ticker = 0;
! 		ts->st.flags.pending_irq = 0;
  	}
  }
--- 63,67 ----
  			_Py_Ticker = 0;
  		ts->st.ticker = 0;
! 		current->flags.pending_irq = 0;
  	}
  }
***************
*** 116,118 ****
--- 107,167 ----
  }
  
+ int
+ slp_resurrect_and_kill(PyObject *self, void(*killer)(PyObject *))
+ {
+ 	/* modelled after typeobject.c's call_finalizer */
+ 
+ 	PyObject *error_type, *error_value, *error_traceback;
+ 
+ 	/* Temporarily resurrect the object. */
+ #ifdef Py_TRACE_REFS
+ #  ifndef Py_REF_DEBUG
+ #    error "Py_TRACE_REFS defined but Py_REF_DEBUG not."
+ #  endif
+ 	/* much too complicated if Py_TRACE_REFS defined */
+ 	_Py_NewReference((PyObject *)self);
+ #  ifdef COUNT_ALLOCS
+ 	/* compensate for boost in _Py_NewReference; note that
+ 	 * _Py_RefTotal was also boosted; we'll knock that down later.
+ 	 */
+ 	self->ob_type->tp_allocs--;
+ #  endif
+ #else /* !Py_TRACE_REFS */
+ 	/* Py_INCREF boosts _Py_RefTotal if Py_REF_DEBUG is defined */
+ 	Py_INCREF(self);
+ #endif /* !Py_TRACE_REFS */
+ 
+ 	/* Save the current exception, if any. */
+ 	PyErr_Fetch(&error_type, &error_value, &error_traceback);
+ 
+ 	killer(self);
+ 
+ 	/* 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.
+ 	 */
+ #ifdef Py_REF_DEBUG
+ 	/* _Py_RefTotal was boosted either by _Py_NewReference or
+ 	 * Py_INCREF above.
+ 	 */
+ 	_Py_RefTotal--;
+ #endif
+ 	if (--self->ob_refcnt > 0) {
+ #ifdef COUNT_ALLOCS
+ 		self->ob_type->tp_frees--;
+ #endif
+ 		return -1; /* __del__ added a reference; don't delete now */
+ 	}
+ #ifdef Py_TRACE_REFS
+ 	_Py_ForgetReference((PyObject *)self);
+ #  ifdef COUNT_ALLOCS
+ 	/* compensate for increment in _Py_ForgetReference */
+ 	self->ob_type->tp_frees--;
+ #  endif
+ #endif
+ 	return 0;
+ }
+ 
  #endif

Index: stacklesseval.c
===================================================================
RCS file: /home/cvs/slpdev/src/2.3/dev/Stackless/core/stacklesseval.c,v
retrieving revision 1.146
retrieving revision 1.147
diff -C2 -d -r1.146 -r1.147
*** stacklesseval.c	2 May 2004 01:52:48 -0000	1.146
--- stacklesseval.c	12 May 2004 15:33:56 -0000	1.147
***************
*** 98,101 ****
--- 98,102 ----
  	if (*cst && (*cst)->ob_size == size && (*cst)->ob_refcnt == 1) {
  		/* reuse it */
+ 		(*cst)->task = task;
  		return *cst;
  	}
***************
*** 322,325 ****
--- 323,327 ----
  	PyThreadState *ts = PyThreadState_GET();
  	PyObject *exception, *value, *tb;
+ 	int count = 0;
  
  	if (ts != tstate) {
***************
*** 336,340 ****
--- 338,344 ----
  		while (cs->task == NULL && cs->next != csfirst) {
  			cs = cs->next;
+ 			++count; /* for debugging */
  		}
+ 		count = 0;
  		if (cs->task == NULL)
  			return;
***************
*** 559,563 ****
  	--ts->st.nesting_level;
  	/* see whether we need to trigger a pending interrupt */
! 	if (ts->st.flags.pending_irq)
  		slp_check_pending_irq();
  	return retval;
--- 563,569 ----
  	--ts->st.nesting_level;
  	/* see whether we need to trigger a pending interrupt */
! 	/* note that an interrupt handler guarantees current to exist */
! 	if (ts->st.interrupt != NULL &&
! 	    ts->st.current->flags.pending_irq)
  		slp_check_pending_irq();
  	return retval;


_______________________________________________
Stackless-checkins mailing list
Stackless-checkins at stackless.com
http://www.stackless.com/mailman/listinfo/stackless-checkins



More information about the Stackless-checkins mailing list