ERROR: halting stuck metamethod

Hello

I'm working on implementing a 64bit integer class for our devices and I keep getting the following error whenever I call the multiplication metamethod:

"ERROR: halting stuck metamethod"

Does anybody know what this error means or better yet what could be causing it? I cannot seem to find any documentation for it nor do I have any idea what could be causing it. If I run the same code as a separate function rather than a metamethod it works just fine.

Any help would be much appreciated.

The relevant code is:


class int64{
_v = blob(8);

constructor(v){
_v = blob(8);
if(typeof(v)=="blob" || typeof(v)=="string")
for(local i = 0; i<8 && i<v.len(); i++)
if(typeof(v)=="string")
_v[v.len()-1-i] = v[i];
else
_v[i] = v[i];
else if(typeof(v)=="integer"){
_v.writen(v,'i');
if(v<0)
_v.writen(0xffffffff,'i');
}
}

// Multiplication Metamethod
function _mul(y){
local b = blob(8);
for(local i = 0; i<8; i++){
local n=0;
for(local j = 0; (i+j)<8; j++)
b[i+j] = n = b[i+j] + (_v[i])*(y._v[j]) + (n>>8); // NB: carry = n>>8
}
return int64(b);
}

// To String Metamethod
function _tostring(){
_v.seek(0);
local vl = _v.readn('i');
local vh = _v.readn('i');
return (vh==0)?format("0x%02x",vl):format("0x%x%08x",vh,vl);
}

// Other functions...
}

x1 <- int64(0x23d577);
x2 <- int64(0x729f012);
x3 <- x1*x2; // Error apparently occurs here!!
server.log(x3);

Comments

  • edited November 2015
    What's happening here is that your Squirrel agent code gets run in a time-slicing system with everybody else's Squirrel agent code. And, for technical reasons, Squirrel can't be time-sliced while executing a metamethod. So each metamethod call is itself limited to one time-slice (1,000 Squirrel bytecode instructions) before it gets aborted as a potential CPU hog, i.e. as potentially unfair to other agents running on the same system. There's no way around this other than to make the metamethod use fewer instructions; in the specific case of int64 multiply, you could try doing the calculation 16 bits at a time instead of 8. Or just do the calculation in a non-metamethod function.

    Device-side Squirrel code, which can't be "unfair" to anyone except itself and so is not time-sliced, does not have this restriction.

    Peter
  • @smittytone Not sure what you mean by that? That line is calling the class's multiplication metamethod (_mul(y)) on the instance x1 isn't it?

    @peter OK, thank you. That is interesting and helpful to know. Looks like I need to reevaluate my approach to this. Thanks for the suggestions too.
  • I think smittytone's meaning is that you can get yourself in trouble using the built in operands. For example:

    x1 <- 1234;
    x2 <- int64(5678);
    x3 <- x1*x2; //Fails because integer type doesn't know how to handle int64s
    x4 <- x2*x1; //Succeeds
    x5 <- x2*1 //Fails because _mul assumes op


    As longs as you don't ever mix data types you should be ok for short operations as Peter points out.

    I've actually been working on an arbitrary precision signed integer class which could be used for 64-bit variables but it only supports addition and subtraction, but you can find the code here if it's helpful:
    https://gist.github.com/blindman2k/8a2d62f337f011171980
  • @brandon Right I get you. Good point I will take note to remember that.

    And neat. Nice to see somebody else working on a similar problem. Out of curiosity did you avoid adding multiplication for similiar reasons? Shrinking the multiplication function down to less operations is proving harder than I thought, even switching the arithmetic to 16 bits rather than 8.
  • I did wonder at one point about providing bignum support in Squirrel. Normally I'm dead against such things, at least in device Squirrel, because it uses up precious improm space (costing everyone) for something that could in theory be implemented as a Squirrel library on top (costing only those people who use it). But in this case the improm already contains a four-function bignum library, because the RSA cryptography used by its TLS connection demands one.

    Peter
  • I avoided multiplication because it wasn't a feature I needed, it's significant amounts of code space and it looked hard.

    For what you are doing, I would make a static library with all the math bits and a data representation class such as:

    class int64{
    _v = blob(8);

    constructor(v){
    ...
    }

    function _tostring(){
    ...
    }

    function _typeof(){
    ...
    }
    }

    class math64{
    static add(a, b){
    if(typeof a != "int64"){
    a = int64(a);
    }
    if(typeof b != "int64"){
    b = int64(b);
    }
    ...
    }

    static mul(a,b){
    ...
    }
    }

    x1 <- int64(1234);
    x2 <- 456;
    x3 <- math64.mul(x1, x2);
    x4 <- math64.add(x2, x3);

  • Oops, dropped the "function" keyword. Those should be
    static function mul(a,b){
  • Ok thanks for the help. I've reworked the class and got the multiplication function working now. Thanks for the help.

    I do have one further question about executing metamethods on the agents.

    If I run the sequence of test code on my int64 class below it runs just fine but if I add one of the commented out 'server.log(-n3);' lines I get the "ERROR: halting stuck metamethod" again which seems unexpected. There is nothing wrong with the line itself: its already in the sequence 4 times, but that particular positioning of it causes it to fail. What could be happening here?

    My thought was that the _tostring() metamethod was being called towards the end of a time slice and thus being cut off partway through, in spite of easily fitting within a time slice. If so does this mean that any metamethod can randomly (or at least unexpectedly) fail and cause this error?


    n1 <- int64(78274);
    server.log(n1);
    n2 <- int64(0x7f012);
    server.log(n2);
    n3 <- n1*n2;
    server.log(-n3); // Call this line once it works fine. Call it again and it fails
    // server.log(-n3); // Uncomment this and I get "ERROR: halting stuck metamethod" here
    server.log(n3);
    server.log(-n3);
    server.log(-n3);
    server.log(-n3); // Seems I can call the line 3 times before failing here...
    // server.log(-n3); // Uncomment this and I get "ERROR: halting stuck metamethod" here
  • @ThomasAuberson - do you care to share your complete class (at least what you have working so far)?

    I'd like to be able to generate Firebase Push ID's in the agent and dealing with numbers larger than 32-bits is a requirement...

    @peter - true first class support of a bignum library would be a welcome addition!
  • Update: adding support for big integers is in our work backlog for a future release (but unlikely to be in time for impOS 36)
  • The limitation with the single slice metamethod is already unfortunate, but I cannot understand how my very simple _tostring method is prone to this...


    return format("%i-%02i-%02i %02i:%02i:%02i", dateTime.year, dateTime.month+1, dateTime.day, dateTime.hour, dateTime.min, dateTime.sec)


    Mind that dateTime is a date(). Either format is very expensive or there might actually be a bug.
  • I've found metamethods to be a disappointment too. They look powerful and useful on paper, but have been too fickle in practice.
Sign In or Register to comment.