From 2efed5995739837ec3863e1a6f568faf10da6c2a Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 1 Oct 2018 11:31:53 +0100 Subject: [PATCH] Retry unlocking if EPERM See: https://github.com/electron-userland/electron-json-storage/issues/116 Signed-off-by: Juan Cruz Viotti --- lib/lock.js | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/storage.js | 21 +++--------- 2 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 lib/lock.js diff --git a/lib/lock.js b/lib/lock.js new file mode 100644 index 0000000..a38a84a --- /dev/null +++ b/lib/lock.js @@ -0,0 +1,87 @@ +/* + * The MIT License + * + * Copyright (c) 2018 Juan Cruz Viotti. https://github.com/jviotti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +'use strict'; + +const lockFile = require('lockfile'); + +/** + * @summary Lock options + * @type {Object} + * @private + */ +const lockOptions = { + stale: 10000, + retries: Infinity, + retryWait: 50 +}; + +/** + * @summary Create a lock file + * @function + * @public + * + * @param {String} file - lock file + * @param {Function} callback - callback (error) + * + * @example + * lock.lock('foo.lock', function(error) { + * if (error) { + * throw error; + * } + * }) + */ +exports.lock = function(file, callback) { + lockFile.lock(file, lockOptions, callback); +}; + +/** + * @summary Unlock a lock file + * @function + * @public + * + * @param {String} file - lock file + * @param {Function} callback - callback (error) + * + * @example + * lock.unlock('foo.lock', function(error) { + * if (error) { + * throw error; + * } + * }) + */ +exports.unlock = function(file, callback, times) { + times = times || 0; + + lockFile.unlock(file, function(error) { + if (error && error.code === 'EPERM' && times < 10) { + setTimeout(function() { + exports.unlock(file, callback, times + 1); + }, 1000); + return; + } + + return callback(error); + }); +}; diff --git a/lib/storage.js b/lib/storage.js index ff08aa4..8828cd0 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -35,18 +35,7 @@ const rimraf = require('rimraf'); const mkdirp = require('mkdirp'); const path = require('path'); const utils = require('./utils'); -const lockFile = require('lockfile'); - -/** - * @summary Lock options - * @type {Object} - * @private - */ -const lockOptions = { - stale: 10000, - retries: Infinity, - retryWait: 50 -}; +const lock = require('./lock'); /** * @summary Get the default data path @@ -143,7 +132,7 @@ exports.get = function(key, options, callback) { mkdirp(path.dirname(fileName), callback); }, function(made, next) { - lockFile.lock(utils.getLockFileName(fileName), lockOptions, function(error) { + lock.lock(utils.getLockFileName(fileName), function(error) { if (error && error.code === 'EEXIST') { return exports.get(key, options, callback); } @@ -176,7 +165,7 @@ exports.get = function(key, options, callback) { return callback(null, objectJSON); } ], function(error, result) { - lockFile.unlock(utils.getLockFileName(fileName), function(lockError) { + lock.unlock(utils.getLockFileName(fileName), function(lockError) { if (error) { return callback(error); } @@ -319,7 +308,7 @@ exports.set = function(key, json, options, callback) { }); }, function(data, next) { - lockFile.lock(utils.getLockFileName(fileName), lockOptions, function(error) { + lock.lock(utils.getLockFileName(fileName), function(error) { if (error && error.code === 'EEXIST') { return exports.set(key, json, options, callback); } @@ -331,7 +320,7 @@ exports.set = function(key, json, options, callback) { fs.writeFile(fileName, data, callback); } ], function(error) { - lockFile.unlock(utils.getLockFileName(fileName), function(lockError) { + lock.unlock(utils.getLockFileName(fileName), function(lockError) { if (error) { return callback(error); }