Skip to content

Commit

Permalink
Fix failing to acquire lock in case of parallel runs (#1524)
Browse files Browse the repository at this point in the history
Signed-off-by: Bernat Gabor <[email protected]>
  • Loading branch information
gaborbernat authored Jan 29, 2020
1 parent c6d94f5 commit 5629b41
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/changelog/1516.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Creating virtual environments in parallel fail with cannot acquire lock within app data - by ``gaborbernat``.
13 changes: 10 additions & 3 deletions src/virtualenv/util/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from __future__ import absolute_import, unicode_literals

import logging
import os
from contextlib import contextmanager
from threading import Lock

from filelock import FileLock, Timeout

from virtualenv.util.path import Path, ensure_dir
from virtualenv.util.path import Path


class _CountedFileLock(FileLock):
Expand Down Expand Up @@ -72,11 +73,17 @@ def __exit__(self, exc_type, exc_val, exc_tb):
self._release(self._lock)

def _lock_file(self, lock):
ensure_dir(self.path)
# multiple processes might be trying to get a first lock... so we cannot check if this directory exist without
# a lock, but that lock might then become expensive, and it's not clear where that lock should live.
# Instead here we just ignore if we fail to create the directory.
try:
os.makedirs(str(self.path))
except OSError:
pass
try:
lock.acquire(0.0001)
except Timeout:
logging.debug("lock file %s present, will block until released", self._lock.lock_file)
logging.debug("lock file %s present, will block until released", lock.lock_file)
lock.release() # release the acquire try from above
lock.acquire()

Expand Down
15 changes: 15 additions & 0 deletions tests/unit/create/test_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import logging
import os
import stat
import subprocess
import sys
from itertools import product
from threading import Thread

import pytest
import six
Expand Down Expand Up @@ -275,3 +277,16 @@ def test_cross_major(cross_python, coverage_env, tmp_path, current_fastest):
coverage_env()
env = PythonInfo.from_exe(str(result.creator.exe))
assert env.version_info.major != CURRENT.version_info.major


def test_create_parallel(tmp_path, monkeypatch):
monkeypatch.setenv(str("_VIRTUALENV_OVERRIDE_APP_DATA"), str(tmp_path))

def create(count):
subprocess.check_call([sys.executable, "-m", "virtualenv", str(tmp_path / "venv{}".format(count))])

threads = [Thread(target=create, args=(i,)) for i in range(1, 4)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()

0 comments on commit 5629b41

Please sign in to comment.