This is an experimental implementation, a few details need refining and may/will change in the future. In particular, the usage of some global or static variables should be replaced by thread variables, so that this becomes compatible with the usage of OS threads.
The details provided here are additional explanations to the comments found in the files NRE2.h and NRE2.c; in case of conflict, I guess the comments in those files take priority.
The muThread
structure is declared in NRE2.h; we maintain a circular
list of threads, the manager will assign execution time to each of
them following a simple round-robin policy - see details below:
typedef struct muThread { EvalFrame *evalFrameQPtr; /* The top EvalFrame in the thread's stack. */ EvalFunction *evalFunPtr; /* The EvalFunction for the top EvalFrame. */ int result; struct muThread *next; struct muThread *prev; int flags; } muThread;
The flags indicate the current state of the muThread
; for now,
only the value TCL_MUTHREAD_BLOCKED
is defined (and not used); this
will have to be thought over when extensions that actually exploit
this functionality are implemented.
A new muThread
is created, linked into the list and made current
by a call to Tcl_NewMuThread()
; similarly, a call to Tcl_FreeMuThread()
will remove the current thread - it should be called after cleaning
it up.
The muThread
system is initialized by the first call to Tcl_NewThread()
;
such a call is now made from Tcl_Main()
.
Evaluation and return functions have the same type, declared in NRE2.h:
typedef struct EvalFrame *EvalFramePtr; typedef int EvalFunction _ANSI_ARGS_ ((int result, EvalFramePtr evalFramePtr));
Return functions should pop the eval frame before returning (see below on how to do that); otherwise, they will be called again. By the way, this provides an interesting mechanism for loop control - just do a conditional popping ...
We define in NRE2.h the EvalFrame
structure as a member of a
linked list:
typedef struct EvalFrame { Tcl_Interp *interp; EvalFunction *retFunPtr; /* function to call on return; continuation */ struct EvalFrame *prev; /* previous EvalFrame in the muThread stack */ void *data1; void *data2; void *data3; void *data4; void *data5; void *data6; } EvalFrame;
The data fields are set by the function that sets an EvalFrame
.
They serve the double purpose of passing arguments to the function
that should eval the frame, as well as to the return function.
A new EvalFrame
(already queued at the top of the current muThread
's
stack) is obtained as the return value of the function Tcl_PushEvalFrame()
;
a call to Tcl_PopEvalFrame()
removes the current EvalFrame
from the
stack and returns to a pool of unused frames for reuse.
I do not think I can do a better job at explaining the functioning
of the flow managers than directing the reader to look at the coding
of Tcl_EvalFrames(evalFunPtr)
in NRE2.c
A typical call to evaluate a frame in a new manager is in the
body of the function EvalObjv
(tclParse.c)
evalFramePtr = Tcl_PushEvalFrame(); evalFramePtr->interp = interp; evalFramePtr->data1 = (void *) objc; evalFramePtr->data2 = (void *) objv; evalFramePtr->data3 = (void *) command; evalFramePtr->data4 = (void *) length; evalFramePtr->data5 = (void *) flags; return Tcl_EvalFrames(ObjvEvaluationEngine);
The non-recursive version TclEvalObjvNRE
just directs the execution
to a previous manager, by replacing the last line with
Tcl_EvalFramesNRE(ObjvEvaluationEngine);
This particular example does not require any processing on return,
so that the field evalFramePtr->retFunPtr
retains its default
NULL
value.
The function TclExecuteByteCode
(tclExecute.c) requires different
entry points (currently three, more in the future) as it has to process
also the return of iterative calls.
C does not provide the possibility of having different entry
points to the same function (i.e., there is no analog to Fortran's
ENTRY
). The only solution to this conundrum is to program a switch
on entry that directs to the correct section of the code. There are
some problems with this approach (as compared to the labels-as-values
approach described below):
Using labels-as-values, this can be implemented as an indirect
jump: the EvalFrame
contains the jump-address for reentry, instead
of an integer value on which to switch
. Both versions are implemented
here, labels-as-values will be chosen when compiling with gcc.
All public Tcl evaluation functions are implemented to call a new manager, so that they are safe for usage by functions that are not aware of NRE.