Bug 199857 - [WTF] Thread::removeFromThreadGroup leaks weak pointers.
Summary: [WTF] Thread::removeFromThreadGroup leaks weak pointers.
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Web Template Framework (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2019-07-17 05:32 PDT by Takashi Komori
Modified: 2019-08-12 20:02 PDT (History)
19 users (show)

See Also:


Attachments
Patch (1.26 KB, patch)
2019-07-17 05:40 PDT, Takashi Komori
no flags Details | Formatted Diff | Diff
Patch (1.35 KB, patch)
2019-07-17 05:56 PDT, Takashi Komori
no flags Details | Formatted Diff | Diff
Patch (6.33 KB, patch)
2019-08-06 00:02 PDT, Takashi Komori
no flags Details | Formatted Diff | Diff
Patch (6.29 KB, patch)
2019-08-06 00:29 PDT, Takashi Komori
no flags Details | Formatted Diff | Diff
Patch (6.33 KB, patch)
2019-08-09 00:53 PDT, Takashi Komori
no flags Details | Formatted Diff | Diff
Patch (6.33 KB, patch)
2019-08-09 01:52 PDT, Takashi Komori
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Takashi Komori 2019-07-17 05:32:35 PDT
ThreadGroup's destructor calls Thread::removeFromThreadGroup and tries to remove its weak pointer from Thread.m_threadGroups.
As the call is from destructor the instance of ThreadGroup is already released and the removing always fail.

As a result, the size of m_thraeadGroups won't decrease.
Comment 1 Takashi Komori 2019-07-17 05:40:30 PDT
Created attachment 374292 [details]
Patch
Comment 2 Takashi Komori 2019-07-17 05:56:47 PDT
Created attachment 374293 [details]
Patch
Comment 3 Brent Fulgham 2019-07-26 18:09:18 PDT
Comment on attachment 374293 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=374293&action=review

> Source/WTF/ChangeLog:8
> +        This patch makes Thread::removeFromThreadGroup remove all released weak pointers.

Is it possible to make a unit test for this bug?
Comment 4 Yusuke Suzuki 2019-07-26 19:37:40 PDT
Comment on attachment 374293 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=374293&action=review

Nice catch! Discussed with Mark about whether this works with the current protocol of ThreadGroup and Thread, and we think that the key is that we should remove ThreadGroup unconditionally without using `weakPtr.lock()`. To achieve it simply with O(1), I commented about the HashMap based approach here. Does it seem good for you too?

> Source/WTF/wtf/Threading.cpp:259
>              return sharedPtr.get() == &threadGroup;
>          return false;
>      });

Nice catch! I think the key is that, if we can just remove threadGroup unconditionally from this list, it should work.
So, ideally, the code should be like,

return &threadGroup == weakPtr.get();

Then it works. But the problem is that std::weak_ptr does not have an original raw pointer. So, can you change the code like,

1. Instead of holding a Vector<std::weak_ptr<ThreadGroup>>, let's hold HashMap<ThreadGroup*, std::weak_ptr<ThreadGroup>>
2. Remove the entry from that hash map in this function
3. In L214, iterate the hash map and collect shared_ptr from the value's weak_ptr

Or, another simple way is that holding struct { ThreadGroup* threadGroup; std::weak_ptr<ThreadGroup> weakPtr } in a Vector, but I think HashMap one is better.
Comment 5 Yusuke Suzuki 2019-07-26 19:41:48 PDT
Comment on attachment 374293 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=374293&action=review

>> Source/WTF/ChangeLog:8
>> +        This patch makes Thread::removeFromThreadGroup remove all released weak pointers.
> 
> Is it possible to make a unit test for this bug?

I think we can test it. Can you add a test to Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp?
We can test it like,

1. Create a ThreadGroup
2. Add many Threads
3. Keep Threads live by using condition variable
4. Destroy ThreadGroup
5. Check the count of ThreadGroups of each Thread

To get the value of (5), we need a method in Thread like,

