Skip to content

Commit 70015fa

Browse files
committed
Fix logic bugs and enhance destruction safety in block_queue
1 parent 4bcf887 commit 70015fa

1 file changed

Lines changed: 56 additions & 19 deletions

File tree

log/block_queue.h

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class block_queue
2121
{
2222
if (max_size <= 0)
2323
{
24+
std::cerr << "Fatal Error: block_queue init (max_size <= 0)" << std::endl;
2425
exit(-1);
2526
}
2627

@@ -29,6 +30,7 @@ class block_queue
2930
m_size = 0;
3031
m_front = -1;
3132
m_back = -1;
33+
m_close = false;
3234
}
3335

3436
void clear()
@@ -40,14 +42,17 @@ class block_queue
4042
m_mutex.unlock();
4143
}
4244

43-
~block_queue()
44-
{
45+
~block_queue() {
4546
m_mutex.lock();
46-
if (m_array != NULL)
47+
m_close = true;
48+
m_cond.broadcast(); // 强行叫醒所有还在睡觉的 pop 线程
49+
if (m_array != NULL){
4750
delete [] m_array;
48-
51+
m_array = NULL;
52+
}
4953
m_mutex.unlock();
5054
}
55+
5156
//判断队列是否满了
5257
bool full()
5358
{
@@ -77,20 +82,24 @@ class block_queue
7782
bool front(T &value)
7883
{
7984
m_mutex.lock();
80-
if (0 == m_size)
85+
// check: 如果队列已关闭或为空,直接返回
86+
if (m_close || 0 == m_size)
8187
{
8288
m_mutex.unlock();
8389
return false;
8490
}
85-
value = m_array[m_front];
91+
// 求队首元素索引
92+
int index = ( m_front +1 ) % m_max_size;
93+
value = m_array[index];
8694
m_mutex.unlock();
8795
return true;
8896
}
8997
//返回队尾元素
9098
bool back(T &value)
9199
{
92100
m_mutex.lock();
93-
if (0 == m_size)
101+
// check: 如果队列已关闭或为空,直接返回
102+
if (m_close || 0 == m_size)
94103
{
95104
m_mutex.unlock();
96105
return false;
@@ -128,6 +137,13 @@ class block_queue
128137
{
129138

130139
m_mutex.lock();
140+
141+
// check: 如果队列已关闭,直接返回
142+
if (m_close) {
143+
m_mutex.unlock();
144+
return false;
145+
}
146+
131147
if (m_size >= m_max_size)
132148
{
133149

@@ -141,7 +157,7 @@ class block_queue
141157

142158
m_size++;
143159

144-
m_cond.broadcast();
160+
m_cond.signal(); // 减少唤醒多个线程带来的可能额外性能损耗
145161
m_mutex.unlock();
146162
return true;
147163
}
@@ -151,15 +167,26 @@ class block_queue
151167

152168
m_mutex.lock();
153169
while (m_size <= 0)
154-
{
155-
170+
{
171+
// check: 直接退出返回
172+
if (m_close) {
173+
m_mutex.unlock();
174+
return false;
175+
}
176+
156177
if (!m_cond.wait(m_mutex.get()))
157178
{
158179
m_mutex.unlock();
159180
return false;
160181
}
161182
}
162183

184+
// check:从 wait 醒来后,可能资源已被析构,必须再次判断
185+
if (m_close) {
186+
m_mutex.unlock();
187+
return false;
188+
}
189+
163190
m_front = (m_front + 1) % m_max_size;
164191
item = m_array[m_front];
165192
m_size--;
@@ -173,20 +200,29 @@ class block_queue
173200
struct timespec t = {0, 0};
174201
struct timeval now = {0, 0};
175202
gettimeofday(&now, NULL);
203+
204+
long long total_ns = (now.tv_usec * 1000LL) + ((ms_timeout % 1000) * 1000000LL);
205+
t.tv_sec = now.tv_sec + (ms_timeout / 1000) + (total_ns / 1000000000LL);
206+
t.tv_nsec = total_ns % 1000000000LL;
207+
176208
m_mutex.lock();
177-
if (m_size <= 0)
178-
{
179-
t.tv_sec = now.tv_sec + ms_timeout / 1000;
180-
t.tv_nsec = (ms_timeout % 1000) * 1000;
181-
if (!m_cond.timewait(m_mutex.get(), t))
182-
{
209+
210+
// 用 while 对抗虚假唤醒
211+
while (m_size <= 0) {
212+
// check: 直接退出返回
213+
if (m_close) {
183214
m_mutex.unlock();
184215
return false;
185216
}
217+
// timewait 内部是用绝对时间比较的,所以 t 算好一次就可以一直用
218+
if (!m_cond.timewait(m_mutex.get(), t)) {
219+
m_mutex.unlock();
220+
return false; // 真正超时
221+
}
186222
}
187-
188-
if (m_size <= 0)
189-
{
223+
224+
// check:从 timewait 醒来后,可能资源已被析构,必须再次判断
225+
if (m_close) {
190226
m_mutex.unlock();
191227
return false;
192228
}
@@ -207,6 +243,7 @@ class block_queue
207243
int m_max_size;
208244
int m_front;
209245
int m_back;
246+
bool m_close;
210247
};
211248

212249
#endif

0 commit comments

Comments
 (0)