node-midi: Crashing with assertion/segmentation fault/bus error

Issuing new (require('midi')).input().getPortCount(); in a node repl gives me one of the following:

Assertion failed: (0), function uv_close, file ../deps/uv/src/unix/core.c, line 165. Abort trap: 6

Segmentation fault: 11

Bus error 10

But only after using the repl for a while. Easiest way for me to reproduce is by repeatedly pressing enter some 30 times.

Can reproduce on Mac running OS X 10.11.2, node v5.4.0 and on a Raspberry Pi running Linux 4.1.13, node v4.2.1.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 31 (2 by maintainers)

Commits related to this issue

Most upvoted comments

@grav Here’s an example, hacked down a bit so it only depends on node-midi, child_process, and segfault-handler.

midispawn.js

const cp = require('child_process');
const exitEvents = ['close', 'disconnect', 'error', 'exit'];

let inPortNumber = 1, outPortNumber = 1;
let outProcess = mountOutput(outPortNumber);
let inProcess = mountInput(inPortNumber, outProcess);

function mountInput(portNumber, outProcess) {
  let inProcess = cp.fork('./midiinchild.js', [portNumber]);

  inProcess.on('message', (m) => {
    m.thing = 'inProcess';
    outProcess.send(m);
  });

  exitEvents.forEach((eventType) => {
    inProcess.on(eventType, () => {
      console.log("inProcess sent (" + eventType + ")");
      outProcess.send({ method: 'crash', thing: 'inProcess' });
      mountInput(portNumber, outProcess);
    });
  });

  return inProcess;
}

function mountOutput(portNumber) {
  let outProcess = cp.fork('./midioutchild.js', [portNumber]);

  exitEvents.forEach((eventType) => {
    outProcess.on(eventType, () => {
      console.log("outProcess sent (" + eventType + ")");
      process.exit();
    });
  });

  return outProcess;
}

midiinchild.js

const midi = require('midi'), midiInput = new midi.input();
const SegfaultHandler = require('segfault-handler');

midiInput.on('message', (dt, msg) => {
  process.send({ method: 'midi', bytes: msg });
});

midiInput.openPort(Number.parseInt(process.argv[2], 10));

process.send({ method: 'ready' });

SegfaultHandler.registerHandler('incrash.log', (signal, address, stack) => {
  console.log({ signal, address, stack });
});

midioutchild.js

const midi = require('midi'), midiOutput = new midi.output();
const SegfaultHandler = require('segfault-handler');

let crashedThing = null;

midiOutput.openPort(Number.parseInt(process.argv[2], 10));

process.on('message', (m) => {
  if (m.method === 'crash' && !crashedThing) {
    crashedThing = m.thing;
    console.log(crashedThing + " crashed!");
    return;
  }

  if (m.method === 'ready' && m.thing === crashedThing) {
    console.log(crashedThing + "is back online!");
    crashedThing = null;
    return;
  }

  if (m.method === 'midi') {
    console.log("Received MIDI from input. Data: " + m.bytes.join(','));
    midiOutput.sendMessage(m.bytes);
  }
});

SegfaultHandler.registerHandler('outcrash.log', function(signal, address, stack) {
  console.log({signal, address, stack});
});

Happy days. Reopen if it comes back!

Found a workaround that may be an additional clue to solve the bug. I can create many midi inputs and still create a midi output without crashing, in a raspberry PI B model 3, by making a new process, using the node js child_process

I created a parent, that besides making many midi outputs, it does the following:

var child_process = require('child_process');

var child = child_process.fork('./midiInputWorkaround.js');
  child.send({type:"initialize",val:0});
  child.on('message', function(message) {
    console.log('input sends:', message);
 });

Then, the midiInputWorkaround.js file contains:

var midi = require('midi');
var inputList=[];

process.on('message', function(message) {
  messageResponses={
    initialize:function(val){
      console.log("initializing MIDI inputs",val);
      var inputPortCount=(new midi.input()).getPortCount();
      //for(var a=0; a<inputPortCount; a++){
        var a=1;
        var input = new midi.input();
        var portName=input.getPortName(a);
        input.openPort(a);
        input.ignoreTypes(false, false, false);
        console.log(" opening in port["+a+"]="+portName);
        input.on('message',function(a,b){
          process.send({type:"midi",val:b});
        });
      //}
      process.send({type:"initResult",val:inputList});
    }
  }
  if(messageResponses[message.type]){
    messageResponses[message.type](message.val);
  }
});

The for loop is there as “decoration” because when the library gets fixed, I can copy the code back. In my context, it will only make one midi input. As test, I fed a stream of clock messages into the midi input. If I turn a CC knob, it crashes.

I’ve found a workaround, at least for my problem: All calls to input() and output() must be closed if unused:

	var midiInput = new libmidi.input();
	// Opening a port and closing it seems to prevent some nasty segfaults
	midiInput.openPort(0);
	midiInput.closePort();
	var midiOutput = new libmidi.output();
	midiOutput.openPort(0);
	midiOutput.closePort();

I can get away without calling openPort, but then closePort ends up printing an error message.

I’m circumventing this for the moment by forking input and output into child processes and respawning when a child process crashes. Seems to work although I haven’t thoroughly tested what or how many messages are dropped. Latency seems fine, surprisingly. 100% of crashes are on the input handling process, and it seems to me there is an issue with the callback handling.