loop: add clear_fd_wait method
Add a helper method that clears the fd for a given loop device but also ensures that the loop device is not bound to the supplied fd anymore. Check the function documentation for more information. Add a corresponding test.
This commit is contained in:
parent
a367a0df1d
commit
d8e48c0511
2 changed files with 127 additions and 0 deletions
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import contextlib
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
from tempfile import TemporaryDirectory, TemporaryFile
|
||||
|
||||
import pytest
|
||||
|
|
@ -87,3 +89,72 @@ def test_basic(tempdir):
|
|||
|
||||
with pytest.raises(RuntimeError):
|
||||
ctl.loop_for_fd(0)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
|
||||
def test_clear_fd_wait(tempdir):
|
||||
|
||||
path = os.path.join(tempdir, "test.img")
|
||||
ctl = loop.LoopControl()
|
||||
|
||||
assert ctl
|
||||
|
||||
delay_time = 0.25
|
||||
|
||||
def close_loop(lo, barrier):
|
||||
barrier.wait()
|
||||
time.sleep(delay_time)
|
||||
print("closing loop")
|
||||
lo.close()
|
||||
|
||||
lo, lo2, f = None, None, None
|
||||
try:
|
||||
f = open(path, "wb+")
|
||||
f.truncate(1024)
|
||||
f.flush()
|
||||
lo = ctl.loop_for_fd(f.fileno(), autoclear=False)
|
||||
assert lo
|
||||
|
||||
# Increase reference count of the loop to > 1 thus
|
||||
# preventing the kernel from immediately closing the
|
||||
# device. Instead the kernel will set the autoclear
|
||||
# attribute and return
|
||||
lo2 = loop.Loop(lo.minor)
|
||||
assert lo2
|
||||
|
||||
# as long as the second loop is alive, the kernel can
|
||||
# not clear the fd and thus we will get a timeout
|
||||
with pytest.raises(TimeoutError):
|
||||
lo.clear_fd_wait(f.fileno(), 0.1, 0.01)
|
||||
|
||||
# start a thread and sync with a barrier, then close
|
||||
# the loop device in the background thread while the
|
||||
# main thread is waiting in `clear_fd_wait`. We wait
|
||||
# four times the delay time of the thread to ensure
|
||||
# we don't get a timeout.
|
||||
barrier = threading.Barrier(2)
|
||||
thread = threading.Thread(
|
||||
target=close_loop,
|
||||
args=(lo2, barrier)
|
||||
)
|
||||
barrier.reset()
|
||||
thread.start()
|
||||
barrier.wait()
|
||||
|
||||
lo.clear_fd_wait(f.fileno(), 4*delay_time, delay_time/10)
|
||||
|
||||
# no timeout exception has occurred and thus the device
|
||||
# must not be be bound to the original file anymore
|
||||
assert not lo.is_bound_to(f.fileno())
|
||||
|
||||
finally:
|
||||
if lo2:
|
||||
lo2.close()
|
||||
if lo:
|
||||
with contextlib.suppress(OSError):
|
||||
lo.clear_fd()
|
||||
lo.close()
|
||||
if f:
|
||||
f.close()
|
||||
|
||||
ctl.close()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue