|  | /* Lock-free btree for manually registered unwind frames.  */ | 
|  | /* Copyright (C) 2022-2024 Free Software Foundation, Inc. | 
|  | Contributed by Thomas Neumann | 
|  |  | 
|  | This file is part of GCC. | 
|  |  | 
|  | GCC is free software; you can redistribute it and/or modify it under | 
|  | the terms of the GNU General Public License as published by the Free | 
|  | Software Foundation; either version 3, or (at your option) any later | 
|  | version. | 
|  |  | 
|  | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | 
|  | WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|  | for more details. | 
|  |  | 
|  | Under Section 7 of GPL version 3, you are granted additional | 
|  | permissions described in the GCC Runtime Library Exception, version | 
|  | 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License and | 
|  | a copy of the GCC Runtime Library Exception along with this program; | 
|  | see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #ifndef GCC_UNWIND_DW2_BTREE_H | 
|  | #define GCC_UNWIND_DW2_BTREE_H | 
|  |  | 
|  | #include <stdbool.h> | 
|  |  | 
|  | // Common logic for version locks. | 
|  | struct version_lock | 
|  | { | 
|  | // The lock itself. The lowest bit indicates an exclusive lock, | 
|  | // the second bit indicates waiting threads. All other bits are | 
|  | // used as counter to recognize changes. | 
|  | // Overflows are okay here, we must only prevent overflow to the | 
|  | // same value within one lock_optimistic/validate | 
|  | // range. Even on 32 bit platforms that would require 1 billion | 
|  | // frame registrations within the time span of a few assembler | 
|  | // instructions. | 
|  | uintptr_type version_lock; | 
|  | }; | 
|  |  | 
|  | #ifdef __GTHREAD_HAS_COND | 
|  | // We should never get contention within the tree as it rarely changes. | 
|  | // But if we ever do get contention we use these for waiting. | 
|  | static __gthread_mutex_t version_lock_mutex = __GTHREAD_MUTEX_INIT; | 
|  | static __gthread_cond_t version_lock_cond = __GTHREAD_COND_INIT; | 
|  | #endif | 
|  |  | 
|  | // Initialize in locked state. | 
|  | static inline void | 
|  | version_lock_initialize_locked_exclusive (struct version_lock *vl) | 
|  | { | 
|  | vl->version_lock = 1; | 
|  | } | 
|  |  | 
|  | // Try to lock the node exclusive. | 
|  | static inline bool | 
|  | version_lock_try_lock_exclusive (struct version_lock *vl) | 
|  | { | 
|  | uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST); | 
|  | if (state & 1) | 
|  | return false; | 
|  | return __atomic_compare_exchange_n (&(vl->version_lock), &state, state | 1, | 
|  | false, __ATOMIC_SEQ_CST, | 
|  | __ATOMIC_SEQ_CST); | 
|  | } | 
|  |  | 
|  | // Lock the node exclusive, blocking as needed. | 
|  | static void | 
|  | version_lock_lock_exclusive (struct version_lock *vl) | 
|  | { | 
|  | #ifndef __GTHREAD_HAS_COND | 
|  | restart: | 
|  | #endif | 
|  |  | 
|  | // We should virtually never get contention here, as frame | 
|  | // changes are rare. | 
|  | uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST); | 
|  | if (!(state & 1)) | 
|  | { | 
|  | if (__atomic_compare_exchange_n (&(vl->version_lock), &state, state | 1, | 
|  | false, __ATOMIC_SEQ_CST, | 
|  | __ATOMIC_SEQ_CST)) | 
|  | return; | 
|  | } | 
|  |  | 
|  | // We did get contention, wait properly. | 
|  | #ifdef __GTHREAD_HAS_COND | 
|  | __gthread_mutex_lock (&version_lock_mutex); | 
|  | state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST); | 
|  | while (true) | 
|  | { | 
|  | // Check if the lock is still held. | 
|  | if (!(state & 1)) | 
|  | { | 
|  | if (__atomic_compare_exchange_n (&(vl->version_lock), &state, | 
|  | state | 1, false, __ATOMIC_SEQ_CST, | 
|  | __ATOMIC_SEQ_CST)) | 
|  | { | 
|  | __gthread_mutex_unlock (&version_lock_mutex); | 
|  | return; | 
|  | } | 
|  | else | 
|  | { | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Register waiting thread. | 
|  | if (!(state & 2)) | 
|  | { | 
|  | if (!__atomic_compare_exchange_n (&(vl->version_lock), &state, | 
|  | state | 2, false, __ATOMIC_SEQ_CST, | 
|  | __ATOMIC_SEQ_CST)) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // And sleep. | 
|  | __gthread_cond_wait (&version_lock_cond, &version_lock_mutex); | 
|  | state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST); | 
|  | } | 
|  | #else | 
|  | // Spin if we do not have condition variables available. | 
|  | // We expect no contention here, spinning should be okay. | 
|  | goto restart; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Release a locked node and increase the version lock. | 
|  | static void | 
|  | version_lock_unlock_exclusive (struct version_lock *vl) | 
|  | { | 
|  | // increase version, reset exclusive lock bits | 
|  | uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST); | 
|  | uintptr_type ns = (state + 4) & (~((uintptr_type) 3)); | 
|  | state = __atomic_exchange_n (&(vl->version_lock), ns, __ATOMIC_SEQ_CST); | 
|  |  | 
|  | #ifdef __GTHREAD_HAS_COND | 
|  | if (state & 2) | 
|  | { | 
|  | // Wake up waiting threads. This should be extremely rare. | 
|  | __gthread_mutex_lock (&version_lock_mutex); | 
|  | __gthread_cond_broadcast (&version_lock_cond); | 
|  | __gthread_mutex_unlock (&version_lock_mutex); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Acquire an optimistic "lock". Note that this does not lock at all, it | 
|  | // only allows for validation later. | 
|  | static inline bool | 
|  | version_lock_lock_optimistic (const struct version_lock *vl, uintptr_type *lock) | 
|  | { | 
|  | uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST); | 
|  | *lock = state; | 
|  |  | 
|  | // Acquiring the lock fails when there is currently an exclusive lock. | 
|  | return !(state & 1); | 
|  | } | 
|  |  | 
|  | // Validate a previously acquired "lock". | 
|  | static inline bool | 
|  | version_lock_validate (const struct version_lock *vl, uintptr_type lock) | 
|  | { | 
|  | // Prevent the reordering of non-atomic loads behind the atomic load. | 
|  | // Hans Boehm, Can Seqlocks Get Along with Programming Language Memory | 
|  | // Models?, Section 4. | 
|  | __atomic_thread_fence (__ATOMIC_ACQUIRE); | 
|  |  | 
|  | // Check that the node is still in the same state. | 
|  | uintptr_type state = __atomic_load_n (&(vl->version_lock), __ATOMIC_SEQ_CST); | 
|  | return (state == lock); | 
|  | } | 
|  |  | 
|  | // The largest possible separator value. | 
|  | static const uintptr_type max_separator = ~((uintptr_type) (0)); | 
|  |  | 
|  | struct btree_node; | 
|  |  | 
|  | // Inner entry. The child tree contains all entries <= separator. | 
|  | struct inner_entry | 
|  | { | 
|  | uintptr_type separator; | 
|  | struct btree_node *child; | 
|  | }; | 
|  |  | 
|  | // Leaf entry. Stores an object entry. | 
|  | struct leaf_entry | 
|  | { | 
|  | uintptr_type base, size; | 
|  | struct object *ob; | 
|  | }; | 
|  |  | 
|  | // Node types. | 
|  | enum node_type | 
|  | { | 
|  | btree_node_inner, | 
|  | btree_node_leaf, | 
|  | btree_node_free | 
|  | }; | 
|  |  | 
|  | // Node sizes. Chosen such that the result size is roughly 256 bytes. | 
|  | #define max_fanout_inner 15 | 
|  | #define max_fanout_leaf 10 | 
|  |  | 
|  | // A btree node. | 
|  | struct btree_node | 
|  | { | 
|  | // The version lock used for optimistic lock coupling. | 
|  | struct version_lock version_lock; | 
|  | // The number of entries. | 
|  | unsigned entry_count; | 
|  | // The type. | 
|  | enum node_type type; | 
|  | // The payload. | 
|  | union | 
|  | { | 
|  | // The inner nodes have fence keys, i.e., the right-most entry includes a | 
|  | // separator. | 
|  | struct inner_entry children[max_fanout_inner]; | 
|  | struct leaf_entry entries[max_fanout_leaf]; | 
|  | } content; | 
|  | }; | 
|  |  | 
|  | // Is an inner node? | 
|  | static inline bool | 
|  | btree_node_is_inner (const struct btree_node *n) | 
|  | { | 
|  | return n->type == btree_node_inner; | 
|  | } | 
|  |  | 
|  | // Is a leaf node? | 
|  | static inline bool | 
|  | btree_node_is_leaf (const struct btree_node *n) | 
|  | { | 
|  | return n->type == btree_node_leaf; | 
|  | } | 
|  |  | 
|  | // Should the node be merged? | 
|  | static inline bool | 
|  | btree_node_needs_merge (const struct btree_node *n) | 
|  | { | 
|  | return n->entry_count < (btree_node_is_inner (n) ? (max_fanout_inner / 2) | 
|  | : (max_fanout_leaf / 2)); | 
|  | } | 
|  |  | 
|  | // Get the fence key for inner nodes. | 
|  | static inline uintptr_type | 
|  | btree_node_get_fence_key (const struct btree_node *n) | 
|  | { | 
|  | // For inner nodes we just return our right-most entry. | 
|  | return n->content.children[n->entry_count - 1].separator; | 
|  | } | 
|  |  | 
|  | // Find the position for a slot in an inner node. | 
|  | static unsigned | 
|  | btree_node_find_inner_slot (const struct btree_node *n, uintptr_type value) | 
|  | { | 
|  | for (unsigned index = 0, ec = n->entry_count; index != ec; ++index) | 
|  | if (n->content.children[index].separator >= value) | 
|  | return index; | 
|  | return n->entry_count; | 
|  | } | 
|  |  | 
|  | // Find the position for a slot in a leaf node. | 
|  | static unsigned | 
|  | btree_node_find_leaf_slot (const struct btree_node *n, uintptr_type value) | 
|  | { | 
|  | for (unsigned index = 0, ec = n->entry_count; index != ec; ++index) | 
|  | if (n->content.entries[index].base + n->content.entries[index].size > value) | 
|  | return index; | 
|  | return n->entry_count; | 
|  | } | 
|  |  | 
|  | // Try to lock the node exclusive. | 
|  | static inline bool | 
|  | btree_node_try_lock_exclusive (struct btree_node *n) | 
|  | { | 
|  | return version_lock_try_lock_exclusive (&(n->version_lock)); | 
|  | } | 
|  |  | 
|  | // Lock the node exclusive, blocking as needed. | 
|  | static inline void | 
|  | btree_node_lock_exclusive (struct btree_node *n) | 
|  | { | 
|  | version_lock_lock_exclusive (&(n->version_lock)); | 
|  | } | 
|  |  | 
|  | // Release a locked node and increase the version lock. | 
|  | static inline void | 
|  | btree_node_unlock_exclusive (struct btree_node *n) | 
|  | { | 
|  | version_lock_unlock_exclusive (&(n->version_lock)); | 
|  | } | 
|  |  | 
|  | // Acquire an optimistic "lock". Note that this does not lock at all, it | 
|  | // only allows for validation later. | 
|  | static inline bool | 
|  | btree_node_lock_optimistic (const struct btree_node *n, uintptr_type *lock) | 
|  | { | 
|  | return version_lock_lock_optimistic (&(n->version_lock), lock); | 
|  | } | 
|  |  | 
|  | // Validate a previously acquire lock. | 
|  | static inline bool | 
|  | btree_node_validate (const struct btree_node *n, uintptr_type lock) | 
|  | { | 
|  | return version_lock_validate (&(n->version_lock), lock); | 
|  | } | 
|  |  | 
|  | // Insert a new separator after splitting. | 
|  | static void | 
|  | btree_node_update_separator_after_split (struct btree_node *n, | 
|  | uintptr_type old_separator, | 
|  | uintptr_type new_separator, | 
|  | struct btree_node *new_right) | 
|  | { | 
|  | unsigned slot = btree_node_find_inner_slot (n, old_separator); | 
|  | for (unsigned index = n->entry_count; index > slot; --index) | 
|  | n->content.children[index] = n->content.children[index - 1]; | 
|  | n->content.children[slot].separator = new_separator; | 
|  | n->content.children[slot + 1].child = new_right; | 
|  | n->entry_count++; | 
|  | } | 
|  |  | 
|  | // A btree. Suitable for static initialization, all members are zero at the | 
|  | // beginning. | 
|  | struct btree | 
|  | { | 
|  | // The root of the btree. | 
|  | struct btree_node *root; | 
|  | // The free list of released node. | 
|  | struct btree_node *free_list; | 
|  | // The version lock used to protect the root. | 
|  | struct version_lock root_lock; | 
|  | }; | 
|  |  | 
|  | // Initialize a btree. Not actually used, just for exposition. | 
|  | static inline void | 
|  | btree_init (struct btree *t) | 
|  | { | 
|  | t->root = NULL; | 
|  | t->free_list = NULL; | 
|  | t->root_lock.version_lock = 0; | 
|  | }; | 
|  |  | 
|  | static void | 
|  | btree_release_tree_recursively (struct btree *t, struct btree_node *n); | 
|  |  | 
|  | // Destroy a tree and release all nodes. | 
|  | static void | 
|  | btree_destroy (struct btree *t) | 
|  | { | 
|  | // Disable the mechanism before cleaning up. | 
|  | struct btree_node *old_root | 
|  | = __atomic_exchange_n (&(t->root), NULL, __ATOMIC_SEQ_CST); | 
|  | if (old_root) | 
|  | btree_release_tree_recursively (t, old_root); | 
|  |  | 
|  | // Release all free nodes. | 
|  | while (t->free_list) | 
|  | { | 
|  | struct btree_node *next = t->free_list->content.children[0].child; | 
|  | free (t->free_list); | 
|  | t->free_list = next; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Allocate a node. This node will be returned in locked exclusive state. | 
|  | static struct btree_node * | 
|  | btree_allocate_node (struct btree *t, bool inner) | 
|  | { | 
|  | while (true) | 
|  | { | 
|  | // Try the free list first. | 
|  | struct btree_node *next_free | 
|  | = __atomic_load_n (&(t->free_list), __ATOMIC_SEQ_CST); | 
|  | if (next_free) | 
|  | { | 
|  | if (!btree_node_try_lock_exclusive (next_free)) | 
|  | continue; | 
|  | // The node might no longer be free, check that again after acquiring | 
|  | // the exclusive lock. | 
|  | if (next_free->type == btree_node_free) | 
|  | { | 
|  | struct btree_node *ex = next_free; | 
|  | if (__atomic_compare_exchange_n ( | 
|  | &(t->free_list), &ex, next_free->content.children[0].child, | 
|  | false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) | 
|  | { | 
|  | next_free->entry_count = 0; | 
|  | next_free->type = inner ? btree_node_inner : btree_node_leaf; | 
|  | return next_free; | 
|  | } | 
|  | } | 
|  | btree_node_unlock_exclusive (next_free); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // No free node available, allocate a new one. | 
|  | struct btree_node *new_node | 
|  | = (struct btree_node *) (malloc (sizeof (struct btree_node))); | 
|  | version_lock_initialize_locked_exclusive ( | 
|  | &(new_node->version_lock)); // initialize the node in locked state. | 
|  | new_node->entry_count = 0; | 
|  | new_node->type = inner ? btree_node_inner : btree_node_leaf; | 
|  | return new_node; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Release a node. This node must be currently locked exclusively and will | 
|  | // be placed in the free list. | 
|  | static void | 
|  | btree_release_node (struct btree *t, struct btree_node *node) | 
|  | { | 
|  | // We cannot release the memory immediately because there might still be | 
|  | // concurrent readers on that node. Put it in the free list instead. | 
|  | node->type = btree_node_free; | 
|  | struct btree_node *next_free | 
|  | = __atomic_load_n (&(t->free_list), __ATOMIC_SEQ_CST); | 
|  | do | 
|  | { | 
|  | node->content.children[0].child = next_free; | 
|  | } while (!__atomic_compare_exchange_n (&(t->free_list), &next_free, node, | 
|  | false, __ATOMIC_SEQ_CST, | 
|  | __ATOMIC_SEQ_CST)); | 
|  | btree_node_unlock_exclusive (node); | 
|  | } | 
|  |  | 
|  | // Recursively release a tree. The btree is by design very shallow, thus | 
|  | // we can risk recursion here. | 
|  | static void | 
|  | btree_release_tree_recursively (struct btree *t, struct btree_node *node) | 
|  | { | 
|  | btree_node_lock_exclusive (node); | 
|  | if (btree_node_is_inner (node)) | 
|  | { | 
|  | for (unsigned index = 0; index < node->entry_count; ++index) | 
|  | btree_release_tree_recursively (t, node->content.children[index].child); | 
|  | } | 
|  | btree_release_node (t, node); | 
|  | } | 
|  |  | 
|  | // Check if we are splitting the root. | 
|  | static void | 
|  | btree_handle_root_split (struct btree *t, struct btree_node **node, | 
|  | struct btree_node **parent) | 
|  | { | 
|  | // We want to keep the root pointer stable to allow for contention | 
|  | // free reads. Thus, we split the root by first moving the content | 
|  | // of the root node to a new node, and then split that new node. | 
|  | if (!*parent) | 
|  | { | 
|  | // Allocate a new node, this guarantees us that we will have a parent | 
|  | // afterwards. | 
|  | struct btree_node *new_node | 
|  | = btree_allocate_node (t, btree_node_is_inner (*node)); | 
|  | struct btree_node *old_node = *node; | 
|  | new_node->entry_count = old_node->entry_count; | 
|  | new_node->content = old_node->content; | 
|  | old_node->content.children[0].separator = max_separator; | 
|  | old_node->content.children[0].child = new_node; | 
|  | old_node->entry_count = 1; | 
|  | old_node->type = btree_node_inner; | 
|  |  | 
|  | *parent = old_node; | 
|  | *node = new_node; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Split an inner node. | 
|  | static void | 
|  | btree_split_inner (struct btree *t, struct btree_node **inner, | 
|  | struct btree_node **parent, uintptr_type target) | 
|  | { | 
|  | // Check for the root. | 
|  | btree_handle_root_split (t, inner, parent); | 
|  |  | 
|  | // Create two inner node. | 
|  | uintptr_type right_fence = btree_node_get_fence_key (*inner); | 
|  | struct btree_node *left_inner = *inner; | 
|  | struct btree_node *right_inner = btree_allocate_node (t, true); | 
|  | unsigned split = left_inner->entry_count / 2; | 
|  | right_inner->entry_count = left_inner->entry_count - split; | 
|  | for (unsigned index = 0; index < right_inner->entry_count; ++index) | 
|  | right_inner->content.children[index] | 
|  | = left_inner->content.children[split + index]; | 
|  | left_inner->entry_count = split; | 
|  | uintptr_type left_fence = btree_node_get_fence_key (left_inner); | 
|  | btree_node_update_separator_after_split (*parent, right_fence, left_fence, | 
|  | right_inner); | 
|  | if (target <= left_fence) | 
|  | { | 
|  | *inner = left_inner; | 
|  | btree_node_unlock_exclusive (right_inner); | 
|  | } | 
|  | else | 
|  | { | 
|  | *inner = right_inner; | 
|  | btree_node_unlock_exclusive (left_inner); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Split a leaf node. | 
|  | static void | 
|  | btree_split_leaf (struct btree *t, struct btree_node **leaf, | 
|  | struct btree_node **parent, uintptr_type fence, | 
|  | uintptr_type target) | 
|  | { | 
|  | // Check for the root. | 
|  | btree_handle_root_split (t, leaf, parent); | 
|  |  | 
|  | // Create two leaf nodes. | 
|  | uintptr_type right_fence = fence; | 
|  | struct btree_node *left_leaf = *leaf; | 
|  | struct btree_node *right_leaf = btree_allocate_node (t, false); | 
|  | unsigned split = left_leaf->entry_count / 2; | 
|  | right_leaf->entry_count = left_leaf->entry_count - split; | 
|  | for (unsigned index = 0; index != right_leaf->entry_count; ++index) | 
|  | right_leaf->content.entries[index] | 
|  | = left_leaf->content.entries[split + index]; | 
|  | left_leaf->entry_count = split; | 
|  | uintptr_type left_fence = right_leaf->content.entries[0].base - 1; | 
|  | btree_node_update_separator_after_split (*parent, right_fence, left_fence, | 
|  | right_leaf); | 
|  | if (target <= left_fence) | 
|  | { | 
|  | *leaf = left_leaf; | 
|  | btree_node_unlock_exclusive (right_leaf); | 
|  | } | 
|  | else | 
|  | { | 
|  | *leaf = right_leaf; | 
|  | btree_node_unlock_exclusive (left_leaf); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Merge (or balance) child nodes. | 
|  | static struct btree_node * | 
|  | btree_merge_node (struct btree *t, unsigned child_slot, | 
|  | struct btree_node *parent, uintptr_type target) | 
|  | { | 
|  | // Choose the emptiest neighbor and lock both. The target child is already | 
|  | // locked. | 
|  | unsigned left_slot; | 
|  | struct btree_node *left_node, *right_node; | 
|  | if ((child_slot == 0) | 
|  | || (((child_slot + 1) < parent->entry_count) | 
|  | && (parent->content.children[child_slot + 1].child->entry_count | 
|  | < parent->content.children[child_slot - 1].child->entry_count))) | 
|  | { | 
|  | left_slot = child_slot; | 
|  | left_node = parent->content.children[left_slot].child; | 
|  | right_node = parent->content.children[left_slot + 1].child; | 
|  | btree_node_lock_exclusive (right_node); | 
|  | } | 
|  | else | 
|  | { | 
|  | left_slot = child_slot - 1; | 
|  | left_node = parent->content.children[left_slot].child; | 
|  | right_node = parent->content.children[left_slot + 1].child; | 
|  | btree_node_lock_exclusive (left_node); | 
|  | } | 
|  |  | 
|  | // Can we merge both nodes into one node? | 
|  | unsigned total_count = left_node->entry_count + right_node->entry_count; | 
|  | unsigned max_count | 
|  | = btree_node_is_inner (left_node) ? max_fanout_inner : max_fanout_leaf; | 
|  | if (total_count <= max_count) | 
|  | { | 
|  | // Merge into the parent? | 
|  | if (parent->entry_count == 2) | 
|  | { | 
|  | // Merge children into parent. This can only happen at the root. | 
|  | if (btree_node_is_inner (left_node)) | 
|  | { | 
|  | for (unsigned index = 0; index != left_node->entry_count; ++index) | 
|  | parent->content.children[index] | 
|  | = left_node->content.children[index]; | 
|  | for (unsigned index = 0; index != right_node->entry_count; | 
|  | ++index) | 
|  | parent->content.children[index + left_node->entry_count] | 
|  | = right_node->content.children[index]; | 
|  | } | 
|  | else | 
|  | { | 
|  | parent->type = btree_node_leaf; | 
|  | for (unsigned index = 0; index != left_node->entry_count; ++index) | 
|  | parent->content.entries[index] | 
|  | = left_node->content.entries[index]; | 
|  | for (unsigned index = 0; index != right_node->entry_count; | 
|  | ++index) | 
|  | parent->content.entries[index + left_node->entry_count] | 
|  | = right_node->content.entries[index]; | 
|  | } | 
|  | parent->entry_count = total_count; | 
|  | btree_release_node (t, left_node); | 
|  | btree_release_node (t, right_node); | 
|  | return parent; | 
|  | } | 
|  | else | 
|  | { | 
|  | // Regular merge. | 
|  | if (btree_node_is_inner (left_node)) | 
|  | { | 
|  | for (unsigned index = 0; index != right_node->entry_count; | 
|  | ++index) | 
|  | left_node->content.children[left_node->entry_count++] | 
|  | = right_node->content.children[index]; | 
|  | } | 
|  | else | 
|  | { | 
|  | for (unsigned index = 0; index != right_node->entry_count; | 
|  | ++index) | 
|  | left_node->content.entries[left_node->entry_count++] | 
|  | = right_node->content.entries[index]; | 
|  | } | 
|  | parent->content.children[left_slot].separator | 
|  | = parent->content.children[left_slot + 1].separator; | 
|  | for (unsigned index = left_slot + 1; index + 1 < parent->entry_count; | 
|  | ++index) | 
|  | parent->content.children[index] | 
|  | = parent->content.children[index + 1]; | 
|  | parent->entry_count--; | 
|  | btree_release_node (t, right_node); | 
|  | btree_node_unlock_exclusive (parent); | 
|  | return left_node; | 
|  | } | 
|  | } | 
|  |  | 
|  | // No merge possible, rebalance instead. | 
|  | if (left_node->entry_count > right_node->entry_count) | 
|  | { | 
|  | // Shift from left to right. | 
|  | unsigned to_shift | 
|  | = (left_node->entry_count - right_node->entry_count) / 2; | 
|  | if (btree_node_is_inner (left_node)) | 
|  | { | 
|  | for (unsigned index = 0; index != right_node->entry_count; ++index) | 
|  | { | 
|  | unsigned pos = right_node->entry_count - 1 - index; | 
|  | right_node->content.children[pos + to_shift] | 
|  | = right_node->content.children[pos]; | 
|  | } | 
|  | for (unsigned index = 0; index != to_shift; ++index) | 
|  | right_node->content.children[index] | 
|  | = left_node->content | 
|  | .children[left_node->entry_count - to_shift + index]; | 
|  | } | 
|  | else | 
|  | { | 
|  | for (unsigned index = 0; index != right_node->entry_count; ++index) | 
|  | { | 
|  | unsigned pos = right_node->entry_count - 1 - index; | 
|  | right_node->content.entries[pos + to_shift] | 
|  | = right_node->content.entries[pos]; | 
|  | } | 
|  | for (unsigned index = 0; index != to_shift; ++index) | 
|  | right_node->content.entries[index] | 
|  | = left_node->content | 
|  | .entries[left_node->entry_count - to_shift + index]; | 
|  | } | 
|  | left_node->entry_count -= to_shift; | 
|  | right_node->entry_count += to_shift; | 
|  | } | 
|  | else | 
|  | { | 
|  | // Shift from right to left. | 
|  | unsigned to_shift | 
|  | = (right_node->entry_count - left_node->entry_count) / 2; | 
|  | if (btree_node_is_inner (left_node)) | 
|  | { | 
|  | for (unsigned index = 0; index != to_shift; ++index) | 
|  | left_node->content.children[left_node->entry_count + index] | 
|  | = right_node->content.children[index]; | 
|  | for (unsigned index = 0; index != right_node->entry_count - to_shift; | 
|  | ++index) | 
|  | right_node->content.children[index] | 
|  | = right_node->content.children[index + to_shift]; | 
|  | } | 
|  | else | 
|  | { | 
|  | for (unsigned index = 0; index != to_shift; ++index) | 
|  | left_node->content.entries[left_node->entry_count + index] | 
|  | = right_node->content.entries[index]; | 
|  | for (unsigned index = 0; index != right_node->entry_count - to_shift; | 
|  | ++index) | 
|  | right_node->content.entries[index] | 
|  | = right_node->content.entries[index + to_shift]; | 
|  | } | 
|  | left_node->entry_count += to_shift; | 
|  | right_node->entry_count -= to_shift; | 
|  | } | 
|  | uintptr_type left_fence; | 
|  | if (btree_node_is_leaf (left_node)) | 
|  | { | 
|  | left_fence = right_node->content.entries[0].base - 1; | 
|  | } | 
|  | else | 
|  | { | 
|  | left_fence = btree_node_get_fence_key (left_node); | 
|  | } | 
|  | parent->content.children[left_slot].separator = left_fence; | 
|  | btree_node_unlock_exclusive (parent); | 
|  | if (target <= left_fence) | 
|  | { | 
|  | btree_node_unlock_exclusive (right_node); | 
|  | return left_node; | 
|  | } | 
|  | else | 
|  | { | 
|  | btree_node_unlock_exclusive (left_node); | 
|  | return right_node; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Insert an entry. | 
|  | static bool | 
|  | btree_insert (struct btree *t, uintptr_type base, uintptr_type size, | 
|  | struct object *ob) | 
|  | { | 
|  | // Sanity check. | 
|  | if (!size) | 
|  | return false; | 
|  |  | 
|  | // Access the root. | 
|  | struct btree_node *iter, *parent = NULL; | 
|  | { | 
|  | version_lock_lock_exclusive (&(t->root_lock)); | 
|  | iter = t->root; | 
|  | if (iter) | 
|  | { | 
|  | btree_node_lock_exclusive (iter); | 
|  | } | 
|  | else | 
|  | { | 
|  | t->root = iter = btree_allocate_node (t, false); | 
|  | } | 
|  | version_lock_unlock_exclusive (&(t->root_lock)); | 
|  | } | 
|  |  | 
|  | // Walk down the btree with classic lock coupling and eager splits. | 
|  | // Strictly speaking this is not performance optimal, we could use | 
|  | // optimistic lock coupling until we hit a node that has to be modified. | 
|  | // But that is more difficult to implement and frame registration is | 
|  | // rare anyway, we use simple locking for now. | 
|  |  | 
|  | uintptr_type fence = max_separator; | 
|  | while (btree_node_is_inner (iter)) | 
|  | { | 
|  | // Use eager splits to avoid lock coupling up. | 
|  | if (iter->entry_count == max_fanout_inner) | 
|  | btree_split_inner (t, &iter, &parent, base); | 
|  |  | 
|  | unsigned slot = btree_node_find_inner_slot (iter, base); | 
|  | if (parent) | 
|  | btree_node_unlock_exclusive (parent); | 
|  | parent = iter; | 
|  | fence = iter->content.children[slot].separator; | 
|  | iter = iter->content.children[slot].child; | 
|  | btree_node_lock_exclusive (iter); | 
|  | } | 
|  |  | 
|  | // Make sure we have space. | 
|  | if (iter->entry_count == max_fanout_leaf) | 
|  | btree_split_leaf (t, &iter, &parent, fence, base); | 
|  | if (parent) | 
|  | btree_node_unlock_exclusive (parent); | 
|  |  | 
|  | // Insert in node. | 
|  | unsigned slot = btree_node_find_leaf_slot (iter, base); | 
|  | if ((slot < iter->entry_count) && (iter->content.entries[slot].base == base)) | 
|  | { | 
|  | // Duplicate entry, this should never happen. | 
|  | btree_node_unlock_exclusive (iter); | 
|  | return false; | 
|  | } | 
|  | for (unsigned index = iter->entry_count; index > slot; --index) | 
|  | iter->content.entries[index] = iter->content.entries[index - 1]; | 
|  | struct leaf_entry *e = &(iter->content.entries[slot]); | 
|  | e->base = base; | 
|  | e->size = size; | 
|  | e->ob = ob; | 
|  | iter->entry_count++; | 
|  | btree_node_unlock_exclusive (iter); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Remove an entry. | 
|  | static struct object * | 
|  | btree_remove (struct btree *t, uintptr_type base) | 
|  | { | 
|  | // Access the root. | 
|  | version_lock_lock_exclusive (&(t->root_lock)); | 
|  | struct btree_node *iter = t->root; | 
|  | if (iter) | 
|  | btree_node_lock_exclusive (iter); | 
|  | version_lock_unlock_exclusive (&(t->root_lock)); | 
|  | if (!iter) | 
|  | return NULL; | 
|  |  | 
|  | // Same strategy as with insert, walk down with lock coupling and | 
|  | // merge eagerly. | 
|  | while (btree_node_is_inner (iter)) | 
|  | { | 
|  | unsigned slot = btree_node_find_inner_slot (iter, base); | 
|  | struct btree_node *next = iter->content.children[slot].child; | 
|  | btree_node_lock_exclusive (next); | 
|  | if (btree_node_needs_merge (next)) | 
|  | { | 
|  | // Use eager merges to avoid lock coupling up. | 
|  | iter = btree_merge_node (t, slot, iter, base); | 
|  | } | 
|  | else | 
|  | { | 
|  | btree_node_unlock_exclusive (iter); | 
|  | iter = next; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Remove existing entry. | 
|  | unsigned slot = btree_node_find_leaf_slot (iter, base); | 
|  | if ((slot >= iter->entry_count) || (iter->content.entries[slot].base != base)) | 
|  | { | 
|  | // Not found, this should never happen. | 
|  | btree_node_unlock_exclusive (iter); | 
|  | return NULL; | 
|  | } | 
|  | struct object *ob = iter->content.entries[slot].ob; | 
|  | for (unsigned index = slot; index + 1 < iter->entry_count; ++index) | 
|  | iter->content.entries[index] = iter->content.entries[index + 1]; | 
|  | iter->entry_count--; | 
|  | btree_node_unlock_exclusive (iter); | 
|  | return ob; | 
|  | } | 
|  |  | 
|  | // Find the corresponding entry for the given address. | 
|  | static struct object * | 
|  | btree_lookup (const struct btree *t, uintptr_type target_addr) | 
|  | { | 
|  | // Within this function many loads are relaxed atomic loads. | 
|  | // Use a macro to keep the code reasonable. | 
|  | #define RLOAD(x) __atomic_load_n (&(x), __ATOMIC_RELAXED) | 
|  |  | 
|  | // For targets where unwind info is usually not registered through these | 
|  | // APIs anymore, avoid any sequential consistent atomics. | 
|  | // Use relaxed MO here, it is up to the app to ensure that the library | 
|  | // loading/initialization happens-before using that library in other | 
|  | // threads (in particular unwinding with that library's functions | 
|  | // appearing in the backtraces).  Calling that library's functions | 
|  | // without waiting for the library to initialize would be racy. | 
|  | if (__builtin_expect (!RLOAD (t->root), 1)) | 
|  | return NULL; | 
|  |  | 
|  | // The unwinding tables are mostly static, they only change when | 
|  | // frames are added or removed. This makes it extremely unlikely that they | 
|  | // change during a given unwinding sequence. Thus, we optimize for the | 
|  | // contention free case and use optimistic lock coupling. This does not | 
|  | // require any writes to shared state, instead we validate every read. It is | 
|  | // important that we do not trust any value that we have read until we call | 
|  | // validate again. Data can change at arbitrary points in time, thus we always | 
|  | // copy something into a local variable and validate again before acting on | 
|  | // the read. In the unlikely event that we encounter a concurrent change we | 
|  | // simply restart and try again. | 
|  |  | 
|  | restart: | 
|  | struct btree_node *iter; | 
|  | uintptr_type lock; | 
|  | { | 
|  | // Accessing the root node requires defending against concurrent pointer | 
|  | // changes Thus we couple rootLock -> lock on root node -> validate rootLock | 
|  | if (!version_lock_lock_optimistic (&(t->root_lock), &lock)) | 
|  | goto restart; | 
|  | iter = RLOAD (t->root); | 
|  | if (!version_lock_validate (&(t->root_lock), lock)) | 
|  | goto restart; | 
|  | if (!iter) | 
|  | return NULL; | 
|  | uintptr_type child_lock; | 
|  | if ((!btree_node_lock_optimistic (iter, &child_lock)) | 
|  | || (!version_lock_validate (&(t->root_lock), lock))) | 
|  | goto restart; | 
|  | lock = child_lock; | 
|  | } | 
|  |  | 
|  | // Now we can walk down towards the right leaf node. | 
|  | while (true) | 
|  | { | 
|  | enum node_type type = RLOAD (iter->type); | 
|  | unsigned entry_count = RLOAD (iter->entry_count); | 
|  | if (!btree_node_validate (iter, lock)) | 
|  | goto restart; | 
|  | if (!entry_count) | 
|  | return NULL; | 
|  |  | 
|  | if (type == btree_node_inner) | 
|  | { | 
|  | // We cannot call find_inner_slot here because we need (relaxed) | 
|  | // atomic reads here. | 
|  | unsigned slot = 0; | 
|  | while ( | 
|  | ((slot + 1) < entry_count) | 
|  | && (RLOAD (iter->content.children[slot].separator) < target_addr)) | 
|  | ++slot; | 
|  | struct btree_node *child = RLOAD (iter->content.children[slot].child); | 
|  | if (!btree_node_validate (iter, lock)) | 
|  | goto restart; | 
|  |  | 
|  | // The node content can change at any point in time, thus we must | 
|  | // interleave parent and child checks. | 
|  | uintptr_type child_lock; | 
|  | if (!btree_node_lock_optimistic (child, &child_lock)) | 
|  | goto restart; | 
|  | if (!btree_node_validate (iter, lock)) | 
|  | goto restart; // make sure we still point to the correct node after | 
|  | // acquiring the optimistic lock. | 
|  |  | 
|  | // Go down | 
|  | iter = child; | 
|  | lock = child_lock; | 
|  | } | 
|  | else | 
|  | { | 
|  | // We cannot call find_leaf_slot here because we need (relaxed) | 
|  | // atomic reads here. | 
|  | unsigned slot = 0; | 
|  | while (((slot + 1) < entry_count) | 
|  | && (RLOAD (iter->content.entries[slot].base) | 
|  | + RLOAD (iter->content.entries[slot].size) | 
|  | <= target_addr)) | 
|  | ++slot; | 
|  | struct leaf_entry entry; | 
|  | entry.base = RLOAD (iter->content.entries[slot].base); | 
|  | entry.size = RLOAD (iter->content.entries[slot].size); | 
|  | entry.ob = RLOAD (iter->content.entries[slot].ob); | 
|  | if (!btree_node_validate (iter, lock)) | 
|  | goto restart; | 
|  |  | 
|  | // Check if we have a hit. | 
|  | if ((entry.base <= target_addr) | 
|  | && (target_addr < entry.base + entry.size)) | 
|  | { | 
|  | return entry.ob; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  | #undef RLOAD | 
|  | } | 
|  |  | 
|  | #endif /* unwind-dw2-btree.h */ |