Search code examples
node.jsasynchronousprocessinfinite-loop

Is there any programmatic way to break infinite loop in NodeJS?


Ultimately speaking - is there a practical method (also maybe by inserting some JS constructs into the code) to break or halt the long lasting JS code during the execution? For example: can it be interrupted by some process.* object constructs, or similar? Or the other way? The valid solution may even include the NodeJS process to be killed and/or restarted. Thank you!

EDIT: I need to execute some particular user code on the server, using Function clause (ala eval, - let alone security concerns). I cannot insert any extra code inside it, only enclose it. What I need is to have a possibility to break user code after 5 minutes, if it is not finished by this time. For example:

usercode = 'Some code from the user';
pre_code = 'some controlling code for breaking the user code';
post_code = 'another controlling code';

fcode = pre_code + usercode + post_code;

<preparations for breaking usercode>

(new Function(fcode))(); // This MUST exit in 5 minutes

Solution

  • Edit:

    Answering your edit. I see the intention now. If it is running in nodejs, you can use worker_thread for that https://nodejs.org/api/worker_threads.html#worker_threads_worker_workerdata.

    For example:

    // main.js
    const runCode = (code) => { 
      const worker = new Worker("./code-executor.js", { workerData: { code: guestCode } });
      const promise = new Promise((resolve) => {
        setTimeout(() => worker.kill(), 60000 * 5);
        worker.on("error", () => {
          return reject(new SomeCustomError())
        });
        worker.on("message", (message) => {
          if(message.success) return resolve(message.result);
          return reject(new Error(message.error));
        });
      });
    
      promise.finally(() => { worker.kill() });
    
      return promise;
    }
    
    // code-executor.js
    
    const { workerData, parentPort } = require("worker_threads");
    const { code } = workerData;
    
    Promise.resolve()
     .then(() => (new Function(fcode))())
     .then((result) => {
       parentPort.postMessage({
         success: true,
         result: value
       })
     })
     .catch((error) => {
       parentPort.postMessage({
         success: true,
         error: error.message
       })
     });
    

    If it's in browser https://developer.mozilla.org/en-US/docs/Web/API/Worker The WebAPI is not exactly the same but the logic should be similar

    Original

    Killing a process. Also read: https://nodejs.org/api/process.html#process_signal_events

    process.kill(pid, "SIGINT")

    "Killing" a long running function, you gotta hack a bit. There's no elegant solution. Inject a controller which can be mutated outside of the long running function. To stop it from the outside, set controller.isStopped = true

    
    export const STOP_EXECUTION = Symbol();
    
    function longRunning(controller){
      ... codes
    
      // add stopping point
      if(controller.isStopped) throw STOP_EXECUTION;
    
      ... codes
    
      // add stopping point
      if(controller.isStopped) throw STOP_EXECUTION;
    
      ... codes
    }
    
    // catch it by 
    
    try{
      longRunnning();
    }catch(e){
      switch(true){
        e === STOP_EXECUTION: ...;  // the longRunning function is stopped from the outside
        default: ...;               // the longRunning function is throwing not because of being stopped 
    
      }
    }
    
    

    Gist: https://gist.github.com/Kelerchian/3824ca4ce1be390d34c5147db671cc9b