1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2003-2018. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21
22 /*
23 * Description: A fast allocator intended for temporary allocation.
24 * When allocating, only the first block in the free list
25 * is inspected, if this block doesn't fit a new carrier
26 * is created. NOTE: this allocator can behave really bad
27 * if misused.
28 *
29 * This module is a callback-module for erl_alloc_util.c
30 *
31 * Author: Rickard Green
32 */
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37 #include "global.h"
38 #define GET_ERL_AF_ALLOC_IMPL
39 #include "erl_afit_alloc.h"
40
41 struct AFFreeBlock_t_ {
42 Block_t block_head;
43 AFFreeBlock_t *prev;
44 AFFreeBlock_t *next;
45 };
46 #define AF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->block_head)
47
48 #define MIN_MBC_SZ (16*1024)
49 #define MIN_MBC_FIRST_FREE_SZ (4*1024)
50
51 /* Prototypes of callback functions */
52 static Block_t * get_free_block (Allctr_t *, Uint, Block_t *, Uint);
53 static void link_free_block (Allctr_t *, Block_t *);
54 static void unlink_free_block (Allctr_t *, Block_t *);
55
56
57 static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
58 void *arg, Uint **, Uint *);
59 static void init_atoms (void);
60
61 static int atoms_initialized = 0;
62
63 void
erts_afalc_init(void)64 erts_afalc_init(void)
65 {
66 atoms_initialized = 0;
67 }
68
69 Allctr_t *
erts_afalc_start(AFAllctr_t * afallctr,AFAllctrInit_t * afinit,AllctrInit_t * init)70 erts_afalc_start(AFAllctr_t *afallctr,
71 AFAllctrInit_t *afinit,
72 AllctrInit_t *init)
73 {
74 struct {
75 int dummy;
76 AFAllctr_t allctr;
77 } zero = {0};
78 /* The struct with a dummy element first is used in order to avoid (an
79 incorrect) gcc warning. gcc warns if {0} is used as initializer of
80 a struct when the first member is a struct (not if, for example,
81 the third member is a struct). */
82
83 Allctr_t *allctr = (Allctr_t *) afallctr;
84
85 sys_memcpy((void *) afallctr, (void *) &zero.allctr, sizeof(AFAllctr_t));
86
87 allctr->mbc_header_size = sizeof(Carrier_t);
88 allctr->min_mbc_size = MIN_MBC_SZ;
89 allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
90 allctr->min_block_size = sizeof(AFFreeBlock_t);
91 allctr->vsn_str = ERTS_ALC_AF_ALLOC_VSN_STR;
92
93 /* Callback functions */
94 allctr->get_free_block = get_free_block;
95 allctr->link_free_block = link_free_block;
96 allctr->unlink_free_block = unlink_free_block;
97 allctr->info_options = info_options;
98
99 allctr->get_next_mbc_size = NULL;
100 allctr->creating_mbc = NULL;
101 allctr->destroying_mbc = NULL;
102 allctr->add_mbc = NULL;
103 allctr->remove_mbc = NULL;
104 allctr->largest_fblk_in_mbc = NULL;
105 allctr->init_atoms = init_atoms;
106
107 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
108 allctr->check_block = NULL;
109 allctr->check_mbc = NULL;
110 #endif
111
112 allctr->atoms_initialized = 0;
113
114 if (!erts_alcu_start(allctr, init))
115 return NULL;
116
117 return allctr;
118 }
119
120 static Block_t *
get_free_block(Allctr_t * allctr,Uint size,Block_t * cand_blk,Uint cand_size)121 get_free_block(Allctr_t *allctr, Uint size, Block_t *cand_blk, Uint cand_size)
122 {
123 AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
124
125 ASSERT(!cand_blk || cand_size >= size);
126
127 if (afallctr->free_list && AF_BLK_SZ(afallctr->free_list) >= size) {
128 AFFreeBlock_t *res = afallctr->free_list;
129 afallctr->free_list = res->next;
130 if (res->next)
131 res->next->prev = NULL;
132 return (Block_t *) res;
133 }
134 else
135 return NULL;
136 }
137
138 static void
link_free_block(Allctr_t * allctr,Block_t * block)139 link_free_block(Allctr_t *allctr, Block_t *block)
140 {
141 AFFreeBlock_t *blk = (AFFreeBlock_t *) block;
142 AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
143
144 if (afallctr->free_list && AF_BLK_SZ(afallctr->free_list) > AF_BLK_SZ(blk)) {
145 blk->next = afallctr->free_list->next;
146 blk->prev = afallctr->free_list;
147 afallctr->free_list->next = blk;
148 }
149 else {
150 blk->next = afallctr->free_list;
151 blk->prev = NULL;
152 afallctr->free_list = blk;
153 }
154
155 if (blk->next)
156 blk->next->prev = blk;
157 }
158
159 static void
unlink_free_block(Allctr_t * allctr,Block_t * block)160 unlink_free_block(Allctr_t *allctr, Block_t *block)
161 {
162 AFFreeBlock_t *blk = (AFFreeBlock_t *) block;
163 AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
164
165 if (blk->prev)
166 blk->prev->next = blk->next;
167 else
168 afallctr->free_list = blk->next;
169 if (blk->next)
170 blk->next->prev = blk->prev;
171 }
172
173
174 static struct {
175 Eterm as;
176 Eterm af;
177 #ifdef DEBUG
178 Eterm end_of_atoms;
179 #endif
180 } am;
181
atom_init(Eterm * atom,char * name)182 static void ERTS_INLINE atom_init(Eterm *atom, char *name)
183 {
184 *atom = am_atom_put(name, sys_strlen(name));
185 }
186 #define AM_INIT(AM) atom_init(&am.AM, #AM)
187
188 static void
init_atoms(void)189 init_atoms(void)
190 {
191 #ifdef DEBUG
192 Eterm *atom;
193 #endif
194
195 if (atoms_initialized)
196 return;
197
198 #ifdef DEBUG
199 for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
200 *atom = THE_NON_VALUE;
201 }
202 #endif
203
204 AM_INIT(as);
205 AM_INIT(af);
206
207 #ifdef DEBUG
208 for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
209 ASSERT(*atom != THE_NON_VALUE);
210 }
211 #endif
212
213 atoms_initialized = 1;
214 }
215
216
217 #define bld_uint erts_bld_uint
218 #define bld_cons erts_bld_cons
219 #define bld_tuple erts_bld_tuple
220
221 static ERTS_INLINE void
add_2tup(Uint ** hpp,Uint * szp,Eterm * lp,Eterm el1,Eterm el2)222 add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
223 {
224 *lp = bld_cons(hpp, szp, bld_tuple(hpp, szp, 2, el1, el2), *lp);
225 }
226
227 static Eterm
info_options(Allctr_t * allctr,char * prefix,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)228 info_options(Allctr_t *allctr,
229 char *prefix,
230 fmtfn_t *print_to_p,
231 void *print_to_arg,
232 Uint **hpp,
233 Uint *szp)
234 {
235 Eterm res = THE_NON_VALUE;
236
237 if (print_to_p) {
238 erts_print(*print_to_p, print_to_arg, "%sas: af\n", prefix);
239 }
240
241 if (hpp || szp) {
242
243 if (!atoms_initialized)
244 erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized",
245 __FILE__, __LINE__);;
246
247 res = NIL;
248 add_2tup(hpp, szp, &res, am.as, am.af);
249 }
250
251 return res;
252 }
253
254
255
256 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
257 * NOTE: erts_afalc_test() is only supposed to be used for testing. *
258 * *
259 * Keep alloc_SUITE_data/allocator_test.h updated if changes are made *
260 * to erts_afalc_test() *
261 \* */
262
263 UWord
erts_afalc_test(UWord op,UWord a1,UWord a2)264 erts_afalc_test(UWord op, UWord a1, UWord a2)
265 {
266 switch (op) {
267 default: ASSERT(0); return ~((UWord) 0);
268 }
269 }
270