| 1216 | } |
| 1217 | |
| 1218 | static void |
| 1219 | prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) { |
| 1220 | prof_tdata_t *tdata = prof_tdata_get(tsd, false); |
| 1221 | prof_gctx_t *gctx; |
| 1222 | |
| 1223 | /* |
| 1224 | * Standard tree iteration won't work here, because as soon as we |
| 1225 | * decrement gctx->nlimbo and unlock gctx, another thread can |
| 1226 | * concurrently destroy it, which will corrupt the tree. Therefore, |
| 1227 | * tear down the tree one node at a time during iteration. |
| 1228 | */ |
| 1229 | while ((gctx = gctx_tree_first(gctxs)) != NULL) { |
| 1230 | gctx_tree_remove(gctxs, gctx); |
| 1231 | malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock); |
| 1232 | { |
| 1233 | prof_tctx_t *next; |
| 1234 | |
| 1235 | next = NULL; |
| 1236 | do { |
| 1237 | prof_tctx_t *to_destroy = |
| 1238 | tctx_tree_iter(&gctx->tctxs, next, |
| 1239 | prof_tctx_finish_iter, |
| 1240 | (void *)tsd_tsdn(tsd)); |
| 1241 | if (to_destroy != NULL) { |
| 1242 | next = tctx_tree_next(&gctx->tctxs, |
| 1243 | to_destroy); |
| 1244 | tctx_tree_remove(&gctx->tctxs, |
| 1245 | to_destroy); |
| 1246 | idalloctm(tsd_tsdn(tsd), to_destroy, |
| 1247 | NULL, NULL, true, true); |
| 1248 | } else { |
| 1249 | next = NULL; |
| 1250 | } |
| 1251 | } while (next != NULL); |
| 1252 | } |
| 1253 | gctx->nlimbo--; |
| 1254 | if (prof_gctx_should_destroy(gctx)) { |
| 1255 | gctx->nlimbo++; |
| 1256 | malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); |
| 1257 | prof_gctx_try_destroy(tsd, tdata, gctx, tdata); |
| 1258 | } else { |
| 1259 | malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); |
| 1260 | } |
| 1261 | } |
| 1262 | } |
| 1263 | |
| 1264 | struct prof_tdata_merge_iter_arg_s { |
| 1265 | tsdn_t *tsdn; |
no test coverage detected