|
17 | 17 | """Fixtures for Nova tests."""
|
18 | 18 |
|
19 | 19 | import collections
|
| 20 | +import contextlib |
20 | 21 | from contextlib import contextmanager
|
21 | 22 | import functools
|
22 | 23 | import logging as std_logging
|
|
28 | 29 | import futurist
|
29 | 30 | import mock
|
30 | 31 | from openstack import service_description
|
| 32 | +from oslo_concurrency import lockutils |
31 | 33 | from oslo_config import cfg
|
32 | 34 | from oslo_db import exception as db_exc
|
33 | 35 | from oslo_db.sqlalchemy import enginefacade
|
@@ -405,7 +407,7 @@ def __init__(self):
|
405 | 407 | # to point to a cell, we need to take an exclusive lock to
|
406 | 408 | # prevent any other calls to get_context_manager() until we
|
407 | 409 | # reset to the default.
|
408 |
| - self._cell_lock = utils.ReaderWriterLock() |
| 410 | + self._cell_lock = ReaderWriterLock() |
409 | 411 |
|
410 | 412 | def _cache_schema(self, connection_str):
|
411 | 413 | # NOTE(melwitt): See the regular Database fixture for why
|
@@ -1721,3 +1723,37 @@ def test_case_id_wrapper(*args, **kwargs):
|
1721 | 1723 | # our initialization to the child eventlet
|
1722 | 1724 | self.useFixture(
|
1723 | 1725 | fixtures.MonkeyPatch('nova.utils.spawn_n', wrapped_spawn_n))
|
| 1726 | + |
| 1727 | + |
| 1728 | +class ReaderWriterLock(lockutils.ReaderWriterLock): |
| 1729 | + """Wrap oslo.concurrency lockutils.ReaderWriterLock to support eventlet. |
| 1730 | +
|
| 1731 | + As of fasteners >= 0.15, the workaround code to use eventlet.getcurrent() |
| 1732 | + if eventlet patching is detected has been removed and |
| 1733 | + threading.current_thread is being used instead. Although we are running in |
| 1734 | + a greenlet in our test environment, we are not running in a greenlet of |
| 1735 | + type GreenThread. A GreenThread is created by calling eventlet.spawn() and |
| 1736 | + spawn() is not used to run our tests. At the time of this writing, the |
| 1737 | + eventlet patched threading.current_thread() method falls back to the |
| 1738 | + original unpatched current_thread() method if it is not called from a |
| 1739 | + GreenThead [1] and that breaks our tests involving this fixture. |
| 1740 | +
|
| 1741 | + We can work around this by patching threading.current_thread() with |
| 1742 | + eventlet.getcurrent() during creation of the lock object, if we detect we |
| 1743 | + are eventlet patched. If we are not eventlet patched, we use a no-op |
| 1744 | + context manager. |
| 1745 | +
|
| 1746 | + Note: this wrapper should be used for any ReaderWriterLock because any lock |
| 1747 | + may possibly be running inside a plain greenlet created by spawn_n(). |
| 1748 | +
|
| 1749 | + See https://github.com/eventlet/eventlet/issues/731 for details. |
| 1750 | +
|
| 1751 | + [1] https://github.com/eventlet/eventlet/blob/v0.32.0/eventlet/green/threading.py#L128 # noqa |
| 1752 | + """ |
| 1753 | + |
| 1754 | + def __init__(self, *a, **kw): |
| 1755 | + eventlet_patched = eventlet.patcher.is_monkey_patched('thread') |
| 1756 | + mpatch = fixtures.MonkeyPatch( |
| 1757 | + 'threading.current_thread', eventlet.getcurrent) |
| 1758 | + with mpatch if eventlet_patched else contextlib.ExitStack(): |
| 1759 | + super().__init__(*a, **kw) |
0 commit comments