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.

npm install race condition

See original GitHub issue

Description

When using hap-nodejs via npm I discovered that it actually mattered in what order the packages hap-nodejs and node-persist@0.0.8 are installed.

When I initially attempted to use this library, I looked at the BridgedCore.js example. Then I installed the needed packages and tried to refactor the code, so that it uses the npm dependencies.

I encountered a lot of problems related to localStorage being undefined. This is because of a hack, this library relies on. It trusts node-persist to be the same instance on the library & BridgedCore.js files. However, when you install hap-nodejs first, npm will install two instances of node-persist, which breaks the localStorage.

Case Distinction

a) install hap-nodejs first

This reliably fails on both of my systems, since npm will install two instances of node-persist.

Console Output

HAP-NodeJS starting...
/home/dodekeract/code/HAP-NodeJS/node_modules/hap-nodejs/node_modules/node-persist/node-persist.js:107
        return localStorage.getItem(key, callback);
                           ^

TypeError: Cannot read property 'getItem' of undefined
    at Object.nodePersist.getItem (/home/dodekeract/code/HAP-NodeJS/node_modules/hap-nodejs/node_modules/node-persist/node-persist.js:107:28)
    at Function.AccessoryInfo.load (/home/dodekeract/code/HAP-NodeJS/node_modules/hap-nodejs/lib/model/AccessoryInfo.js:103:23)
    at Bridge.Accessory.publish (/home/dodekeract/code/HAP-NodeJS/node_modules/hap-nodejs/lib/Accessory.js:417:39)
    at Object.<anonymous> (/home/dodekeract/code/HAP-NodeJS/BridgedCore.js:25:8)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:139:18)

npm list