unsigned Thread::numberOfThreadGroups()
{
    auto locker = holdLock(m_mutex);
    return m_threadGroups.size();
}
Comment 6 Takashi Komori 2019-08-01 01:30:34 PDT
(In reply to Yusuke Suzuki from comment #5)
> Comment on attachment 374293 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=374293&action=review
> 
> >> Source/WTF/ChangeLog:8
> >> +        This patch makes Thread::removeFromThreadGroup remove all released weak pointers.
> > 
> > Is it possible to make a unit test for this bug?
> 
> I think we can test it. Can you add a test to
> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp?
> We can test it like,
> 
> 1. Create a ThreadGroup
> 2. Add many Threads
> 3. Keep Threads live by using condition variable
> 4. Destroy ThreadGroup
> 5. Check the count of ThreadGroups of each Thread
> 
> To get the value of (5), we need a method in Thread like,
> 
> unsigned Thread::numberOfThreadGroups()
> {
>     auto locker = holdLock(m_mutex);
>     return m_threadGroups.size();
> }

Thank you for your detailed advice!
I'll try to make a test.
Comment 7 Takashi Komori 2019-08-06 00:02:47 PDT
Created attachment 375610 [details]
Patch
Comment 8 Takashi Komori 2019-08-06 00:29:34 PDT
Created attachment 375612 [details]
Patch
Comment 9 Yusuke Suzuki 2019-08-07 22:50:47 PDT
Comment on attachment 375612 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=375612&action=review

Some early comments. The code looks good to me.

> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp:156
> +    const int NumberOfThreads = 64;

Use `unsigned` to avoid gtest build failure.

> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp:162
> +    Vector<RefPtr<Thread>> threads;

Use `Ref`.

> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp:165
> +    for (int i = 0; i < NumberOfThreads; i++) {

Use `unsigned`.

> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp:166
> +        auto thread = Thread::create("ThreadGroupWorker", [&](void) {

`(void)` is not necessary.

> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp:167
> +            LockHolder locker(lock);

Use `auto locker = holdLock(lock)`.

> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp:168
> +            threadRunningCondition.wait(lock, [&](void) {

`(void)` is not necessary.

> Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp:184
> +    for (int i = 0; i < NumberOfThreads; i++)

Use `unsigned`.
Comment 10 Takashi Komori 2019-08-09 00:53:58 PDT
Created attachment 375898 [details]
Patch
Comment 11 Takashi Komori 2019-08-09 01:52:48 PDT
Created attachment 375899 [details]
Patch
Comment 12 Yusuke Suzuki 2019-08-09 02:53:01 PDT
Comment on attachment 375899 [details]
Patch

r=me
Comment 13 Darin Adler 2019-08-09 09:38:53 PDT
Comment on attachment 375899 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=375899&action=review

> Source/WTF/wtf/Threading.h:296
> +    HashMap<ThreadGroup*, std::weak_ptr<ThreadGroup>> m_threadGroupMap;

Future cleanup thought: Can we make HashSet<std::weak_ptr<ThreadGroup>> work for a case like this, rather than requiring use of a map?
Comment 14 Yusuke Suzuki 2019-08-09 10:41:03 PDT
Comment on attachment 375899 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=375899&action=review

>> Source/WTF/wtf/Threading.h:296
>> +    HashMap<ThreadGroup*, std::weak_ptr<ThreadGroup>> m_threadGroupMap;
> 
> Future cleanup thought: Can we make HashSet<std::weak_ptr<ThreadGroup>> work for a case like this, rather than requiring use of a map?

Yeah, I would like to make this too :D
Unfortunately, currently, `std::weak_ptr` does not have a way to get a raw pointer if the held object is dead (I would like to have `std::weak_ptr::get()`...).
So we need our own WeakPtr implementation which is thread-safe and having a method (like WeakPtr::getRaw()) returning an original pointer regardless of liveness (thread-safe weak pointer is why we are using std::weak_ptr and std::shared_ptr instead of RefPtr and WeakPtr here).
Comment 15 WebKit Commit Bot 2019-08-12 20:01:17 PDT
Comment on attachment 375899 [details]
Patch

Clearing flags on attachment: 375899

Committed r248586: <https://trac.webkit.org/changeset/248586>
Comment 16 WebKit Commit Bot 2019-08-12 20:01:19 PDT
All reviewed patches have been landed.  Closing bug.
Comment 17 Radar WebKit Bug Importer 2019-08-12 20:02:23 PDT
<rdar://problem/54239165>