instruction limiter on the amx lets u limit function runs in instruction count
authorCarsten Haitzler <raster@rasterman.com>
Tue, 19 Oct 2004 09:36:35 +0000 (09:36 +0000)
committerCarsten Haitzler <raster@rasterman.com>
Tue, 19 Oct 2004 09:36:35 +0000 (09:36 +0000)
SVN revision: 11905

legacy/embryo/src/bin/embryo_main.c
legacy/embryo/src/lib/Embryo.h
legacy/embryo/src/lib/embryo_amx.c
legacy/embryo/src/lib/embryo_private.h

index 990f1ac..d37c4c6 100644 (file)
@@ -209,6 +209,7 @@ main(int argc,char *argv[])
    int r = EMBRYO_PROGRAM_OK;
    int err;
    int args = 0;
+   int instruct = 0;
    char *file = NULL;
    char *func = NULL;
 
@@ -273,23 +274,36 @@ main(int argc,char *argv[])
          }
      }
    r = EMBRYO_PROGRAM_OK;
+   fn = EMBRYO_FUNCTION_MAIN;
    if (func)
      {
        fn = embryo_program_function_find(ep, func);
-       if (fn != EMBRYO_FUNCTION_NONE)
-         {
-            while ((r = embryo_program_run(ep, fn)) == EMBRYO_PROGRAM_SLEEP);
-         }
-       else
+       if (fn == EMBRYO_FUNCTION_NONE)
          {
             printf("Unable to find public function %s()\n"
                    "Executing main() instead\n", func);
-            while ((r = embryo_program_run(ep, EMBRYO_FUNCTION_MAIN)) == EMBRYO_PROGRAM_SLEEP);
+            fn = EMBRYO_FUNCTION_MAIN;
          }
      }
-   else
+   embryo_program_max_cycle_run_set(ep, 100000000);
+   for (;;)
      {
-       while ((r = embryo_program_run(ep, EMBRYO_FUNCTION_MAIN)) == EMBRYO_PROGRAM_SLEEP);
+       r = embryo_program_run(ep, fn);
+       if (r == EMBRYO_PROGRAM_SLEEP)
+         {
+            fn = EMBRYO_FUNCTION_CONT;
+            printf("SLEEP INSTRUCTION!\n");
+            continue;
+         }
+       else if (r == EMBRYO_PROGRAM_TOOLONG)
+         {
+            fn = EMBRYO_FUNCTION_CONT;
+            instruct++;
+            printf("Executed %i00 million instructions!\n", instruct);
+            continue;
+         }
+       else
+         break;
      }
    embryo_program_vm_pop(ep);
    if (r == EMBRYO_PROGRAM_FAIL)
index 4dfffaa..d0a4eef 100644 (file)
@@ -43,10 +43,11 @@ extern "C" {
   /** An invalid cell reference */
 #define EMBRYO_CELL_NONE     0x7fffffff 
    /* program run return values */
-#define EMBRYO_PROGRAM_OK    1
-#define EMBRYO_PROGRAM_SLEEP 2
-#define EMBRYO_PROGRAM_BUSY  3
-#define EMBRYO_PROGRAM_FAIL  0
+#define EMBRYO_PROGRAM_OK      1
+#define EMBRYO_PROGRAM_SLEEP   2
+#define EMBRYO_PROGRAM_BUSY    3
+#define EMBRYO_PROGRAM_TOOLONG 4
+#define EMBRYO_PROGRAM_FAIL    0
 
    typedef unsigned int                Embryo_UCell;
    typedef int                         Embryo_Cell;
@@ -94,6 +95,8 @@ extern "C" {
    int              embryo_program_recursion_get(Embryo_Program *ep);
    int              embryo_program_run(Embryo_Program *ep, Embryo_Function func);
    Embryo_Cell      embryo_program_return_value_get(Embryo_Program *ep);
+   void             embryo_program_max_cycle_run_set(Embryo_Program *ep, int max);
+   int              embryo_program_max_cycle_run_get(Embryo_Program *ep);
    int              embryo_parameter_cell_push(Embryo_Program *ep, Embryo_Cell cell);
    int              embryo_parameter_string_push(Embryo_Program *ep, char *str);
    int              embryo_parameter_cell_array_push(Embryo_Program *ep, Embryo_Cell *cells, int num);
index 04e3a9c..2afee87 100644 (file)
@@ -1013,6 +1013,8 @@ embryo_program_recursion_get(Embryo_Program *ep)
  * @return  @c EMBRYO_PROGRAM_OK on success.  @c EMBRYO_PROGRAM_SLEEP if the
  *          program is halted by the Small @c sleep call.
  *          @c EMBRYO_PROGRAM_FAIL if there is an error.
+ *          @c EMBRYO_PROGRAM_TOOLONG if the program executes for longer than
+ *          it is allowed to in abstract machine instruction count.
  * @ingroup Embryo_Run_Group
  */
 int
@@ -1028,6 +1030,8 @@ embryo_program_run(Embryo_Program *ep, Embryo_Function fn)
    unsigned char    op;
    Embryo_Cell      offs;
    int              num;
+   int              max_run_cycles;
+   int              cycle_count;
 #ifdef EMBRYO_EXEC_JUMPTABLE
    /* we limit the jumptable to 256 elements. why? above we forced "op" to be
     * a unsigned char - that means 256 max values. we limit opcode overflow
@@ -1346,10 +1350,19 @@ embryo_program_run(Embryo_Program *ep, Embryo_Function fn)
 
    /* track recursion depth */
    ep->run_count++;
-   
+
+   max_run_cycles = ep->max_run_cycles;
    /* start running */
-   for (;;)
+   for (cycle_count = 0;;)
      {
+       if (max_run_cycles > 0)
+         {
+            if (cycle_count >= max_run_cycles)
+              {
+                 TOOLONG(ep);
+              }
+            cycle_count++;
+         }
        op = (Embryo_Opcode)*cip++;
        SWITCH(op);
        CASE(EMBRYO_OP_LOAD_PRI);
@@ -2106,10 +2119,9 @@ embryo_program_run(Embryo_Program *ep, Embryo_Function fn)
 #endif         
        SWITCHEND;
      }
+   ep->max_run_cycles = max_run_cycles;
    ep->run_count--;
-   
    ep->hea = hea_start;
-   
    return EMBRYO_PROGRAM_OK;
 }
 
@@ -2129,6 +2141,81 @@ embryo_program_return_value_get(Embryo_Program *ep)
 }
 
 /**
+ * Sets the maximum number of abstract machine cycles any given program run
+ * can execute before being put to sleep and returning.
+ * 
+ * @param   ep The given program.
+ * @param   max The number of machine cycles as a limit.
+ * 
+ * This sets the maximum number of abstract machine (virtual machine)
+ * instructions that a single run of an embryo function (even if its main)
+ * can use before embryo embryo_program_run() reutrns with the value
+ * EMBRYO_PROGRAM_TOOLONG. If the function fully executes within this number
+ * of cycles, embryo_program_run() will return as normal with either 
+ * EMBRYO_PROGRAM_OK, EMBRYO_PROGRAM_FAIL or EMBRYO_PROGRAM_SLEEP. If the
+ * run exceeds this instruction count, then EMBRYO_PROGRAM_TOOLONG will be
+ * returned indicating the program exceeded its run count. If the app wishes
+ * to continue running this anyway - it is free to process its own events or
+ * whatever it wants and continue the function by calling 
+ * embryo_program_run(program, EMBRYO_FUNCTION_CONT); which will start the
+ * run again until the instruction count is reached. This can keep being done
+ * to allow the calling program to still be able to control things outside the
+ * embryo function being called. If the maximum run cycle count is 0 then the
+ * program is allowed to run forever only returning when it is done.
+ * 
+ * It is important to note that abstract machine cycles are NOT the same as
+ * the host machine cpu cycles. They are not fixed in runtime per cycle, so
+ * this is more of a helper tool than a way to HARD-FORCE a script to only
+ * run for a specific period of time. If the cycle count is set to something
+ * low like 5000 or 1000, then every 1000 (or 5000) cycles control will be
+ * returned to the calling process where it can check a timer to see if a
+ * physical runtime limit has been elapsed and then abort runing further
+ * assuming a "runaway script" or keep continuing the script run. This
+ * limits resolution to only that many cycles which do not take a determined
+ * amount of time to execute, as this varies from cpu to cpu and also depends
+ * on how loaded the system is. Making the max cycle run too low will
+ * impact performance requiring the abstract machine to do setup and teardown
+ * cycles too often comapred to cycles actually executed.
+ * 
+ * Also note it does NOT include nested abstract machines. IF this abstract
+ * machine run calls embryo script that calls a native function that in turn
+ * calls more embryo script, then the 2nd (and so on) levels are not included
+ * in this run count. They can set their own max instruction count values
+ * separately.
+ * 
+ * The default max cycle run value is 0 in any program until set with this
+ * function.
+ * 
+ * @ingroup Embryo_Run_Group
+ */
+void
+embryo_program_max_cycle_run_set(Embryo_Program *ep, int max)
+{
+   if (!ep) return;
+   if (max < 0) max = 0;
+   ep->max_run_cycles = max;
+}
+
+/**
+ * Retreives the maximum number of abstract machine cycles a program is allowed
+ * to run.
+ * @param   ep The given program.
+ * @return  The number of cycles a run cycle is allowed to run for this 
+ *          program.
+ * 
+ * This returns the value set by embryo_program_max_cycle_run_set(). See
+ * embryo_program_max_cycle_run_set() for more information.
+ * 
+ * @ingroup Embryo_Run_Group
+ */
+int
+embryo_program_max_cycle_run_get(Embryo_Program *ep)
+{
+   if (!ep) return 0;
+   return ep->max_run_cycles;
+}
+
+/**
  * @defgroup Embryo_Parameter_Group Function Parameter Functions
  *
  * Functions that set parameters for the next function that is called.
index f0f43ce..2955f1f 100644 (file)
@@ -189,16 +189,17 @@ __entryswap32(*((unsigned int *)(entry) + 1))) \
 #define EMBRYO_MAGIC        0xf1e0 /* magic byte pattern */
 #define EMBRYO_FLAG_COMPACT 0x04   /* compact encoding */
 #define EMBRYO_FLAG_RELOC   0x8000 /* jump/call addresses relocated */