hap-nodejs-bug@1.0.0 /home/dodekeract/code/HAP-NodeJS
β”œβ”€β”¬ hap-nodejs@0.3.2
β”‚ β”œβ”€β”¬ curve25519@1.1.0 (git://github.com/KhaosT/node-curve25519.git#922eae612bb379fa84e8deb29f80c53d57c8be7b)
β”‚ β”‚ β”œβ”€β”€ bindings@1.2.1
β”‚ β”‚ └── nan@2.3.2
β”‚ β”œβ”€β”¬ debug@2.2.0
β”‚ β”‚ └── ms@0.7.1
β”‚ β”œβ”€β”¬ ed25519@0.0.3 (git://github.com/KhaosT/ed25519.git#6356bc9e6e4643d56020293c14920ac12bcaa1ac)
β”‚ β”‚ β”œβ”€β”€ bindings@1.2.1
β”‚ β”‚ └── nan@2.3.2
β”‚ β”œβ”€β”¬ mdns@2.3.2
β”‚ β”‚ β”œβ”€β”€ bindings@1.2.1
β”‚ β”‚ └── nan@2.2.1
β”‚ β”œβ”€β”¬ node-persist@0.0.8
β”‚ β”‚ β”œβ”€β”€ mkdirp@0.3.5
β”‚ β”‚ └── q@1.1.2
β”‚ └─┬ srp@0.2.0 (git://github.com/KhaosT/node-srp.git#7feff53254897a0b1b9f0667ad17a8beb78acbb9)
β”‚   └─┬ bignum@0.11.0
β”‚     └── nan@2.3.2
└─┬ node-persist@0.0.8
  β”œβ”€β”€ mkdirp@0.3.5
  └── q@1.1.2

Two separate instances of node-persist.

b) install node-persist@0.0.8 first

This fixed the issue, since only one instance of node-persist will be installed. Took me about 4 hours to figure this one out though.

Console Output

HAP-NodeJS starting...

npm list

hap-nodejs-bug@1.0.0 /home/dodekeract/code/HAP-NodeJS
β”œβ”€β”¬ hap-nodejs@0.3.2
β”‚ β”œβ”€β”¬ curve25519@1.1.0 (git://github.com/KhaosT/node-curve25519.git#922eae612bb379fa84e8deb29f80c53d57c8be7b)
β”‚ β”‚ β”œβ”€β”€ bindings@1.2.1
β”‚ β”‚ └── nan@2.3.2
β”‚ β”œβ”€β”¬ debug@2.2.0
β”‚ β”‚ └── ms@0.7.1
β”‚ β”œβ”€β”¬ ed25519@0.0.3 (git://github.com/KhaosT/ed25519.git#6356bc9e6e4643d56020293c14920ac12bcaa1ac)
β”‚ β”‚ β”œβ”€β”€ bindings@1.2.1
β”‚ β”‚ └── nan@2.3.2
β”‚ β”œβ”€β”¬ mdns@2.3.2
β”‚ β”‚ β”œβ”€β”€ bindings@1.2.1
β”‚ β”‚ └── nan@2.2.1
β”‚ └─┬ srp@0.2.0 (git://github.com/KhaosT/node-srp.git#7feff53254897a0b1b9f0667ad17a8beb78acbb9)
β”‚   └─┬ bignum@0.11.0
β”‚     └── nan@2.3.2
└─┬ node-persist@0.0.8
  β”œβ”€β”€ mkdirp@0.3.5
  └── q@1.1.2

As you can see, there is only one instance of node-persist.

Code To Reproduce This

BridgedCore.js (modified)

var path = require('path');
var storage = require('node-persist');
var uuid = require('hap-nodejs').uuid;
var Bridge = require('hap-nodejs').Bridge;
var Accessory = require('hap-nodejs').Accessory;

console.log("HAP-NodeJS starting...");

// Initialize our storage system
storage.initSync();

// Start by creating our Bridge which will host all loaded Accessories
var bridge = new Bridge('Node Bridge', uuid.generate("Node Bridge"));

// Listen for bridge identification event
bridge.on('identify', function(paired, callback) {
  console.log("Node Bridge identify");
  callback(); // success
});

bridge.addBridgedAccessory(require('./Light_accessory.js').accessory);

// Publish the Bridge on the local network.
bridge.publish({
  username: "CC:22:3D:E3:CE:F6",
  port: 51826,
  pincode: "031-45-154",
  category: Accessory.Categories.BRIDGE
});

Light_accessory.js (modified)

var Accessory = require('hap-nodejs').Accessory;
var Service = require('hap-nodejs').Service;
var Characteristic = require('hap-nodejs').Characteristic;
var uuid = require('hap-nodejs').uuid;

// here's a fake hardware device that we'll expose to HomeKit
var FAKE_LIGHT = {
  powerOn: false,
  brightness: 100, // percentage

  setPowerOn: function(on) {
    console.log("Turning the light %s!", on ? "on" : "off");
    FAKE_LIGHT.powerOn = on;
  },
  setBrightness: function(brightness) {
    console.log("Setting light brightness to %s", brightness);
    FAKE_LIGHT.brightness = brightness;
  },
  identify: function() {
    console.log("Identify the light!");
  }
}

// Generate a consistent UUID for our light Accessory that will remain the same even when
// restarting our server. We use the `uuid.generate` helper function to create a deterministic
// UUID based on an arbitrary "namespace" and the word "light".
var lightUUID = uuid.generate('hap-nodejs:accessories:light');

// This is the Accessory that we'll return to HAP-NodeJS that represents our fake light.
var light = exports.accessory = new Accessory('Light', lightUUID);

// Add properties for publishing (in case we're using Core.js and not BridgedCore.js)
light.username = "1A:2B:3C:4D:5E:FF";
light.pincode = "031-45-154";

// set some basic properties (these values are arbitrary and setting them is optional)
light
  .getService(Service.AccessoryInformation)
  .setCharacteristic(Characteristic.Manufacturer, "Oltica")
  .setCharacteristic(Characteristic.Model, "Rev-1")
  .setCharacteristic(Characteristic.SerialNumber, "A1S2NASF88EW");

// listen for the "identify" event for this Accessory
light.on('identify', function(paired, callback) {
  FAKE_LIGHT.identify();
  callback(); // success
});

// Add the actual Lightbulb Service and listen for change events from iOS.
// We can see the complete list of Services and Characteristics in `lib/gen/HomeKitTypes.js`
light
  .addService(Service.Lightbulb, "Fake Light") // services exposed to the user should have "names" like "Fake Light" for us
  .getCharacteristic(Characteristic.On)
  .on('set', function(value, callback) {
    FAKE_LIGHT.setPowerOn(value);
    callback(); // Our fake Light is synchronous - this value has been successfully set
  });

// We want to intercept requests for our current power state so we can query the hardware itself instead of
// allowing HAP-NodeJS to return the cached Characteristic.value.
light
  .getService(Service.Lightbulb)
  .getCharacteristic(Characteristic.On)
  .on('get', function(callback) {

    // this event is emitted when you ask Siri directly whether your light is on or not. you might query
    // the light hardware itself to find this out, then call the callback. But if you take longer than a
    // few seconds to respond, Siri will give up.

    var err = null; // in case there were any problems

    if (FAKE_LIGHT.powerOn) {
      console.log("Are we on? Yes.");
      callback(err, true);
    }
    else {
      console.log("Are we on? No.");
      callback(err, false);
    }
  });

// also add an "optional" Characteristic for Brightness
light
  .getService(Service.Lightbulb)
  .addCharacteristic(Characteristic.Brightness)
  .on('get', function(callback) {
    callback(null, FAKE_LIGHT.brightness);
  })
  .on('set', function(value, callback) {
    FAKE_LIGHT.setBrightness(value);
    callback();
  })

Proposed Solution

Since relying on only having one instance of node-persist is apparently error-prone, I suggest to just allow users to pass in any storage solution they want to pass into this library. At least allow passing an instance of node-persist to have a reliable way to fix this issue.

Versions

npm: 2.15.1 node: 4.4.3 hap-nodejs: 0.3.2 node-persist: 0.0.8 OS: Ubuntu 16.04 & Raspbian Jessie

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:2
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

7reactions
underbluewaterscommented, Jun 16, 2016

Okay, found a solution to the problem with node-persist initialization. For me at least. I was able to call the init method exposed by the main hap-nodejs module and that solved the issue. So in my code, it looks like this:

// no need for this
// const storage = require('node-persist');
// storage.initSync();
const HAP = require('hap-nodejs');
HAP.init();
1reaction
KhaosTcommented, May 9, 2016

@dodekeract and if you want, here is the sample test I created trying to reproduce this problem. After npm install it runs fine.

Read more comments on GitHub >

github_iconTop Results From Across the Web

npm install fails intermittently due to race condition in Redirect ...
It was extremely difficult to investigate because it's a race condition that will stop happening if you change any number of things (theΒ ......
Read more >
handle-race-condition - npm
Module that solves race condition problem with async calls. Latest version: 3.0.2, last published: ... Install. npm i handle-race-condition ...
Read more >
Node.js race conditions
A race condition is a type of programming error that can occur when multiple processes or threads are accessing the same shared resource,...
Read more >
How to deal with race conditions in nodejs - Stack Overflow
try this, you can't do async call like this with map. There are patterns to solve this issue. Promise.all is one of them....
Read more >
NodeJS - Race Condition File-Write - SKF write-ups
First make sure nodejs and npm are installed on your host machine. After installation, we go to the folder of the lab we...
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