Recursive Function Hanging

Posted on
  • I am running 1v95.4 on the NODE MCU ESP8266 dev board. When I run this code...

    var temp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
    
    var send = function(times, i) {
      console.log('foobar', i);
      
      
      if (i < times.length) {
        send(times, i + 1);
      }
    };
    
    send(temp, 1);
    

    I get this...

    >foobar 1
    foobar 2
    foobar 3
    foobar 4
    foobar 5
    foobar 6
    foobar 7
    foobar 8
    foobar 9
    foobar 10
    foobar 11
    foobar 12
    foobar 13
    ERROR: Prompt not detected - upload failed. Trying to recover...
    

    Some times it will make it to 16, but it always hangs. Does anyone know what may be happening here?

  • ...it is always the same issue: Active executing code in level 0 messes with the upload completion detection from editor as well as console, and in your case it is even worse: your console.log() goes directly after that upload process. Reason: the (default, regular) console connection is used to upload... and because arriving code is executed as soon as it is a complete JavaScript statement or expression. The upload uses a timeout to check for a 'completion prompt' from Espruino. It does not get it but rather gets stuff from your application.

    Put your function invocation code into a function (which you call within) onInit() function. This has other advantages: you will be able to save() your code in flash (enter save() in the console after upload), and the code will run on power on.

    PS: without the convenience of last code line (setTimeout(onInit,...), you have to start your code every time after upload by entering onInit(); in the console... Helpful read up on these subjects is the conversation about simple explanation how to save code that espruino run on start?.

    var temp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
    var send = function(times, i) {
      console.log('foobar', i);
      if (i < times.length) {
        send(times, i + 1);
      }
    };
    
    function onInit() {
      send(temp, 1);
    }
    
    setTimeout(onInit, 1000); // DEVELOPMENT CONVENIENCE - REMOVE BEFORE SAVING
    
  • Thank you! When I run it with the onInit() I get this...

    foobar 1
    foobar 2
    foobar 3
    foobar 4
    foobar 5
    foobar 6
    foobar 7
    foobar 8
    foobar 9
    foobar 10
    foobar 11
    foobar 12
     ets Jan  8 2013,rst cause:2, boot mode:(3,6)
    load 0x40100000, len 2408, room 16
    tail 8
    chksum 0xe5
    load 0x3ffe8000, len 776, room 0
    tail 8
    chksum 0x84
    load 0x3ffe8310, len 632, room 0
    tail 8
    chksum 0xd8
    csum 0xd8
    2nd boot version : 1.6
      SPI Speed      : 80MHz
      SPI Mode       : QIO
      SPI Flash Size & Map: 32Mbit(512KB+512KB)
    jump to run user1 @ 1000
    ãì2oäpòN|ìlàl`ã{²pþ
    
  • @user84505 looks like you found a ESP8266 specific hard limit or bug

    My device freezes or resets with cause 2 or 4, if there are more than eleven entires in temp[]

  • looks like stack overflow... I though assume @Gordon would make that fail more gracefully. So I go for the cpu hog-ing for longer then allowed on the ESP8266 platform. There is a limit - in ms - that another process than the ESP8266 core (tending to WiFi) can hog the CPU...

    Normally, a piece of JavaScript is triggered by a Hardware, for example, pin state change (setWatch), timer (incl. setTimeout(), setInterval()), and communication (data sent, received data available) event, and ends within that time frame serving these events. After serving by the JS interpreter, control returns to ESP8266 core first, and if it goes idle, the next piece of JS is picked up if there are still events for it in the event queue.

    ESP8266 is just not the platform to run two sophisticated systems on a single processor...Wifi is - time wise -just too demanding and leaves only little computing resources to other activities. Therefore, Espruino-Wifi or a plain Original or Pico combined with a ESP8266 (ESP-01) are the solution: ESP8266 has enough juice to tend to Wifi and have communication with the host-mc over serial. The host-mc works the application code and communicates to ESP8266 over the serial.

  • switching to a build with debug info you get this details:

    ASSERT(jsvGetLocks(var) < 15) FAILED AT src/jsvar.c:585
      #1[r10,l2] Object {
        #2[r1,l2] ASSERT(jsvGetLocks(var) < 15) FAILED AT src/jsvar.c:571
     33789> ASSERT FAILED AT src/jsvar.c:571
    ---console end---
     33794> CRASHING.
     33796>
     33797>
     33798> ***** Fatal exception 9
     33801> pc=0x4020bd31 sp=0x3fffe690 excvaddr=0x0000dead
     33806> ps=0x00000030 sar=0x00000008 vpri=0x3ffe99ef
     33810> r00: 0x4020bd2b=1075887403 r01: 0x3fffe690=1073735312 r02: 0x0000dead=     57005
     33818> r03: 0x0000beef=     48879 r04: 0x00000000=         0 r05: 0x00000018=        24
     33826> r06: 0x6000001c=1610612764 r07: 0x00000082=       130 r08: 0x00000000=         0
     33834> r09: 0xffffffff=        -1 r10: 0x0000000c=        12 r11: 0x0000002d=        45
     33842> r12: 0x00000016=        22 r13: 0x3ffe99e2=1073650146 r14: 0x3ffe98e3=1073649891
     33850> r15: 0x3fffe790
    
  • @MaBe, thanks for the in-depth information... not a stack overflow but something of a similar pattern: In Espruino automatic memory management / garbage collection support, a variable can obviously only 'be held hostage' (be locked 'up') by max. 15 running contexts... (or what ever the context's naming by @Gordon is)... which at the same time limits also the calling / call stack depth. This may be variable per platform depending on available memory?

  • JSV_LOCK_MAX is hard coded for every platform, see src/jsutils.h

  • Thanks @MaBe & @allObjects!

    Why does this need to be locked/limited? recursion is a big deal in JS.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Recursive Function Hanging

Posted by Avatar for calebbrewer @calebbrewer

Actions