Skip to content

Commit cd541c7

Browse files
committed
Merge pull request bower#1077 from neoziro/fix-concurrent-renaming
Fix bug with concurrent renaming
2 parents 3074711 + 8016bd4 commit cd541c7

2 files changed

Lines changed: 63 additions & 18 deletions

File tree

lib/core/ResolveCache.js

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,34 +108,55 @@ ResolveCache.prototype.store = function (canonicalDir, pkgMeta) {
108108
release = that._getPkgRelease(pkgMeta);
109109
dir = path.join(that._dir, sourceId, release);
110110

111-
// Check if directory exists
112-
return Q.nfcall(fs.stat, dir)
113-
.then(function () {
114-
// If it does exists, remove it
115-
return Q.nfcall(rimraf, dir);
116-
}, function (err) {
117-
// If directory does not exists, ensure its basename
118-
// is created
119-
if (err.code === 'ENOENT') {
120-
return Q.nfcall(mkdirp, path.dirname(dir));
121-
}
111+
var checkExistingDirectory;
112+
var key = 'moving:' + dir;
122113

123-
throw err;
124-
})
114+
if (! that._cache.has(key)) {
115+
// Check if directory exists
116+
checkExistingDirectory = Q.nfcall(fs.stat, dir)
117+
.then(function () {
118+
// If it does exists, remove it
119+
return Q.nfcall(rimraf, dir);
120+
}, function (err) {
121+
// If directory does not exists, ensure its basename
122+
// is created
123+
if (err.code === 'ENOENT') {
124+
return Q.nfcall(mkdirp, path.dirname(dir));
125+
}
126+
127+
throw err;
128+
});
129+
}
130+
else {
131+
checkExistingDirectory = new Q();
132+
}
133+
134+
return checkExistingDirectory
125135
// Move the canonical to sourceId/target
126136
.then(function () {
127-
return Q.nfcall(fs.rename, canonicalDir, dir)
137+
// We store the renaming in cache.
138+
var promise;
139+
if (that._cache.has(key)) {
140+
promise = that._cache.get(key);
141+
}
142+
else {
143+
promise = Q.nfcall(fs.rename, canonicalDir, dir);
144+
that._cache.set(key, promise);
145+
}
146+
147+
return promise
128148
.fail(function (err) {
129149
// If error is EXDEV it means that we are trying to rename
130150
// across different drives, so we copy and remove it instead
131151
if (err.code !== 'EXDEV') {
132152
throw err;
133153
}
134154

135-
return copy.copyDir(canonicalDir, dir)
136-
.then(function () {
137-
return Q.nfcall(rimraf, canonicalDir);
138-
});
155+
return copy.copyDir(canonicalDir, dir);
156+
})
157+
// To match all case, we remove the directory.
158+
.then(function () {
159+
return Q.nfcall(rimraf, canonicalDir);
139160
});
140161
});
141162
})

test/core/resolveCache.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,30 @@ describe('ResolveCache', function () {
310310
})
311311
.done();
312312
});
313+
314+
it('should be possible to store two package at same time', function (next) {
315+
var store = resolveCache.store.bind(resolveCache, tempPackage, {
316+
name: 'foo',
317+
_source: 'foo',
318+
_target: 'foo/bar'
319+
});
320+
var store2 = resolveCache.store.bind(resolveCache, tempPackage2, {
321+
name: 'foo',
322+
_source: 'foo',
323+
_target: 'foo/bar'
324+
});
325+
326+
Q.all([store(), store2()]).then(function (dirs) {
327+
var dir = dirs[0];
328+
expect(dir).to.equal(path.join(cacheDir, md5('foo'), 'foo%2Fbar'));
329+
expect(fs.existsSync(dir)).to.be(true);
330+
expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
331+
expect(fs.existsSync(tempPackage)).to.be(false);
332+
expect(fs.existsSync(tempPackage2)).to.be(false);
333+
334+
next();
335+
}).done();
336+
});
313337
});
314338

315339
describe('.versions', function () {

0 commit comments

Comments
 (0)