Replace atomics in gpr_arena_aloc with a mutex in the slow path

pull/16069/head
Hope Casey-Allen 6 years ago
parent d52661af9a
commit 95390f943e
  1. 88
      src/core/lib/gpr/arena.cc

@ -25,6 +25,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/atm.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include "src/core/lib/gpr/alloc.h"
@ -36,8 +37,6 @@
#ifdef SIMPLE_ARENA_FOR_DEBUGGING
#include <grpc/support/sync.h>
struct gpr_arena {
gpr_mu mu;
void** ptrs;
@ -78,14 +77,17 @@ void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
// would allow us to use the alignment actually needed by the caller.
typedef struct zone {
size_t size_begin;
size_t size_end;
gpr_atm next_atm;
size_t size_begin; // All the space we have set aside for allocations up
// until this zone.
size_t size_end; // size_end = size_begin plus all the space we set aside for
// allocations in zone z itself.
zone* next;
} zone;
struct gpr_arena {
gpr_atm size_so_far;
zone initial_zone;
gpr_mu arena_growth_mutex;
};
static void* zalloc_aligned(size_t size) {
@ -99,15 +101,17 @@ gpr_arena* gpr_arena_create(size_t initial_size) {
gpr_arena* a = static_cast<gpr_arena*>(zalloc_aligned(
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size));
a->initial_zone.size_end = initial_size;
gpr_mu_init(&a->arena_growth_mutex);
return a;
}
size_t gpr_arena_destroy(gpr_arena* arena) {
gpr_mu_destroy(&arena->arena_growth_mutex);
gpr_atm size = gpr_atm_no_barrier_load(&arena->size_so_far);
zone* z = (zone*)gpr_atm_no_barrier_load(&arena->initial_zone.next_atm);
zone* z = arena->initial_zone.next;
gpr_free_aligned(arena);
while (z) {
zone* next_z = (zone*)gpr_atm_no_barrier_load(&z->next_atm);
zone* next_z = z->next;
gpr_free_aligned(z);
z = next_z;
}
@ -116,37 +120,55 @@ size_t gpr_arena_destroy(gpr_arena* arena) {
void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size);
size_t start = static_cast<size_t>(
size_t previous_size_of_arena_allocations = static_cast<size_t>(
gpr_atm_no_barrier_fetch_add(&arena->size_so_far, size));
size_t updated_size_of_arena_allocations =
previous_size_of_arena_allocations + size;
zone* z = &arena->initial_zone;
while (start > z->size_end) {
zone* next_z = (zone*)gpr_atm_acq_load(&z->next_atm);
if (next_z == nullptr) {
size_t next_z_size =
static_cast<size_t>(gpr_atm_no_barrier_load(&arena->size_so_far));
next_z = static_cast<zone*>(zalloc_aligned(
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + next_z_size));
next_z->size_begin = z->size_end;
next_z->size_end = z->size_end + next_z_size;
if (!gpr_atm_rel_cas(&z->next_atm, static_cast<gpr_atm>(NULL),
(gpr_atm)next_z)) {
gpr_free_aligned(next_z);
next_z = (zone*)gpr_atm_acq_load(&z->next_atm);
// Check to see if the allocation isn't able to end in the initial zone.
// This statement is true only in the uncommon case because of our arena
// sizing historesis (that is, most calls should have a large enough initial
// zone and will not need to grow the arena).
if (updated_size_of_arena_allocations > z->size_end) {
// Find a zone to fit this allocation
gpr_mu_lock(&arena->arena_growth_mutex);
while (updated_size_of_arena_allocations > z->size_end) {
if (z->next == nullptr) {
// Note that we do an extra increment of size_so_far to prevent multiple
// simultaneous callers from stepping on each other. However, this extra
// increment means some space in the arena is wasted.
// So whenever we need to allocate x bytes and there are x - n (where
// n > 0) remaining in the current zone, we will waste x bytes (x - n
// in the current zone and n in the new zone).
previous_size_of_arena_allocations = static_cast<size_t>(
gpr_atm_no_barrier_fetch_add(&arena->size_so_far, size));
updated_size_of_arena_allocations =
previous_size_of_arena_allocations + size;
size_t next_z_size = updated_size_of_arena_allocations;
z->next = static_cast<zone*>(zalloc_aligned(
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + next_z_size));
z->next->size_begin = z->size_end;
z->next->size_end = z->size_end + next_z_size;
}
z = z->next;
}
z = next_z;
}
if (start + size > z->size_end) {
return gpr_arena_alloc(arena, size);
gpr_mu_unlock(&arena->arena_growth_mutex);
}
GPR_ASSERT(start >= z->size_begin);
GPR_ASSERT(start + size <= z->size_end);
char* ptr = (z == &arena->initial_zone)
? reinterpret_cast<char*>(arena) +
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena))
: reinterpret_cast<char*>(z) +
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone));
return ptr + start - z->size_begin;
GPR_ASSERT(previous_size_of_arena_allocations >= z->size_begin);
GPR_ASSERT(updated_size_of_arena_allocations <= z->size_end);
// Skip the first part of the zone, which just contains tracking information.
// For the initial zone, this is the gpr_arena struct and for any other zone,
// it's the zone struct.
char* start_of_allocation_space =
(z == &arena->initial_zone)
? reinterpret_cast<char*>(arena) +
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena))
: reinterpret_cast<char*>(z) +
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone));
// previous_size_of_arena_allocations - size_begin is how many bytes have been
// allocated into the current zone
return start_of_allocation_space + previous_size_of_arena_allocations -
z->size_begin;
}
#endif // SIMPLE_ARENA_FOR_DEBUGGING

Loading…
Cancel
Save