-#define GETPARAM(v)  (v = *(Embryo_Cell *)cip++)
-#define PUSH(v)      (stk -= sizeof(Embryo_Cell), *(Embryo_Cell *)(data + (int)stk) = v)
-#define POP(v)       (v = *(Embryo_Cell *)(data + (int)stk), stk += sizeof(Embryo_Cell))
-#define ABORT(ep,v)  {(ep)->stk = reset_stk; (ep)->hea = reset_hea; ep->run_count--; ep->error = v; return 0;}
-#define OK(ep,v)     {(ep)->stk = reset_stk; (ep)->hea = reset_hea; ep->run_count--; ep->error = v; return 1;}
-#define STKMARGIN    ((Embryo_Cell)(16 * sizeof(Embryo_Cell)))
-#define CHKMARGIN()  if ((hea + STKMARGIN) > stk) {ep->error = EMBRYO_ERROR_STACKERR; return 0;}
-#define CHKSTACK()   if (stk > ep->stp) {ep->run_count--; ep->error = EMBRYO_ERROR_STACKLOW; return 0;}
-#define CHKHEAP()    if (hea < ep->hlw) {ep->run_count--; ep->error = EMBRYO_ERROR_HEAPLOW; return 0;}
-#define CHKMEM(x)    if ((((x) >= hea) && ((x) < stk)) || ((Embryo_UCell)(x) >= (Embryo_UCell)ep->stp)) ABORT(ep, EMBRYO_ERROR_MEMACCESS);
+#define GETPARAM(v)         (v = *(Embryo_Cell *)cip++)
+#define PUSH(v)             (stk -= sizeof(Embryo_Cell), *(Embryo_Cell *)(data + (int)stk) = v)
+#define POP(v)              (v = *(Embryo_Cell *)(data + (int)stk), stk += sizeof(Embryo_Cell))
+#define ABORT(ep,v)         {(ep)->stk = reset_stk; (ep)->hea = reset_hea; (ep)->run_count--; ep->error = v; (ep)->max_run_cycles = max_run_cycles; return EMBRYO_PROGRAM_FAIL;}
+#define OK(ep,v)            {(ep)->stk = reset_stk; (ep)->hea = reset_hea; (ep)->run_count--; ep->error = v; (ep)->max_run_cycles = max_run_cycles; return EMBRYO_PROGRAM_OK;}
+#define TOOLONG(ep)         {(ep)->pri = pri; (ep)->cip = (Embryo_Cell)((unsigned char *)cip - code); (ep)->alt = alt; (ep)->frm = frm; (ep)->stk = stk; (ep)->hea = hea; (ep)->reset_stk = reset_stk; (ep)->reset_hea = reset_hea; (ep)->run_count--; (ep)->max_run_cycles = max_run_cycles; return EMBRYO_PROGRAM_TOOLONG;}
+#define STKMARGIN           ((Embryo_Cell)(16 * sizeof(Embryo_Cell)))
+#define CHKMARGIN()         if ((hea + STKMARGIN) > stk) {ep->error = EMBRYO_ERROR_STACKERR; return 0;}
+#define CHKSTACK()          if (stk > ep->stp) {ep->run_count--; ep->error = EMBRYO_ERROR_STACKLOW; return 0;}
+#define CHKHEAP()           if (hea < ep->hlw) {ep->run_count--; ep->error = EMBRYO_ERROR_HEAPLOW; return 0;}
+#define CHKMEM(x)           if ((((x) >= hea) && ((x) < stk)) || ((Embryo_UCell)(x) >= (Embryo_UCell)ep->stp)) ABORT(ep, EMBRYO_ERROR_MEMACCESS);
 
 typedef struct _Embryo_Param        Embryo_Param;
 typedef struct _Embryo_Header       Embryo_Header;
@@ -249,6 +250,8 @@ struct _Embryo_Program
    
    int            run_count;
    
+   int            max_run_cycles;
+   
    void          *data;
 };