rwaldron / galileo-io

Intel Galileo & Intel Edison IO Plugin for Johnny-Five
http://johnny-five.io
MIT License
101 stars 26 forks source link

"exit" process isn't triggered #12

Closed qwertypants closed 10 years ago

qwertypants commented 10 years ago

I would like to trigger the "exit" process to run a custom function, however the "exit" never seems to be triggered properly. I used the "SIGINT" event to trigger an "exit" state, however it requires the use of a GPIO library to stop reading GPIO's. This approach seems to work best, however the analogRead function still runs for a few seconds after this is triggered.

var LPD8806 = require('LPD8806-node');  //https://github.com/v0od0oChild/LPD8806-node
var leds = new LPD8806(10, '/dev/spidev1.0');
var gpios = require('gpios'); // https://github.com/v0od0oChild/MuzzleyGalileoDemos/blob/master/lib/gpios.js

var Galileo = require('galileo-io');
var board = new Galileo();

board.analogRead('A0', function(data) {
    console.log(data);
});

board.on('ready', function(){
    leds.fillRGB(255, 0, 0);

    // 'exit' attempt #1: Does not get triggered when closing node server.
//    this.on('exit', function(){
//        console.log('Inside "ready" shutdown');
//        shutDown();
//    });
});

    // 'exit' attempt #2: This also does not get triggered on exit.
//board.on('exit', function(){
//    console.log('Exit outside "ready".');
//    shutDown();
//});

// 'exit' attempt #3 using "SIGINT"
// This does work, however the analogRead still runs a few times after it's triggered
// This also requires a the gpio library to shut everything down.
process.on('SIGINT', function() {
    shutDown();
    gpios.stopReadingGpios();
    gpios.unexportAll(function() {
        process.exit();
    });
});

function shutDown(){
    console.log("Shutting leds down..");
    leds.allOFF();
}
rwaldron commented 10 years ago

All pin interaction must happen after the ready event has been emitted. This won't work reliably and should be completely avoided:

board.analogRead('A0', function(data) {
    console.log(data);
});

board.on('ready', function(){
...

I'm also confused about the additional need for the gpios library—how is that aware of what Galileo-io is doing? How are you triggering SIGINT? Galileo-io's sysfs reading mechanism is bound to process ticks which abide the event loop and cease on exit.

Here is the program I wrote last week when I was trying to repro your issue:

var Galileo = require("galileo-io");
var board = new Galileo();

board.on("ready", function() {
  this.analogRead("A0", function(data) {
    console.log( "A0",  data );
  });

  process.on("exit", function() {
    console.log( "process exiting..." );
  });

  setTimeout(function() {
    process.exit(0);
  }, 2000);
});

Here is your example, reduced to eliminate aspects that Galileo-io has no control over, and corrected to read A0 only after ready is emitted:

var Galileo = require("galileo-io");
var board = new Galileo();

board.on("ready", function() {
  console.log("Ready...");
  board.analogRead("A0", function(data) {
    console.log(data);
  });
});

process.on("SIGINT", function() {
  console.log("Shutting leds down..");
  process.exit(0);
});

Typing Control-C prints "Shutting leds down..." and the program terminates immediately after

rwaldron commented 10 years ago

I figured out why it appeared that the gpios library was effective: it had nothing to do with the library itself, only that it called process.exit();

rwaldron commented 10 years ago

Sidenote

That gpios library is pathologically slow. When I was writing Galileo-io I did filesystem IO performance measurements of writing to GPIOs using various methods, including the exec("echo....") pattern used in gpios; that approach was 42.5x slower than the fastest approach to the same operation.

qwertypants commented 10 years ago

Thanks for the note on the gpio library, I removed it. Here's an updated example that triggers the exit process using the SIGINT event:

var LPD8806 = require('LPD8806-node');
var leds = new LPD8806(10, '/dev/spidev1.0');

var Galileo = require('galileo-io');
var board = new Galileo();

board.on('ready', function(){
    leds.fillRGB(255, 0, 0);
    console.log('Ready...');

    this.analogRead('A0', function(data) {
        console.log(data);
    });

    process.on('exit', function(){
        console.log('process.on("exit")');
        leds.allOFF();
    });
});

process.on('SIGINT', function() {
    console.log('process.on("SIGINT")');
    // Trigger exit process
    process.exit(0);
});
rwaldron commented 10 years ago

I don't have this led device, so can we stick to reduced cases that only involve Galileo-io?

I ran this:

var Galileo = require('galileo-io');
var board = new Galileo();

board.on('ready', function(){
  console.log('Ready...');

  this.analogRead('A0', function(data) {
    console.log(data);
  });

  process.on('exit', function(){
    console.log('process.on("exit")');
  });
});

process.on('SIGINT', function() {
  console.log('process.on("SIGINT")');
  // Trigger exit process
  process.exit(0);
});

Typed "Control-C" and this is the result:

I just pinged @RussTheAerialist about SPI's write/transfer operations—if those are async then you can't call them from within process.on("exit", ...) because there is no event loop turn that will follow.

qwertypants commented 10 years ago

Sounds good (re: -LPD8806).

The last snippet I added gives me my desired result, as your recent code also demonstrates. As long as the "SIGINT" event triggers the process.exit(0) event, then anything in process.on('exit') will be properly executed.

rwaldron commented 10 years ago

Yes, that's correct.