Here is an example test program exercising "start" and "reply":

   t=.test~new
   t~m1
   t~m2
   t~~start(m2)~~start(m2)~~start(m2)
   say m=.message~new(t,"m2")
   m~copy~send
   m~copy~start
   m~copy~start
   m~copy~send
   say "1. ~start ..." t~start(m2)
   t~start(m1)
   call syssleep0.01 say .context~name"..." say "2. ~start ..." t~start(m1)
   t~start(m2)
   call syssleep0.01 ::class test
   ::method m1
      say .context~name"#1 (m1: before reply)" reply say .context~name"#2 (m1: after 
reply)" ::method m2
      say .context~name
      call syssleep random(0,10)/1000 say "--- m2: about to return ---"

Here is a snip from the tracelog in xml rendering (from tracetool..rex/traceutil.cls, WIP), representing the invocation of the method M1 from line # 17:

Among other things one can see augmented information meant to allow for analyzing a tracelog offline (after the traced Rexx program to analyze has run).

---rony



On 18.08.2024 18:21, Rony G. Flatscher wrote:
On 18.08.2024 15:18, Rony G. Flatscher wrote:
On 18.08.2024 14:44, Rick McGuire wrote:
One thing you've missed is that there are situations where there is no spawning activation. For example, from native code, I can attach a thread, create a message object and send it a START message. In that situation, there is no activation to create a stack frame from.

Went through the code and it seems that in this case "MessageClass::start()" gets invoked as well? If so, there will be an oldActivity and a newActivity as well, so I probably misunderstood your remark.

If there is no stack frame available, then in "Activity::setCallerStackFrameAsStringTable(...)" this will yield an empty StringTtable to be created as a result of invoking "RexxActivation::getStackFrameAsStringTable(StackFrameClass * stackFrame)". The stringtable gets a THREAD entry added before returning it (the existence of a THREAD entry in the TraceObject's callerStackFrame stringtable indicates that the caller with that thread ID spawned the activity; this is meant to indicate that no stackframe was available at the time of creation).

Also, the stack frame in question might be a native activation, so you need to make sure that these function as well.

Again not sure what the implications of a native activation are in this context.

"Activity::generateCallerStackFrame(bool skipFirst)" was modeled after "Activity::generateStackFrames(bool skipFirst)" assuming that it would therefore cover all scenarios related to dealing with stack frames.

Should the stack frame be NULL then as described above an empty StringTable gets created (meant to indicate that no stackframe was available at the time of creation) and a THREAD entry will be added to indicate the thread ID on which the spawn took place.

If this is not sufficient, then please let me know what needs to be done.

---rony

P.S.: The rexxref book needs to be updated to explain the cicrumstance when a THREAD entry gets added (and why it can be the case that the callerStackFrame StringTable otherwise may not contain stackframe related entries, e.g. when spawned from native code).

P.P.S.: As there was a left-over statement that serves no purpose anymore I removed it, hence here the updated diff:

    Index:*interpreter/classes/MessageClass.cpp*
    ===================================================================
    --- interpreter/classes/MessageClass.cpp    (revision 12859)
    +++ interpreter/classes/MessageClass.cpp    (working copy)
    @@ -537,6 +537,10 @@
          // spawn a new activity off of the old activity
          Activity *oldActivity = ActivityManager::currentActivity;
          Activity *newActivity = oldActivity->spawnReply();
    +
    + // save current frame info in a StringTable as it has caused the creation 
of a new activity
    + Activity::setCallerStackFrameAsStringTable(oldActivity, newActivity, 
true); // create
    stackframe from parent/caller
    +
          // mark which activity we're running on, then dispatch the message
          // on the new activity (which is sitting waiting for work to perform)
          setField(startActivity, newActivity);
    Index:*interpreter/concurrency/Activity.cpp*
    ===================================================================
    --- interpreter/concurrency/Activity.cpp    (revision 12859)
    +++ interpreter/concurrency/Activity.cpp    (working copy)
    @@ -1159,31 +1159,53 @@
      }
+
      /**
    - * Generates a StackFrame from the parent and returns it.
    + * Generate the caller's stack frame.
       *
    - * @return parent's StackFrame or NULLOBJECT, if no parent exists
    + * @param skipFirst Determines if we should skip the first frame: + * 
false for reply in
    RexxActivation::run(...), + * true for Message::start() and in the general 
case. + * + *
    @return The stackframe of the caller which caused a spawned activity.
       */
    -StackFrameClass* Activity::generateParentStackFrame()
    +StackFrameClass* Activity::generateCallerStackFrame(bool skipFirst)
      {
    -    // create lists for both the stack frames and the traceback lines
    -    StackFrameClass *parentStackFrame = NULLOBJECT;
    -
          ActivationFrame *frame = activationFrames;
    + StackFrameClass *callerStackFrame = NULL;
- if (frame != NULL)
    + if (frame && skipFirst)
          {
    -        frame = frame->next;    // get parent
    -        if (frame != NULL)
    -        {
    -            parentStackFrame = frame->createStackFrame();
    -        }
    + frame = frame->next;
          }
    -    return parentStackFrame;
    + if (frame) + { + callerStackFrame = frame->createStackFrame(); + } + 
return callerStackFrame;
      }
/**
    + * Generates and saves a StringTable to store stackFrame infos of 
oldActivity in newActivity
    + * (for TraceObject), used by MessageClass::start(). + * + */ +void
    Activity::setCallerStackFrameAsStringTable(Activity *oldActivity, Activity 
*newActivity, bool
    skipFirst) +{ + // get caller stackframe, if any + StackFrameClass 
*oldActivityStackFrame =
    oldActivity -> generateCallerStackFrame(skipFirst); + // returns a 
StringTable that may be
    empty if oldActivityStackFrame==NULLOBJECT + newActivity ->
    
spawnerStackFrameInfo=RexxActivation::getStackFrameAsStringTable(oldActivityStackFrame);
 + //
    save thread ID to indicate from which thread ID the spawnReply() came from 
(helpful, e.g., if
    StringTable is empty) + // if oldActivity NULL, then THREAD will be 0 to 
indicate this +
    newActivity -> spawnerStackFrameInfo -> put(new_integer(oldActivity -> 
getIdntfr()),
    GlobalNames::THREAD ); + return; +}
    +
    +
    +
    +/**
       * Build a message and perform the indicated substitutions.
       *
       * @param messageCode
    Index:*interpreter/concurrency/Activity.hpp*
    ===================================================================
    --- interpreter/concurrency/Activity.hpp    (revision 12859)
    +++ interpreter/concurrency/Activity.hpp    (working copy)
    @@ -159,8 +159,12 @@
          void        unwindToFrame(RexxActivation *frame);
          void        cleanupStackFrame(ActivationBase *poppedStackFrame);
          ArrayClass  *generateStackFrames(bool skipFirst);
    -    StackFrameClass  *generateParentStackFrame();
+ // allow TraceObject's callerStackFrame entry to indicate the caller that caused the spawned
    activity + StackFrameClass* Activity::generateCallerStackFrame(bool ); + 
static void
    Activity::setCallerStackFrameAsStringTable(Activity *oldActivity, Activity 
*newActivity,
    bool); + StringTable *spawnerStackFrameInfo;
    +
          Activity *spawnReply();
void exitKernel();
    Index:*interpreter/execution/RexxActivation.cpp*
    ===================================================================
    --- interpreter/execution/RexxActivation.cpp        (revision 12859)
    +++ interpreter/execution/RexxActivation.cpp        (working copy)
    @@ -702,6 +702,9 @@
activity = oldActivity->spawnReply(); + // save current frame info in a StringTable as it has caused the creation of a new activity
    + Activity::setCallerStackFrameAsStringTable(oldActivity, activity, false);
    +
                      // save the pointer to the start of our stack frame.  
We're
                      // going to need to release this after we migrate 
everything
                      // over.
    @@ -5157,8 +5160,23 @@
          }
          else if (tracePrefix == TRACE_PREFIX_INVOCATION)    // tracing an 
invocation entry
          {
    -        StackFrameClass *stackFrame = activity -> 
generateParentStackFrame();
    -        traceObject -> put(stackFrame ? 
getStackFrameAsStringTable(stackFrame) : TheNilObject, 
GlobalNames::CALLERSTACKFRAME);
    + StackFrameClass *stackFrame = activity -> generateCallerStackFrame(true); 
+ if (stackFrame)
    + { + traceObject -> put(getStackFrameAsStringTable(stackFrame),
    GlobalNames::CALLERSTACKFRAME); + } + else + { + if (activity -> 
spawnerStackFrameInfo) //
    spawner's frame infos saved as a StringTable? + { + // allows for analyzing 
trace logs to
    identify the caller of a spawned activity + traceObject -> put(activity ->
    spawnerStackFrameInfo, GlobalNames::CALLERSTACKFRAME); + } + else + { + 
traceObject ->
    put(TheNilObject, GlobalNames::CALLERSTACKFRAME); + } + }
          }
// METHODCALL, fill in method related information
    @@ -5232,26 +5250,28 @@
      *   the identityHash of the StackFrame entries EXECUTABLE and TARGET if 
they are not
      *   .nil.
      *
    -*   @param stackFrame the StackFrame object to use
    -*   @return a StringTable with all the entries of stackFrame
    +* @param stackFrame the StackFrame object to use, may be NULLOBJECT +* 
@return a StringTable
    with all the entries of stackFrame, if available
      */
      StringTable * RexxActivation::getStackFrameAsStringTable(StackFrameClass 
* stackFrame)
      {
    -    ProtectedObject result;
          StringTable *tmpStringTable = new_string_table();
    -    // array
    -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::ARGUMENTS , 
result), GlobalNames::ARGUMENTS );
    + if (stackFrame != NULLOBJECT) + { + ProtectedObject result; + // array + 
tmpStringTable ->
    put(stackFrame->sendMessage(GlobalNames::ARGUMENTS , result), 
GlobalNames::ARGUMENTS );
- // strings
    -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::LINE      , 
result), GlobalNames::LINE      );
    -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::NAME      , 
result), GlobalNames::NAME      );
    -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TRACELINE , 
result), GlobalNames::TRACELINE );
    -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TYPE      , 
result), GlobalNames::TYPE      );
    + // strings + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::LINE , result),
    GlobalNames::LINE ); + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::NAME ,
    result), GlobalNames::NAME ); + tmpStringTable ->
    put(stackFrame->sendMessage(GlobalNames::TRACELINE , result), 
GlobalNames::TRACELINE ); +
    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TYPE , result), 
GlobalNames::TYPE );
- // objects
    -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::EXECUTABLE, 
result), GlobalNames::EXECUTABLE);
    -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TARGET    , 
result), GlobalNames::TARGET    );
    -
    + // objects + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::EXECUTABLE, result),
    GlobalNames::EXECUTABLE); + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::TARGET
    , result), GlobalNames::TARGET ); + }
          return tmpStringTable;
      }
_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to