1 /*
2 *
3 * Copyright 2017 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/gpr/arena.h"
22
23 #include <string.h>
24
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/atm.h>
27 #include <grpc/support/log.h>
28 #include <grpc/support/sync.h>
29
30 #include "src/core/lib/gpr/alloc.h"
31
32 // Uncomment this to use a simple arena that simply allocates the
33 // requested amount of memory for each call to gpr_arena_alloc(). This
34 // effectively eliminates the efficiency gain of using an arena, but it
35 // may be useful for debugging purposes.
36 //#define SIMPLE_ARENA_FOR_DEBUGGING
37
38 #ifdef SIMPLE_ARENA_FOR_DEBUGGING
39
40 struct gpr_arena {
41 gpr_mu mu;
42 void** ptrs;
43 size_t num_ptrs;
44 };
45
gpr_arena_create(size_t ignored_initial_size)46 gpr_arena* gpr_arena_create(size_t ignored_initial_size) {
47 gpr_arena* arena = (gpr_arena*)gpr_zalloc(sizeof(*arena));
48 gpr_mu_init(&arena->mu);
49 return arena;
50 }
51
gpr_arena_destroy(gpr_arena * arena)52 size_t gpr_arena_destroy(gpr_arena* arena) {
53 gpr_mu_destroy(&arena->mu);
54 for (size_t i = 0; i < arena->num_ptrs; ++i) {
55 gpr_free(arena->ptrs[i]);
56 }
57 gpr_free(arena->ptrs);
58 gpr_free(arena);
59 return 1; // Value doesn't matter, since it won't be used.
60 }
61
gpr_arena_alloc(gpr_arena * arena,size_t size)62 void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
63 gpr_mu_lock(&arena->mu);
64 arena->ptrs =
65 (void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1));
66 void* retval = arena->ptrs[arena->num_ptrs++] = gpr_zalloc(size);
67 gpr_mu_unlock(&arena->mu);
68 return retval;
69 }
70
71 #else // SIMPLE_ARENA_FOR_DEBUGGING
72
73 // TODO(roth): We currently assume that all callers need alignment of 16
74 // bytes, which may be wrong in some cases. As part of converting the
75 // arena API to C++, we should consider replacing gpr_arena_alloc() with a
76 // template that takes the type of the value being allocated, which
77 // would allow us to use the alignment actually needed by the caller.
78
79 typedef struct zone {
80 zone* next;
81 } zone;
82
83 struct gpr_arena {
84 // Keep track of the total used size. We use this in our call sizing
85 // historesis.
86 gpr_atm total_used;
87 size_t initial_zone_size;
88 zone initial_zone;
89 zone* last_zone;
90 gpr_mu arena_growth_mutex;
91 };
92
zalloc_aligned(size_t size)93 static void* zalloc_aligned(size_t size) {
94 void* ptr = gpr_malloc_aligned(size, GPR_MAX_ALIGNMENT);
95 memset(ptr, 0, size);
96 return ptr;
97 }
98
gpr_arena_create(size_t initial_size)99 gpr_arena* gpr_arena_create(size_t initial_size) {
100 initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
101 gpr_arena* a = static_cast<gpr_arena*>(zalloc_aligned(
102 GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size));
103 a->initial_zone_size = initial_size;
104 a->last_zone = &a->initial_zone;
105 gpr_mu_init(&a->arena_growth_mutex);
106 return a;
107 }
108
gpr_arena_destroy(gpr_arena * arena)109 size_t gpr_arena_destroy(gpr_arena* arena) {
110 gpr_mu_destroy(&arena->arena_growth_mutex);
111 gpr_atm size = gpr_atm_no_barrier_load(&arena->total_used);
112 zone* z = arena->initial_zone.next;
113 gpr_free_aligned(arena);
114 while (z) {
115 zone* next_z = z->next;
116 gpr_free_aligned(z);
117 z = next_z;
118 }
119 return static_cast<size_t>(size);
120 }
121
gpr_arena_alloc(gpr_arena * arena,size_t size)122 void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
123 size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size);
124 size_t begin = gpr_atm_no_barrier_fetch_add(&arena->total_used, size);
125 if (begin + size <= arena->initial_zone_size) {
126 return reinterpret_cast<char*>(arena) +
127 GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + begin;
128 } else {
129 // If the allocation isn't able to end in the initial zone, create a new
130 // zone for this allocation, and any unused space in the initial zone is
131 // wasted. This overflowing and wasting is uncommon because of our arena
132 // sizing historesis (that is, most calls should have a large enough initial
133 // zone and will not need to grow the arena).
134 gpr_mu_lock(&arena->arena_growth_mutex);
135 zone* z = static_cast<zone*>(
136 zalloc_aligned(GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + size));
137 arena->last_zone->next = z;
138 arena->last_zone = z;
139 gpr_mu_unlock(&arena->arena_growth_mutex);
140 return reinterpret_cast<char*>(z) +
141 GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone));
142 }
143 }
144
145 #endif // SIMPLE_ARENA_FOR_DEBUGGING
146