Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[core, ios, qt, android] Close race condition in RunLoop (issue #9620) #10537

Merged
merged 1 commit into from
Nov 27, 2017

Conversation

ChrisLoer
Copy link
Contributor

See #9620 (comment)

Because a message we queue from the foreground may cause the background to complete, exit, and tear down the AsyncTask, we have to block queue processing until we've finished our call to AsyncTask::send().

Broadening the scope of a mutex is scary, but I audited the code of our four implementations of AsyncTask and I don't see any way this could cause a deadlock.

If we wanted to keep the scope of the mutex as limited as possible, we could move the mutex scope-broadening to RunLoop::stop(), with a custom implementation that bypasses invoke/push, but I'm not sure it would be worth the extra complexity. I suppose the scenario to worry about is that since we're holding the mutex while we call async->send we could cause the background to wake up and immediately be blocked on the mutex... but it doesn't seem like it would be that common or that costly when it did happen?

/cc @tmpsantos @jfirebaugh @kkaefer @ivovandongen

@ChrisLoer
Copy link
Contributor Author

I audited the code of our four implementations of AsyncTask and I don't see any way this could cause a deadlock.

😅 My in-depth audit missed that the Android RunLoop doesn't use AsyncTask... The Android 'wake' looks like write(fds[PIPE_IN], "\n", 1)... which is... I dunno, is it OK to hold a mutex while writing to a file descriptor?

withMutex([&] {
queue.push(std::move(task));
impl->wake();
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we lock the entire function, we can also remove the withMutex function altogether and just move the std::lock_guard into every function.

Copy link
Contributor

@tmpsantos tmpsantos Nov 23, 2017

Choose a reason for hiding this comment

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

👍 on @kkaefer point here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

withMutex is also used in process, and I think there's a readability win in using the same syntax to mark all locked sections in the code. If we were to stop using withMutex in push, I think it would also make sense to change the call in process to something like:

...
{
  std::lock_guard<std::mutex> lock(mutex);
  queue_.swap(queue);
}
...

Between those two options... 🤷‍♂️ ? Doesn't make a big difference to me, but at some point we thought withMutex was a clearer way to mark the locked section of code.

@tmpsantos
Copy link
Contributor

tmpsantos commented Nov 23, 2017

is it OK to hold a mutex while writing to a file descriptor?

Should be fine.

@kkaefer kkaefer added the Core The cross-platform C++ core, aka mbgl label Nov 24, 2017
Because a message we queue from the foreground may cause the background to complete, exit, and tear down the AsyncTask, we have to block queue processing until we've finished our call to AsyncTask::send().
Broadening the scope of a mutex is scary, but I audited the code of our four implementations of AsyncTask and I don't see any way this could cause a deadlock.
@ChrisLoer ChrisLoer merged commit 5da5ba7 into master Nov 27, 2017
@tmpsantos tmpsantos deleted the cloer_9620 branch November 28, 2017 15:59
@tmpsantos
Copy link
Contributor

@ChrisLoer thanks! This apparently fixed a hang I could only reproduce on Qt + Windows.

This was referenced Dec 5, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Core The cross-platform C++ core, aka mbgl
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants