Skip to content

Commit 90d7568

Browse files
committed
Use an immutable std::weak_ptr for creating borrowed_ptr
to avoid a race condition with reset(). Also remove copy constructor, because writing the shared_ptr itself is not thread safe.
1 parent fc2b718 commit 90d7568

2 files changed

Lines changed: 20 additions & 24 deletions

File tree

src/test/borrowabletest.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ TEST_F(BorrowableTest, SingleThread) {
2222

2323
TEST_F(BorrowableTest, TwoThreads) {
2424
int i = 1;
25-
int j = 2;
2625

2726
auto borrowable = borrowable_ptr(&i);
2827

@@ -42,7 +41,15 @@ TEST_F(BorrowableTest, TwoThreads) {
4241
borrowed_ptr borrowed2 = borrowable.borrow();
4342
int* p1 = borrowed1.get();
4443
int* p2 = borrowed1.get();
45-
qDebug() << "future2" << (p1 ? *p1 : 0) << (p2 ? *p2 : 0);
44+
qDebug() << "future2 a" << (p1 ? *p1 : 0) << (p2 ? *p2 : 0);
45+
}
46+
QThread::usleep(1);
47+
for (int k = 0; k < 2; ++k) {
48+
borrowed_ptr borrowed1 = borrowable.borrow();
49+
borrowed_ptr borrowed2 = borrowable.borrow();
50+
int* p1 = borrowed1.get();
51+
int* p2 = borrowed1.get();
52+
qDebug() << "future2 b" << (p1 ? *p1 : 0) << (p2 ? *p2 : 0);
4653
}
4754
});
4855

@@ -51,8 +58,7 @@ TEST_F(BorrowableTest, TwoThreads) {
5158
qDebug() << "main";
5259
}
5360

54-
// replace borrowable object
55-
borrowable = borrowable_ptr(&j);
61+
borrowable.reset();
5662

5763
// Wait for both tasks to complete
5864
future1.waitForFinished();

src/util/borrowable_ptr.h

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class borrowable_ptr {
104104
explicit borrowable_ptr(Tp* p) {
105105
if (p) {
106106
m_sharedPtr = std::shared_ptr<Tp>(p, borrowable_deleter(&m_mutex));
107+
m_weakPtr = m_sharedPtr;
107108
}
108109
}
109110

@@ -112,31 +113,18 @@ class borrowable_ptr {
112113
m_mutex.unlock();
113114
}
114115

115-
// @brief Assign a new raw pointer to the `borrowable_ptr` but not owning
116-
// @param p Raw pointer to the new object.
117-
// @return Reference to this instance.
118-
borrowable_ptr& operator=(Tp* p) {
119-
reset();
120-
m_mutex.unlock();
121-
m_sharedPtr = std::shared_ptr<Tp>(p, borrowable_deleter(&m_mutex));
122-
return *this;
123-
}
124-
125116
// @brief Borrow a strong reference to the managed object.
126117
// @return A `borrowed_ptr` instance pointing to the managed object.
127118
borrowed_ptr<Tp> borrow() {
128-
return borrowed_ptr<Tp>(m_sharedPtr);
129-
}
130-
131-
borrowable_ptr& operator=(const borrowable_ptr& other) {
132-
this->operator=(other.get());
133-
return *this;
119+
return borrowed_ptr<Tp>(m_weakPtr.lock());
134120
}
135121

136122
void reset() {
137-
m_sharedPtr.reset();
138-
// Wait until all borrowed references are released.
139-
m_mutex.lock();
123+
if (m_sharedPtr.get()) {
124+
m_sharedPtr.reset();
125+
// Wait until all borrowed references are released.
126+
m_mutex.lock();
127+
}
140128
}
141129

142130
// @brief Get the raw pointer to the managed object.
@@ -147,5 +135,7 @@ class borrowable_ptr {
147135

148136
private:
149137
QMutex m_mutex;
150-
std::shared_ptr<Tp> m_sharedPtr; ///< Non-owning shared pointer to the managed object.
138+
// must not be changed after construction, because changing shared pointers is not thread safe.
139+
std::weak_ptr<Tp> m_weakPtr; ///< Non-owning shared pointer for sharing,
140+
std::shared_ptr<Tp> m_sharedPtr; ///< Owning private shared pointer to the managed object.
151141
};

0 commit comments

Comments
 (0)