| 1487 | } |
| 1488 | |
| 1489 | static void |
| 1490 | prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) { |
| 1491 | prof_tdata_t *tdata = prof_tdata_get(tsd, false); |
| 1492 | prof_gctx_t *gctx; |
| 1493 | |
| 1494 | /* |
| 1495 | * Standard tree iteration won't work here, because as soon as we |
| 1496 | * decrement gctx->nlimbo and unlock gctx, another thread can |
| 1497 | * concurrently destroy it, which will corrupt the tree. Therefore, |
| 1498 | * tear down the tree one node at a time during iteration. |
| 1499 | */ |
| 1500 | while ((gctx = gctx_tree_first(gctxs)) != NULL) { |
| 1501 | gctx_tree_remove(gctxs, gctx); |
| 1502 | malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock); |
| 1503 | { |
| 1504 | prof_tctx_t *next; |
| 1505 | |
| 1506 | next = NULL; |
| 1507 | do { |
| 1508 | prof_tctx_t *to_destroy = |
| 1509 | tctx_tree_iter(&gctx->tctxs, next, |
| 1510 | prof_tctx_finish_iter, |
| 1511 | (void *)tsd_tsdn(tsd)); |
| 1512 | if (to_destroy != NULL) { |
| 1513 | next = tctx_tree_next(&gctx->tctxs, |
| 1514 | to_destroy); |
| 1515 | tctx_tree_remove(&gctx->tctxs, |
| 1516 | to_destroy); |
| 1517 | idalloctm(tsd_tsdn(tsd), to_destroy, |
| 1518 | NULL, NULL, true, true); |
| 1519 | } else { |
| 1520 | next = NULL; |
| 1521 | } |
| 1522 | } while (next != NULL); |
| 1523 | } |
| 1524 | gctx->nlimbo--; |
| 1525 | if (prof_gctx_should_destroy(gctx)) { |
| 1526 | gctx->nlimbo++; |
| 1527 | malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); |
| 1528 | prof_gctx_try_destroy(tsd, tdata, gctx, tdata); |
| 1529 | } else { |
| 1530 | malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock); |
| 1531 | } |
| 1532 | } |
| 1533 | } |
| 1534 | |
| 1535 | struct prof_tdata_merge_iter_arg_s { |
| 1536 | tsdn_t *tsdn; |
no test coverage detected