-
Notifications
You must be signed in to change notification settings - Fork 84
Fix iOS termination crash in ProvisionalDispatcher
#2059
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
Changes from all commits
424ed0e
d7ef467
885f3d6
ed2225c
aab4a18
3aca27f
bc46d84
27c9e25
71873fa
c67f1f4
781512e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,12 @@ void ProvisionalDispatcher::drain(Event::Dispatcher& event_dispatcher) { | |
| // because of behavioral oddities in Event::Dispatcher: event_dispatcher_->isThreadSafe() will | ||
| // crash. | ||
| Thread::LockGuard lock(state_lock_); | ||
|
|
||
| // Don't perform any work on the dispatcher if marked as terminated. | ||
| if (terminated_) { | ||
| return; | ||
| } | ||
|
Comment on lines
+16
to
+19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this ever happen? Can we terminate before we've drained?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure, but it seems safest to check here considering engine termination is exposed in the public API so a user could invoke it at any time. We've also already acquired the lock here, which is the expensive part, the boolean check should add effectively no overhead. |
||
|
|
||
| RELEASE_ASSERT(!drained_, "ProvisionalDispatcher::drain must only occur once"); | ||
| drained_ = true; | ||
| event_dispatcher_ = &event_dispatcher; | ||
|
|
@@ -24,6 +30,11 @@ void ProvisionalDispatcher::drain(Event::Dispatcher& event_dispatcher) { | |
| envoy_status_t ProvisionalDispatcher::post(Event::PostCb callback) { | ||
| Thread::LockGuard lock(state_lock_); | ||
|
|
||
| // Don't perform any work on the dispatcher if marked as terminated. | ||
| if (terminated_) { | ||
| return ENVOY_FAILURE; | ||
| } | ||
|
|
||
| if (drained_) { | ||
| event_dispatcher_->post(callback); | ||
| return ENVOY_SUCCESS; | ||
|
|
@@ -68,5 +79,10 @@ bool ProvisionalDispatcher::trackedObjectStackIsEmpty() const { | |
|
|
||
| TimeSource& ProvisionalDispatcher::timeSource() { return event_dispatcher_->timeSource(); } | ||
|
|
||
| void ProvisionalDispatcher::terminate() { | ||
| Thread::LockGuard lock(state_lock_); | ||
| terminated_ = true; | ||
| } | ||
|
|
||
| } // namespace Event | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,11 @@ class ProvisionalDispatcher : public ScopeTracker { | |
| */ | ||
| virtual TimeSource& timeSource(); | ||
|
|
||
| /** | ||
| * Marks the dispatcher as terminated, preventing any future work from being enqueued. | ||
| */ | ||
| virtual void terminate(); | ||
|
|
||
| // Used for testing. | ||
| Thread::ThreadSynchronizer& synchronizer() { return synchronizer_; } | ||
|
|
||
|
|
@@ -69,6 +74,7 @@ class ProvisionalDispatcher : public ScopeTracker { | |
| std::list<Event::PostCb> init_queue_ GUARDED_BY(state_lock_); | ||
| Event::Dispatcher* event_dispatcher_{}; | ||
| Thread::ThreadSynchronizer synchronizer_; | ||
| bool terminated_ GUARDED_BY(state_lock_){}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't matter a ton but if you made this atomic it could be checked outside of the lock
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Considering that this only needs to be checked in places where we've already acquired the lock, it seems we should leverage that. |
||
| }; | ||
|
|
||
| using ProvisionalDispatcherPtr = std::unique_ptr<ProvisionalDispatcher>; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be before the join? I'm not that familiar with this but isn't the dispatcher running on main_thread_, so if we're calling it here then the dispatcher is already not running?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. I can move it and see if the tests in #2129 still pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests still pass with #2129, pushed in 27c9e25.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this causes a data race, caught by the TSan tests: https://github.com/envoyproxy/envoy-mobile/runs/6026097993?check_suite_focus=true
Reverting.