Skip to content

Commit 799d054

Browse files
benlangmuirtkremenek
authored andcommitted
Fix ThreadSafeRefCountedBase*::Release() (#3673)
C++ atomic's fetch_sub returns the previous value, where we want to check the new value. This was causing massive memory leaks in SourceKit. For ThreadSafeRefCountedBase, just switch to the one in LLVM that's already correct. We should move the VPTR one to LLVM as well and then we can get rid of this header. rdar://problem/27358273
1 parent 07282c1 commit 799d054

File tree

7 files changed

+60
-36
lines changed

7 files changed

+60
-36
lines changed

include/swift/Basic/ThreadSafeRefCounted.h

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,10 @@
1515

1616
#include <atomic>
1717
#include <cassert>
18+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1819

1920
namespace swift {
2021

21-
/// A thread-safe version of \c llvm::RefCountedBase.
22-
///
23-
/// A generic base class for objects that wish to have their lifetimes managed
24-
/// using reference counts. Classes subclass \c ThreadSafeRefCountedBase to
25-
/// obtain such functionality, and are typically handled with
26-
/// \c IntrusiveRefCntPtr "smart pointers" which automatically handle the
27-
/// management of reference counts.
28-
/// FIXME: This should eventually move to llvm.
29-
template <class Derived>
30-
class ThreadSafeRefCountedBase {
31-
mutable std::atomic<unsigned> ref_cnt;
32-
33-
protected:
34-
ThreadSafeRefCountedBase() : ref_cnt(0) {}
35-
36-
public:
37-
void Retain() const {
38-
ref_cnt.fetch_add(1, std::memory_order_acq_rel);
39-
}
40-
41-
void Release() const {
42-
int refCount =
43-
static_cast<int>(ref_cnt.fetch_sub(1, std::memory_order_acq_rel));
44-
assert(refCount >= 0 && "Reference count was already zero.");
45-
if (refCount == 0) delete static_cast<const Derived*>(this);
46-
}
47-
};
48-
4922
/// A class that has the same function as \c ThreadSafeRefCountedBase, but with
5023
/// a virtual destructor.
5124
///
@@ -62,12 +35,11 @@ class ThreadSafeRefCountedBaseVPTR {
6235

6336
public:
6437
void Retain() const {
65-
ref_cnt.fetch_add(1, std::memory_order_acq_rel);
38+
ref_cnt += 1;
6639
}
6740

6841
void Release() const {
69-
int refCount =
70-
static_cast<int>(ref_cnt.fetch_sub(1, std::memory_order_acq_rel));
42+
int refCount = static_cast<int>(--ref_cnt);
7143
assert(refCount >= 0 && "Reference count was already zero.");
7244
if (refCount == 0) delete this;
7345
}

include/swift/IDE/CodeCompletionCache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class CodeCompletionCache {
4949
}
5050
};
5151

52-
struct Value : public ThreadSafeRefCountedBase<Value> {
52+
struct Value : public llvm::ThreadSafeRefCountedBase<Value> {
5353
llvm::sys::TimeValue ModuleModificationTime;
5454
CodeCompletionResultSink Sink;
5555
};

tools/SourceKit/include/SourceKit/Core/LLVM.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace llvm {
4343
// Reference counting.
4444
template <typename T> class IntrusiveRefCntPtr;
4545
template <typename T> struct IntrusiveRefCntPtrInfo;
46+
template <class Derived> class ThreadSafeRefCountedBase;
4647

4748
class raw_ostream;
4849
// TODO: DenseMap, ...
@@ -69,7 +70,6 @@ namespace llvm {
6970
}
7071

7172
namespace swift {
72-
template <class Derived> class ThreadSafeRefCountedBase;
7373
class ThreadSafeRefCountedBaseVPTR;
7474
}
7575

@@ -95,7 +95,7 @@ namespace SourceKit {
9595
// Reference counting.
9696
using llvm::IntrusiveRefCntPtr;
9797
using llvm::IntrusiveRefCntPtrInfo;
98-
using swift::ThreadSafeRefCountedBase;
98+
using llvm::ThreadSafeRefCountedBase;
9999
using swift::ThreadSafeRefCountedBaseVPTR;
100100
template <typename T> class ThreadSafeRefCntPtr;
101101

tools/SourceKit/lib/SwiftLang/SwiftInterfaceGenContext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace SourceKit {
3131
typedef IntrusiveRefCntPtr<ASTUnit> ASTUnitRef;
3232

3333
class SwiftInterfaceGenContext :
34-
public swift::ThreadSafeRefCountedBase<SwiftInterfaceGenContext> {
34+
public llvm::ThreadSafeRefCountedBase<SwiftInterfaceGenContext> {
3535
public:
3636
static SwiftInterfaceGenContextRef create(StringRef DocumentName,
3737
bool IsModule,

tools/SourceKit/lib/SwiftLang/SwiftInvocation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace SourceKit {
2626

2727
/// Encompasses an invocation for getting an AST. This is used to control AST
2828
/// sharing among different requests.
29-
class SwiftInvocation : public swift::ThreadSafeRefCountedBase<SwiftInvocation> {
29+
class SwiftInvocation : public llvm::ThreadSafeRefCountedBase<SwiftInvocation> {
3030
public:
3131
~SwiftInvocation();
3232

unittests/Basic/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_swift_unittest(SwiftBasicTests
1919
SourceManager.cpp
2020
StringExtrasTest.cpp
2121
SuccessorMapTest.cpp
22+
ThreadSafeRefCntPointerTests.cpp
2223
TreeScopedHashTableTests.cpp
2324
Unicode.cpp
2425
${generated_tests}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "swift/Basic/ThreadSafeRefCounted.h"
2+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
3+
#include "gtest/gtest.h"
4+
5+
using llvm::IntrusiveRefCntPtr;
6+
7+
struct TestRelease : llvm::ThreadSafeRefCountedBase<TestRelease> {
8+
bool &destroy;
9+
TestRelease(bool &destroy) : destroy(destroy) {}
10+
~TestRelease() { destroy = true; }
11+
};
12+
13+
TEST(ThreadSafeRefCountedBase, ReleaseSimple) {
14+
bool destroyed = false;
15+
{
16+
IntrusiveRefCntPtr<TestRelease> ref = new TestRelease(destroyed);
17+
}
18+
EXPECT_TRUE(destroyed);
19+
}
20+
TEST(ThreadSafeRefCountedBase, Release) {
21+
bool destroyed = false;
22+
{
23+
IntrusiveRefCntPtr<TestRelease> ref = new TestRelease(destroyed);
24+
ref->Retain();
25+
ref->Release();
26+
}
27+
EXPECT_TRUE(destroyed);
28+
}
29+
30+
struct TestReleaseVPTR : swift::ThreadSafeRefCountedBaseVPTR {
31+
bool &destroy;
32+
TestReleaseVPTR(bool &destroy) : destroy(destroy) {}
33+
virtual ~TestReleaseVPTR() { destroy = true; }
34+
};
35+
36+
TEST(ThreadSafeRefCountedBaseVPTR, ReleaseSimple) {
37+
bool destroyed = false;
38+
{
39+
IntrusiveRefCntPtr<TestReleaseVPTR> ref = new TestReleaseVPTR(destroyed);
40+
}
41+
EXPECT_TRUE(destroyed);
42+
}
43+
TEST(ThreadSafeRefCountedBaseVPTR, Release) {
44+
bool destroyed = false;
45+
{
46+
IntrusiveRefCntPtr<TestReleaseVPTR> ref = new TestReleaseVPTR(destroyed);
47+
ref->Retain();
48+
ref->Release();
49+
}
50+
EXPECT_TRUE(destroyed);
51+
}

0 commit comments

Comments
 (0)