Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No more segfaults: a new approach for greening existing locks #866

Merged
merged 12 commits into from Jan 11, 2024

Conversation

itamarst
Copy link
Contributor

@itamarst itamarst commented Jan 2, 2024

  1. Works better on older Python 3.
  2. Doesn't cause segfaults!!!!
  3. Somewhat worse in terms of finding RLocks than what we had before, but in practice unlikely to be an issue.
  4. Adds a warning about unupgraded RLocks.
  5. Improved test coverage.
  6. As of latest iteration, only a few milliseconds slower than previous version.

Fixes #864

@itamarst itamarst linked an issue Jan 2, 2024 that may be closed by this pull request
Copy link

codecov bot commented Jan 2, 2024

Codecov Report

Attention: 8 lines in your changes are missing coverage. Please review.

Comparison is base (a94a3cf) 54% compared to head (239aa6a) 54%.

Files Patch % Lines
eventlet/patcher.py 83% 6 Missing and 2 partials ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##           master   #866   +/-   ##
=====================================
  Coverage      54%    54%           
=====================================
  Files          88     88           
  Lines        9744   9741    -3     
  Branches     1814   1811    -3     
=====================================
+ Hits         5297   5302    +5     
+ Misses       4081   4076    -5     
+ Partials      366    363    -3     
Flag Coverage Δ
ipv6 22% <6%> (+<1%) ⬆️
py310epolls 53% <83%> (+<1%) ⬆️
py310poll 53% <83%> (+<1%) ⬆️
py310selects 52% <83%> (+<1%) ⬆️
py311epolls 53% <83%> (+<1%) ⬆️
py312epolls 50% <83%> (+<1%) ⬆️
py37epolls 50% <83%> (+<1%) ⬆️
py38epolls 53% <83%> (+<1%) ⬆️
py38openssl 51% <83%> (+<1%) ⬆️
py38poll 53% <83%> (+<1%) ⬆️
py38selects 52% <83%> (+<1%) ⬆️
py39dnspython1 51% <83%> (+<1%) ⬆️
py39epolls 53% <83%> (+<1%) ⬆️
py39poll 53% <83%> (+<1%) ⬆️
py39selects 52% <83%> (+<1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@itamarst
Copy link
Contributor Author

itamarst commented Jan 2, 2024

Will fix test failures later today.

eventlet/patcher.py Outdated Show resolved Hide resolved
_find_instances(obj, klass, visited, found)

if isinstance(container, dict):
for v in list(container.values()):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we worry about keys?

for k, v in list(container.items()):
    add_or_traverse(k)
    add_or_traverse(v)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main reason to support dict values is that we previously were updating things like function locals. Not doing that anymore, but potentially someone has some class that uses mapping dictionaries for attributes. We could support keys but that's changing the way you look things up, which seems iffier, and... beyond a certain point we can't handle every case. So we could, but a better answer is "monkey patch early", or better yet "don't use eventlet".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another example of something we could upgrade is a set that contains RLocks. And something we maybe could do but would be a lot harder is tuples.

eventlet/patcher.py Show resolved Hide resolved
eventlet/patcher.py Outdated Show resolved Hide resolved

if isinstance(container, dict):
for k, v in list(container.items()):
if v is old:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if k is old:
    if v is old:
        container[new] = new
    else:
        container[new] = container[k]
    del container[k]
elif v is old:
    ...

?
A diff like

diff --git a/tests/isolated/patcher_existing_locks_preexisting.py b/tests/isolated/patcher_existing_locks_preexisting.py
index e3306ee..04be763 100644
--- a/tests/isolated/patcher_existing_locks_preexisting.py
+++ b/tests/isolated/patcher_existing_locks_preexisting.py
@@ -16,7 +16,7 @@ class NS:
     class NS2:
         lock = threading.RLock()
 
-    dict = {1: 2, 12: threading.RLock()}
+    dict = {1: 2, 12: threading.RLock(), threading.RLock(): 3, NS2.lock: NS2.lock}
     list = [0, threading.RLock()]
 
 
@@ -35,6 +35,10 @@ if __name__ == '__main__':
     ensure_upgraded(NS.lock)
     ensure_upgraded(NS.NS2.lock)
     ensure_upgraded(NS.dict[12])
+    for k in NS.dict:
+        if not isinstance(k, int):
+            ensure_upgraded(k)
+    ensure_upgraded(NS.dict[NS.NS2.lock])
     ensure_upgraded(NS.list[1])
     if sys.version_info[:2] > (3, 9):
         ensure_upgraded(unittest.mock.NonCallableMock._lock)

should demonstrate the existing trouble.

eventlet/patcher.py Outdated Show resolved Hide resolved
# https://github.com/eventlet/eventlet/issues/864 was fixed.
if sys.version_info[:2] > (3, 9):
print(unittest.mock.NonCallableMock._lock)
print(NS.lock)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused about how this passes with these prints...

Oh, e51f72a started allowing multiple lines as long as the last one was pass... why'd we do that?

Oh. Hmm...

Program .../tests/isolated/patcher_fork_after_monkey_patch.py output:
---
.../tests/isolated/patcher_fork_after_monkey_patch.py:38: DeprecationWarning: This process (pid=166324) is multi-threaded, use of fork() may lead to deadlocks in the child.
  if os.fork() == 0:
pass

---

Copy link
Contributor Author

@itamarst itamarst Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I made those test framework changes because of noise like that.

print(unittest.mock.NonCallableMock._lock)
print(NS.lock)
eventlet.monkey_patch()
ensure_upgraded(NS.lock)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange: running this directly, I trip the RuntimeError here, but it passes when run through tests/patcher_test.py...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, it passes for me if I run it directly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both launches types works for me.

@itamarst itamarst requested review from tipabu and a team January 5, 2024 17:45
@itamarst itamarst changed the title A new approach for greening existing locks No more segfaults: a new approach for greening existing locks Jan 9, 2024
if "PYTEST_CURRENT_TEST" in os.environ:
return
# We don't want to use gc.get_objects() to find locks because that doesn't
# work in older Python, but it's fine for logging purposes.
Copy link
Member

@4383 4383 Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it is fine to use gc.get_objects for logging purposes? I'm not sure to understand that reason and I think I won't be the only one.

Using gc.get_objects won't work with Python release lower than 3.10 and we still support Python 3.7.

That would lead to at least to remaining_rlocks = 0 or in worst case to a runtime error, no?
Then, logs won't show the following error.

So, either this comment should be updated to explain why using gc.get_objects is not a problem, or the code should be modified to do not call this method, or I misunderstood something. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just useful information. On older Python you will get less useful information, but it doesn't hurt you in any way. I updated the docstring to try to reflect this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the details

@itamarst itamarst requested a review from 4383 January 10, 2024 16:47
Copy link
Member

@4383 4383 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

print(unittest.mock.NonCallableMock._lock)
print(NS.lock)
eventlet.monkey_patch()
ensure_upgraded(NS.lock)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both launches types works for me.

eventlet/patcher.py Show resolved Hide resolved
@itamarst itamarst merged commit dec2ed5 into master Jan 11, 2024
23 checks passed
@itamarst itamarst deleted the 864-greening-existing-locks-doesnt-always-work-right branch January 11, 2024 14:42
openstack-mirroring pushed a commit to openstack/requirements that referenced this pull request Jan 16, 2024
Several important and urgent fixes are released there.

0.34.3
======

eventlet/eventlet#875

* Fix security issue in the wsgi module related to RFC 9112 eventlet/eventlet#826
* Fix segfault, a new approach for greening existing locks eventlet/eventlet#866
* greendns: fix getaddrinfo parameter name eventlet/eventlet#809
* Fix deprecation warning on ssl.PROTOCOL_TLS eventlet/eventlet#872
* Pytests, fix error at teardown of TestGreenSocket.test_full_duplex eventlet/eventlet#871
* Skip test which uses Py cgi module eventlet/eventlet#865
* Drop old code based on python < 3.7.34.2
======

eventlet/eventlet#861

* Allowing inheritance of GreenSSLSocket without overriding the __new_ method eventlet/eventlet#796
* [bug] Fix broken API related to `__version__` removal eventlet/eventlet#859
* [doc] Fix pypi broken link eventlet/eventlet#857

0.34.1
======

eventlet/eventlet#842

* [bug] Fix memory leak in greendns eventlet/eventlet#810
* [infra] Fix OIDC authentication failure eventlet/eventlet#855
* [bug] Ignore asyncore and asynchat for Python 3.12+ eventlet/eventlet#804

0.34.0 (Not released on Pypi but landed with 0.34.1)
====================================================

* Dropped support for Python 3.6 and earlier.
* Fix Python 3.13 compat by adding missing attibute '_is_main_interpreter' eventlet/eventlet#847
* Add support of Python 3.12 eventlet/eventlet#817
* Drop unmaintained and unused stdlib tests eventlet/eventlet#820
* Fix tests and CI for Python 3.7 and higher eventlet/eventlet#831 and eventlet/eventlet#832
* Stop claiming to create universal wheels eventlet/eventlet#841
* Fix green logging locks for Python versions <= 3.10 eventlet/eventlet#754

Change-Id: Ib2e59a207b86ae90fa391bf1dff7819851dc9c9b
openstack-mirroring pushed a commit to openstack/openstack that referenced this pull request Jan 16, 2024
* Update requirements from branch 'master'
  to 827a86739e66ffb537b5ea7a65a3cc74eb3fabf1
  - Merge "Update eventlet to 0.34.3"
  - Update eventlet to 0.34.3
    
    Several important and urgent fixes are released there.
    
    0.34.3
    ======
    
    eventlet/eventlet#875
    
    * Fix security issue in the wsgi module related to RFC 9112 eventlet/eventlet#826
    * Fix segfault, a new approach for greening existing locks eventlet/eventlet#866
    * greendns: fix getaddrinfo parameter name eventlet/eventlet#809
    * Fix deprecation warning on ssl.PROTOCOL_TLS eventlet/eventlet#872
    * Pytests, fix error at teardown of TestGreenSocket.test_full_duplex eventlet/eventlet#871
    * Skip test which uses Py cgi module eventlet/eventlet#865
    * Drop old code based on python < 3.7.34.2
    ======
    
    eventlet/eventlet#861
    
    * Allowing inheritance of GreenSSLSocket without overriding the __new_ method eventlet/eventlet#796
    * [bug] Fix broken API related to `__version__` removal eventlet/eventlet#859
    * [doc] Fix pypi broken link eventlet/eventlet#857
    
    0.34.1
    ======
    
    eventlet/eventlet#842
    
    * [bug] Fix memory leak in greendns eventlet/eventlet#810
    * [infra] Fix OIDC authentication failure eventlet/eventlet#855
    * [bug] Ignore asyncore and asynchat for Python 3.12+ eventlet/eventlet#804
    
    0.34.0 (Not released on Pypi but landed with 0.34.1)
    ====================================================
    
    * Dropped support for Python 3.6 and earlier.
    * Fix Python 3.13 compat by adding missing attibute '_is_main_interpreter' eventlet/eventlet#847
    * Add support of Python 3.12 eventlet/eventlet#817
    * Drop unmaintained and unused stdlib tests eventlet/eventlet#820
    * Fix tests and CI for Python 3.7 and higher eventlet/eventlet#831 and eventlet/eventlet#832
    * Stop claiming to create universal wheels eventlet/eventlet#841
    * Fix green logging locks for Python versions <= 3.10 eventlet/eventlet#754
    
    Change-Id: Ib2e59a207b86ae90fa391bf1dff7819851dc9c9b
zsiciarz added a commit to zsiciarz/eventlet that referenced this pull request Jan 18, 2024
0.34.3

* Fix security issue in the wsgi module related to RFC 9112 eventlet#826
* Fix segfault, a new approach for greening existing locks eventlet#866
* greendns: fix getaddrinfo parameter name eventlet#809
* Fix deprecation warning on ssl.PROTOCOL_TLS eventlet#872
* Pytests, fix error at teardown of TestGreenSocket.test_full_duplex eventlet#871
* Skip test which uses Py cgi module eventlet#865
* Drop old code based on python < 3.7
openstack-mirroring pushed a commit to openstack/openstack that referenced this pull request Jan 30, 2024
* Update requirements from branch 'master'
  to 08f829d8375b4059af365191e0907069be9fb739
  - Update eventlet to 0.35.0
    
    0.35.0
    ======
    
    eventlet/eventlet#897
    
    * [fix] fix truncate size nullable eventlet/eventlet#789
    * [fix] Handle transport endpoint shutdown in conditions eventlet/eventlet#884
    * [fix] Rework reject_bad_requests option eventlet/eventlet#890
    * [fix] Fix NameError introduced by #826 eventlet/eventlet#890
    * [feature] Support awaiting GreenThread in an `async def` context eventlet/eventlet#889
    * [feature] Asyncio hub support for Python 3.7 to 3.9 eventlet/eventlet#886
    * [fix] Fix bad exceptions handlings eventlet/eventlet#883
    * [feature] Support using asyncio coroutines from inside greenlets eventlet/eventlet#877
    * [removal] Remove deprecated CGIHTTPServer and SimpleHTTPServer eventlet/eventlet#881
    * [feature] Add an asyncio hub for eventlet eventlet/eventlet#870
    
    0.34.3
    ======
    
    eventlet/eventlet#875
    
    * Fix security issue in the wsgi module related to RFC 9112 eventlet/eventlet#826
    * Fix segfault, a new approach for greening existing locks eventlet/eventlet#866
    * greendns: fix getaddrinfo parameter name eventlet/eventlet#809
    * Fix deprecation warning on ssl.PROTOCOL_TLS eventlet/eventlet#872
    * Pytests, fix error at teardown of TestGreenSocket.test_full_duplex eventlet/eventlet#871
    * Skip test which uses Py cgi module eventlet/eventlet#865
    * Drop old code based on python < 3.7.34.2
    ======
    
    eventlet/eventlet#861
    
    * Allowing inheritance of GreenSSLSocket without overriding the __new_ method eventlet/eventlet#796
    * [bug] Fix broken API related to `__version__` removal eventlet/eventlet#859
    * [doc] Fix pypi broken link eventlet/eventlet#857
    
    0.34.1
    ======
    
    eventlet/eventlet#842
    
    * [bug] Fix memory leak in greendns eventlet/eventlet#810
    * [infra] Fix OIDC authentication failure eventlet/eventlet#855
    * [bug] Ignore asyncore and asynchat for Python 3.12+ eventlet/eventlet#804
    
    0.34.0 (Not released on Pypi but landed with 0.34.1)
    ====================================================
    
    * Dropped support for Python 3.6 and earlier.
    * Fix Python 3.13 compat by adding missing attibute '_is_main_interpreter' eventlet/eventlet#847
    * Add support of Python 3.12 eventlet/eventlet#817
    * Drop unmaintained and unused stdlib tests eventlet/eventlet#820
    * Fix tests and CI for Python 3.7 and higher eventlet/eventlet#831 and eventlet/eventlet#832
    * Stop claiming to create universal wheels eventlet/eventlet#841
    * Fix green logging locks for Python versions <= 3.10 eventlet/eventlet#754
    
    Change-Id: I909be1d1812eaed574525866dbc123083684571d
openstack-mirroring pushed a commit to openstack/requirements that referenced this pull request Jan 30, 2024
0.35.0
======

eventlet/eventlet#897

* [fix] fix truncate size nullable eventlet/eventlet#789
* [fix] Handle transport endpoint shutdown in conditions eventlet/eventlet#884
* [fix] Rework reject_bad_requests option eventlet/eventlet#890
* [fix] Fix NameError introduced by #826 eventlet/eventlet#890
* [feature] Support awaiting GreenThread in an `async def` context eventlet/eventlet#889
* [feature] Asyncio hub support for Python 3.7 to 3.9 eventlet/eventlet#886
* [fix] Fix bad exceptions handlings eventlet/eventlet#883
* [feature] Support using asyncio coroutines from inside greenlets eventlet/eventlet#877
* [removal] Remove deprecated CGIHTTPServer and SimpleHTTPServer eventlet/eventlet#881
* [feature] Add an asyncio hub for eventlet eventlet/eventlet#870

0.34.3
======

eventlet/eventlet#875

* Fix security issue in the wsgi module related to RFC 9112 eventlet/eventlet#826
* Fix segfault, a new approach for greening existing locks eventlet/eventlet#866
* greendns: fix getaddrinfo parameter name eventlet/eventlet#809
* Fix deprecation warning on ssl.PROTOCOL_TLS eventlet/eventlet#872
* Pytests, fix error at teardown of TestGreenSocket.test_full_duplex eventlet/eventlet#871
* Skip test which uses Py cgi module eventlet/eventlet#865
* Drop old code based on python < 3.7.34.2
======

eventlet/eventlet#861

* Allowing inheritance of GreenSSLSocket without overriding the __new_ method eventlet/eventlet#796
* [bug] Fix broken API related to `__version__` removal eventlet/eventlet#859
* [doc] Fix pypi broken link eventlet/eventlet#857

0.34.1
======

eventlet/eventlet#842

* [bug] Fix memory leak in greendns eventlet/eventlet#810
* [infra] Fix OIDC authentication failure eventlet/eventlet#855
* [bug] Ignore asyncore and asynchat for Python 3.12+ eventlet/eventlet#804

0.34.0 (Not released on Pypi but landed with 0.34.1)
====================================================

* Dropped support for Python 3.6 and earlier.
* Fix Python 3.13 compat by adding missing attibute '_is_main_interpreter' eventlet/eventlet#847
* Add support of Python 3.12 eventlet/eventlet#817
* Drop unmaintained and unused stdlib tests eventlet/eventlet#820
* Fix tests and CI for Python 3.7 and higher eventlet/eventlet#831 and eventlet/eventlet#832
* Stop claiming to create universal wheels eventlet/eventlet#841
* Fix green logging locks for Python versions <= 3.10 eventlet/eventlet#754

Change-Id: I909be1d1812eaed574525866dbc123083684571d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Greening existing locks doesn't always work right
4 participants