• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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