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

bot: fix race-condition in dispatch #1728

Merged
merged 1 commit into from
Nov 20, 2019

Conversation

Exirel
Copy link
Contributor

@Exirel Exirel commented Oct 26, 2019

The scenario is:

  • dispatch is called for a pretrigger
  • it looks into the list of callable by priority
  • it triggers one of the first callables, which wants to remove a callable of the same priority
  • because Python must not allow a dict to be modified while it is iterated over, Python raises an exception

If we put a lock here, Python won't raise the exception... but we'll get a deadlock. To prevent that, instead, we copy the list of callables, and return the ones that are triggered. If any of them wants to change the callables of the same priority, they won't raise an error, because we are not iterating anymore on the dict.

Hoo-ray!

@dgw have fun with my miserable grammar at 2am.

@Exirel Exirel added Medium Priority Bugfix Generally, PRs that reference (and fix) one or more issue(s) labels Oct 26, 2019
@Exirel Exirel added this to the 7.0.0 milestone Oct 26, 2019
Copy link
Member

@dgw dgw left a comment

Choose a reason for hiding this comment

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

Not many notes, relative to the patch size. But you weren't kidding about it being doc-heavy!

sopel/bot.py Outdated Show resolved Hide resolved
sopel/bot.py Outdated Show resolved Hide resolved
sopel/bot.py Outdated Show resolved Hide resolved
sopel/bot.py Outdated Show resolved Hide resolved
sopel/bot.py Outdated Show resolved Hide resolved
sopel/bot.py Outdated Show resolved Hide resolved
sopel/module.py Outdated Show resolved Hide resolved
@Exirel Exirel force-pushed the fix-race-condition-on-reload branch from 082ba78 to 608f673 Compare October 28, 2019 10:41
@Exirel
Copy link
Contributor Author

Exirel commented Oct 28, 2019

Fixed!

But you weren't kidding about it being doc-heavy!

Yup, and I believe an overhaul of sopel.module's docstrings could be a good thing to do. I might do that in few days/weeks.

@Exirel Exirel requested a review from dgw October 28, 2019 10:44
Copy link
Member

@dgw dgw left a comment

Choose a reason for hiding this comment

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

Apply this one last single-letter change and squash away. 🚀

sopel/bot.py Outdated Show resolved Hide resolved
@Exirel Exirel force-pushed the fix-race-condition-on-reload branch from 6b8d4ba to 2ec778f Compare October 30, 2019 09:52
@Exirel
Copy link
Contributor Author

Exirel commented Oct 30, 2019

All squashed into one single commit!

@dgw
Copy link
Member

dgw commented Nov 18, 2019

@Exirel This is approaching the top of the merge queue, but it has a conflict now. :/

The scenario is:

* dispatch is called for a pretrigger
* it looks into the list of callable by priority
* it triggers one of the first callables, which wants to remove a
  callable of the same priority
* because Python must not allow a dict to be modified while it is
  iterated over, Python raises an exception

If we put a lock here, Python won't raise the exception... but we'll get
a deadlock. To prevent that, instead, we copy the list of callables, and
return the ones that are triggered. If any of them wants to change the
callables of the same priority, they won't raise an error, because we
are not iterating anymore on the dict.

Hoo-ray!

(with applied review suggestions from dgw)

Co-Authored-By: dgw <dgw@technobabbl.es>
@Exirel Exirel force-pushed the fix-race-condition-on-reload branch from 2ec778f to 59704e6 Compare November 19, 2019 10:38
@Exirel
Copy link
Contributor Author

Exirel commented Nov 19, 2019

Conflict fixed. That was just the little output_prefix thing... 😄

@dgw dgw merged commit 55d5835 into sopel-irc:master Nov 20, 2019
dgw added a commit that referenced this pull request Nov 22, 2019
Assigning the tuple returned by `_is_pretrigger_blocked()` to both vars
instead of unpacking the values makes both of them always evaluate to
`True` in Boolean contexts. Which magically blocks everyone from doing
anything, unless `get_triggered_callables()` decides they're unblockable
by virtue of `trigger.admin` being `True` (or if the function is marked
unblockable, of course). Fun times!

Introduced by #1728 — `.blame Exirel`! I share no responsibility for
missing this in code review. Nope, none at all. :P

Let this be a lesson to us all: Test things as a non-admin sometimes.
Just in case. (New testing infrastructure should help us catch this sort
of thing automatically in a lot of cases, once our unit tests have been
updated to use the new mocks.)
@Exirel Exirel deleted the fix-race-condition-on-reload branch January 14, 2020 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bugfix Generally, PRs that reference (and fix) one or more issue(s) Medium Priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants