Skip to content
This repository was archived by the owner on Jun 24, 2022. It is now read-only.

Commit c457a32

Browse files
RunLoop::dispatch should only call wakeUp when needed
https://bugs.webkit.org/show_bug.cgi?id=213705 Reviewed by Brady Eidson. Source/WTF: RunLoop::wakeUp is super expensive (at least on Darwin platforms). Back when IndexedDB used RunLoop::dispatch, RunLoop::wakeUp accounted for 15% of all database lookup time. We can reduce the cost a bit by only scheduling a wakeUp when the queue is empty. * wtf/RunLoop.cpp: (WTF::RunLoop::performWork): Take all the functions out of the pending queue before we start executing them; that way, we can tell if a new function has been added to the pending queue since we enetered performWork. (WTF::RunLoop::dispatch): Only call wakeUp if we need to. * wtf/RunLoop.h: Tools: Added some tests for interesting edge cases. Now that we have tests, we don't need so many comments. * TestWebKitAPI/Tests/WTF/RunLoop.cpp: (TestWebKitAPI::TEST): Test that re-entry maintiains ordering. Test that re-entry does not deadlock with cross-thread posting. (This deadlock is why we can't just use a naive check for an empty queue.) * TestWebKitAPI/glib/UtilitiesGLib.cpp: (TestWebKitAPI::Util::run): (TestWebKitAPI::Util::spinRunLoop): (TestWebKitAPI::Util::sleep): Try to fix the GLib RunLoop testing code. GLib fails my two new tests, and seems to fail other RunLoop-y tests. I think this is because g_idle_add is defined not to work in nested RunLoops. Let's see if using WTF's RunLoop API instead can help. git-svn-id: http://svn.webkit.org/repository/webkit/trunk@263973 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 2827b74 commit c457a32

File tree

6 files changed

+123
-55
lines changed

6 files changed

+123
-55
lines changed

Source/WTF/ChangeLog

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
2020-07-06 Geoffrey Garen <[email protected]>
2+
3+
RunLoop::dispatch should only call wakeUp when needed
4+
https://bugs.webkit.org/show_bug.cgi?id=213705
5+
6+
Reviewed by Brady Eidson.
7+
8+
RunLoop::wakeUp is super expensive (at least on Darwin platforms). Back
9+
when IndexedDB used RunLoop::dispatch, RunLoop::wakeUp accounted for 15%
10+
of all database lookup time.
11+
12+
We can reduce the cost a bit by only scheduling a wakeUp when the queue
13+
is empty.
14+
15+
* wtf/RunLoop.cpp:
16+
(WTF::RunLoop::performWork): Take all the functions out of the pending
17+
queue before we start executing them; that way, we can tell if a new
18+
function has been added to the pending queue since we enetered
19+
performWork.
20+
21+
(WTF::RunLoop::dispatch): Only call wakeUp if we need to.
22+
23+
* wtf/RunLoop.h:
24+
125
2020-07-04 Darin Adler <[email protected]>
226

327
[Cocoa] Add almost everything from FeatureDefines.xcconfig to PlatformEnableCocoa.h

Source/WTF/wtf/RunLoop.cpp

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -97,45 +97,25 @@ bool RunLoop::isMain()
9797

