1 | /* |
2 | * arenax.c |
3 | * |
4 | * Copyright (C) 2012-2014 Aerospike, Inc. |
5 | * |
6 | * Portions may be licensed to Aerospike, Inc. under one or more contributor |
7 | * license agreements. |
8 | * |
9 | * This program is free software: you can redistribute it and/or modify it under |
10 | * the terms of the GNU Affero General Public License as published by the Free |
11 | * Software Foundation, either version 3 of the License, or (at your option) any |
12 | * later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, but WITHOUT |
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
16 | * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
17 | * details. |
18 | * |
19 | * You should have received a copy of the GNU Affero General Public License |
20 | * along with this program. If not, see http://www.gnu.org/licenses/ |
21 | */ |
22 | |
23 | //========================================================== |
24 | // Includes. |
25 | // |
26 | |
27 | #include "arenax.h" |
28 | |
29 | #include <stdbool.h> |
30 | #include <stddef.h> |
31 | #include <stdint.h> |
32 | #include <string.h> |
33 | #include <sys/types.h> |
34 | |
35 | #include "citrusleaf/alloc.h" |
36 | |
37 | #include "cf_mutex.h" |
38 | #include "fault.h" |
39 | #include "xmem.h" |
40 | |
41 | |
42 | //========================================================== |
43 | // Typedefs & constants. |
44 | // |
45 | |
46 | // Must be in-sync with cf_arenax_err: |
47 | const char* ARENAX_ERR_STRINGS[] = { |
48 | "ok" , |
49 | "bad parameter" , |
50 | "error creating stage" , |
51 | "error attaching stage" , |
52 | "error detaching stage" , |
53 | "unknown error" |
54 | }; |
55 | |
56 | |
57 | //========================================================== |
58 | // Public API. |
59 | // |
60 | |
61 | // Return persistent memory size needed. Excludes stages, which cf_arenax |
62 | // handles internally. |
63 | size_t |
64 | cf_arenax_sizeof() |
65 | { |
66 | return sizeof(cf_arenax); |
67 | } |
68 | |
69 | // Convert cf_arenax_err to meaningful string. |
70 | const char* |
71 | cf_arenax_errstr(cf_arenax_err err) |
72 | { |
73 | if (err < 0 || err > CF_ARENAX_ERR_UNKNOWN) { |
74 | err = CF_ARENAX_ERR_UNKNOWN; |
75 | } |
76 | |
77 | return ARENAX_ERR_STRINGS[err]; |
78 | } |
79 | |
80 | // Create a cf_arenax object in persistent memory. Also create and attach the |
81 | // first arena stage in persistent memory. |
82 | void |
83 | cf_arenax_init(cf_arenax* arena, cf_xmem_type xmem_type, |
84 | const void* xmem_type_cfg, key_t key_base, uint32_t element_size, |
85 | uint32_t chunk_count, uint32_t stage_capacity, uint32_t max_stages, |
86 | uint32_t flags) |
87 | { |
88 | if (max_stages == 0) { |
89 | max_stages = CF_ARENAX_MAX_STAGES; |
90 | } |
91 | else if (max_stages > CF_ARENAX_MAX_STAGES) { |
92 | cf_crash(CF_ARENAX, "max stages %u too large" , max_stages); |
93 | } |
94 | |
95 | arena->xmem_type = xmem_type; |
96 | arena->xmem_type_cfg = xmem_type_cfg; |
97 | arena->key_base = key_base; |
98 | arena->element_size = element_size; |
99 | arena->chunk_count = chunk_count; |
100 | arena->stage_capacity = stage_capacity; |
101 | arena->max_stages = max_stages; |
102 | arena->flags = flags; |
103 | |
104 | arena->stage_size = (size_t)stage_capacity * element_size; |
105 | |
106 | arena->free_h = 0; |
107 | |
108 | if (chunk_count == 1) { |
109 | arena->pool_len = 0; |
110 | arena->pool_buf = NULL; |
111 | } |
112 | else { |
113 | arena->pool_len = arena->stage_capacity; |
114 | arena->pool_buf = |
115 | cf_malloc(arena->pool_len * sizeof(cf_arenax_chunk)); |
116 | } |
117 | |
118 | arena->pool_i = 0; |
119 | |
120 | // Skip 0:0 so null handle is never used. |
121 | arena->at_stage_id = 0; |
122 | arena->at_element_id = arena->chunk_count; |
123 | |
124 | if ((flags & CF_ARENAX_BIGLOCK) != 0) { |
125 | cf_mutex_init(&arena->lock); |
126 | } |
127 | |
128 | arena->stage_count = 0; |
129 | memset(arena->stages, 0, sizeof(arena->stages)); |
130 | |
131 | // Add first stage. |
132 | if (cf_arenax_add_stage(arena) != CF_ARENAX_OK) { |
133 | cf_crash(CF_ARENAX, "failed to add first stage" ); |
134 | } |
135 | |
136 | // Clear the null element - allocation bypasses it, but it may be read. |
137 | memset(cf_arenax_resolve(arena, 0), 0, element_size * chunk_count); |
138 | } |
139 | |
140 | // Allocate an element within the arena. |
141 | cf_arenax_handle |
142 | cf_arenax_alloc(cf_arenax* arena, cf_arenax_puddle* puddle) |
143 | { |
144 | if (puddle != NULL) { |
145 | return cf_arenax_alloc_chunked(arena, puddle); |
146 | } |
147 | |
148 | if ((arena->flags & CF_ARENAX_BIGLOCK) != 0) { |
149 | cf_mutex_lock(&arena->lock); |
150 | } |
151 | |
152 | cf_arenax_handle h; |
153 | |
154 | // Check free list first. |
155 | if (arena->free_h != 0) { |
156 | h = arena->free_h; |
157 | |
158 | free_element* p_free_element = cf_arenax_resolve(arena, h); |
159 | |
160 | arena->free_h = p_free_element->next_h; |
161 | } |
162 | // Otherwise keep end-allocating. |
163 | else { |
164 | if (arena->at_element_id >= arena->stage_capacity) { |
165 | if (cf_arenax_add_stage(arena) != CF_ARENAX_OK) { |
166 | if ((arena->flags & CF_ARENAX_BIGLOCK) != 0) { |
167 | cf_mutex_unlock(&arena->lock); |
168 | } |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | arena->at_stage_id++; |
174 | arena->at_element_id = 0; |
175 | } |
176 | |
177 | cf_arenax_set_handle(&h, arena->at_stage_id, arena->at_element_id); |
178 | |
179 | arena->at_element_id++; |
180 | } |
181 | |
182 | if ((arena->flags & CF_ARENAX_BIGLOCK) != 0) { |
183 | cf_mutex_unlock(&arena->lock); |
184 | } |
185 | |
186 | if ((arena->flags & CF_ARENAX_CALLOC) != 0) { |
187 | memset(cf_arenax_resolve(arena, h), 0, arena->element_size); |
188 | } |
189 | |
190 | return h; |
191 | } |
192 | |
193 | // Free an element. |
194 | void |
195 | cf_arenax_free(cf_arenax* arena, cf_arenax_handle h, cf_arenax_puddle* puddle) |
196 | { |
197 | if (puddle != NULL) { |
198 | cf_arenax_free_chunked(arena, h, puddle); |
199 | return; |
200 | } |
201 | |
202 | free_element* p_free_element = cf_arenax_resolve(arena, h); |
203 | |
204 | if ((arena->flags & CF_ARENAX_BIGLOCK) != 0) { |
205 | cf_mutex_lock(&arena->lock); |
206 | } |
207 | |
208 | p_free_element->magic = FREE_MAGIC; |
209 | p_free_element->next_h = arena->free_h; |
210 | arena->free_h = h; |
211 | |
212 | if ((arena->flags & CF_ARENAX_BIGLOCK) != 0) { |
213 | cf_mutex_unlock(&arena->lock); |
214 | } |
215 | } |
216 | |
217 | // Convert cf_arenax_handle to memory address. |
218 | void* |
219 | cf_arenax_resolve(cf_arenax* arena, cf_arenax_handle h) |
220 | { |
221 | return arena->stages[h >> ELEMENT_ID_NUM_BITS] + |
222 | ((h & ELEMENT_ID_MASK) * arena->element_size); |
223 | } |
224 | |