Skip to content

Commit 6e3a7eb

Browse files
committed
WIP another windows test
Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
1 parent 8aa0212 commit 6e3a7eb

3 files changed

Lines changed: 37 additions & 16 deletions

File tree

tests/test_updater_ng.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,6 @@ def test_parallel_updaters(self) -> None:
362362
# Refresh many updaters in parallel many times, using the same local metadata cache.
363363
# This should reveal race conditions.
364364

365-
# windows on GitHub actions *will* fail with "[Errno 36] Resource deadlock avoided"
366-
# if iterations is in the hundreds
367365
iterations = 50
368366
process_count = 10
369367

@@ -396,7 +394,6 @@ def test_parallel_updaters(self) -> None:
396394
errout += "Parallel Refresh script failed:"
397395
errout += f"\nprocess stdout: \n{stdout.decode()}"
398396
errout += f"\nprocess stderr: \n{stderr.decode()}"
399-
400397
if errout:
401398
self.fail(
402399
f"One or more scripts failed parallel refresh test:\n{errout}"

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ commands_pre =
2929
python3 -m pip install --force-reinstall git+https://github.com/secure-systems-lab/securesystemslib.git@main#egg=securesystemslib[crypto]
3030

3131
commands =
32-
python3 -m coverage run -m unittest
32+
python3 -m coverage run -m unittest -v
3333
python3 -m coverage report -m
3434

3535
[testenv:lint]

tuf/ngclient/updater.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import os
5959
import shutil
6060
import tempfile
61+
import time
62+
import traceback
6163
from pathlib import Path
6264
from typing import IO, TYPE_CHECKING, cast
6365
from urllib import parse
@@ -79,21 +81,44 @@
7981
# advisory file locking for posix
8082
import fcntl
8183

82-
def _lock_file(f: IO) -> None:
83-
if f.writable():
84+
@contextlib.contextmanager
85+
def _lock_file(path: str) -> Iterator[IO]:
86+
with open(path, "wb") as f:
8487
fcntl.lockf(f, fcntl.LOCK_EX)
88+
yield f
8589

8690
except ModuleNotFoundError:
8791
# Windows file locking
8892
import msvcrt
8993

90-
def _lock_file(f: IO) -> None:
91-
# On Windows we lock a byte range and file must not be empty
92-
f.write(b"\0")
93-
f.flush()
94-
f.seek(0)
94+
@contextlib.contextmanager
95+
def _lock_file(path: str) -> Iterator[IO]:
96+
err = None
97+
locked = False
98+
for i in range(100):
99+
try:
100+
with open(path, "wb") as f:
101+
# file must not be empty
102+
f.write(b"\0")
103+
f.flush()
104+
f.seek(0)
105+
msvcrt.locking(f.fileno(), msvcrt.LK_LOCK, 1)
106+
locked = True
107+
yield f
108+
return
109+
except OSError as e:
110+
if locked:
111+
# yield raised
112+
raise e
113+
err = e
114+
logger.warning(
115+
"Unsuccessful lock attempt %d for %s: %s", i, path, e
116+
)
117+
logger.warning(traceback.format_exc())
118+
time.sleep(0.3)
95119

96-
msvcrt.locking(f.fileno(), msvcrt.LK_LOCK, 1)
120+
if err:
121+
raise err
97122

98123

99124
class Updater:
@@ -171,9 +196,9 @@ def _lock_metadata(self) -> Iterator[None]:
171196
# Ensure the whole metadata directory structure exists
172197
rootdir = Path(self._dir, "root_history")
173198
rootdir.mkdir(exist_ok=True, parents=True)
199+
174200
logger.debug("Getting metadata lock...")
175-
with open(os.path.join(self._dir, ".lock"), "wb") as f:
176-
_lock_file(f)
201+
with _lock_file(os.path.join(self._dir, ".lock")):
177202
yield
178203
logger.debug("Released metadata lock")
179204

@@ -336,8 +361,7 @@ def download_target(
336361
targetinfo.verify_length_and_hashes(target_file)
337362

338363
target_file.seek(0)
339-
with open(filepath, "wb") as destination_file:
340-
_lock_file(destination_file)
364+
with _lock_file(filepath) as destination_file:
341365
shutil.copyfileobj(target_file, destination_file)
342366

343367
logger.debug("Downloaded target %s", targetinfo.path)

0 commit comments

Comments
 (0)