mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-05-14 00:46:21 +03:00
Revert PR #2608: Manually revert commits 70c94d..f9c0b5
This commit is contained in:
parent
9186700eb3
commit
e2da7477f8
30 changed files with 124 additions and 1364 deletions
|
|
@ -34,8 +34,6 @@ main(void) {
|
|||
P(arena_t);
|
||||
P(arena_stats_t);
|
||||
P(base_t);
|
||||
P(bin_t);
|
||||
P(bin_with_batch_t);
|
||||
P(decay_t);
|
||||
P(edata_t);
|
||||
P(ecache_t);
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef JEMALLOC_TEST_FORK_H
|
||||
#define JEMALLOC_TEST_FORK_H
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
# include <sys/wait.h>
|
||||
|
||||
static inline void
|
||||
fork_wait_for_child_exit(int pid) {
|
||||
int status;
|
||||
while (true) {
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
test_fail("Unexpected waitpid() failure.");
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
test_fail(
|
||||
"Unexpected child termination due to "
|
||||
"signal %d",
|
||||
WTERMSIG(status));
|
||||
break;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
test_fail("Unexpected child exit value %d",
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_TEST_FORK_H */
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
#include "test/jemalloc_test.h"
|
||||
|
||||
#include "jemalloc/internal/batcher.h"
|
||||
|
||||
TEST_BEGIN(test_simple) {
|
||||
enum { NELEMS_MAX = 10, DATA_BASE_VAL = 100, NRUNS = 5 };
|
||||
batcher_t batcher;
|
||||
size_t data[NELEMS_MAX];
|
||||
for (size_t nelems = 0; nelems < NELEMS_MAX; nelems++) {
|
||||
batcher_init(&batcher, nelems);
|
||||
for (int run = 0; run < NRUNS; run++) {
|
||||
for (int i = 0; i < NELEMS_MAX; i++) {
|
||||
data[i] = (size_t)-1;
|
||||
}
|
||||
for (size_t i = 0; i < nelems; i++) {
|
||||
size_t idx = batcher_push_begin(
|
||||
TSDN_NULL, &batcher, 1);
|
||||
assert_zu_eq(i, idx, "Wrong index");
|
||||
assert_zu_eq((size_t)-1, data[idx],
|
||||
"Expected uninitialized slot");
|
||||
data[idx] = DATA_BASE_VAL + i;
|
||||
batcher_push_end(TSDN_NULL, &batcher);
|
||||
}
|
||||
if (nelems > 0) {
|
||||
size_t idx = batcher_push_begin(
|
||||
TSDN_NULL, &batcher, 1);
|
||||
assert_zu_eq(BATCHER_NO_IDX, idx,
|
||||
"Shouldn't be able to push into a full "
|
||||
"batcher");
|
||||
}
|
||||
|
||||
size_t npop = batcher_pop_begin(TSDN_NULL, &batcher);
|
||||
if (nelems == 0) {
|
||||
assert_zu_eq(npop, BATCHER_NO_IDX,
|
||||
"Shouldn't get any items out of an empty "
|
||||
"batcher");
|
||||
} else {
|
||||
assert_zu_eq(npop, nelems,
|
||||
"Wrong number of elements popped");
|
||||
}
|
||||
for (size_t i = 0; i < nelems; i++) {
|
||||
assert_zu_eq(data[i], DATA_BASE_VAL + i,
|
||||
"Item popped out of order!");
|
||||
}
|
||||
if (nelems != 0) {
|
||||
batcher_pop_end(TSDN_NULL, &batcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_multi_push) {
|
||||
size_t idx, nelems;
|
||||
batcher_t batcher;
|
||||
batcher_init(&batcher, 11);
|
||||
/* Push two at a time, 5 times, for 10 total. */
|
||||
for (int i = 0; i < 5; i++) {
|
||||
idx = batcher_push_begin(TSDN_NULL, &batcher, 2);
|
||||
assert_zu_eq(2 * i, idx, "Should push in order");
|
||||
batcher_push_end(TSDN_NULL, &batcher);
|
||||
}
|
||||
/* Pushing two more should fail -- would put us at 12 elems. */
|
||||
idx = batcher_push_begin(TSDN_NULL, &batcher, 2);
|
||||
assert_zu_eq(BATCHER_NO_IDX, idx, "Should be out of space");
|
||||
/* But one more should work */
|
||||
idx = batcher_push_begin(TSDN_NULL, &batcher, 1);
|
||||
assert_zu_eq(10, idx, "Should be out of space");
|
||||
batcher_push_end(TSDN_NULL, &batcher);
|
||||
nelems = batcher_pop_begin(TSDN_NULL, &batcher);
|
||||
batcher_pop_end(TSDN_NULL, &batcher);
|
||||
assert_zu_eq(11, nelems, "Should have popped everything");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
enum {
|
||||
STRESS_TEST_ELEMS = 10,
|
||||
STRESS_TEST_THREADS = 4,
|
||||
STRESS_TEST_OPS = 1000 * 1000,
|
||||
STRESS_TEST_PUSH_TO_POP_RATIO = 5,
|
||||
};
|
||||
|
||||
typedef struct stress_test_data_s stress_test_data_t;
|
||||
struct stress_test_data_s {
|
||||
batcher_t batcher;
|
||||
mtx_t pop_mtx;
|
||||
atomic_u32_t thread_id;
|
||||
|
||||
uint32_t elems_data[STRESS_TEST_ELEMS];
|
||||
size_t push_count[STRESS_TEST_ELEMS];
|
||||
size_t pop_count[STRESS_TEST_ELEMS];
|
||||
atomic_zu_t atomic_push_count[STRESS_TEST_ELEMS];
|
||||
atomic_zu_t atomic_pop_count[STRESS_TEST_ELEMS];
|
||||
};
|
||||
|
||||
/*
|
||||
* Note: 0-indexed. If one element is set and you want to find it, you call
|
||||
* get_nth_set(elems, 0).
|
||||
*/
|
||||
static size_t
|
||||
get_nth_set(bool elems_owned[STRESS_TEST_ELEMS], size_t n) {
|
||||
size_t ntrue = 0;
|
||||
for (size_t i = 0; i < STRESS_TEST_ELEMS; i++) {
|
||||
if (elems_owned[i]) {
|
||||
ntrue++;
|
||||
}
|
||||
if (ntrue > n) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert_not_reached(
|
||||
"Asked for the %zu'th set element when < %zu are "
|
||||
"set",
|
||||
n, n);
|
||||
/* Just to silence a compiler warning. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
stress_test_thd(void *arg) {
|
||||
stress_test_data_t *data = arg;
|
||||
size_t prng = atomic_fetch_add_u32(&data->thread_id, 1, ATOMIC_RELAXED);
|
||||
|
||||
size_t nelems_owned = 0;
|
||||
bool elems_owned[STRESS_TEST_ELEMS] = {0};
|
||||
size_t local_push_count[STRESS_TEST_ELEMS] = {0};
|
||||
size_t local_pop_count[STRESS_TEST_ELEMS] = {0};
|
||||
|
||||
for (int i = 0; i < STRESS_TEST_OPS; i++) {
|
||||
size_t rnd = prng_range_zu(
|
||||
&prng, STRESS_TEST_PUSH_TO_POP_RATIO);
|
||||
if (rnd == 0 || nelems_owned == 0) {
|
||||
size_t nelems = batcher_pop_begin(
|
||||
TSDN_NULL, &data->batcher);
|
||||
if (nelems == BATCHER_NO_IDX) {
|
||||
continue;
|
||||
}
|
||||
for (size_t i = 0; i < nelems; i++) {
|
||||
uint32_t elem = data->elems_data[i];
|
||||
assert_false(elems_owned[elem],
|
||||
"Shouldn't already own what we just "
|
||||
"popped");
|
||||
elems_owned[elem] = true;
|
||||
nelems_owned++;
|
||||
local_pop_count[elem]++;
|
||||
data->pop_count[elem]++;
|
||||
}
|
||||
batcher_pop_end(TSDN_NULL, &data->batcher);
|
||||
} else {
|
||||
size_t elem_to_push_idx = prng_range_zu(
|
||||
&prng, nelems_owned);
|
||||
size_t elem = get_nth_set(
|
||||
elems_owned, elem_to_push_idx);
|
||||
assert_true(elems_owned[elem],
|
||||
"Should own element we're about to pop");
|
||||
elems_owned[elem] = false;
|
||||
local_push_count[elem]++;
|
||||
data->push_count[elem]++;
|
||||
nelems_owned--;
|
||||
size_t idx = batcher_push_begin(
|
||||
TSDN_NULL, &data->batcher, 1);
|
||||
assert_zu_ne(idx, BATCHER_NO_IDX,
|
||||
"Batcher can't be full -- we have one of its "
|
||||
"elems!");
|
||||
data->elems_data[idx] = (uint32_t)elem;
|
||||
batcher_push_end(TSDN_NULL, &data->batcher);
|
||||
}
|
||||
}
|
||||
|
||||
/* Push all local elems back, flush local counts to the shared ones. */
|
||||
size_t push_idx = 0;
|
||||
if (nelems_owned != 0) {
|
||||
push_idx = batcher_push_begin(
|
||||
TSDN_NULL, &data->batcher, nelems_owned);
|
||||
assert_zu_ne(
|
||||
BATCHER_NO_IDX, push_idx, "Should be space to push");
|
||||
}
|
||||
for (size_t i = 0; i < STRESS_TEST_ELEMS; i++) {
|
||||
if (elems_owned[i]) {
|
||||
data->elems_data[push_idx] = (uint32_t)i;
|
||||
push_idx++;
|
||||
local_push_count[i]++;
|
||||
data->push_count[i]++;
|
||||
}
|
||||
atomic_fetch_add_zu(&data->atomic_push_count[i],
|
||||
local_push_count[i], ATOMIC_RELAXED);
|
||||
atomic_fetch_add_zu(&data->atomic_pop_count[i],
|
||||
local_pop_count[i], ATOMIC_RELAXED);
|
||||
}
|
||||
if (nelems_owned != 0) {
|
||||
batcher_push_end(TSDN_NULL, &data->batcher);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_stress) {
|
||||
stress_test_data_t data;
|
||||
batcher_init(&data.batcher, STRESS_TEST_ELEMS);
|
||||
bool err = mtx_init(&data.pop_mtx);
|
||||
assert_false(err, "mtx_init failure");
|
||||
atomic_store_u32(&data.thread_id, 0, ATOMIC_RELAXED);
|
||||
for (int i = 0; i < STRESS_TEST_ELEMS; i++) {
|
||||
data.push_count[i] = 0;
|
||||
data.pop_count[i] = 0;
|
||||
atomic_store_zu(&data.atomic_push_count[i], 0, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&data.atomic_pop_count[i], 0, ATOMIC_RELAXED);
|
||||
|
||||
size_t idx = batcher_push_begin(TSDN_NULL, &data.batcher, 1);
|
||||
assert_zu_eq(i, idx, "Should push in order");
|
||||
data.elems_data[idx] = i;
|
||||
batcher_push_end(TSDN_NULL, &data.batcher);
|
||||
}
|
||||
|
||||
thd_t threads[STRESS_TEST_THREADS];
|
||||
for (int i = 0; i < STRESS_TEST_THREADS; i++) {
|
||||
thd_create(&threads[i], stress_test_thd, &data);
|
||||
}
|
||||
for (int i = 0; i < STRESS_TEST_THREADS; i++) {
|
||||
thd_join(threads[i], NULL);
|
||||
}
|
||||
for (int i = 0; i < STRESS_TEST_ELEMS; i++) {
|
||||
assert_zu_ne(
|
||||
0, data.push_count[i], "Should have done something!");
|
||||
assert_zu_eq(data.push_count[i], data.pop_count[i],
|
||||
"every element should be pushed and popped an equal number "
|
||||
"of times");
|
||||
assert_zu_eq(data.push_count[i],
|
||||
atomic_load_zu(&data.atomic_push_count[i], ATOMIC_RELAXED),
|
||||
"atomic and non-atomic count should be equal given proper "
|
||||
"synchronization");
|
||||
assert_zu_eq(data.pop_count[i],
|
||||
atomic_load_zu(&data.atomic_pop_count[i], ATOMIC_RELAXED),
|
||||
"atomic and non-atomic count should be equal given proper "
|
||||
"synchronization");
|
||||
}
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void) {
|
||||
return test_no_reentrancy(test_simple, test_multi_push, test_stress);
|
||||
}
|
||||
|
|
@ -1,270 +0,0 @@
|
|||
#include "test/jemalloc_test.h"
|
||||
#include "test/fork.h"
|
||||
|
||||
enum {
|
||||
STRESS_THREADS = 3,
|
||||
STRESS_OBJECTS_PER_THREAD = 1000,
|
||||
STRESS_ALLOC_SZ = PAGE / 2,
|
||||
};
|
||||
|
||||
typedef struct stress_thread_data_s stress_thread_data_t;
|
||||
struct stress_thread_data_s {
|
||||
unsigned thd_id;
|
||||
atomic_zu_t *ready_thds;
|
||||
atomic_zu_t *done_thds;
|
||||
void **to_dalloc;
|
||||
};
|
||||
|
||||
static atomic_zu_t push_failure_count;
|
||||
static atomic_zu_t pop_attempt_results[2];
|
||||
static atomic_zu_t dalloc_zero_slab_count;
|
||||
static atomic_zu_t dalloc_nonzero_slab_count;
|
||||
static atomic_zu_t dalloc_nonempty_list_count;
|
||||
|
||||
static bool
|
||||
should_skip() {
|
||||
return
|
||||
/*
|
||||
* We do batching operations on tcache flush pathways; we can't if
|
||||
* caching is disabled.
|
||||
*/
|
||||
!opt_tcache ||
|
||||
/* We rely on tcache fill/flush operations of the size we use. */
|
||||
opt_tcache_max < STRESS_ALLOC_SZ
|
||||
/*
|
||||
* Some of the races we want to trigger are fiddly enough that they
|
||||
* only show up under real concurrency. We add 1 to account for the
|
||||
* main thread, which also does some work.
|
||||
*/
|
||||
|| ncpus < STRESS_THREADS + 1;
|
||||
}
|
||||
|
||||
static void
|
||||
increment_push_failure(size_t push_idx) {
|
||||
if (push_idx == BATCHER_NO_IDX) {
|
||||
atomic_fetch_add_zu(&push_failure_count, 1, ATOMIC_RELAXED);
|
||||
} else {
|
||||
assert_zu_lt(push_idx, 4, "Only 4 elems");
|
||||
volatile size_t x = 10000;
|
||||
while (--x) {
|
||||
/* Spin for a while, to try to provoke a failure. */
|
||||
if (x == push_idx) {
|
||||
#ifdef _WIN32
|
||||
SwitchToThread();
|
||||
#else
|
||||
sched_yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
increment_pop_attempt(size_t elems_to_pop) {
|
||||
bool elems = (elems_to_pop != BATCHER_NO_IDX);
|
||||
atomic_fetch_add_zu(&pop_attempt_results[elems], 1, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static void
|
||||
increment_slab_dalloc_count(unsigned slab_dalloc_count, bool list_empty) {
|
||||
if (slab_dalloc_count > 0) {
|
||||
atomic_fetch_add_zu(
|
||||
&dalloc_nonzero_slab_count, 1, ATOMIC_RELAXED);
|
||||
} else {
|
||||
atomic_fetch_add_zu(&dalloc_zero_slab_count, 1, ATOMIC_RELAXED);
|
||||
}
|
||||
if (!list_empty) {
|
||||
atomic_fetch_add_zu(
|
||||
&dalloc_nonempty_list_count, 1, ATOMIC_RELAXED);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flush_tcache() {
|
||||
assert_d_eq(0, mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
|
||||
"Unexpected mallctl failure");
|
||||
}
|
||||
|
||||
static void *
|
||||
stress_thread(void *arg) {
|
||||
stress_thread_data_t *data = arg;
|
||||
uint64_t prng_state = data->thd_id;
|
||||
atomic_fetch_add_zu(data->ready_thds, 1, ATOMIC_RELAXED);
|
||||
while (atomic_load_zu(data->ready_thds, ATOMIC_RELAXED)
|
||||
!= STRESS_THREADS) {
|
||||
/* Spin */
|
||||
}
|
||||
for (int i = 0; i < STRESS_OBJECTS_PER_THREAD; i++) {
|
||||
dallocx(data->to_dalloc[i], 0);
|
||||
if (prng_range_u64(&prng_state, 3) == 0) {
|
||||
flush_tcache();
|
||||
}
|
||||
}
|
||||
flush_tcache();
|
||||
atomic_fetch_add_zu(data->done_thds, 1, ATOMIC_RELAXED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run main_thread_fn in conditions that trigger all the various edge cases and
|
||||
* subtle race conditions.
|
||||
*/
|
||||
static void
|
||||
stress_run(void (*main_thread_fn)(), int nruns) {
|
||||
bin_batching_test_ndalloc_slabs_max = 1;
|
||||
bin_batching_test_after_push_hook = &increment_push_failure;
|
||||
bin_batching_test_mid_pop_hook = &increment_pop_attempt;
|
||||
bin_batching_test_after_unlock_hook = &increment_slab_dalloc_count;
|
||||
|
||||
atomic_store_zu(&push_failure_count, 0, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&pop_attempt_results[0], 0, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&pop_attempt_results[1], 0, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&dalloc_zero_slab_count, 0, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&dalloc_nonzero_slab_count, 0, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&dalloc_nonempty_list_count, 0, ATOMIC_RELAXED);
|
||||
|
||||
for (int run = 0; run < nruns; run++) {
|
||||
thd_t thds[STRESS_THREADS];
|
||||
stress_thread_data_t thd_datas[STRESS_THREADS];
|
||||
atomic_zu_t ready_thds;
|
||||
atomic_store_zu(&ready_thds, 0, ATOMIC_RELAXED);
|
||||
atomic_zu_t done_thds;
|
||||
atomic_store_zu(&done_thds, 0, ATOMIC_RELAXED);
|
||||
|
||||
void *ptrs[STRESS_THREADS][STRESS_OBJECTS_PER_THREAD];
|
||||
for (int i = 0; i < STRESS_THREADS; i++) {
|
||||
thd_datas[i].thd_id = i;
|
||||
thd_datas[i].ready_thds = &ready_thds;
|
||||
thd_datas[i].done_thds = &done_thds;
|
||||
thd_datas[i].to_dalloc = ptrs[i];
|
||||
for (int j = 0; j < STRESS_OBJECTS_PER_THREAD; j++) {
|
||||
void *ptr = mallocx(STRESS_ALLOC_SZ, 0);
|
||||
assert_ptr_not_null(ptr, "alloc failure");
|
||||
ptrs[i][j] = ptr;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < STRESS_THREADS; i++) {
|
||||
thd_create(&thds[i], stress_thread, &thd_datas[i]);
|
||||
}
|
||||
while (atomic_load_zu(&done_thds, ATOMIC_RELAXED)
|
||||
!= STRESS_THREADS) {
|
||||
main_thread_fn();
|
||||
}
|
||||
for (int i = 0; i < STRESS_THREADS; i++) {
|
||||
thd_join(thds[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bin_batching_test_ndalloc_slabs_max = (unsigned)-1;
|
||||
bin_batching_test_after_push_hook = NULL;
|
||||
bin_batching_test_mid_pop_hook = NULL;
|
||||
bin_batching_test_after_unlock_hook = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
do_allocs_frees() {
|
||||
enum { NALLOCS = 32 };
|
||||
flush_tcache();
|
||||
void *ptrs[NALLOCS];
|
||||
for (int i = 0; i < NALLOCS; i++) {
|
||||
ptrs[i] = mallocx(STRESS_ALLOC_SZ, 0);
|
||||
}
|
||||
for (int i = 0; i < NALLOCS; i++) {
|
||||
dallocx(ptrs[i], 0);
|
||||
}
|
||||
flush_tcache();
|
||||
}
|
||||
|
||||
static void
|
||||
test_arena_reset_main_fn() {
|
||||
do_allocs_frees();
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_arena_reset) {
|
||||
int err;
|
||||
unsigned arena;
|
||||
unsigned old_arena;
|
||||
|
||||
test_skip_if(should_skip());
|
||||
test_skip_if(opt_percpu_arena != percpu_arena_disabled);
|
||||
|
||||
size_t arena_sz = sizeof(arena);
|
||||
err = mallctl("arenas.create", (void *)&arena, &arena_sz, NULL, 0);
|
||||
assert_d_eq(0, err, "Arena creation failed");
|
||||
|
||||
err = mallctl("thread.arena", &old_arena, &arena_sz, &arena, arena_sz);
|
||||
assert_d_eq(0, err, "changing arena failed");
|
||||
|
||||
stress_run(&test_arena_reset_main_fn, /* nruns */ 10);
|
||||
|
||||
flush_tcache();
|
||||
|
||||
char buf[100];
|
||||
malloc_snprintf(buf, sizeof(buf), "arena.%u.reset", arena);
|
||||
err = mallctl(buf, NULL, NULL, NULL, 0);
|
||||
assert_d_eq(0, err, "Couldn't change arena");
|
||||
|
||||
do_allocs_frees();
|
||||
|
||||
err = mallctl("thread.arena", NULL, NULL, &old_arena, arena_sz);
|
||||
assert_d_eq(0, err, "changing arena failed");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
static void
|
||||
test_fork_main_fn() {
|
||||
#ifndef _WIN32
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
test_fail("Fork failure!");
|
||||
} else if (pid == 0) {
|
||||
/* Child */
|
||||
do_allocs_frees();
|
||||
_exit(0);
|
||||
} else {
|
||||
fork_wait_for_child_exit(pid);
|
||||
do_allocs_frees();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_fork) {
|
||||
#ifdef _WIN32
|
||||
test_skip("No fork on windows");
|
||||
#endif
|
||||
test_skip_if(should_skip());
|
||||
stress_run(&test_fork_main_fn, /* nruns */ 10);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
static void
|
||||
test_races_main_fn() {
|
||||
do_allocs_frees();
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_races) {
|
||||
test_skip_if(should_skip());
|
||||
|
||||
stress_run(&test_races_main_fn, /* nruns */ 400);
|
||||
|
||||
assert_zu_lt(0, atomic_load_zu(&push_failure_count, ATOMIC_RELAXED),
|
||||
"Should have seen some push failures");
|
||||
assert_zu_lt(0, atomic_load_zu(&pop_attempt_results[0], ATOMIC_RELAXED),
|
||||
"Should have seen some pop failures");
|
||||
assert_zu_lt(0, atomic_load_zu(&pop_attempt_results[1], ATOMIC_RELAXED),
|
||||
"Should have seen some pop successes");
|
||||
assert_zu_lt(0, atomic_load_zu(&dalloc_zero_slab_count, ATOMIC_RELAXED),
|
||||
"Expected some frees that didn't empty a slab");
|
||||
assert_zu_lt(0,
|
||||
atomic_load_zu(&dalloc_nonzero_slab_count, ATOMIC_RELAXED),
|
||||
"expected some frees that emptied a slab");
|
||||
assert_zu_lt(0,
|
||||
atomic_load_zu(&dalloc_nonempty_list_count, ATOMIC_RELAXED),
|
||||
"expected some frees that used the empty list");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void) {
|
||||
return test_no_reentrancy(test_arena_reset, test_races, test_fork);
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This value of max_batched_size effectively requires all bins to be batched;
|
||||
# our page limits are fuzzy, but we bound slab item counts to 2**32, so we'd be
|
||||
# at multi-gigabyte minimum page sizes.
|
||||
# The reason for this sort of hacky approach is that we want to
|
||||
# allocate/deallocate PAGE/2-sized objects (to trigger the "non-empty" ->
|
||||
# "empty" and "non-empty"-> "full" transitions often, which have special
|
||||
# handling). But the value of PAGE isn't easily available in test scripts.
|
||||
export MALLOC_CONF="narenas:2,bin_shards:1-1000000000:3,max_batched_size:1000000000,remote_free_max_batch:1,remote_free_max:4"
|
||||
|
|
@ -1,5 +1,34 @@
|
|||
#include "test/jemalloc_test.h"
|
||||
#include "test/fork.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
static void
|
||||
wait_for_child_exit(int pid) {
|
||||
int status;
|
||||
while (true) {
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
test_fail("Unexpected waitpid() failure.");
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
test_fail(
|
||||
"Unexpected child termination due to "
|
||||
"signal %d",
|
||||
WTERMSIG(status));
|
||||
break;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
test_fail("Unexpected child exit value %d",
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_BEGIN(test_fork) {
|
||||
#ifndef _WIN32
|
||||
|
|
@ -37,7 +66,7 @@ TEST_BEGIN(test_fork) {
|
|||
/* Child. */
|
||||
_exit(0);
|
||||
} else {
|
||||
fork_wait_for_child_exit(pid);
|
||||
wait_for_child_exit(pid);
|
||||
}
|
||||
#else
|
||||
test_skip("fork(2) is irrelevant to Windows");
|
||||
|
|
@ -60,7 +89,7 @@ do_fork_thd(void *arg) {
|
|||
test_fail("Exec failed");
|
||||
} else {
|
||||
/* Parent */
|
||||
fork_wait_for_child_exit(pid);
|
||||
wait_for_child_exit(pid);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -97,7 +126,7 @@ TEST_BEGIN(test_fork_multithreaded) {
|
|||
do_test_fork_multithreaded();
|
||||
_exit(0);
|
||||
} else {
|
||||
fork_wait_for_child_exit(pid);
|
||||
wait_for_child_exit(pid);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue