diff --git a/Makefile.am b/Makefile.am index 581c2182..9b648388 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,7 @@ lib_LTLIBRARIES = libuv.la libuv_la_CFLAGS = @CFLAGS@ libuv_la_LDFLAGS = -no-undefined -version-info 11:0:0 libuv_la_SOURCES = src/fs-poll.c \ + src/heap-inl.h \ src/inet.c \ src/queue.h \ src/uv-common.c \ diff --git a/Makefile.mingw b/Makefile.mingw index bc175b54..b424f906 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -29,6 +29,7 @@ INCLUDES = include/stdint-msvc2008.h \ include/uv-version.h \ include/uv-win.h \ include/uv.h \ + src/heap-inl.h \ src/queue.h \ src/uv-common.h \ src/win/atomicops-inl.h \ diff --git a/include/uv-unix.h b/include/uv-unix.h index 34695713..58d61097 100644 --- a/include/uv-unix.h +++ b/include/uv-unix.h @@ -177,16 +177,16 @@ typedef struct { void* idle_handles[2]; \ void* async_handles[2]; \ struct uv__async async_watcher; \ - /* RB_HEAD(uv__timers, uv_timer_s) */ \ - struct uv__timers { \ - struct uv_timer_s* rbh_root; \ - } timer_handles; \ + struct { \ + void* min; \ + unsigned int nelts; \ + } timer_heap; \ + uint64_t timer_counter; \ uint64_t time; \ int signal_pipefd[2]; \ uv__io_t signal_io_watcher; \ uv_signal_t child_watcher; \ int emfile_fd; \ - uint64_t timer_counter; \ UV_PLATFORM_LOOP_FIELDS \ #define UV_REQ_TYPE_PRIVATE /* empty */ @@ -265,14 +265,8 @@ typedef struct { int pending; \ #define UV_TIMER_PRIVATE_FIELDS \ - /* RB_ENTRY(uv_timer_s) tree_entry; */ \ - struct { \ - struct uv_timer_s* rbe_left; \ - struct uv_timer_s* rbe_right; \ - struct uv_timer_s* rbe_parent; \ - int rbe_color; \ - } tree_entry; \ uv_timer_cb timer_cb; \ + void* heap_node[3]; \ uint64_t timeout; \ uint64_t repeat; \ uint64_t start_id; diff --git a/src/heap-inl.h b/src/heap-inl.h new file mode 100644 index 00000000..84b9a9d7 --- /dev/null +++ b/src/heap-inl.h @@ -0,0 +1,238 @@ +/* Copyright (c) 2013, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UV_SRC_HEAP_H_ +#define UV_SRC_HEAP_H_ + +#include /* NULL */ + +#if defined(__GNUC__) +# define HEAP_EXPORT(declaration) __attribute__((unused)) static declaration +#else +# define HEAP_EXPORT(declaration) static declaration +#endif + +struct heap_node { + struct heap_node* left; + struct heap_node* right; + struct heap_node* parent; +}; + +/* A binary min heap. The usual properties hold: the root is the lowest + * element in the set, the height of the tree is at most log2(nodes) and + * it's always a complete binary tree. + * + * The heap function try hard to detect corrupted tree nodes at the cost + * of a minor reduction in performance. Compile with -DNDEBUG to disable. + */ +struct heap { + struct heap_node* min; + unsigned int nelts; +}; + +/* Return non-zero if a < b. */ +typedef int (*heap_compare_fn)(const struct heap_node* a, + const struct heap_node* b); + +/* Public functions. */ +HEAP_EXPORT(void heap_init(struct heap* heap)); +HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)); +HEAP_EXPORT(void heap_insert(struct heap* heap, + struct heap_node* newnode, + heap_compare_fn less_than)); +HEAP_EXPORT(void heap_remove(struct heap* heap, + struct heap_node* node, + heap_compare_fn less_than)); +HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)); + +/* Implementation follows. */ + +HEAP_EXPORT(void heap_init(struct heap* heap)) { + heap->min = NULL; + heap->nelts = 0; +} + +HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)) { + return heap->min; +} + +/* Swap parent with child. Child moves closer to the root, parent moves away. */ +static void heap_node_swap(struct heap* heap, + struct heap_node* parent, + struct heap_node* child) { + struct heap_node* sibling; + struct heap_node t; + + t = *parent; + *parent = *child; + *child = t; + + parent->parent = child; + if (child->left == child) { + child->left = parent; + sibling = child->right; + } else { + child->right = parent; + sibling = child->left; + } + if (sibling != NULL) + sibling->parent = child; + + if (parent->left != NULL) + parent->left->parent = parent; + if (parent->right != NULL) + parent->right->parent = parent; + + if (child->parent == NULL) + heap->min = child; + else if (child->parent->left == parent) + child->parent->left = child; + else + child->parent->right = child; +} + +HEAP_EXPORT(void heap_insert(struct heap* heap, + struct heap_node* newnode, + heap_compare_fn less_than)) { + struct heap_node** parent; + struct heap_node** child; + unsigned int path; + unsigned int n; + unsigned int k; + + newnode->left = NULL; + newnode->right = NULL; + newnode->parent = NULL; + + /* Calculate the path from the root to the insertion point. This is a min + * heap so we always insert at the left-most free node of the bottom row. + */ + path = 0; + for (k = 0, n = 1 + heap->nelts; n >= 2; k += 1, n /= 2) + path = (path << 1) | (n & 1); + + /* Now traverse the heap using the path we calculated in the previous step. */ + parent = child = &heap->min; + while (k > 0) { + parent = child; + if (path & 1) + child = &(*child)->right; + else + child = &(*child)->left; + path >>= 1; + k -= 1; + } + + /* Insert the new node. */ + newnode->parent = *parent; + *child = newnode; + heap->nelts += 1; + + /* Walk up the tree and check at each node if the heap property holds. + * It's a min heap so parent < child must be true. + */ + while (newnode->parent != NULL && less_than(newnode, newnode->parent)) + heap_node_swap(heap, newnode->parent, newnode); +} + +HEAP_EXPORT(void heap_remove(struct heap* heap, + struct heap_node* node, + heap_compare_fn less_than)) { + struct heap_node* smallest; + struct heap_node** max; + struct heap_node* child; + unsigned int path; + unsigned int k; + unsigned int n; + + if (heap->nelts == 0) + return; + + /* Calculate the path from the min (the root) to the max, the left-most node + * of the bottom row. + */ + path = 0; + for (k = 0, n = heap->nelts; n >= 2; k += 1, n /= 2) + path = (path << 1) | (n & 1); + + /* Now traverse the heap using the path we calculated in the previous step. */ + max = &heap->min; + while (k > 0) { + if (path & 1) + max = &(*max)->right; + else + max = &(*max)->left; + path >>= 1; + k -= 1; + } + + heap->nelts -= 1; + + /* Unlink the max node. */ + child = *max; + *max = NULL; + + if (child == node) { + /* We're removing either the max or the last node in the tree. */ + if (child == heap->min) { + heap->min = NULL; + } + return; + } + + /* Replace the to be deleted node with the max node. */ + child->left = node->left; + child->right = node->right; + child->parent = node->parent; + + if (child->left != NULL) { + child->left->parent = child; + } + + if (child->right != NULL) { + child->right->parent = child; + } + + if (node->parent == NULL) { + heap->min = child; + } else if (node->parent->left == node) { + node->parent->left = child; + } else { + node->parent->right = child; + } + + /* Walk down the subtree and check at each node if the heap property holds. + * It's a min heap so parent < child must be true. If the parent is bigger, + * swap it with the smallest child. + */ + for (;;) { + smallest = child; + if (child->left != NULL && less_than(smallest, child)) + smallest = child->left; + if (child->right != NULL && less_than(smallest, child)) + smallest = child->right; + if (smallest == child) + break; + heap_node_swap(heap, child, smallest); + } +} + +HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)) { + heap_remove(heap, heap->min, less_than); +} + +#undef HEAP_EXPORT + +#endif /* UV_SRC_HEAP_H_ */ diff --git a/src/unix/loop.c b/src/unix/loop.c index 9ca93f3b..daaaea38 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -22,6 +22,7 @@ #include "uv.h" #include "tree.h" #include "internal.h" +#include "heap-inl.h" #include #include #include @@ -80,7 +81,7 @@ static int uv__loop_init(uv_loop_t* loop, int default_loop) { uv__signal_global_once_init(); memset(loop, 0, sizeof(*loop)); - RB_INIT(&loop->timer_handles); + heap_init((struct heap*) &loop->timer_heap); QUEUE_INIT(&loop->wq); QUEUE_INIT(&loop->active_reqs); QUEUE_INIT(&loop->idle_handles); diff --git a/src/unix/timer.c b/src/unix/timer.c index 240efad5..b373f4db 100644 --- a/src/unix/timer.c +++ b/src/unix/timer.c @@ -20,35 +20,41 @@ #include "uv.h" #include "internal.h" +#include "heap-inl.h" + #include #include -static int uv__timer_cmp(const uv_timer_t* a, const uv_timer_t* b) { +static int timer_less_than(const struct heap_node* ha, + const struct heap_node* hb) { + const uv_timer_t* a; + const uv_timer_t* b; + + a = container_of(ha, const uv_timer_t, heap_node); + b = container_of(hb, const uv_timer_t, heap_node); + if (a->timeout < b->timeout) - return -1; - if (a->timeout > b->timeout) return 1; - /* - * compare start_id when both has the same timeout. start_id is - * allocated with loop->timer_counter in uv_timer_start(). + if (b->timeout < a->timeout) + return 0; + + /* Compare start_id when both have the same timeout. start_id is + * allocated with loop->timer_counter in uv_timer_start(). */ if (a->start_id < b->start_id) - return -1; - if (a->start_id > b->start_id) return 1; + if (b->start_id < a->start_id) + return 0; + return 0; } -RB_GENERATE_STATIC(uv__timers, uv_timer_s, tree_entry, uv__timer_cmp) - - int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER); handle->timer_cb = NULL; handle->repeat = 0; - return 0; } @@ -72,7 +78,9 @@ int uv_timer_start(uv_timer_t* handle, /* start_id is the second index to be compared in uv__timer_cmp() */ handle->start_id = handle->loop->timer_counter++; - RB_INSERT(uv__timers, &handle->loop->timer_handles, handle); + heap_insert((struct heap*) &handle->loop->timer_heap, + (struct heap_node*) &handle->heap_node, + timer_less_than); uv__handle_start(handle); return 0; @@ -83,7 +91,9 @@ int uv_timer_stop(uv_timer_t* handle) { if (!uv__is_active(handle)) return 0; - RB_REMOVE(uv__timers, &handle->loop->timer_handles, handle); + heap_remove((struct heap*) &handle->loop->timer_heap, + (struct heap_node*) &handle->heap_node, + timer_less_than); uv__handle_stop(handle); return 0; @@ -114,15 +124,15 @@ uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { int uv__next_timeout(const uv_loop_t* loop) { + const struct heap_node* heap_node; const uv_timer_t* handle; uint64_t diff; - /* RB_MIN expects a non-const tree root. That's okay, it doesn't modify it. */ - handle = RB_MIN(uv__timers, (struct uv__timers*) &loop->timer_handles); - - if (handle == NULL) + heap_node = heap_min((const struct heap*) &loop->timer_heap); + if (heap_node == NULL) return -1; /* block indefinitely */ + handle = container_of(heap_node, const uv_timer_t, heap_node); if (handle->timeout <= loop->time) return 0; @@ -135,9 +145,15 @@ int uv__next_timeout(const uv_loop_t* loop) { void uv__run_timers(uv_loop_t* loop) { + struct heap_node* heap_node; uv_timer_t* handle; - while ((handle = RB_MIN(uv__timers, &loop->timer_handles))) { + for (;;) { + heap_node = heap_min((struct heap*) &loop->timer_heap); + if (heap_node == NULL) + break; + + handle = container_of(heap_node, uv_timer_t, heap_node); if (handle->timeout > loop->time) break; diff --git a/uv.gyp b/uv.gyp index ec85bbdb..00985a43 100644 --- a/uv.gyp +++ b/uv.gyp @@ -63,6 +63,7 @@ 'include/uv-errno.h', 'include/uv-version.h', 'src/fs-poll.c', + 'src/heap-inl.h', 'src/inet.c', 'src/queue.h', 'src/uv-common.c',