View Issue Details

IDProjectCategoryView StatusLast Update
0000477LDMud 3.3Efunspublic2009-01-17 11:12
Reporterfufu Assigned Tofufu  
PrioritynormalSeverityfeatureReproducibilityalways
Status resolvedResolutionfixed 
Fixed in Version3.3.719 
Summary0000477: add get_eval_number() efun
Descriptionint get_eval_number()
Return the current evaluation number. It's incremented for each top-level call. [...]

The intended purpose of this function is to find out if something has already been done for the current command, heart_beat, call_out or whatever. Control flow in mudlibs is often complicated and adding the necessary infrastructure to a mudlib to answer this question is error-prone and tedious. In the driver it's just a few lines of code.
Additional Informationpatch attached. The documentation assumes that the patch in 0000476 has been applied.
TagsNo tags attached.

Relationships

parent of 0000476 resolvedfufu Improve eval statistics gathering (patch) 
related to 0000535 resolvedGnomi set_input_to should be reset before a heart_beat or call_out 

Activities

2006-06-28 12:59

 

get_eval_number.patch (2,838 bytes)   
Index: doc/efun/get_eval_number
===================================================================
--- doc/efun/get_eval_number	(revision 0)
+++ doc/efun/get_eval_number	(revision 0)
@@ -0,0 +1,17 @@
+SYNOPSIS
+        int get_eval_number()
+
+DESCRIPTION
+        Return the current evaluation number. It's incremented for each
+        top-level call. These are: commands, calls to heart_beat, reset,
+        or clean_up, and calls generated by call_out or input_to, master
+        applies triggered by external events, send_erq callbacks and the
+        calls to logon on player login.
+
+        The counter is a 32 bits counter so it can overflow occasionally.
+
+HISTORY
+        Since ldmud 3.3.372
+
+SEE ALSO
+        get_eval_cost()
Index: src/efuns.c
===================================================================
--- src/efuns.c	(revision 2306)
+++ src/efuns.c	(working copy)
@@ -8702,5 +8702,23 @@
     return sp;
 } /* f_utime() */
 
+/*-------------------------------------------------------------------------*/
+svalue_t *
+f_get_eval_number (svalue_t *sp)
+
+/* EFUN get_eval_number()
+ *
+ * Return the current evaluation number. It's incremented for each
+ * top-level call. These are: commands, calls to heart_beat, reset,
+ * or clean_up, and calls generated by call_out or input_to.
+ *
+ * The counter is a 32 bits counter so it will overflow occasionally.
+ */
+
+{
+    push_number(sp, eval_number);
+    return sp;
+} /* f_get_eval_number */
+
 /***************************************************************************/
 
Index: src/func_spec
===================================================================
--- src/func_spec	(revision 2306)
+++ src/func_spec	(working copy)
@@ -772,6 +772,7 @@
 int     set_is_wizard(object, int default: F_CONST1);
 #endif
 
+int     get_eval_number();
 
         /* Obsolete and deprecated functions */
 
Index: src/interpret.c
===================================================================
--- src/interpret.c	(revision 2306)
+++ src/interpret.c	(working copy)
@@ -697,7 +697,8 @@
 
 #endif
 
-       unsigned long total_evalcost;
+unsigned long eval_number; /* evaluation number. 32 bits - will overflow */
+unsigned long total_evalcost;
 static struct timeval eval_begin;
   /* Current total evalcost counter, and start of the evaluation.
    */
