question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

BTHREADS_BACKEND=child_process SIGINT handling

See original GitHub issue

For my specific use case in a node app, I’d like jobs created as processes (lots of IO and long running). Experimenting with the examples, I’ve come up with the parent / child code as per below. The problem is I cannot get the exception handling to work properly without a “sleep” after it calls bree.stop(). With it, it works as expected. My naive understanding is that signal handlers should not be async in nature. Is there a better way? BTW, same problem exists if I use Graceful.

Main "dispatch" app 

const path = require('path');
 
// optional
const ms = require('ms');
const dayjs = require('dayjs');
const Graceful = require('@ladjs/graceful');
const Cabin = require('cabin');
const later = require('@breejs/later');
 
// required
const Bree = require('bree');
const sleep = require('util').promisify(setTimeout);

process.on('SIGINT',  () => {
    myExit('SIGINT');
});
process.on('SIGQUIT',  () => {
    myExit('SIGQUIT');
});
process.on('SIGTERM',  () => {
    myExit('SIGTERM');
});
async function myExit(name) {
    console.log(`\mBREE Parent Ending ${name}`);
    bree.stop();
    //removing sleep here now does not allow child to close properly (at least no logging is seen but process is closed)
    await sleep(500);
    process.exit(0);
}

console.log(`BREE starting with PID:${process.pid}`);

//
// NOTE: see the "Instance Options" section below in this README
// for the complete list of options and their defaults
//
const bree = new Bree({
  //
  // NOTE: by default the `logger` is set to `console`
  // however we recommend you to use CabinJS as it
  // will automatically add application and worker metadata
  // to your log output, and also masks sensitive data for you
  // <https://cabinjs.com>
  //
  // logger: new Cabin(),
 
  //
  // NOTE: instead of passing this Array as an option
  // you can create a `./jobs/index.js` file, exporting
  // this exact same array as `module.exports = [ ... ]`
  // doing so will allow you to keep your job configuration and the jobs
  // themselves all in the same folder and very organized
  //
  // See the "Job Options" section below in this README
  // for the complete list of job options and configurations
  //
  jobs: [
    // runs `./jobs/foo.js` on start
    // 'job-async',
//    'job-async',

    //'job-compute'
    
    // runs `./jobs/worker-5.js` on after 10 minutes have elapsed
    {   
      name: 'job-async-copy',
      interval: 5000,//later.parse.text('every 5 seconds'),
      worker: {
          workerData: { name: 'joe' }
      }

    },
    
  ]
});
 
// handle graceful reloads, pm2 support, and events like SIGHUP, SIGINT, etc.
// const graceful = new Graceful({ brees: [bree] });
// graceful.listen();
 
// start all jobs (this is the equivalent of reloading a crontab):
bree.start();

//bree.stop('job-async');

/*
// start only a specific job:
bree.start('foo');
 
// stop all jobs
bree.stop();
 
// stop only a specific job:
bree.stop('beep');
 
// run all jobs (this does not abide by timeout/interval/cron and spawns workers immediately)
bree.run();
 
// run a specific job (...)
bree.run('beep');
 
// add a job array after initialization:
bree.add(['boop']);
// this must then be started using one of the above methods
 
// add a job after initialization:
bree.add('boop');
// this must then be started using one of the above methods
 
// remove a job after initialization:
bree.remove('boop');
*/

bree.on('worker deleted', (name) => {
    console.log('worker deleted', name);
    // if (name === 'job-async' && !signal) 
    //     bree.run(name);
  });

bree.on('worker created', (name) => {
    console.log(`new worker ${name}`);
});  
Job

const threads = require('bthreads');
const sleep = require('util').promisify(setTimeout);

let signal = false;

process.on('SIGINT',  () => {
    myExit('SIGINT');
});
process.on('SIGQUIT',  () => {
    myExit('SIGQUIT');
});
process.on('SIGTERM',  () => {
    myExit('SIGTERM');
});

function myExit(name) {
    console.log(`ASYNC received ${name}`);
    signal = true;

    // if (threads.parentPort) {
    //     threads.parentPort.postMessage('cancel');
    //     console.log(`Cancel sent`);
    // } else 
    //     console.log(`Exiting...`);
}
if (!threads.isMainThread) {
    threads.parentPort.on('message', async (message) => {
        console.log(`Received message from parent [${message}]`);
    //    await sleep(300);
        if (message === 'cancel') {
            signal = true;
        }   
    });
}

if (threads.isMainThread) {
    console.log(`ASYNC on main threads`);
    
} else {
    console.log(`ASYNC on worker thread`);

}

// if (threads.parentPort) {
//     console.log(`Adding .... event receiver`);
//     threads.parentPort.once('message', async (message) => {
//     });
// }


let a = 1;
let name = 'll'; //threads.hasOwnProperty('workerData.name')  ? "UNKNOWN" : threads.workerData.name
console.log(`async-COPY starting with PID:${process.pid} and name ${name}`);
async function x() {
    for (;;) {
        a++;
        await sleep(300);
        console.log(`COPY loop ${a}`);
        // if (a==5) 
        //     threads.parentPort.postMessage('error');
        if (a>10 || signal) {
            // if (threads.parentPort) {
            //     threads.parentPort.postMessage('COPY exit');
            // }
            if (signal) {
                console.log(`COPY Exiting due to signal...`);
                if (!threads.isMainThread) {
                    console.log(`Sending to Parent...`);
                    threads.parentPort.postMessage('cancelled');
                }
            }
            process.exit(0);
        }
    }
}

x();

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:8 (7 by maintainers)

github_iconTop GitHub Comments

4reactions
niftylettucecommented, Nov 18, 2020

v3.4.0 released to npm, thank you @naz for the fix and quick PR, truly appreciate your contribution, means a lot.

https://github.com/breejs/bree/releases/tag/v3.4.0

1reaction
niftylettucecommented, Nov 18, 2020

I don’t have time to look at this yet, perhaps @shadowgate15 can

Read more comments on GitHub >

github_iconTop Results From Across the Web

Proper handling of SIGINT/SIGQUIT
On first sight, all three solutions (IUE, WUE and WCE) all seem to do what we want: If C-c is hit while the...
Read more >
Signal Handler for SIGINT
If I try to send the SIGINT using kill -INT *process pid* it just terminates the program, no msg printed. Any idea why?...
Read more >
Handling signals - YouTube
Check out our Discord server: https://discord.gg/NFxT8NY.
Read more >
The Basics of Signal Handling
Signals are one of the most basic ways programs can receive messages from the outside world. I've found limited tutorial-type documentation ...
Read more >
Signal Handling
Process can install a signal handler by calling sigaction(2) with the name of ... 2 SIGINT. Interactive attention signal (usually ˆC). Abnormal termination....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found