9898
void RunLoop::performWork()
9999
{
100-
// It is important to handle the functions in the queue one at a time because while inside one of these
101-
// functions we might re-enter RunLoop::performWork() and we need to be able to pick up where we left off.
102-
// See http://webkit.org/b/89590 for more discussion.
103-
104-
// One possible scenario when handling the function queue is as follows:
105-
// - RunLoop::performWork() is invoked with 1 function on the queue
106-
// - Handling that function results in 1 more function being enqueued
107-
// - Handling that one results in yet another being enqueued
108-
// - And so on
109-
//
110-
// In this situation one invocation of performWork() never returns so all other event sources are blocked.
111-
// By only handling up to the number of functions that were in the queue when performWork() is called
112-
// we guarantee to occasionally return from the run loop so other event sources will be allowed to spin.
113-
114-
size_t functionsToHandle = 1;
115100
bool didSuspendFunctions = false;
116101

117-
for (size_t functionsHandled = 0; functionsHandled < functionsToHandle; ++functionsHandled) {
118-
Function<void ()> function;
119-
{
120-
auto locker = holdLock(m_functionQueueLock);
121-
122-
// Even if we start off with N functions to handle and we've only handled less than N functions, the queue
123-
// still might be empty because those functions might have been handled in an inner RunLoop::performWork().
124-
// In that case we should bail here.
125-
if (m_functionQueue.isEmpty())
126-
break;
102+
{
103+
auto locker = holdLock(m_nextIterationLock);
127104

128-
if (m_isFunctionDispatchSuspended) {
129-
didSuspendFunctions = true;
130-
break;
131-
}
105+
// If the RunLoop re-enters or re-schedules, we're expected to execute all functions in order.
106+
while (!m_currentIteration.isEmpty())
107+
m_nextIteration.prepend(m_currentIteration.takeLast());
132108

133-
if (!functionsHandled)
134-
functionsToHandle = m_functionQueue.size();
109+
m_currentIteration = std::exchange(m_nextIteration, { });
110+
}
135111

136-
function = m_functionQueue.takeFirst();
112+
while (!m_currentIteration.isEmpty()) {
113+
if (m_isFunctionDispatchSuspended) {
114+
didSuspendFunctions = true;
115+
break;
137116
}
138-
117+
118+
auto function = m_currentIteration.takeFirst();
139119
function();
140120
}
141121

@@ -149,12 +129,16 @@ void RunLoop::performWork()
149129

150130
void RunLoop::dispatch(Function<void ()>&& function)
151131
{
132+
bool needsWakeup = false;
133+
152134
{
153-
auto locker = holdLock(m_functionQueueLock);
154-
m_functionQueue.append(WTFMove(function));
135+
auto locker = holdLock(m_nextIterationLock);
136+
needsWakeup = m_nextIteration.isEmpty();
137+
m_nextIteration.append(WTFMove(function));
155138
}
156139

157-
wakeUp();
140+
if (needsWakeup)
141+
wakeUp();
158142
}
159143

160144
void RunLoop::suspendFunctionDispatchForCurrentCycle()

Source/WTF/wtf/RunLoop.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,11 @@ class RunLoop final : public FunctionDispatcher {
208208

209209
void performWork();
210210

211-
Lock m_functionQueueLock;
212-
Deque<Function<void()>> m_functionQueue;
211+
Deque<Function<void()>> m_currentIteration;
212+
213+
Lock m_nextIterationLock;
214+
Deque<Function<void()>> m_nextIteration;
215+
213216
bool m_isFunctionDispatchSuspended { false };
214217
bool m_hasSuspendedFunctions { false };
215218

Tools/ChangeLog

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
2020-07-06 Geoffrey Garen <[email protected]>
2+
3+
RunLoop::dispatch should only call wakeUp when needed
4+
https://bugs.webkit.org/show_bug.cgi?id=213705
5+
6+
Reviewed by Brady Eidson.
7+
8+
Added some tests for interesting edge cases.
9+
10+
Now that we have tests, we don't need so many comments.
11+
12+
* TestWebKitAPI/Tests/WTF/RunLoop.cpp:
13+
(TestWebKitAPI::TEST): Test that re-entry maintiains ordering. Test that
14+
re-entry does not deadlock with cross-thread posting. (This deadlock is
15+
why we can't just use a naive check for an empty queue.)
16+
17+
* TestWebKitAPI/glib/UtilitiesGLib.cpp:
18+
(TestWebKitAPI::Util::run):
19+
(TestWebKitAPI::Util::spinRunLoop):
20+
(TestWebKitAPI::Util::sleep): Try to fix the GLib RunLoop testing code.
21+
GLib fails my two new tests, and seems to fail other RunLoop-y tests.
22+
I think this is because g_idle_add is defined not to work in nested
23+
RunLoops. Let's see if using WTF's RunLoop API instead can help.
24+
125
2020-07-06 Carlos Garcia Campos <[email protected]>
226

327
[GTK][WPE] Enable storageAccess tests

Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,48 @@ TEST(WTF_RunLoop, Deadlock)
5555
Util::run(&testFinished);
5656
}
5757

58+
TEST(WTF_RunLoop, NestedInOrder)
59+
{
60+
WTF::initializeMainThread();
61+
62+
bool done = false;
63+
bool didExecuteOuter = false;
64+
65+
RunLoop::main().dispatch([&done, &didExecuteOuter] {
66+
RunLoop::main().dispatch([&done, &didExecuteOuter] {
67+
EXPECT_TRUE(didExecuteOuter);
68+
done = true;
69+
});
70+
71+
Util::run(&done);
72+
});
73+
RunLoop::main().dispatch([&didExecuteOuter] {
74+
didExecuteOuter = true;
75+
});
76+
77+
Util::run(&done);
78+
}
79+
80+
TEST(WTF_RunLoop, DispatchCrossThreadWhileNested)
81+
{
82+
WTF::initializeMainThread();
83+
84+
bool done = false;
85+
86+
RunLoop::main().dispatch([&done] {
87+
Thread::create("DispatchCrossThread", [&done] {
88+
RunLoop::main().dispatch([&done] {
89+
done = true;
90+
});
91+
});
92+
93+
Util::run(&done);
94+
});
95+
RunLoop::main().dispatch([] { });
96+
97+
Util::run(&done);
98+
}
99+
58100
class DerivedOneShotTimer : public RunLoop::Timer<DerivedOneShotTimer> {
59101
public:
60102
DerivedOneShotTimer(bool& testFinished)

Tools/TestWebKitAPI/glib/UtilitiesGLib.cpp

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,22 @@ namespace Util {
3434

3535
void run(bool* done)
3636
{
37-
g_idle_add([](gpointer userData) -> gboolean {
38-
bool* done = static_cast<bool*>(userData);
39-
if (*done)
40-
RunLoop::current().stop();
41-
42-
return !*done;
43-
}, done);
44-
RunLoop::run();
37+
while (!*done)
38+
RunLoop::current().cycle();
4539
}
4640

4741
void spinRunLoop(uint64_t count)
4842
{
49-
g_idle_add([](gpointer userData) -> gboolean {
50-
uint64_t* count = static_cast<uint64_t*>(userData);
51-
return --(*count) ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
52-
}, &count);
43+
while (count--)
44+
RunLoop::current().cycle();
5345
}
5446

5547
void sleep(double seconds)
5648
{
57-
g_timeout_add(seconds * 1000, [](gpointer userData) -> gboolean {
58-
RunLoop::main().stop();
59-
return G_SOURCE_REMOVE;
60-
}, nullptr);
61-
RunLoop::current().stop();
49+
RunLoop::current().dispatchAfter(Seconds(seconds), [] {
50+
RunLoop::current().stop();
51+
});
52+
RunLoop::current().run();
6253
}
6354

6455
} // namespace Util

0 commit comments

Comments
 (0)