This is the planned implementation of gc() which

 * always runs in a debug version of ooRexx (.RexxInfo~debug=.true)
 * does *not* run in a release version of ooRexx (.RexxInfo~debug=.false) to 
cater for Rick's
   preoccupation; however, there is an overrule possibility if the argument 
"force" is supplied for
   edge cases, using it must be deliberate for a release version. This should 
at least rule out
   using it mistakingly in the release version, if used for debugging, but not 
meant for release.

The planned description of gc() would be:

   gc(---+-------------+---)
          |             |
          +---"Force"---+


   Returns /.true/ if the garbage collector was called, /.false/ else. This 
function should be used
   with great care as explicitly invoking the garbage collector imposes quite a 
burden on a running
   system and can severely affect applications. Therefore, garbage collection 
invocations should be
   best left to the interpreter. If invoked from a debug version of ooRexx 
(.RexxInfo~debug=.true),
   the garbage collector will always be called. If invoked from a release 
version of ooRexx
   (.RexxInfo~debug=.false), the garbage collector will not be called, unless 
the optional argument
   “force” is supplied.

Enclosed you'll find the diff.

Here a little test program that invokes the garbage collector 10,000 times and compares this to invoking 10,000 times the reverse() BIF on a debug version of ooRexx:

   d1=.dateTime~new
   count=10000
   t1=test_gc(count)
   t2=test_reverse(count)
   d2=.dateTime~new

   say "test_gc("count")     :" t1 "->" t1/count "per call"
   say "test_reverse("count"):" t2 "->" t2/count "per call"
   say "total duration     :" d2 - d1


   ::routine test_gc
      use arg count
      d1=.dateTime~new
      do count/2
         call gc         -- call the garbage collector
         call gc "force" -- call the garbage collector
      end
      return .dateTime~new - d1

   ::routine test_reverse
      use arg count

      d1=.dateTime~new
      do count/2
         call reverse '' -- call reverse()
         call reverse '' -- call reverse()
      end
      return .dateTime~new - d1

Running this on a quite old PC yields:

   G:\test\orx\gc>rxenv rexx test_gc_speed.rex
   test_gc(10000)     : 00:00:00.971000 -> 00:00:00.000097 per call
   test_reverse(10000): 00:00:00.000000 -> 00:00:00.000000 per call
   total duration     : 00:00:00.971000

   G:\test\orx\gc>rxenv rexx test_gc_speed.rex
   test_gc(10000)     : 00:00:01.005000 -> 00:00:00.000100 per call
   test_reverse(10000): 00:00:00.004000 -> 00:00:00.000000 per call
   total duration     : 00:00:01.009000

   G:\test\orx\gc>rxenv rexx test_gc_speed.rex
   test_gc(10000)     : 00:00:00.987000 -> 00:00:00.000098 per call
   test_reverse(10000): 00:00:00.004000 -> 00:00:00.000000 per call
   total duration     : 00:00:00.991000

   G:\test\orx\gc>rxenv rexx test_gc_speed.rex
   test_gc(10000)     : 00:00:00.972000 -> 00:00:00.000097 per call
   test_reverse(10000): 00:00:00.008000 -> 00:00:00.000000 per call
   total duration     : 00:00:00.980000

Any comments, feedback?

---rony


On 29.08.2025 11:59, Rony G. Flatscher wrote:

Thank you all very much for your feedback (also to those who reacted with a 
direct e-mail to me)!

My take away currently is that

  * using a keyword is not really acceptable (it really looks a little bit 
awkward)
  * restricting the invocation by a predefined number may not be possible if 
programs run 7/24
    (where this feature might be important in edge cases)
  * the hints at popular languages that allow for invoking the garbage 
collector freely, making it
    the responsibility of the programmer to use it cautiously and only, if it 
is needed (e.g. for
    unpinning non-Rexx resources that are held in Rexx objects that have gone 
out of scope)
  * one should clearly document and warn about misusing this feature pointing 
out at potential
    runtime performance implications (like in other programming languages)

---rony


On 26.08.2025 22:28, Rony G. Flatscher wrote:

There is a new library for ooRexx in development by one of my students, where objects from the other side get proxied in registries. For debugging, it is necessary to be able to kick off a garbage collection run.

However, as has been pointed out and discussed, it has been seen as dangerous for the performance of ooRexx programs if ooRexx programmers use such a feature wrongly (e.g., too many times at the wrong place, etc.).

My take would be that if programmers are made aware of that potential problem and strongly advised not to use it for regular programs, only for situations where it becomes important that external resources get freed that are held by Rexx objects that became garbage but do not get garbage collected in a timely manner.

Maybe an approach like this can be acceptable?

  * define a gc() built-in function that works by default in a debug version of 
ooRexx (i.e.,
    RexxInfo~debug yields .true) and returns .true to indicate that the BIF got 
carried out,

  * invoking some gc() on the release version of ooRexx (i.e., RexxInfo~debug 
yields .false) does
    not invoke the garbage collector and returns therefore .false.

      o for very rare, special situations (like freeing external resources as 
timely as possible
        if Rexx objects pinning them down turn to garbage) it may make sense to 
allow gc() to be
        run on a release version; in order to make this explicit one could 
think of allowing an
        argument like "EMERGENCY" (intentionally in uppercase and spelled out), 
but only for the
        release version, which would indeed invoke the garbage collection on a 
release version as
        well and then return .true to indicate it. This would inhibit a 
mistakenly usage of gc().
        If one leaves the gc() invocation from the debug version, it would not 
run the garbage
        collector by default on a release version. If, however, a programmer 
needs a garbage
        collection in the release version, it must be "cumbersomely" invoked, 
such that this can
        only happen intentionally.

What do you think? Any comments?

---rony

Index: interpreter/expression/BuiltinFunctions.cpp
===================================================================
--- interpreter/expression/BuiltinFunctions.cpp (revision 13011)
+++ interpreter/expression/BuiltinFunctions.cpp (working copy)
@@ -1,7 +1,7 @@
 
/*----------------------------------------------------------------------------*/
 /*                                                                            
*/
 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved.             
*/
-/* Copyright (c) 2005-2024 Rexx Language Association. All rights reserved.    
*/
+/* Copyright (c) 2005-2025 Rexx Language Association. All rights reserved.    
*/
 /*                                                                            
*/
 /* This program and the accompanying materials are made available under       
*/
 /* the terms of the Common Public License v1.0 which accompanies this         
*/
@@ -2982,7 +2982,6 @@
     return SystemInterpreter::popEnvironment(context);
 }
 
-
 /**
  * Qualify a stream name.
  */
@@ -3000,6 +2999,45 @@
     return new_string(qualified_name);
 }
 
+/**
+ *  Invoke the garbage collector.
+ */
+BUILTIN(GC)
+{
+    const size_t GC_Min = 0;
+    const size_t GC_Max = 1;
+    const size_t GC_force = 1;
+
+    check_args(GC);
+
+    RexxString *force = optional_string(GC, force);
+
+    bool bRunGC=false;  // default to not run in release mode
+
+    if (force != OREF_NULL) // argument given, check whether argument starts 
with "F" for "force" ...
+    {
+        const char *forceData = force->getStringData();
+        if (forceData[0]==0 || (forceData[0]!='f' && forceData[0]!='F'))
+        {
+            // 40.904 function_name argument argument_number must be one of 
values; found "value".
+            reportException(Error_Incorrect_call_list, "GC", IntegerOne, 
new_string("force, Force, F, f",18), force);
+        }
+        bRunGC=true;        // run the garbage collector in release mode
+    }
+
+#ifdef _DEBUG           // in debug mode always run the GC
+    bRunGC=true;
+#endif
+
+    if (bRunGC)
+    {
+        memoryObject.collectAndUninit(false);   // keep stack
+        return TheTrueObject;   // indicate we called the garbage collector
+    }
+    return TheFalseObject;      // indicate we did not call the garbage 
collector
+}
+
+
 // the following builtin function table must maintain the same order
 // as the BuiltinCode type defined by the RexxToken class.
 pbuiltin LanguageParser::builtinTable[] =
@@ -3085,5 +3123,6 @@
     &builtin_function_ENDLOCAL         ,
     &builtin_function_SETLOCAL         ,
     &builtin_function_QUALIFY          ,
+    &builtin_function_GC               ,
 };
 
Index: interpreter/parser/KeywordConstants.cpp
===================================================================
--- interpreter/parser/KeywordConstants.cpp     (revision 13011)
+++ interpreter/parser/KeywordConstants.cpp     (working copy)
@@ -1,7 +1,7 @@
 
/*----------------------------------------------------------------------------*/
 /*                                                                            
*/
 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved.             
*/
-/* Copyright (c) 2005-2020 Rexx Language Association. All rights reserved.    
*/
+/* Copyright (c) 2005-2025 Rexx Language Association. All rights reserved.    
*/
 /*                                                                            
*/
 /* This program and the accompanying materials are made available under       
*/
 /* the terms of the Common Public License v1.0 which accompanies this         
*/
@@ -191,6 +191,7 @@
     KeywordEntry("FORM",           BUILTIN_FORM),
     KeywordEntry("FORMAT",         BUILTIN_FORMAT),
     KeywordEntry("FUZZ",           BUILTIN_FUZZ),
+    KeywordEntry("GC",             BUILTIN_GC),
     KeywordEntry("INSERT",         BUILTIN_INSERT),
     KeywordEntry("LASTPOS",        BUILTIN_LASTPOS),
     KeywordEntry("LEFT",           BUILTIN_LEFT),
@@ -272,6 +273,7 @@
     KeywordEntry("FORM",           BUILTIN_FORM),
     KeywordEntry("FORMAT",         BUILTIN_FORMAT),
     KeywordEntry("FUZZ",           BUILTIN_FUZZ),
+    KeywordEntry("GC",             BUILTIN_GC),
     KeywordEntry("INSERT",         BUILTIN_INSERT),
     KeywordEntry("LASTPOS",        BUILTIN_LASTPOS),
     KeywordEntry("LEFT",           BUILTIN_LEFT),
Index: interpreter/parser/Token.hpp
===================================================================
--- interpreter/parser/Token.hpp        (revision 13011)
+++ interpreter/parser/Token.hpp        (working copy)
@@ -1,7 +1,7 @@
 
/*----------------------------------------------------------------------------*/
 /*                                                                            
*/
 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved.             
*/
-/* Copyright (c) 2005-2024 Rexx Language Association. All rights reserved.    
*/
+/* Copyright (c) 2005-2025 Rexx Language Association. All rights reserved.    
*/
 /*                                                                            
*/
 /* This program and the accompanying materials are made available under       
*/
 /* the terms of the Common Public License v1.0 which accompanies this         
*/
@@ -508,6 +508,7 @@
     BUILTIN_ENDLOCAL,
     BUILTIN_SETLOCAL,
     BUILTIN_QUALIFY,
+    BUILTIN_GC,
 } BuiltinCode;
 
 
_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to