1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-01 04:38:12 +00:00

Fix a lock issue with some virtualbox vm

Fix  https://github.com/GNS3/gns3-gui/issues/1444
This commit is contained in:
Julien Duponchelle 2016-08-29 10:51:50 +02:00
parent 042a69eecf
commit 0eafb6f06c
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
3 changed files with 47 additions and 3 deletions

View File

@ -30,7 +30,7 @@ import asyncio
from gns3server.utils import parse_version from gns3server.utils import parse_version
from gns3server.utils.telnet_server import TelnetServer from gns3server.utils.telnet_server import TelnetServer
from gns3server.utils.asyncio import wait_for_file_creation, wait_for_named_pipe_creation from gns3server.utils.asyncio import wait_for_file_creation, wait_for_named_pipe_creation, locked_coroutine
from .virtualbox_error import VirtualBoxError from .virtualbox_error import VirtualBoxError
from ..nios.nio_udp import NIOUDP from ..nios.nio_udp import NIOUDP
from ..nios.nio_nat import NIONAT from ..nios.nio_nat import NIONAT
@ -230,7 +230,7 @@ class VirtualBoxVM(BaseVM):
if (yield from self.check_hw_virtualization()): if (yield from self.check_hw_virtualization()):
self._hw_virtualization = True self._hw_virtualization = True
@asyncio.coroutine @locked_coroutine
def stop(self): def stop(self):
""" """
Stops this VirtualBox VM. Stops this VirtualBox VM.

View File

@ -127,3 +127,24 @@ def wait_for_named_pipe_creation(pipe_path, timeout=60):
else: else:
return return
raise asyncio.TimeoutError() raise asyncio.TimeoutError()
def locked_coroutine(f):
"""
Method decorator that replace asyncio.coroutine that warranty
that this specific method of this class instance will not we
executed twice at the same time
"""
@asyncio.coroutine
def new_function(*args, **kwargs):
# In the instance of the class we will store
# a lock has an attribute.
lock_var_name = "__" + f.__name__ + "_lock"
if not hasattr(args[0], lock_var_name):
setattr(args[0], lock_var_name, asyncio.Lock())
with (yield from getattr(args[0], lock_var_name)):
return (yield from f(*args, **kwargs))
return new_function

View File

@ -21,7 +21,7 @@ import pytest
import sys import sys
from unittest.mock import MagicMock from unittest.mock import MagicMock
from gns3server.utils.asyncio import wait_run_in_executor, subprocess_check_output, wait_for_process_termination from gns3server.utils.asyncio import wait_run_in_executor, subprocess_check_output, wait_for_process_termination, locked_coroutine
def test_wait_run_in_executor(loop): def test_wait_run_in_executor(loop):
@ -67,3 +67,26 @@ def test_wait_for_process_termination(loop):
exec = wait_for_process_termination(process, timeout=0.5) exec = wait_for_process_termination(process, timeout=0.5)
with pytest.raises(asyncio.TimeoutError): with pytest.raises(asyncio.TimeoutError):
loop.run_until_complete(asyncio.async(exec)) loop.run_until_complete(asyncio.async(exec))
def test_lock_decorator(loop):
"""
The test check if the the second call to method_to_lock wait for the
first call to finish
"""
class TestLock:
def __init__(self):
self._test_val = 0
@locked_coroutine
def method_to_lock(self):
res = self._test_val
yield from asyncio.sleep(0.1)
self._test_val += 1
return res
i = TestLock()
res = set(loop.run_until_complete(asyncio.gather(i.method_to_lock(), i.method_to_lock())))
assert res == set((0, 1,)) # We use a set to test this to avoid order issue