Skip to content

Commit 19d1577

Browse files
Cleanup Pthread code. (#298)
Most notably plug memory leaks and another potential race. Signed-off-by: Samuel K. Gutierrez <[email protected]>
1 parent dd2bf39 commit 19d1577

6 files changed

+52
-54
lines changed

src/qvi-group-pthread.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,21 @@ qvi_group_pthread::qvi_group_pthread(
4141
assert(ctx && thread_group);
4242
m_context = ctx;
4343
m_context->retain();
44-
//
44+
// No retain() here because the calling side takes care of this. See
45+
// qvi_pthread_group::split() by way of qvi_group_pthread::split().
4546
thgroup = thread_group;
4647
}
4748

4849
qvi_group_pthread::~qvi_group_pthread(void)
4950
{
50-
qvi_delete(&thgroup);
51+
thgroup->release();
52+
m_context->release();
5153
}
5254

5355
int
5456
qvi_group_pthread::self(
5557
qvi_group **
5658
) {
57-
// TODO(skg)
5859
return QV_ERR_NOT_SUPPORTED;
5960
}
6061

@@ -75,7 +76,6 @@ qvi_group_pthread::split(
7576
rc = qvi_new(&ichild, m_context, ithgroup);
7677
out:
7778
if (qvi_unlikely(rc != QV_SUCCESS)) {
78-
qvi_delete(&ithgroup);
7979
qvi_delete(&ichild);
8080
}
8181
*child = ichild;

src/qvi-group-pthread.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ struct qvi_group_pthread : public qvi_group {
7777
make_intrinsic(
7878
qv_scope_intrinsic_t
7979
) {
80-
// Nothing to do here because a Pthread group cannot be created outside
81-
// of another group. For example, a thread_split can be called from a
80+
// Not supported because a Pthread group cannot be created outside of
81+
// another group. For example, a thread_split can be called from a
8282
// process context, which can be an intrinsic group, but not from a
8383
// threaded context alone.
84-
return QV_SUCCESS;
84+
return QV_ERR_NOT_SUPPORTED;
8585
}
8686

8787
virtual int

src/qvi-pthread.cc

+37-38
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,27 @@
1717
#include "qvi-utils.h"
1818

1919
int
20-
qvi_pthread_group::m_init_by_a_single_thread(
20+
qvi_pthread_group::m_start_init_by_a_single_thread(
2121
qvi_pthread_group_context *ctx,
2222
int group_size
2323
) {
24-
m_context = ctx;
24+
assert(ctx && group_size > 0);
2525

26+
m_context = ctx;
2627
m_size = group_size;
2728

28-
int rc = pthread_barrier_init(&m_barrier, NULL, m_size);
29-
if (qvi_unlikely(rc != 0)) throw qvi_runtime_error();
29+
int rc = pthread_barrier_init(&m_barrier, nullptr, m_size);
30+
if (qvi_unlikely(rc != 0)) return rc;
3031

31-
m_data_g = new qvi_bbuff *[m_size]();
32+
m_gather_data = new qvi_bbuff *[m_size]();
3233
for (int i = 0 ; i < group_size ; i++) {
33-
const int rc = qvi_bbuff_new(&m_data_g[i]);
34-
if (qvi_unlikely(rc != 0)) throw qvi_runtime_error();
34+
rc = qvi_bbuff_new(&m_gather_data[i]);
35+
if (qvi_unlikely(rc != QV_SUCCESS)) return rc;
3536
}
36-
m_data_s = new qvi_bbuff**();
37+
m_scatter_data = new qvi_bbuff**();
3738
m_ckrs = new qvi_subgroup_color_key_rank[m_size]();
3839

39-
return rc;
40+
return QV_SUCCESS;
4041
}
4142

4243
/* static */ int
@@ -52,7 +53,7 @@ qvi_pthread_group::m_finish_init_by_all_threads(
5253
group->m_tids.push_back(mytid);
5354
}
5455
// Make sure they all contribute before continuing.
55-
pthread_barrier_wait(&group->m_barrier);
56+
group->barrier();
5657
// Elect one thread to be the worker.
5758
bool worker = false;
5859
{
@@ -61,14 +62,15 @@ qvi_pthread_group::m_finish_init_by_all_threads(
6162
}
6263
// The worker populates the TID to rank mapping, while the others wait.
6364
if (worker) {
65+
std::lock_guard<std::mutex> guard(group->m_mutex);
6466
std::sort(group->m_tids.begin(), group->m_tids.end());
6567

6668
for (int i = 0; i < group->m_size; ++i) {
6769
const pid_t tidi = group->m_tids[i];
6870
group->m_tid2rank.insert({tidi, i});
6971
}
7072
}
71-
pthread_barrier_wait(&group->m_barrier);
73+
group->barrier();
7274
// Everyone can now create their task and populate the mapping table.
7375
{
7476
std::lock_guard<std::mutex> guard(group->m_mutex);
@@ -78,27 +80,28 @@ qvi_pthread_group::m_finish_init_by_all_threads(
7880
group->m_tid2task.insert({mytid, task});
7981
}
8082
// Make sure they all finish before returning.
81-
pthread_barrier_wait(&group->m_barrier);
83+
group->barrier();
8284
return rc;
8385
}
8486

8587
qvi_pthread_group::qvi_pthread_group(
8688
qvi_pthread_group_context *ctx,
8789
int group_size
8890
) {
89-
const int rc = m_init_by_a_single_thread(ctx, group_size);
91+
const int rc = m_start_init_by_a_single_thread(ctx, group_size);
9092
if (qvi_unlikely(rc != QV_SUCCESS)) throw qvi_runtime_error();
91-
// TODO(skg) Add to group table context.
9293
}
9394

9495
qvi_pthread_group::qvi_pthread_group(
9596
qvi_pthread_group *parent_group,
9697
const qvi_subgroup_info &sginfo
9798
) {
98-
assert(sginfo.rank == 0);
99+
assert(sginfo.rank == qvi_subgroup_info::master_rank);
99100

100101
std::lock_guard<std::mutex> guard(parent_group->m_mutex);
101-
const int rc = m_init_by_a_single_thread(parent_group->m_context, sginfo.size);
102+
const int rc = m_start_init_by_a_single_thread(
103+
parent_group->m_context, sginfo.size
104+
);
102105
if (qvi_unlikely(rc != QV_SUCCESS)) throw qvi_runtime_error();
103106

104107
const qvi_group_id_t mygid = parent_group->m_subgroup_gids[sginfo.index];
@@ -124,30 +127,23 @@ qvi_pthread_group::call_first_from_pthread_create(
124127

125128
qvi_pthread_group::~qvi_pthread_group(void)
126129
{
127-
// TODO(skg)
128-
return;
129-
130130
std::lock_guard<std::mutex> guard(m_mutex);
131+
131132
for (auto &tt : m_tid2task) {
132133
qvi_delete(&tt.second);
133134
}
134-
pthread_barrier_destroy(&m_barrier);
135135

136-
if (m_data_g) {
136+
if (m_gather_data) {
137137
for (int i = 0; i < m_size; ++i) {
138-
qvi_bbuff_delete(&m_data_g[i]);
138+
qvi_bbuff_delete(&m_gather_data[i]);
139139
}
140-
delete[] m_data_g;
140+
delete[] m_gather_data;
141141
}
142142

143-
//C++ flavor
144-
/*
145-
for (auto &tt : m_data) {
146-
qvi_delete(&tt);
147-
}
148-
*/
149-
delete m_data_s;
143+
delete m_scatter_data;
150144
delete[] m_ckrs;
145+
146+
pthread_barrier_destroy(&m_barrier);
151147
}
152148

153149
int
@@ -216,8 +212,8 @@ qvi_pthread_group::m_subgroup_info(
216212
const size_t ncolors = color_set.size();
217213
m_ckrs[my_rank].ncolors = ncolors;
218214
// Now that we know the number of distinct colors, populate the
219-
// sub-group IDs. Set rc here and continue until return so we don't hang
220-
// on an error path.
215+
// sub-group IDs. Set rc here and continue until after the next barrier
216+
// so we don't hang on an error path.
221217
rc = qvi_group::next_ids(ncolors, m_subgroup_gids);
222218
}
223219
// All threads wait for the number of colors to be computed.
@@ -265,7 +261,9 @@ qvi_pthread_group::split(
265261
qvi_subgroup_info sginfo;
266262
int rc = m_subgroup_info(color, key, sginfo);
267263
if (qvi_unlikely(rc != QV_SUCCESS)) goto out;
268-
if (sginfo.rank == 0) {
264+
// One thread creates the child group. The rest wait for the instance and
265+
// later grab a pointer to their group based on the sub-group index.
266+
if (sginfo.rank == qvi_subgroup_info::master_rank) {
269267
// Recall this is the parent group.
270268
rc = qvi_new(&ichild, this, sginfo);
271269
barrier();
@@ -274,10 +272,11 @@ qvi_pthread_group::split(
274272
barrier();
275273
const qvi_group_id_t mygid = m_subgroup_gids[sginfo.index];
276274
ichild = m_context->groupid2thgroup.at(mygid);
275+
ichild->retain();
277276
}
278277
// Now we can check if errors happened above.
279278
if (qvi_unlikely(rc != QV_SUCCESS)) goto out;
280-
279+
// Collectively finish child instance initialization.
281280
rc = m_finish_init_by_all_threads(ichild);
282281
out:
283282
if (qvi_unlikely(rc != QV_SUCCESS)) {
@@ -300,7 +299,7 @@ qvi_pthread_group::gather(
300299
int rc = QV_SUCCESS;
301300
{
302301
std::lock_guard<std::mutex> guard(m_mutex);
303-
rc = qvi_bbuff_copy(*txbuff, m_data_g[myrank]);
302+
rc = qvi_bbuff_copy(*txbuff, m_gather_data[myrank]);
304303
*alloc_type = QVI_BBUFF_ALLOC_SHARED_GLOBAL;
305304
}
306305
// Need to ensure that all threads have contributed to m_data_g
@@ -310,7 +309,7 @@ qvi_pthread_group::gather(
310309
*rxbuffs = nullptr;
311310
return QV_ERR_INTERNAL;
312311
}
313-
*rxbuffs = m_data_g;
312+
*rxbuffs = m_gather_data;
314313
return rc;
315314
}
316315

@@ -325,13 +324,13 @@ qvi_pthread_group::scatter(
325324
qvi_bbuff *mybbuff = nullptr;
326325

327326
if (rootid == myrank) {
328-
*m_data_s = txbuffs;
327+
*m_scatter_data = txbuffs;
329328
}
330329

331330
barrier();
332331
{
333332
std::lock_guard<std::mutex> guard(m_mutex);
334-
rc = qvi_bbuff_dup(*((*m_data_s)[myrank]), &mybbuff);
333+
rc = qvi_bbuff_dup(*((*m_scatter_data)[myrank]), &mybbuff);
335334
}
336335
barrier();
337336

src/qvi-pthread.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct qvi_pthread_group;
2828
* all threads that are spawned by a common parent process.
2929
*/
3030
struct qvi_pthread_group_context : public qvi_refc {
31-
/** */
31+
/** Maps group IDs to Pthread group instance pointers. */
3232
std::unordered_map<qvi_group_id_t, qvi_pthread_group *> groupid2thgroup;
3333
};
3434

@@ -51,7 +51,7 @@ struct qvi_pthread_group_pthread_create_args {
5151
, throutine_argp(throutine_argp_a) { }
5252
};
5353

54-
struct qvi_pthread_group {
54+
struct qvi_pthread_group : qvi_refc {
5555
private:
5656
/** Context information. */
5757
qvi_pthread_group_context *m_context = nullptr;
@@ -70,16 +70,16 @@ struct qvi_pthread_group {
7070
/** Used for barrier things. */
7171
pthread_barrier_t m_barrier;
7272
/** Used for gather exchanges. */
73-
qvi_bbuff **m_data_g = nullptr;
73+
qvi_bbuff **m_gather_data = nullptr;
7474
/** Used for scatter exchanges. */
75-
qvi_bbuff ***m_data_s = nullptr;
76-
/** Shared color, key, rank scratch pad used for splitting. */
75+
qvi_bbuff ***m_scatter_data = nullptr;
76+
/** Shared color, key, rank data used for splitting. */
7777
qvi_subgroup_color_key_rank *m_ckrs = nullptr;
7878
/** Shared sub-group IDs. */
7979
std::vector<qvi_group_id_t> m_subgroup_gids;
8080
/** */
8181
int
82-
m_init_by_a_single_thread(
82+
m_start_init_by_a_single_thread(
8383
qvi_pthread_group_context *ctx,
8484
int group_size
8585
);

src/qvi-subgroup.h

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
* doesn't have native support for creating sub-groups.
2828
*/
2929
struct qvi_subgroup_info {
30+
/** The rank of the master task. */
31+
static constexpr int master_rank = 0;
3032
/** Number of sub-groups created from split. */
3133
int ngroups = 0;
3234
/** My sub-group index (from 0 to ngroups - 1). */

tests/test-pthread-split.c

-3
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,11 @@ thread_work(
4747
ctu_scope_report(pthread_subscope, "thread_subscope");
4848
ctu_emit_task_bind(pthread_subscope);
4949

50-
#if 0
5150
rc = qv_scope_free(pthread_subscope);
5251
if (rc != QV_SUCCESS) {
5352
ers = "qv_scope_free failed";
5453
ctu_panic("%s (rc=%s)", ers, qv_strerr(rc));
5554
}
56-
#endif
57-
5855
return NULL;
5956
}
6057

0 commit comments

Comments
 (0)