1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2003-2020. 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 combined "address order best fit"/"best fit" allocator
24 * based on a Red-Black (binary search) Tree. The search,
25 * insert, and delete operations are all O(log n) operations
26 * on a Red-Black Tree. In the "address order best fit" case
27 * n equals number of free blocks, and in the "best fit" case
28 * n equals number of distinct sizes of free blocks. Red-Black
29 * Trees are described in "Introduction to Algorithms", by
30 * Thomas H. Cormen, Charles E. Leiserson, and
31 * Ronald L. Riverest.
32 *
33 * This module is a callback-module for erl_alloc_util.c
34 *
35 * Author: Rickard Green
36 */
37
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42 #include "global.h"
43 #define GET_ERL_BF_ALLOC_IMPL
44 #include "erl_bestfit_alloc.h"
45
46 #ifdef DEBUG
47 #if 0
48 #define HARD_DEBUG
49 #endif
50 #else
51 #undef HARD_DEBUG
52 #endif
53
54 #define MIN_MBC_SZ (16*1024)
55
56 #define TREE_NODE_FLG (((Uint) 1) << 0)
57 #define RED_FLG (((Uint) 1) << 1)
58 #ifdef HARD_DEBUG
59 # define LEFT_VISITED_FLG (((Uint) 1) << 2)
60 # define RIGHT_VISITED_FLG (((Uint) 1) << 3)
61 #endif
62
63 #define IS_TREE_NODE(N) (((RBTree_t *) (N))->flags & TREE_NODE_FLG)
64 #define IS_LIST_ELEM(N) (!IS_TREE_NODE(((RBTree_t *) (N))))
65
66 #define SET_TREE_NODE(N) (((RBTree_t *) (N))->flags |= TREE_NODE_FLG)
67 #define SET_LIST_ELEM(N) (((RBTree_t *) (N))->flags &= ~TREE_NODE_FLG)
68
69 #define IS_RED(N) (((RBTree_t *) (N)) \
70 && ((RBTree_t *) (N))->flags & RED_FLG)
71 #define IS_BLACK(N) (!IS_RED(((RBTree_t *) (N))))
72
73 #define SET_RED(N) (((RBTree_t *) (N))->flags |= RED_FLG)
74 #define SET_BLACK(N) (((RBTree_t *) (N))->flags &= ~RED_FLG)
75
76 #define BF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr)
77
78 #if 1
79 #define RBT_ASSERT ASSERT
80 #else
81 #define RBT_ASSERT(x)
82 #endif
83
84
85 #ifdef HARD_DEBUG
86 static RBTree_t * check_tree(RBTree_t, int, Uint);
87 #endif
88
89 static void tree_delete(Allctr_t *allctr, Block_t *del);
90
91 /* Prototypes of callback functions */
92
93 /* "address order best fit" specific callback functions */
94 static Block_t * aobf_get_free_block (Allctr_t *, Uint,
95 Block_t *, Uint);
96 static void aobf_link_free_block (Allctr_t *, Block_t *);
97 #define aobf_unlink_free_block tree_delete
98
99 /* "best fit" specific callback functions */
100 static Block_t * bf_get_free_block (Allctr_t *, Uint,
101 Block_t *, Uint);
102 static void bf_link_free_block (Allctr_t *, Block_t *);
103 static ERTS_INLINE void bf_unlink_free_block (Allctr_t *, Block_t *);
104
105
106 static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
107 void *, Uint **, Uint *);
108 static void init_atoms (void);
109
110 /* Types... */
111 struct RBTree_t_ {
112 Block_t hdr;
113 Uint flags;
114 RBTree_t *parent;
115 RBTree_t *left;
116 RBTree_t *right;
117 };
118
119 typedef struct {
120 RBTree_t t;
121 RBTree_t *next;
122 } RBTreeList_t;
123
124 #define BF_LIST_NEXT(N) (((RBTreeList_t *) (N))->next)
125 #define BF_LIST_PREV(N) (((RBTreeList_t *) (N))->t.parent)
126
127
128 #ifdef DEBUG
129
130 /* Destroy all tree fields */
131 #define DESTROY_TREE_NODE(N) \
132 sys_memset((void *) (((Block_t *) (N)) + 1), \
133 0xff, \
134 (sizeof(RBTree_t) - sizeof(Block_t)))
135
136 /* Destroy all tree and list fields */
137 #define DESTROY_LIST_ELEM(N) \
138 sys_memset((void *) (((Block_t *) (N)) + 1), \
139 0xff, \
140 (sizeof(RBTreeList_t) - sizeof(Block_t)))
141
142 #else
143
144 #define DESTROY_TREE_NODE(N)
145 #define DESTROY_LIST_ELEM(N)
146
147 #endif
148
149
150 static int atoms_initialized = 0;
151
152 void
erts_bfalc_init(void)153 erts_bfalc_init(void)
154 {
155 atoms_initialized = 0;
156 }
157
158 Allctr_t *
erts_bfalc_start(BFAllctr_t * bfallctr,BFAllctrInit_t * bfinit,AllctrInit_t * init)159 erts_bfalc_start(BFAllctr_t *bfallctr,
160 BFAllctrInit_t *bfinit,
161 AllctrInit_t *init)
162 {
163 struct {
164 int dummy;
165 BFAllctr_t allctr;
166 } zero = {0};
167 /* The struct with a dummy element first is used in order to avoid (an
168 incorrect) gcc warning. gcc warns if {0} is used as initializer of
169 a struct when the first member is a struct (not if, for example,
170 the third member is a struct). */
171
172 Allctr_t *allctr = (Allctr_t *) bfallctr;
173
174 sys_memcpy((void *) bfallctr, (void *) &zero.allctr, sizeof(BFAllctr_t));
175
176 bfallctr->address_order = bfinit->ao;
177
178
179 allctr->mbc_header_size = sizeof(Carrier_t);
180 allctr->min_mbc_size = MIN_MBC_SZ;
181 allctr->min_block_size = (bfinit->ao
182 ? sizeof(RBTree_t)
183 : sizeof(RBTreeList_t));
184
185 allctr->vsn_str = (bfinit->ao
186 ? ERTS_ALC_AOBF_ALLOC_VSN_STR
187 : ERTS_ALC_BF_ALLOC_VSN_STR);
188
189
190 /* Callback functions */
191
192 if (bfinit->ao) {
193 allctr->get_free_block = aobf_get_free_block;
194 allctr->link_free_block = aobf_link_free_block;
195 allctr->unlink_free_block = aobf_unlink_free_block;
196 }
197 else {
198 allctr->get_free_block = bf_get_free_block;
199 allctr->link_free_block = bf_link_free_block;
200 allctr->unlink_free_block = bf_unlink_free_block;
201 }
202 allctr->info_options = info_options;
203
204 allctr->get_next_mbc_size = NULL;
205 allctr->creating_mbc = NULL;
206 allctr->destroying_mbc = NULL;
207 allctr->add_mbc = NULL;
208 allctr->remove_mbc = NULL;
209 allctr->largest_fblk_in_mbc = NULL;
210 allctr->first_fblk_in_mbc = NULL;
211 allctr->next_fblk_in_mbc = NULL;
212 allctr->init_atoms = init_atoms;
213
214 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
215 allctr->check_block = NULL;
216 allctr->check_mbc = NULL;
217 #endif
218
219 allctr->atoms_initialized = 0;
220
221 if (!erts_alcu_start(allctr, init))
222 return NULL;
223
224 return allctr;
225 }
226
227 /*
228 * Red-Black Tree operations needed
229 */
230
231 static ERTS_INLINE void
left_rotate(RBTree_t ** root,RBTree_t * x)232 left_rotate(RBTree_t **root, RBTree_t *x)
233 {
234 RBTree_t *y = x->right;
235 x->right = y->left;
236 if (y->left)
237 y->left->parent = x;
238 y->parent = x->parent;
239 if (!y->parent) {
240 RBT_ASSERT(*root == x);
241 *root = y;
242 }
243 else if (x == x->parent->left)
244 x->parent->left = y;
245 else {
246 RBT_ASSERT(x == x->parent->right);
247 x->parent->right = y;
248 }
249 y->left = x;
250 x->parent = y;
251 }
252
253 static ERTS_INLINE void
right_rotate(RBTree_t ** root,RBTree_t * x)254 right_rotate(RBTree_t **root, RBTree_t *x)
255 {
256 RBTree_t *y = x->left;
257 x->left = y->right;
258 if (y->right)
259 y->right->parent = x;
260 y->parent = x->parent;
261 if (!y->parent) {
262 RBT_ASSERT(*root == x);
263 *root = y;
264 }
265 else if (x == x->parent->right)
266 x->parent->right = y;
267 else {
268 RBT_ASSERT(x == x->parent->left);
269 x->parent->left = y;
270 }
271 y->right = x;
272 x->parent = y;
273 }
274
275
276 /*
277 * Replace node x with node y
278 * NOTE: block header of y is not changed
279 */
280 static ERTS_INLINE void
replace(RBTree_t ** root,RBTree_t * x,RBTree_t * y)281 replace(RBTree_t **root, RBTree_t *x, RBTree_t *y)
282 {
283
284 if (!x->parent) {
285 RBT_ASSERT(*root == x);
286 *root = y;
287 }
288 else if (x == x->parent->left)
289 x->parent->left = y;
290 else {
291 RBT_ASSERT(x == x->parent->right);
292 x->parent->right = y;
293 }
294 if (x->left) {
295 RBT_ASSERT(x->left->parent == x);
296 x->left->parent = y;
297 }
298 if (x->right) {
299 RBT_ASSERT(x->right->parent == x);
300 x->right->parent = y;
301 }
302
303 y->flags = x->flags;
304 y->parent = x->parent;
305 y->right = x->right;
306 y->left = x->left;
307
308 DESTROY_TREE_NODE(x);
309
310 }
311
312 static void
tree_insert_fixup(RBTree_t ** root,RBTree_t * blk)313 tree_insert_fixup(RBTree_t **root, RBTree_t *blk)
314 {
315 RBTree_t *x = blk, *y;
316
317 /*
318 * Rearrange the tree so that it satisfies the Red-Black Tree properties
319 */
320
321 RBT_ASSERT(x != *root && IS_RED(x->parent));
322 do {
323
324 /*
325 * x and its parent are both red. Move the red pair up the tree
326 * until we get to the root or until we can separate them.
327 */
328
329 RBT_ASSERT(IS_RED(x));
330 RBT_ASSERT(IS_BLACK(x->parent->parent));
331 RBT_ASSERT(x->parent->parent);
332
333 if (x->parent == x->parent->parent->left) {
334 y = x->parent->parent->right;
335 if (IS_RED(y)) {
336 SET_BLACK(y);
337 x = x->parent;
338 SET_BLACK(x);
339 x = x->parent;
340 SET_RED(x);
341 }
342 else {
343
344 if (x == x->parent->right) {
345 x = x->parent;
346 left_rotate(root, x);
347 }
348
349 RBT_ASSERT(x == x->parent->parent->left->left);
350 RBT_ASSERT(IS_RED(x));
351 RBT_ASSERT(IS_RED(x->parent));
352 RBT_ASSERT(IS_BLACK(x->parent->parent));
353 RBT_ASSERT(IS_BLACK(y));
354
355 SET_BLACK(x->parent);
356 SET_RED(x->parent->parent);
357 right_rotate(root, x->parent->parent);
358
359 RBT_ASSERT(x == x->parent->left);
360 RBT_ASSERT(IS_RED(x));
361 RBT_ASSERT(IS_RED(x->parent->right));
362 RBT_ASSERT(IS_BLACK(x->parent));
363 break;
364 }
365 }
366 else {
367 RBT_ASSERT(x->parent == x->parent->parent->right);
368 y = x->parent->parent->left;
369 if (IS_RED(y)) {
370 SET_BLACK(y);
371 x = x->parent;
372 SET_BLACK(x);
373 x = x->parent;
374 SET_RED(x);
375 }
376 else {
377
378 if (x == x->parent->left) {
379 x = x->parent;
380 right_rotate(root, x);
381 }
382
383 RBT_ASSERT(x == x->parent->parent->right->right);
384 RBT_ASSERT(IS_RED(x));
385 RBT_ASSERT(IS_RED(x->parent));
386 RBT_ASSERT(IS_BLACK(x->parent->parent));
387 RBT_ASSERT(IS_BLACK(y));
388
389 SET_BLACK(x->parent);
390 SET_RED(x->parent->parent);
391 left_rotate(root, x->parent->parent);
392
393 RBT_ASSERT(x == x->parent->right);
394 RBT_ASSERT(IS_RED(x));
395 RBT_ASSERT(IS_RED(x->parent->left));
396 RBT_ASSERT(IS_BLACK(x->parent));
397 break;
398 }
399 }
400 } while (x != *root && IS_RED(x->parent));
401
402 SET_BLACK(*root);
403
404 }
405
406 /*
407 * The argument types of "Allctr_t *" and "Block_t *" have been
408 * chosen since we then can use tree_delete() as unlink_free_block
409 * callback function in the address order case.
410 */
411 static void
tree_delete(Allctr_t * allctr,Block_t * del)412 tree_delete(Allctr_t *allctr, Block_t *del)
413 {
414 BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
415 Uint spliced_is_black;
416 RBTree_t **root = &bfallctr->mbc_root;
417 RBTree_t *x, *y, *z = (RBTree_t *) del;
418 RBTree_t null_x; /* null_x is used to get the fixup started when we
419 splice out a node without children. */
420
421 null_x.parent = NULL;
422
423
424 #ifdef HARD_DEBUG
425 check_tree(*root, bfallctr->address_order, 0);
426 #endif
427
428 /* Remove node from tree... */
429
430 /* Find node to splice out */
431 if (!z->left || !z->right)
432 y = z;
433 else
434 /* Set y to z:s successor */
435 for (y = z->right; y->left; y = y->left)
436 ;
437 /* splice out y */
438 x = y->left ? y->left : y->right;
439 spliced_is_black = IS_BLACK(y);
440 if (x) {
441 x->parent = y->parent;
442 }
443 else if (!x && spliced_is_black) {
444 x = &null_x;
445 x->flags = 0;
446 SET_BLACK(x);
447 x->right = x->left = NULL;
448 x->parent = y->parent;
449 y->left = x;
450 }
451
452 if (!y->parent) {
453 RBT_ASSERT(*root == y);
454 *root = x;
455 }
456 else if (y == y->parent->left)
457 y->parent->left = x;
458 else {
459 RBT_ASSERT(y == y->parent->right);
460 y->parent->right = x;
461 }
462 if (y != z) {
463 /* We spliced out the successor of z; replace z by the successor */
464 replace(root, z, y);
465 }
466
467 if (spliced_is_black) {
468 /* We removed a black node which makes the resulting tree
469 violate the Red-Black Tree properties. Fixup tree... */
470
471 while (IS_BLACK(x) && x->parent) {
472
473 /*
474 * x has an "extra black" which we move up the tree
475 * until we reach the root or until we can get rid of it.
476 *
477 * y is the sibbling of x
478 */
479
480 if (x == x->parent->left) {
481 y = x->parent->right;
482 RBT_ASSERT(y);
483 if (IS_RED(y)) {
484 RBT_ASSERT(y->right);
485 RBT_ASSERT(y->left);
486 SET_BLACK(y);
487 RBT_ASSERT(IS_BLACK(x->parent));
488 SET_RED(x->parent);
489 left_rotate(root, x->parent);
490 y = x->parent->right;
491 }
492 RBT_ASSERT(y);
493 RBT_ASSERT(IS_BLACK(y));
494 if (IS_BLACK(y->left) && IS_BLACK(y->right)) {
495 SET_RED(y);
496 x = x->parent;
497 }
498 else {
499 if (IS_BLACK(y->right)) {
500 SET_BLACK(y->left);
501 SET_RED(y);
502 right_rotate(root, y);
503 y = x->parent->right;
504 }
505 RBT_ASSERT(y);
506 if (IS_RED(x->parent)) {
507
508 SET_BLACK(x->parent);
509 SET_RED(y);
510 }
511 RBT_ASSERT(y->right);
512 SET_BLACK(y->right);
513 left_rotate(root, x->parent);
514 x = *root;
515 break;
516 }
517 }
518 else {
519 RBT_ASSERT(x == x->parent->right);
520 y = x->parent->left;
521 RBT_ASSERT(y);
522 if (IS_RED(y)) {
523 RBT_ASSERT(y->right);
524 RBT_ASSERT(y->left);
525 SET_BLACK(y);
526 RBT_ASSERT(IS_BLACK(x->parent));
527 SET_RED(x->parent);
528 right_rotate(root, x->parent);
529 y = x->parent->left;
530 }
531 RBT_ASSERT(y);
532 RBT_ASSERT(IS_BLACK(y));
533 if (IS_BLACK(y->right) && IS_BLACK(y->left)) {
534 SET_RED(y);
535 x = x->parent;
536 }
537 else {
538 if (IS_BLACK(y->left)) {
539 SET_BLACK(y->right);
540 SET_RED(y);
541 left_rotate(root, y);
542 y = x->parent->left;
543 }
544 RBT_ASSERT(y);
545 if (IS_RED(x->parent)) {
546 SET_BLACK(x->parent);
547 SET_RED(y);
548 }
549 RBT_ASSERT(y->left);
550 SET_BLACK(y->left);
551 right_rotate(root, x->parent);
552 x = *root;
553 break;
554 }
555 }
556 }
557 SET_BLACK(x);
558
559 if (null_x.parent) {
560 if (null_x.parent->left == &null_x)
561 null_x.parent->left = NULL;
562 else {
563 RBT_ASSERT(null_x.parent->right == &null_x);
564 null_x.parent->right = NULL;
565 }
566 RBT_ASSERT(!null_x.left);
567 RBT_ASSERT(!null_x.right);
568 }
569 else if (*root == &null_x) {
570 *root = NULL;
571 RBT_ASSERT(!null_x.left);
572 RBT_ASSERT(!null_x.right);
573 }
574 }
575
576
577 DESTROY_TREE_NODE(del);
578
579 #ifdef HARD_DEBUG
580 check_tree(root, bfallctr->address_order, 0);
581 #endif
582
583 }
584
585 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
586 * "Address order best fit" specific callbacks. *
587 \* */
588
589 static void
aobf_link_free_block(Allctr_t * allctr,Block_t * block)590 aobf_link_free_block(Allctr_t *allctr, Block_t *block)
591 {
592 BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
593 RBTree_t **root = &bfallctr->mbc_root;
594 RBTree_t *blk = (RBTree_t *) block;
595 Uint blk_sz = BF_BLK_SZ(blk);
596
597
598 blk->flags = 0;
599 blk->left = NULL;
600 blk->right = NULL;
601
602 if (!*root) {
603 blk->parent = NULL;
604 SET_BLACK(blk);
605 *root = blk;
606 }
607 else {
608 RBTree_t *x = *root;
609 while (1) {
610 Uint size;
611
612 size = BF_BLK_SZ(x);
613
614 if (blk_sz < size || (blk_sz == size && blk < x)) {
615 if (!x->left) {
616 blk->parent = x;
617 x->left = blk;
618 break;
619 }
620 x = x->left;
621 }
622 else {
623 if (!x->right) {
624 blk->parent = x;
625 x->right = blk;
626 break;
627 }
628 x = x->right;
629 }
630
631 }
632
633 /* Insert block into size tree */
634 RBT_ASSERT(blk->parent);
635
636 SET_RED(blk);
637 if (IS_RED(blk->parent))
638 tree_insert_fixup(root, blk);
639 }
640
641 #ifdef HARD_DEBUG
642 check_tree(root, 1, 0);
643 #endif
644 }
645
646 #if 0 /* tree_delete() is directly used instead */
647 static void
648 aobf_unlink_free_block(Allctr_t *allctr, Block_t *block)
649 {
650 tree_delete(allctr, block);
651 }
652 #endif
653
654 static Block_t *
aobf_get_free_block(Allctr_t * allctr,Uint size,Block_t * cand_blk,Uint cand_size)655 aobf_get_free_block(Allctr_t *allctr, Uint size,
656 Block_t *cand_blk, Uint cand_size)
657 {
658 BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
659 RBTree_t **root = &bfallctr->mbc_root;
660 RBTree_t *x = *root;
661 RBTree_t *blk = NULL;
662 Uint blk_sz;
663
664 ASSERT(!cand_blk || cand_size >= size);
665
666 while (x) {
667 blk_sz = BF_BLK_SZ(x);
668 if (blk_sz < size) {
669 x = x->right;
670 }
671 else {
672 blk = x;
673 x = x->left;
674 }
675 }
676
677 if (!blk)
678 return NULL;
679
680 #ifdef HARD_DEBUG
681 ASSERT(blk == check_tree(root, 1, size));
682 #endif
683
684 if (cand_blk) {
685 blk_sz = BF_BLK_SZ(blk);
686 if (cand_size < blk_sz)
687 return NULL; /* cand_blk was better */
688 if (cand_size == blk_sz && ((void *) cand_blk) < ((void *) blk))
689 return NULL; /* cand_blk was better */
690 }
691
692 aobf_unlink_free_block(allctr, (Block_t *) blk);
693
694 return (Block_t *) blk;
695 }
696
697
698 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
699 * "Best fit" specific callbacks. *
700 \* */
701
702 static void
bf_link_free_block(Allctr_t * allctr,Block_t * block)703 bf_link_free_block(Allctr_t *allctr, Block_t *block)
704 {
705 BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
706 RBTree_t **root = &bfallctr->mbc_root;
707 RBTree_t *blk = (RBTree_t *) block;
708 Uint blk_sz = BF_BLK_SZ(blk);
709
710 SET_TREE_NODE(blk);
711
712
713 blk->flags = 0;
714 blk->left = NULL;
715 blk->right = NULL;
716
717 if (!*root) {
718 blk->parent = NULL;
719 SET_BLACK(blk);
720 *root = blk;
721 }
722 else {
723 RBTree_t *x = *root;
724 while (1) {
725 Uint size;
726
727 size = BF_BLK_SZ(x);
728
729 if (blk_sz == size) {
730
731 SET_LIST_ELEM(blk);
732 BF_LIST_NEXT(blk) = BF_LIST_NEXT(x);
733 BF_LIST_PREV(blk) = x;
734 if (BF_LIST_NEXT(x))
735 BF_LIST_PREV(BF_LIST_NEXT(x)) = blk;
736 BF_LIST_NEXT(x) = blk;
737
738 return; /* Finnished */
739 }
740 else if (blk_sz < size) {
741 if (!x->left) {
742 blk->parent = x;
743 x->left = blk;
744 break;
745 }
746 x = x->left;
747 }
748 else {
749 if (!x->right) {
750 blk->parent = x;
751 x->right = blk;
752 break;
753 }
754 x = x->right;
755 }
756 }
757
758 RBT_ASSERT(blk->parent);
759
760 SET_RED(blk);
761 if (IS_RED(blk->parent))
762 tree_insert_fixup(root, blk);
763
764 }
765
766 SET_TREE_NODE(blk);
767 BF_LIST_NEXT(blk) = NULL;
768
769 #ifdef HARD_DEBUG
770 check_tree(root, 0, 0);
771 #endif
772 }
773
774 static ERTS_INLINE void
bf_unlink_free_block(Allctr_t * allctr,Block_t * block)775 bf_unlink_free_block(Allctr_t *allctr, Block_t *block)
776 {
777 BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
778 RBTree_t **root = &bfallctr->mbc_root;
779 RBTree_t *x = (RBTree_t *) block;
780
781 if (IS_LIST_ELEM(x)) {
782 /* Remove from list */
783 ASSERT(BF_LIST_PREV(x));
784 BF_LIST_NEXT(BF_LIST_PREV(x)) = BF_LIST_NEXT(x);
785 if (BF_LIST_NEXT(x))
786 BF_LIST_PREV(BF_LIST_NEXT(x)) = BF_LIST_PREV(x);
787 }
788 else if (BF_LIST_NEXT(x)) {
789 /* Replace tree node by next element in list... */
790
791 ASSERT(BF_BLK_SZ(BF_LIST_NEXT(x)) == BF_BLK_SZ(x));
792 ASSERT(IS_TREE_NODE(x));
793 ASSERT(IS_LIST_ELEM(BF_LIST_NEXT(x)));
794
795 #ifdef HARD_DEBUG
796 check_tree(root, 0, 0);
797 #endif
798 replace(root, x, BF_LIST_NEXT(x));
799
800 #ifdef HARD_DEBUG
801 check_tree(bfallctr, 0);
802 #endif
803 }
804 else {
805 /* Remove from tree */
806 tree_delete(allctr, block);
807 }
808
809 DESTROY_LIST_ELEM(x);
810 }
811
812
813 static Block_t *
bf_get_free_block(Allctr_t * allctr,Uint size,Block_t * cand_blk,Uint cand_size)814 bf_get_free_block(Allctr_t *allctr, Uint size,
815 Block_t *cand_blk, Uint cand_size)
816 {
817 BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
818 RBTree_t **root = &bfallctr->mbc_root;
819 RBTree_t *x = *root;
820 RBTree_t *blk = NULL;
821 Uint blk_sz;
822
823 ASSERT(!cand_blk || cand_size >= size);
824
825 while (x) {
826 blk_sz = BF_BLK_SZ(x);
827 if (blk_sz < size) {
828 x = x->right;
829 }
830 else {
831 blk = x;
832 if (blk_sz == size)
833 break;
834 x = x->left;
835 }
836 }
837
838 if (!blk)
839 return NULL;
840
841 ASSERT(IS_TREE_NODE(blk));
842
843
844 #ifdef HARD_DEBUG
845 {
846 RBTree_t *ct_blk = check_tree(root, 0, size);
847 ASSERT(BF_BLK_SZ(ct_blk) == BF_BLK_SZ(blk));
848 }
849 #endif
850
851 if (cand_blk && cand_size <= BF_BLK_SZ(blk))
852 return NULL; /* cand_blk was better */
853
854 /* Use next block if it exist in order to avoid replacing
855 the tree node */
856 blk = BF_LIST_NEXT(blk) ? BF_LIST_NEXT(blk) : blk;
857
858 bf_unlink_free_block(allctr, (Block_t *) blk);
859 return (Block_t *) blk;
860 }
861
862
863 /*
864 * info_options()
865 */
866
867 static struct {
868 Eterm as;
869 Eterm aobf;
870 Eterm bf;
871 #ifdef DEBUG
872 Eterm end_of_atoms;
873 #endif
874 } am;
875
atom_init(Eterm * atom,char * name)876 static void ERTS_INLINE atom_init(Eterm *atom, char *name)
877 {
878 *atom = am_atom_put(name, sys_strlen(name));
879 }
880 #define AM_INIT(AM) atom_init(&am.AM, #AM)
881
882 static void
init_atoms(void)883 init_atoms(void)
884 {
885 #ifdef DEBUG
886 Eterm *atom;
887 #endif
888
889 if (atoms_initialized)
890 return;
891
892 #ifdef DEBUG
893 for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
894 *atom = THE_NON_VALUE;
895 }
896 #endif
897 AM_INIT(as);
898 AM_INIT(aobf);
899 AM_INIT(bf);
900
901 #ifdef DEBUG
902 for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
903 ASSERT(*atom != THE_NON_VALUE);
904 }
905 #endif
906
907 atoms_initialized = 1;
908 }
909
910
911 #define bld_uint erts_bld_uint
912 #define bld_cons erts_bld_cons
913 #define bld_tuple erts_bld_tuple
914
915 static ERTS_INLINE void
add_2tup(Uint ** hpp,Uint * szp,Eterm * lp,Eterm el1,Eterm el2)916 add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
917 {
918 *lp = bld_cons(hpp, szp, bld_tuple(hpp, szp, 2, el1, el2), *lp);
919 }
920
921 static Eterm
info_options(Allctr_t * allctr,char * prefix,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)922 info_options(Allctr_t *allctr,
923 char *prefix,
924 fmtfn_t *print_to_p,
925 void *print_to_arg,
926 Uint **hpp,
927 Uint *szp)
928 {
929 BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
930 Eterm res = THE_NON_VALUE;
931
932 if (print_to_p) {
933 erts_print(*print_to_p,
934 print_to_arg,
935 "%sas: %s\n",
936 prefix,
937 bfallctr->address_order ? "aobf" : "bf");
938 }
939
940 if (hpp || szp) {
941
942 if (!atoms_initialized)
943 erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized",
944 __FILE__, __LINE__);;
945
946 res = NIL;
947 add_2tup(hpp, szp, &res,
948 am.as,
949 bfallctr->address_order ? am.aobf : am.bf);
950 }
951
952 return res;
953 }
954
955
956 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
957 * NOTE: erts_bfalc_test() is only supposed to be used for testing. *
958 * *
959 * Keep alloc_SUITE_data/allocator_test.h updated if changes are made *
960 * to erts_bfalc_test() *
961 \* */
962
963 UWord
erts_bfalc_test(UWord op,UWord a1,UWord a2)964 erts_bfalc_test(UWord op, UWord a1, UWord a2)
965 {
966 switch (op) {
967 case 0x200: return (UWord) ((BFAllctr_t *) a1)->address_order; /* IS_AOBF */
968 case 0x201: return (UWord) ((BFAllctr_t *) a1)->mbc_root;
969 case 0x202: return (UWord) ((RBTree_t *) a1)->parent;
970 case 0x203: return (UWord) ((RBTree_t *) a1)->left;
971 case 0x204: return (UWord) ((RBTree_t *) a1)->right;
972 case 0x205: return (UWord) BF_LIST_NEXT(a1);
973 case 0x206: return (UWord) IS_BLACK((RBTree_t *) a1);
974 case 0x207: return (UWord) IS_TREE_NODE((RBTree_t *) a1);
975 case 0x208: return (UWord) 1; /* IS_BF_ALGO */
976 case 0x20a: return (UWord) !((BFAllctr_t *) a1)->address_order; /* IS_BF */
977 case 0x20b: return (UWord) BF_LIST_PREV(a1);
978 default: ASSERT(0); return ~((UWord) 0);
979 }
980 }
981
982
983 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
984 * Debug functions *
985 \* */
986
987
988 #ifdef HARD_DEBUG
989
990 #define IS_LEFT_VISITED(FB) ((FB)->flags & LEFT_VISITED_FLG)
991 #define IS_RIGHT_VISITED(FB) ((FB)->flags & RIGHT_VISITED_FLG)
992
993 #define SET_LEFT_VISITED(FB) ((FB)->flags |= LEFT_VISITED_FLG)
994 #define SET_RIGHT_VISITED(FB) ((FB)->flags |= RIGHT_VISITED_FLG)
995
996 #define UNSET_LEFT_VISITED(FB) ((FB)->flags &= ~LEFT_VISITED_FLG)
997 #define UNSET_RIGHT_VISITED(FB) ((FB)->flags &= ~RIGHT_VISITED_FLG)
998
999
1000 #if 0
1001 # define PRINT_TREE
1002 #else
1003 # undef PRINT_TREE
1004 #endif
1005
1006 #ifdef PRINT_TREE
1007 static void print_tree(RBTree_t *, int);
1008 #endif
1009
1010 /*
1011 * Checks that the order between parent and children are correct,
1012 * and that the Red-Black Tree properies are satisfied. if size > 0,
1013 * check_tree() returns a node that satisfies "best fit" resp.
1014 * "address order best fit".
1015 *
1016 * The Red-Black Tree properies are:
1017 * 1. Every node is either red or black.
1018 * 2. Every leaf (NIL) is black.
1019 * 3. If a node is red, then both its children are black.
1020 * 4. Every simple path from a node to a descendant leaf
1021 * contains the same number of black nodes.
1022 */
1023
1024 static RBTree_t *
check_tree(RBTree_t * root,int ao,Uint size)1025 check_tree(RBTree_t *root, int ao, Uint size)
1026 {
1027 RBTree_t *res = NULL;
1028 Sint blacks;
1029 Sint curr_blacks;
1030 RBTree_t *x;
1031
1032 #ifdef PRINT_TREE
1033 print_tree(root, ao);
1034 #endif
1035
1036 if (!root)
1037 return res;
1038
1039 x = root;
1040 ASSERT(IS_BLACK(x));
1041 ASSERT(!x->parent);
1042 curr_blacks = 1;
1043 blacks = -1;
1044
1045 while (x) {
1046 if (!IS_LEFT_VISITED(x)) {
1047 SET_LEFT_VISITED(x);
1048 if (x->left) {
1049 x = x->left;
1050 if (IS_BLACK(x))
1051 curr_blacks++;
1052 continue;
1053 }
1054 else {
1055 if (blacks < 0)
1056 blacks = curr_blacks;
1057 ASSERT(blacks == curr_blacks);
1058 }
1059 }
1060
1061 if (!IS_RIGHT_VISITED(x)) {
1062 SET_RIGHT_VISITED(x);
1063 if (x->right) {
1064 x = x->right;
1065 if (IS_BLACK(x))
1066 curr_blacks++;
1067 continue;
1068 }
1069 else {
1070 if (blacks < 0)
1071 blacks = curr_blacks;
1072 ASSERT(blacks == curr_blacks);
1073 }
1074 }
1075
1076
1077 if (IS_RED(x)) {
1078 ASSERT(IS_BLACK(x->right));
1079 ASSERT(IS_BLACK(x->left));
1080 }
1081
1082 ASSERT(x->parent || x == root);
1083
1084 if (x->left) {
1085 ASSERT(x->left->parent == x);
1086 if (ao) {
1087 ASSERT(BF_BLK_SZ(x->left) < BF_BLK_SZ(x)
1088 || (BF_BLK_SZ(x->left) == BF_BLK_SZ(x) && x->left < x));
1089 }
1090 else {
1091 ASSERT(IS_TREE_NODE(x->left));
1092 ASSERT(BF_BLK_SZ(x->left) < BF_BLK_SZ(x));
1093 }
1094 }
1095
1096 if (x->right) {
1097 ASSERT(x->right->parent == x);
1098 if (ao) {
1099 ASSERT(BF_BLK_SZ(x->right) > BF_BLK_SZ(x)
1100 || (BF_BLK_SZ(x->right) == BF_BLK_SZ(x) && x->right > x));
1101 }
1102 else {
1103 ASSERT(IS_TREE_NODE(x->right));
1104 ASSERT(BF_BLK_SZ(x->right) > BF_BLK_SZ(x));
1105 }
1106 }
1107
1108 if (size && BF_BLK_SZ(x) >= size) {
1109 if (ao) {
1110 if (!res
1111 || BF_BLK_SZ(x) < BF_BLK_SZ(res)
1112 || (BF_BLK_SZ(x) == BF_BLK_SZ(res) && x < res))
1113 res = x;
1114 }
1115 else {
1116 if (!res || BF_BLK_SZ(x) < BF_BLK_SZ(res))
1117 res = x;
1118 }
1119 }
1120
1121 UNSET_LEFT_VISITED(x);
1122 UNSET_RIGHT_VISITED(x);
1123 if (IS_BLACK(x))
1124 curr_blacks--;
1125 x = x->parent;
1126
1127 }
1128
1129 ASSERT(curr_blacks == 0);
1130
1131 UNSET_LEFT_VISITED(root);
1132 UNSET_RIGHT_VISITED(root);
1133
1134 return res;
1135
1136 }
1137
1138
1139 #ifdef PRINT_TREE
1140 #define INDENT_STEP 2
1141
1142 #include <stdio.h>
1143
1144 static void
print_tree_aux(RBTree_t * x,int indent)1145 print_tree_aux(RBTree_t *x, int indent)
1146 {
1147 int i;
1148
1149 if (!x) {
1150 for (i = 0; i < indent; i++) {
1151 putc(' ', stderr);
1152 }
1153 fprintf(stderr, "BLACK: nil\r\n");
1154 }
1155 else {
1156 print_tree_aux(x->right, indent + INDENT_STEP);
1157 for (i = 0; i < indent; i++) {
1158 putc(' ', stderr);
1159 }
1160 fprintf(stderr, "%s: sz=%lu addr=0x%lx\r\n",
1161 IS_BLACK(x) ? "BLACK" : "RED",
1162 BF_BLK_SZ(x),
1163 (Uint) x);
1164 print_tree_aux(x->left, indent + INDENT_STEP);
1165 }
1166 }
1167
1168
1169 static void
print_tree(RBTree_t * root,int ao)1170 print_tree(RBTree_t *root, int ao)
1171 {
1172 char *type = ao ? "Size-Adress" : "Size";
1173 fprintf(stderr, " --- %s tree begin ---\r\n", type);
1174 print_tree_aux(root, 0);
1175 fprintf(stderr, " --- %s tree end ---\r\n", type);
1176 }
1177
1178 #endif
1179
1180 #endif
1181