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:
47const 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.
63size_t
64cf_arenax_sizeof()
65{
66 return sizeof(cf_arenax);
67}
68
69// Convert cf_arenax_err to meaningful string.
70const char*
71cf_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.
82void
83cf_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.
141cf_arenax_handle
142cf_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.
194void
195cf_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.
218void*
219cf_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