@@ -772,6 +773,7 @@
  */
 
 {
+    eval_number++;
     total_evalcost = 0;
     if (gettimeofday(&eval_begin, NULL))
     {
Index: src/interpret.h
===================================================================
--- src/interpret.h	(revision 2306)
+++ src/interpret.h	(working copy)
@@ -116,6 +116,7 @@
 extern p_int apply_cache_miss;
 #endif
 
+extern unsigned long eval_number;
 extern unsigned long total_evalcost;
 extern unsigned long last_total_evalcost;
 extern struct timeval last_eval_duration;
get_eval_number.patch (2,838 bytes)   

fufu

2008-07-02 04:31

manager   ~0000650

Should be nonintrusive enough for 3.3, I believe. Is it considered useful by anybody else?

Gnomi

2008-07-02 04:50

manager   ~0000652

I didn't have any need for such a number yet, but I don't object.

zesstra

2008-07-02 04:57

administrator   ~0000653

Same for me, as yet I don't have a real need, but I can imagine to use it at some point in the future.
BTW: We have to keep in mind the overflow of the counter (see other statistics overflow bugs).

Gnomi

2009-01-13 08:06

manager   ~0000877

Can this go into 3.3.719? (I need it for 0000535.)

zesstra

2009-01-13 08:32

administrator   ~0000878

The counter in the current patch is an unsigned long. The comments and documentation speak about a 32bit integer. I think we should be more precise in the code and use uint32(_t) or change the documentation. Or maybe even better use the to-be-created statistic counter type, see 0000521.
The number is stored in an svalue so the LPC side sees negative numbers (probably no problem). But depending on the exact type we use it may lead to a host-system dependent behaviour in LPC.

Gnomi

2009-01-13 14:32

manager   ~0000879

As this counter also (or primarily) goes to LPC it should be of the same type as the LPC int (but maybe only using the positive range).

I don't see a problem with overflows. They will happen, but this number is used to distinguish between threads in one cycle, so that it is improbable to get the same number for different threads. To distinguish over a longer period you should remember the time() too. (To make that clear we could reset the eval number at each cycle?)

zesstra

2009-01-13 15:26

administrator   ~0000881

Actually, the idea of resetting it in each cycle occurred to me as well because then I (wizard) don't have to bother about checking for the overflow (when the number gets actually smaller). But I was not sure, if the application may need it to increase over a longer period than a cycle.
Concerning using p_int: yes, you are probably right.

fufu

2009-01-13 16:40

manager   ~0000882

My original intended use case was to detect whether a function is called twice in the same evaluation. Having false positives with a /very/ low probability would be acceptable. Resetting the counter on each cycle would increase that probability dramatically, so I'd like to avoid it.

I also intended the counters to be compared for equality only. Ordering events (which would make < and <= useful) wasn't an application I had in mind originally. But timestamps can be implemented fairly easily even in the presence of overflows with an sefun:

int last; /* last value from get_eval_number() */
int overflows; /* number of overflows seen by timestamp */

/* assumption: get_eval_timestamp() gets called at least once per __INT_MAX__ top level evaluations */
mixed get_eval_timestamp()
{
    int stamp = get_eval_number();
    if (stamp < last) overflows++;
    last = stamp;
    return ({ overflows, stamp });
}

or, if you want to include the current time:

int then; /* last seen time() */
int last; /* last seen eval number */

/* assumption: fewer than __INT_MAX__ top level evaluations per backend cycle */
mixed get_eval_timestamp()
{
    int stamp = get_eval_number() & __INT_MAX__;
    int now = time();
    if (now != then) {
        then = now;
        last = stamp;
    }
    stamp = (stamp - last) & __INT_MAX__;
    return ({ now, stamp });
}

As for the type, yes, p_int seems to be the right one.

Gnomi

2009-01-13 16:45

manager   ~0000883

Agreed.

zesstra

2009-01-13 16:53

administrator   ~0000884

I am just a bit confused. Isn't impossible that two function calls are in the same top-level evaluation if it is not the same backend cycle? Or better: what do I miss? ;-)

Gnomi

2009-01-13 16:56

manager   ~0000885

Fuchur wants to compare the eval_number only (without regard to time()), and without time() you don't know wether you are in the same backend cycle.

zesstra

2009-01-13 17:08

administrator   ~0000886

Ok, right. I had in my mind a check like
if (debug_info(DINFO_DATA,DID_STATUS,DID_ST_HBEAT_CALLS_TOTAL) && last_eval_no != get_eval_number())

fufu

2009-01-17 08:40

manager   ~0000915

Done in svn rev 2501.

fufu

2009-01-17 11:12

manager   ~0000918

Zesstra pointed out that in light of 0000596, querying the counter should be done with debug_info instead of a new efun. I've changed this in svn rev 2505.

The new interface is debug_info(DINFO_EVAL_NUMBER)

Issue History

Date Modified Username Field Change
2006-06-28 12:59 fufu New Issue
2006-06-28 12:59 fufu File Added: get_eval_number.patch
2008-07-02 04:30 fufu Status new => assigned
2008-07-02 04:30 fufu Assigned To => fufu
2008-07-02 04:31 fufu Note Added: 0000650
2008-07-02 04:33 fufu Relationship added parent of 0000476
2008-07-02 04:50 Gnomi Note Added: 0000652
2008-07-02 04:57 zesstra Note Added: 0000653
2008-07-11 05:52 Gnomi Relationship added related to 0000535
2009-01-13 08:06 Gnomi Note Added: 0000877
2009-01-13 08:32 zesstra Note Added: 0000878
2009-01-13 14:32 Gnomi Note Added: 0000879
2009-01-13 15:26 zesstra Note Added: 0000881
2009-01-13 16:40 fufu Note Added: 0000882
2009-01-13 16:45 Gnomi Note Added: 0000883
2009-01-13 16:53 zesstra Note Added: 0000884
2009-01-13 16:56 Gnomi Note Added: 0000885
2009-01-13 17:08 zesstra Note Added: 0000886
2009-01-17 08:40 fufu Note Added: 0000915
2009-01-17 08:40 fufu Status assigned => resolved
2009-01-17 08:40 fufu Fixed in Version => 3.3.719
2009-01-17 08:40 fufu Resolution open => fixed
2009-01-17 11:12 fufu Note Added: 0000918