📄 erl_bestfit_alloc.c
字号:
/* ``The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved via the world wide web at http://www.erlang.org/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' * * $Id$ *//* * Description: A combined "address order best fit"/"best fit" allocator * based on a Red-Black (binary search) Tree. The search, * insert, and delete operations are all O(log n) operations * on a Red-Black Tree. In the "address order best fit" case * n equals number of free blocks, and in the "best fit" case * n equals number of distinct sizes of free blocks. Red-Black * Trees are described in "Introduction to Algorithms", by * Thomas H. Cormen, Charles E. Leiserson, and * Ronald L. Riverest. * * This module is a callback-module for erl_alloc_util.c * * Author: Rickard Green */#ifdef HAVE_CONFIG_H# include "config.h"#endif#include "global.h"#define GET_ERL_BF_ALLOC_IMPL#include "erl_bestfit_alloc.h"#ifdef DEBUG#if 0#define HARD_DEBUG#endif#else#undef HARD_DEBUG#endif#define MIN_MBC_SZ (16*1024)#define MIN_MBC_FIRST_FREE_SZ (4*1024)#define TREE_NODE_FLG (((Uint) 1) << 0)#define RED_FLG (((Uint) 1) << 1)#ifdef HARD_DEBUG# define LEFT_VISITED_FLG (((Uint) 1) << 2)# define RIGHT_VISITED_FLG (((Uint) 1) << 3)#endif#define IS_TREE_NODE(N) (((RBTree_t *) (N))->flags & TREE_NODE_FLG)#define IS_LIST_ELEM(N) (!IS_TREE_NODE(((RBTree_t *) (N))))#define SET_TREE_NODE(N) (((RBTree_t *) (N))->flags |= TREE_NODE_FLG)#define SET_LIST_ELEM(N) (((RBTree_t *) (N))->flags &= ~TREE_NODE_FLG)#define IS_RED(N) (((RBTree_t *) (N)) \ && ((RBTree_t *) (N))->flags & RED_FLG)#define IS_BLACK(N) (!IS_RED(((RBTree_t *) (N))))#define SET_RED(N) (((RBTree_t *) (N))->flags |= RED_FLG)#define SET_BLACK(N) (((RBTree_t *) (N))->flags &= ~RED_FLG)#undef ASSERT#define ASSERT ASSERT_EXPR#if 1#define RBT_ASSERT ASSERT#else#define RBT_ASSERT(x)#endif#ifdef HARD_DEBUGstatic RBTree_t * check_tree(BFAllctr_t *, Uint);#endifstatic void tree_delete(Allctr_t *allctr, Block_t *del);/* Prototypes of callback functions *//* "address order best fit" specific callback functions */static Block_t * aobf_get_free_block (Allctr_t *, Uint);static void aobf_link_free_block (Allctr_t *, Block_t *);#define aobf_unlink_free_block tree_delete/* "best fit" specific callback functions */static Block_t * bf_get_free_block (Allctr_t *, Uint);static void bf_link_free_block (Allctr_t *, Block_t *);static ERTS_INLINE void bf_unlink_free_block (Allctr_t *, Block_t *);static Eterm info_options (Allctr_t *, char *, int *, void *, Uint **, Uint *);static void init_atoms (void);/* Types... */struct RBTree_t_ { Block_t hdr; Uint flags; RBTree_t *parent; RBTree_t *left; RBTree_t *right;};typedef struct { RBTree_t t; RBTree_t *next;} RBTreeList_t;#define LIST_NEXT(N) (((RBTreeList_t *) (N))->next)#define LIST_PREV(N) (((RBTreeList_t *) (N))->t.parent)#ifdef DEBUG/* Destroy all tree fields */#define DESTROY_TREE_NODE(N) \ sys_memset((void *) (((Block_t *) (N)) + 1), \ 0xff, \ (sizeof(RBTree_t) - sizeof(Block_t)))/* Destroy all tree and list fields */#define DESTROY_LIST_ELEM(N) \ sys_memset((void *) (((Block_t *) (N)) + 1), \ 0xff, \ (sizeof(RBTreeList_t) - sizeof(Block_t)))#else#define DESTROY_TREE_NODE(N)#define DESTROY_LIST_ELEM(N)#endifstatic int atoms_initialized = 0;voiderts_bfalc_init(void){ atoms_initialized = 0;}Allctr_t *erts_bfalc_start(BFAllctr_t *bfallctr, BFAllctrInit_t *bfinit, AllctrInit_t *init){ BFAllctr_t nulled_state = {{0}}; /* {{0}} is used instead of {0}, in order to avoid (an incorrect) gcc warning. gcc warns if {0} is used as initializer of a struct when the first member is a struct (not if, for example, the third member is a struct). */ Allctr_t *allctr = (Allctr_t *) bfallctr; sys_memcpy((void *) bfallctr, (void *) &nulled_state, sizeof(BFAllctr_t)); bfallctr->address_order = bfinit->ao; allctr->mbc_header_size = sizeof(Carrier_t); allctr->min_mbc_size = MIN_MBC_SZ; allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ; allctr->min_block_size = (bfinit->ao ? sizeof(RBTree_t) : sizeof(RBTreeList_t)); allctr->vsn_str = (bfinit->ao ? ERTS_ALC_AOBF_ALLOC_VSN_STR : ERTS_ALC_BF_ALLOC_VSN_STR); /* Callback functions */ if (bfinit->ao) { allctr->get_free_block = aobf_get_free_block; allctr->link_free_block = aobf_link_free_block; allctr->unlink_free_block = aobf_unlink_free_block; } else { allctr->get_free_block = bf_get_free_block; allctr->link_free_block = bf_link_free_block; allctr->unlink_free_block = bf_unlink_free_block; } allctr->info_options = info_options; allctr->get_next_mbc_size = NULL; allctr->creating_mbc = NULL; allctr->destroying_mbc = NULL; allctr->init_atoms = init_atoms;#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG allctr->check_block = NULL; allctr->check_mbc = NULL;#endif allctr->atoms_initialized = 0; if (!erts_alcu_start(allctr, init)) return NULL; return allctr;}/* * Red-Black Tree operations needed */static ERTS_INLINE voidleft_rotate(RBTree_t **root, RBTree_t *x){ RBTree_t *y = x->right; x->right = y->left; if (y->left) y->left->parent = x; y->parent = x->parent; if (!y->parent) { RBT_ASSERT(*root == x); *root = y; } else if (x == x->parent->left) x->parent->left = y; else { RBT_ASSERT(x == x->parent->right); x->parent->right = y; } y->left = x; x->parent = y;}static ERTS_INLINE voidright_rotate(RBTree_t **root, RBTree_t *x){ RBTree_t *y = x->left; x->left = y->right; if (y->right) y->right->parent = x; y->parent = x->parent; if (!y->parent) { RBT_ASSERT(*root == x); *root = y; } else if (x == x->parent->right) x->parent->right = y; else { RBT_ASSERT(x == x->parent->left); x->parent->left = y; } y->right = x; x->parent = y;}/* * Replace node x with node y * NOTE: block header of y is not changed */static ERTS_INLINE voidreplace(RBTree_t **root, RBTree_t *x, RBTree_t *y){ if (!x->parent) { RBT_ASSERT(*root == x); *root = y; } else if (x == x->parent->left) x->parent->left = y; else { RBT_ASSERT(x == x->parent->right); x->parent->right = y; } if (x->left) { RBT_ASSERT(x->left->parent == x); x->left->parent = y; } if (x->right) { RBT_ASSERT(x->right->parent == x); x->right->parent = y; } y->flags = x->flags; y->parent = x->parent; y->right = x->right; y->left = x->left; DESTROY_TREE_NODE(x);}static voidtree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk){ RBTree_t *x = blk, *y; /* * Rearrange the tree so that it satisfies the Red-Black Tree properties */ RBT_ASSERT(x != bfallctr->root && IS_RED(x->parent)); do { /* * x and its parent are both red. Move the red pair up the tree * until we get to the root or until we can separate them. */ RBT_ASSERT(IS_RED(x)); RBT_ASSERT(IS_BLACK(x->parent->parent)); RBT_ASSERT(x->parent->parent); if (x->parent == x->parent->parent->left) { y = x->parent->parent->right; if (IS_RED(y)) { SET_BLACK(y); x = x->parent; SET_BLACK(x); x = x->parent; SET_RED(x); } else { if (x == x->parent->right) { x = x->parent; left_rotate(&bfallctr->root, x); } RBT_ASSERT(x == x->parent->parent->left->left); RBT_ASSERT(IS_RED(x)); RBT_ASSERT(IS_RED(x->parent)); RBT_ASSERT(IS_BLACK(x->parent->parent)); RBT_ASSERT(IS_BLACK(y)); SET_BLACK(x->parent); SET_RED(x->parent->parent); right_rotate(&bfallctr->root, x->parent->parent); RBT_ASSERT(x == x->parent->left); RBT_ASSERT(IS_RED(x)); RBT_ASSERT(IS_RED(x->parent->right)); RBT_ASSERT(IS_BLACK(x->parent)); break; } } else { RBT_ASSERT(x->parent == x->parent->parent->right); y = x->parent->parent->left; if (IS_RED(y)) { SET_BLACK(y); x = x->parent; SET_BLACK(x); x = x->parent; SET_RED(x); } else { if (x == x->parent->left) { x = x->parent; right_rotate(&bfallctr->root, x); } RBT_ASSERT(x == x->parent->parent->right->right); RBT_ASSERT(IS_RED(x)); RBT_ASSERT(IS_RED(x->parent)); RBT_ASSERT(IS_BLACK(x->parent->parent)); RBT_ASSERT(IS_BLACK(y)); SET_BLACK(x->parent); SET_RED(x->parent->parent); left_rotate(&bfallctr->root, x->parent->parent); RBT_ASSERT(x == x->parent->right); RBT_ASSERT(IS_RED(x)); RBT_ASSERT(IS_RED(x->parent->left)); RBT_ASSERT(IS_BLACK(x->parent)); break; } } } while (x != bfallctr->root && IS_RED(x->parent)); SET_BLACK(bfallctr->root);}/* * The argument types of "Allctr_t *" and "Block_t *" have been * chosen since we then can use tree_delete() as unlink_free_block * callback function in the address order case. */static voidtree_delete(Allctr_t *allctr, Block_t *del){ BFAllctr_t *bfallctr = (BFAllctr_t *) allctr; Uint spliced_is_black; RBTree_t *x, *y, *z = (RBTree_t *) del; RBTree_t null_x; /* null_x is used to get the fixup started when we splice out a node without children. */ null_x.parent = NULL;#ifdef HARD_DEBUG check_tree(bfallctr, 0);#endif /* Remove node from tree... */ /* Find node to splice out */ if (!z->left || !z->right) y = z; else /* Set y to z:s successor */ for(y = z->right; y->left; y = y->left); /* splice out y */ x = y->left ? y->left : y->right; spliced_is_black = IS_BLACK(y); if (x) { x->parent = y->parent; } else if (!x && spliced_is_black) { x = &null_x; x->flags = 0; SET_BLACK(x); x->right = x->left = NULL; x->parent = y->parent; y->left = x; } if (!y->parent) { RBT_ASSERT(bfallctr->root == y); bfallctr->root = x; } else if (y == y->parent->left) y->parent->left = x; else { RBT_ASSERT(y == y->parent->right); y->parent->right = x; } if (y != z) { /* We spliced out the successor of z; replace z by the successor */ replace(&bfallctr->root, z, y); } if (spliced_is_black) { /* We removed a black node which makes the resulting tree violate the Red-Black Tree properties. Fixup tree... */ while (IS_BLACK(x) && x->parent) { /* * x has an "extra black" which we move up the tree * until we reach the root or until we can get rid of it. * * y is the sibbling of x */ if (x == x->parent->left) { y = x->parent->right; RBT_ASSERT(y); if (IS_RED(y)) { RBT_ASSERT(y->right); RBT_ASSERT(y->left); SET_BLACK(y); RBT_ASSERT(IS_BLACK(x->parent)); SET_RED(x->parent); left_rotate(&bfallctr->root, x->parent); y = x->parent->right; } RBT_ASSERT(y); RBT_ASSERT(IS_BLACK(y)); if (IS_BLACK(y->left) && IS_BLACK(y->right)) { SET_RED(y); x = x->parent; } else { if (IS_BLACK(y->right)) { SET_BLACK(y->left); SET_RED(y); right_rotate(&bfallctr->root, y); y = x->parent->right; } RBT_ASSERT(y); if (IS_RED(x->parent)) { SET_BLACK(x->parent); SET_RED(y); } RBT_ASSERT(y->right); SET_BLACK(y->right); left_rotate(&bfallctr->root, x->parent); x = bfallctr->root; break; } } else { RBT_ASSERT(x == x->parent->right); y = x->parent->left; RBT_ASSERT(y); if (IS_RED(y)) { RBT_ASSERT(y->right); RBT_ASSERT(y->left); SET_BLACK(y); RBT_ASSERT(IS_BLACK(x->parent)); SET_RED(x->parent); right_rotate(&bfallctr->root, x->parent); y = x->parent->left; } RBT_ASSERT(y); RBT_ASSERT(IS_BLACK(y)); if (IS_BLACK(y->right) && IS_BLACK(y->left)) { SET_RED(y); x = x->parent; } else { if (IS_BLACK(y->left)) { SET_BLACK(y->right); SET_RED(y); left_rotate(&bfallctr->root, y); y = x->parent->left; } RBT_ASSERT(y); if (IS_RED(x->parent)) { SET_BLACK(x->parent); SET_RED(y); } RBT_ASSERT(y->left); SET_BLACK(y->left); right_rotate(&bfallctr->root, x->parent); x = bfallctr->root; break; } } } SET_BLACK(x); if (null_x.parent) { if (null_x.parent->left == &null_x) null_x.parent->left = NULL; else { RBT_ASSERT(null_x.parent->right == &null_x); null_x.parent->right = NULL; } RBT_ASSERT(!null_x.left); RBT_ASSERT(!null_x.right); } else if (bfallctr->root == &null_x) { bfallctr->root = NULL; RBT_ASSERT(!null_x.left); RBT_ASSERT(!null_x.right); } } DESTROY_TREE_NODE(del);#ifdef HARD_DEBUG check_tree(bfallctr, 0);#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -