Fix arena 0 deferral_allowed flag init

Arena 0 have a dedicated initialization path, which differs from
initialization path of other arenas. The main difference for the purpose
of this change is that we initialize arena 0 before we initialize
background threads. HPA shard options have `deferral_allowed` flag which
should be equal to `background_thread_enabled()` return value, but it
wasn't the case before this change, because for arena 0
`background_thread_enabled()` was initialized correctly after arena 0
initialization phase already ended.

Below is initialization sequence for arena 0 after this commit to
illustrate everything still should be initialized correctly.

* `hpa_central_init` initializes HPA Central, before we initialize every
  HPA shard (including arena's 0).
* `background_thread_boot1` initializes `background_thread_enabled()`
  return value.
* `pa_shard_enable_hpa` initializes arena 0 HPA shard.

```
                       malloc_init_hard -------------
                      /           /                  \
                     /           /                    \
                    /           /                      \
malloc_init_hard_a0_locked  background_thread_boot1  pa_shard_enable_hpa
        /                     /                          \
       /                     /                            \
      /                     /                              \
arena_boot       background_thread_enabled_seta         hpa_shard_init
     |
     |
pa_central_init
     |
     |
hpa_central_init
```
This commit is contained in:
Dmitry Ilvokhin 2025-02-05 04:32:31 -08:00 committed by Qi Wang
parent 421b17a622
commit 499f306859
3 changed files with 47 additions and 9 deletions

View file

@ -1,6 +1,31 @@
#include "test/jemalloc_test.h"
#include "test/sleep.h"
TEST_BEGIN(test_hpa_background_thread_a0_initialized) {
/*
* Arena 0 has dedicated initialization path. We'd like to make sure
* deferral_allowed value initialized correctly from the start of the
* application.
*/
test_skip_if(!config_stats);
test_skip_if(!hpa_supported());
test_skip_if(!have_background_thread);
test_skip_if(san_guard_enabled());
bool enabled = false;
size_t sz = sizeof(enabled);
int err = mallctl("background_thread", (void *)&enabled, &sz, NULL, 0);
expect_d_eq(err, 0, "Unexpected mallctl() failure");
expect_true(enabled, "Background thread should be enabled");
arena_t *a0 = arena_get(TSDN_NULL, 0, false);
expect_ptr_ne(a0, NULL, "");
bool deferral_allowed = a0->pa_shard.hpa_shard.opts.deferral_allowed;
expect_true(deferral_allowed,
"Should have deferral_allowed option enabled for arena #0");
}
TEST_END
static void
sleep_for_background_thread_interval(void) {
/*
@ -207,6 +232,12 @@ main(void) {
opt_background_thread = true;
}
return test_no_reentrancy(
/*
* Unfortunately, order of tests is important here. We need to
* make sure arena #0 initialized correctly, before we start
* turning background thread on and off in other tests.
*/
test_hpa_background_thread_a0_initialized,
test_hpa_background_thread_purges,
test_hpa_background_thread_enable_disable);
}