Fix leak calling [NSThread currentThread] when underlying thread created outside NSThread API#1872
Conversation
99b69fd to
40a8535
Compare
…ted outside NSThread API
40a8535 to
ed56adb
Compare
|
Also in CI build 😄 |
|
|
||
| // So create a new instance | ||
| currentThread = [[NSThread alloc] init]; | ||
| [currentThread _associateWithCurrentThread]; |
There was a problem hiding this comment.
Actually, it seems like this could be wrapped up into the current thread machinery. At file scope, a thread_local StrongId<NSThread>, which _associateWith... and _threadObjectFrom... could refer to. That way, its lifetime would be managed by the same thing that keeps track of its current-threadiness.
There was a problem hiding this comment.
That makes sense. I'll include that + some cleanups in the next commit - I can see there's e.g. an unused pthread tls key in that file too.
There was a problem hiding this comment.
@DHowett-MSFT I don't think non-POD file scope thread_locals are supported by this version of clang (c2?).
e.g. this is fine at file scope
struct Blah {
Blah() {
abort();
}
~Blah() {
abort();
}
};
thread_local Blah blah;
But even if thread_local worked the lazy approach avoids the static initialization/destruction overhead for every thread created outside the NS* APIs.
I also haven't confirmed but I think this means there's a leak from thread_local use in NSProgress: https://github.com/Microsoft/WinObjC/blob/develop/Frameworks/Foundation/NSProgress.mm#L50
There was a problem hiding this comment.
Good find and somewhat worrisome. Yes, with NSProgress.mm, there is a leak. s_currentProgressStack could become a thread local static inside _getProgressStackForCurrentThread much like you have done here.
|
|
||
| // Ensure the reference grabbed above is | ||
| // the only one remaining after thread exit | ||
| EXPECT_EQ(1, [current retainCount]); |
There was a problem hiding this comment.
I am not sure I understand how this is correct. alloc init should add a reference and currentThread retain should add one?
There was a problem hiding this comment.
In the body of the lambda we're not creating the NSThread via alloc init. We just get a non-owning reference by the call to |currentThread|. The call to |currentThread| does happen to create an owning reference (because it's called from a thread not created via the NSThread API), but this is relinquished on thread exit via the thread_local StrongId destructor.
I'll add some comments to the test case when I add in @DHowett-MSFT's suggested changes - likely sometime tonight. It might also be clearer to convert that test to ARC - but I had some difficulty attempting this initially.
There was a problem hiding this comment.
@rajsesh-msft there's a few more asserts in the unit test which should improve things
rajsesh
left a comment
There was a problem hiding this comment.
Would really like to see the changes suggested by @DHowett-MSFT
43c6fb9 to
e4d5228
Compare
StrongId with a few cleanups and removal of pthread tls function use. Additional asserts in unit test.
e4d5228 to
d91e792
Compare
| static StrongId<NSThread> s_mainThread; | ||
| static BOOL s_isMultiThreaded = NO; | ||
|
|
||
| static NSThread* setOrGetCurrentThread(NSThread* thread) { |
There was a problem hiding this comment.
nit: the private functions should be _set* per coding conventions.
There was a problem hiding this comment.
@rajsesh-msft I'll open a pull with the NSProgress change
|
|
||
| // So create a new instance | ||
| currentThread = [[NSThread alloc] init]; | ||
| [currentThread _associateWithCurrentThread]; |
There was a problem hiding this comment.
Good find and somewhat worrisome. Yes, with NSProgress.mm, there is a leak. s_currentProgressStack could become a thread local static inside _getProgressStackForCurrentThread much like you have done here.
rajsesh
left a comment
There was a problem hiding this comment.
would be great to get the nits addressed, but looks good otherwise.
|
Submitted these for CI build again. |
|
It looks like these never completed build, but they did submit status updates... @pradipd do you know what happened? |
|
Very timely @DHowett-MSFT. Take a look at #1937 that @pradipd just opened. |
|
facepalm |
…ng NSThread.isMainThread (but guard against case where main thread also does not exist)
|
@pradipd thanks for the test fix! I took the opportunity to push another change. No change in behavior - but this avoids creating a new thread-lifetime NSThread object unnecessarily (isMainThread is called frequently via UIView RunSynchronouslyOnMainThread) |
|
Back in CI build. |
|
Back in CI build again. Sorry @ehren! |
|
Thanks @ehren! |
This is an alternative for the naive leak fix in #1834 (which introduces a use after free).
I took a look at supporting the cdecl destructor argument of pthread_key_create but it looks like any fixes there would complicate the implementation of pthread_setspecific/pthread_getspecific. So this commit just calls the Fls functions directly from NSThread.