Skip to content

Commit af1804a

Browse files
Add multithreaded tests (#62)
* add multithreaded tests * add comment * Fix typo --------- Co-authored-by: Jonathan Ehwald <github@ehwald.info>
1 parent f0a02fc commit af1804a

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

tests/test_archive_reader.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import sys
44
import tarfile
5+
from concurrent.futures import ThreadPoolExecutor
56
from random import randint
67

78
import psutil
@@ -558,3 +559,28 @@ def test_unpack_raises_when_archive_entries_are_not_iterable(target_path, archiv
558559
ArchiveUnpackingError, match="failed to iterate over archive"
559560
):
560561
reader.unpack(target_path)
562+
563+
564+
def test_unpack_raises_when_sent_to_another_thread(
565+
source_path, target_path, archive_path, write_mode, read_mode
566+
):
567+
input_dir = source_path / "dir"
568+
input_dir.mkdir()
569+
input_file = input_dir / "file.txt"
570+
input_file.touch()
571+
572+
with tarfile.open(archive_path, write_mode) as archive:
573+
archive.add(input_dir)
574+
archive.add(input_file)
575+
576+
def worker(reader, target_path):
577+
reader.unpack(target_path)
578+
579+
# pyo3's PanicException isn't exported to Python and inherits from BaseException
580+
# see https://github.com/PyO3/pyo3/issues/3918
581+
with pytest.raises(
582+
BaseException, match="fastar::reader::ArchiveReader is unsendable"
583+
):
584+
with ArchiveReader.open(archive_path, read_mode) as reader:
585+
with ThreadPoolExecutor(max_workers=1) as tpe:
586+
tpe.submit(worker, reader, target_path).result()

tests/test_archive_writer.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import sys
22
import tarfile
3+
import threading
4+
from concurrent.futures import ThreadPoolExecutor
35

46
import psutil
57
import pytest
@@ -1036,3 +1038,39 @@ def test_append_handles_arcnames_of_type_str(
10361038
with tarfile.open(archive_path, read_mode) as archive:
10371039
assert archive.getnames() == ["nested/file.txt"]
10381040
assert archive.getmember("nested/file.txt").isfile()
1041+
1042+
1043+
def test_multithreaded_append(
1044+
source_path, target_path, archive_path, write_mode, read_mode
1045+
):
1046+
def worker(barrier, writer, target_path, lock, thread_index):
1047+
barrier.wait()
1048+
thread_target_path = target_path / f"thread_{thread_index}"
1049+
thread_target_path.touch()
1050+
# PyO3 ensures only one mutable borrow is allowed at a time so we use a
1051+
# lock. Calling append concurrently will raise "RuntimeError: already
1052+
# borrowed" here.
1053+
with lock:
1054+
writer.append(thread_target_path)
1055+
1056+
num_workers = 4
1057+
barrier = threading.Barrier(num_workers)
1058+
lock = threading.Lock()
1059+
1060+
with ArchiveWriter.open(archive_path, write_mode) as writer:
1061+
with ThreadPoolExecutor(max_workers=num_workers) as tpe:
1062+
try:
1063+
futures = []
1064+
for i in range(num_workers):
1065+
futures.append(
1066+
tpe.submit(worker, barrier, writer, target_path, lock, i)
1067+
)
1068+
finally:
1069+
# avoid deadlocks if any threads failed to spawn
1070+
if len(futures) < num_workers:
1071+
barrier.abort()
1072+
# join spawned threads
1073+
[f.result() for f in futures]
1074+
1075+
with tarfile.open(archive_path, read_mode) as archive:
1076+
assert sorted(archive.getnames()) == [f"thread_{i}" for i in range(num_workers)]

0 commit comments

Comments
 (0)