Title: gh-91351: Support re-entrancy in importlib/_bootstrap.py by exarkun · Pull Request #94342 · python/cpython · GitHub
Open Graph Title: gh-91351: Support re-entrancy in importlib/_bootstrap.py by exarkun · Pull Request #94342 · python/cpython
X Title: gh-91351: Support re-entrancy in importlib/_bootstrap.py by exarkun · Pull Request #94342 · python/cpython
Description: This is a PR against the 3.9 branch (I will forward-port changes after addressing other review feedback). Note that most of the size of the diff for this PR is generated changes to re-freeze _bootstrap.py and most of what's left after subtracting that is new comments about how the implementation works to make it easier to understand. See #91351 for details about the problem. Re-entrancy is always tricky and given the requirements of _bootstrap.py (to operate with re-entrancy and multi-threading and to do so without exposing any of the details to application code doing an import) I think this goes double. This PR does a few things to achieve better safety in the face of re-entrancy: Switch some data structures to those that support atomic operation so that they are consistent in case of asynchronous re-entrancy (eg from the garbage collector or a signal handler). Add more RLock-like behavior to prevent deadlocks in the re-entrant case. Update the deadlock detection algorithm to support the fact that one thread might be "blocked on" acquiring the module lock for more than one module at a time. I'm not quite sure I believe this new version of the code is 100% correct with respect to re-entrancy but it does fixes mishandling of two specific cases: A re-entrant import is performed between the time _blocking_on is populated and cleaned up inside _ModuleLock.acquire. Previous this failed with a KeyError (as described in the linked issue). A re-entrant import is performed while the module lock is held inside _ModuleLock.acquire. Previously this failed by deadlocking. This PR also does not include any new unit tests. I have a small stand-alone program which can reproduce both of these but only with the assistance of some additional instrumentation inside _bootstrap.py to make sure the re-entrancy happens at the interesting times. If adding this kind of instrumentation is acceptable then it may be possible to turn this program into some unit tests. It may also be possible to simplify _BlockingOnManager by switching _ModuleLock.lock to an RLock. That solution didn't originally occur to me so I developed this - but if others think that is a better approach I think it's a fairly simple change. For reference, here is a stand-alone reproducer. This one isn't quite deterministic but by running the codepath over and over it seems to be fairly reliable in reproducing one of the problem codepaths on my system. For a completely deterministic reproducer, I think _bootstrap.py instrumentation is required. import sys, socket, gc class Cycle: pass def a_cycle(): c = Cycle() c.cycle = c c.s = socket.socket() def main(): while True: # import a module that socket.__del__ is going to import to exercise # re-entrant _ModuleLock.lock handling a_cycle() import linecache del sys.modules["linecache"] main() Issue: gh-91351
Open Graph Description: This is a PR against the 3.9 branch (I will forward-port changes after addressing other review feedback). Note that most of the size of the diff for this PR is generated changes to re-freeze _boots...
X Description: This is a PR against the 3.9 branch (I will forward-port changes after addressing other review feedback). Note that most of the size of the diff for this PR is generated changes to re-freeze _boots...
Opengraph URL: https://github.com/python/cpython/pull/94342
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:1ef44453-ed04-256c-10b2-5974f77c81a7 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | B5EC:11169F:2207A9E:2C9B397:696B0F7F |
| html-safe-nonce | 42d0497879a1838eb62e4d84892f411968a3b384fb132c19878488f6f35edb44 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCNUVDOjExMTY5RjoyMjA3QTlFOjJDOUIzOTc6Njk2QjBGN0YiLCJ2aXNpdG9yX2lkIjoiMjcxNTA3OTQwNDg4OTUwOTc1OSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 38469a84fd76c7caf664e1c81a38a02a84f7b632244164cc5559757f21ab9684 |
| hovercard-subject-tag | pull_request:980327775 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/python/cpython/pull/94342/files |
| twitter:image | https://avatars.githubusercontent.com/u/254565?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/254565?s=400&v=4 |
| og:image:alt | This is a PR against the 3.9 branch (I will forward-port changes after addressing other review feedback). Note that most of the size of the diff for this PR is generated changes to re-freeze _boots... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5f99f7c1d70f01da5b93e5ca90303359738944d8ab470e396496262c66e60b8d |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 82560a55c6b2054555076f46e683151ee28a19bc |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width