Ticket #7606: 0001-Red-black-trees-27-no-sleeper-fairness-copy-of-22.patch
| File 0001-Red-black-trees-27-no-sleeper-fairness-copy-of-22.patch, 41.5 KB (added by ezyang, 4 months ago) |
|---|
-
includes/RtsAPI.h
From 441dfe32b93c67ed3eb22d502382aab3910a3d9d Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" <ezyang@mit.edu> Date: Sat, 26 Jan 2013 06:46:23 -0800 Subject: [PATCH] Red-black trees 27 - no sleeper fairness (copy of 22) Signed-off-by: Edward Z. Yang <ezyang@mit.edu> --- includes/RtsAPI.h | 2 + includes/rts/storage/TSO.h | 1 + includes/stg/MiscClosures.h | 2 + rts/Capability.c | 13 +- rts/Capability.h | 15 +- rts/RBTree.c | 521 +++++++++++++++++++++++++++++++++++++++++++ rts/RBTree.h | 90 ++++++++ rts/Schedule.c | 28 +-- rts/Schedule.h | 115 +++++++--- rts/StgMiscClosures.cmm | 5 + rts/Threads.c | 3 + rts/sm/Sanity.c | 4 +- 12 files changed, 734 insertions(+), 65 deletions(-) create mode 100644 rts/RBTree.c create mode 100644 rts/RBTree.h diff --git a/includes/RtsAPI.h b/includes/RtsAPI.h index ca87662..0ccd8aa 100644
a b 37 37 */ 38 38 typedef struct Capability_ Capability; 39 39 40 typedef struct StgRB_ StgRB; 41 40 42 /* 41 43 * The public view of a Capability: we can be sure it starts with 42 44 * these two components (but it may have more private fields). -
includes/rts/storage/TSO.h
diff --git a/includes/rts/storage/TSO.h b/includes/rts/storage/TSO.h index 20a67a2..eb0f06b 100644
a b 257 257 258 258 /* this is the NIL ptr for a TSO queue (e.g. runnable queue) */ 259 259 #define END_TSO_QUEUE ((StgTSO *)(void*)&stg_END_TSO_QUEUE_closure) 260 #define RB_NULL ((StgRB *)(void*)&stg_RB_NULL_closure) 260 261 261 262 #endif /* RTS_STORAGE_TSO_H */ -
includes/stg/MiscClosures.h
diff --git a/includes/stg/MiscClosures.h b/includes/stg/MiscClosures.h index e6d5fcd..31f18af 100644
a b 114 114 RTS_ENTRY(stg_MUT_VAR_CLEAN); 115 115 RTS_ENTRY(stg_MUT_VAR_DIRTY); 116 116 RTS_ENTRY(stg_END_TSO_QUEUE); 117 RTS_ENTRY(stg_RB_NULL); 117 118 RTS_ENTRY(stg_MSG_TRY_WAKEUP); 118 119 RTS_ENTRY(stg_MSG_THROWTO); 119 120 RTS_ENTRY(stg_MSG_BLACKHOLE); … … 142 143 /* closures */ 143 144 144 145 RTS_CLOSURE(stg_END_TSO_QUEUE_closure); 146 RTS_CLOSURE(stg_RB_NULL_closure); 145 147 RTS_CLOSURE(stg_NO_FINALIZER_closure); 146 148 RTS_CLOSURE(stg_dummy_ret_closure); 147 149 RTS_CLOSURE(stg_forceIO_closure); -
rts/Capability.c
diff --git a/rts/Capability.c b/rts/Capability.c index 811df58..2839891 100644
a b 26 26 #include "sm/GC.h" // for gcWorkerThread() 27 27 #include "STM.h" 28 28 #include "RtsUtils.h" 29 #include "RBTree.h" 29 30 30 31 #include <string.h> 31 32 … … 230 231 cap->idle = 0; 231 232 cap->disabled = rtsFalse; 232 233 233 cap->run_queue_hd = END_TSO_QUEUE; 234 cap->run_queue_tl = END_TSO_QUEUE; 234 cap->promoted_run_queue_hd = END_TSO_QUEUE; 235 235 236 236 #if defined(THREADED_RTS) 237 237 initMutex(&cap->lock); … … 282 282 cap->r.rCCCS = NULL; 283 283 #endif 284 284 285 cap->rb_free_list = RB_NULL; 286 cap->run_count = 0; 287 cap->run_rbtree = rbCreateNode(cap, END_TSO_QUEUE); 288 cap->run_active = cap->run_rbtree; 289 285 290 traceCapCreate(cap); 286 291 traceCapsetAssignCap(CAPSET_OSPROCESS_DEFAULT, i); 287 292 traceCapsetAssignCap(CAPSET_CLOCKDOMAIN_DEFAULT, i); … … 1007 1012 // or fewer Capabilities as GC threads, but just in case there 1008 1013 // are more, we mark every Capability whose number is the GC 1009 1014 // thread's index plus a multiple of the number of GC threads. 1010 evac(user, (StgClosure **)(void *)&cap-> run_queue_hd);1011 evac(user, (StgClosure **)(void *)&cap->run_queue_tl);1015 evac(user, (StgClosure **)(void *)&cap->promoted_run_queue_hd); 1016 rbEvacuate(evac, user, cap->run_rbtree); 1012 1017 #if defined(THREADED_RTS) 1013 1018 evac(user, (StgClosure **)(void *)&cap->inbox); 1014 1019 #endif -
rts/Capability.h
diff --git a/rts/Capability.h b/rts/Capability.h index 81322c8..ae320c8 100644
a b 55 55 // access to its run queue, so can wake up threads without 56 56 // taking a lock, and the common path through the scheduler is 57 57 // also lock-free. 58 StgTSO *run_queue_hd; 59 StgTSO *run_queue_tl; 58 StgTSO *promoted_run_queue_hd; 59 // XXX move StgRB somewhere not RBTree.h to break circular dep 60 StgRB *run_rbtree; 61 StgRB *run_active; // cached pointer to min red-black tree element 62 StgRB *rb_free_list; // free list of RBNodes 63 nat run_count; 60 64 61 65 // [SSS] Stride scheduling extensions. The Task with this 62 66 // Capability has exclusive access to this variable. … … 153 157 // These properties should be true when a Task is holding a Capability 154 158 #define ASSERT_FULL_CAPABILITY_INVARIANTS(cap,task) \ 155 159 ASSERT(cap->running_task != NULL && cap->running_task == task); \ 160 ASSERT((cap->run_rbtree == RB_NULL) == (cap->run_active == RB_NULL)); \ 161 ASSERT((cap->run_active == RB_NULL && cap->run_rbtree == RB_NULL) ||\ 162 (RB_HEAD_GET(cap->run_active) != END_TSO_QUEUE && RB_TAIL_GET(cap->run_active) != END_TSO_QUEUE) || \ 163 (RB_HEAD_GET(cap->run_active) == END_TSO_QUEUE && RB_TAIL_GET(cap->run_active) == END_TSO_QUEUE && \ 164 RB_LEFT_GET(cap->run_active) == RB_NULL && RB_RIGHT_GET(cap->run_active) == RB_NULL));\ 156 165 ASSERT(task->cap == cap); \ 157 166 ASSERT_PARTIAL_CAPABILITY_INVARIANTS(cap,task) 158 167 … … 162 171 // Task is bound, its thread has just blocked, and it may have been 163 172 // moved to another Capability. 164 173 #define ASSERT_PARTIAL_CAPABILITY_INVARIANTS(cap,task) \ 165 ASSERT(cap->run_queue_hd == END_TSO_QUEUE ? \166 cap->run_queue_tl == END_TSO_QUEUE : 1); \167 174 ASSERT(myTask() == task); \ 168 175 ASSERT_TASK_ID(task); 169 176 -
(a) /dev/null vs. (b) b/rts/RBTree.c
diff --git a/rts/RBTree.c b/rts/RBTree.c new file mode 100644 index 0000000..13170f8
a b 1 /* ----------------------------------------------------------------------------- 2 * 3 * (c) The GHC Team, 2013 4 * 5 * Red-black trees 6 * 7 * The core algorithm is based off of jemalloc's red-black tree 8 * implementation <http://www.canonware.com/rb/>, which has the following license: 9 * 10 * Copyright (C) 2002-2012 Jason Evans <jasone@canonware.com>. All rights reserved. 11 * Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved. 12 * Copyright (C) 2009-2012 Facebook, Inc. All rights reserved. 13 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are met: 16 * 1. Redistributions of source code must retain the above copyright notice(s), 17 * this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright notice(s), 19 * this list of conditions and the following disclaimer in the documentation 20 * and/or other materials provided with the distribution. 21 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS 23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 25 * EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 30 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * ---------------------------------------------------------------------------*/ 34 35 #include "PosixSource.h" 36 #include "Rts.h" 37 38 #include "RtsUtils.h" 39 #include "RBTree.h" 40 41 STATIC_INLINE StgRB* 42 rbRotateLeft(Capability *cap, StgRB *a) { 43 ASSERT(a != RB_NULL); 44 StgRB *r = RB_RIGHT_GET(a); 45 ASSERT(r != RB_NULL); 46 RB_RIGHT_SET(cap, a, RB_LEFT_GET(r)); 47 RB_LEFT_SET(cap, r, a); 48 return r; 49 } 50 51 STATIC_INLINE StgRB* 52 rbRotateRight(Capability *cap, StgRB *a) { 53 ASSERT(a != RB_NULL); 54 StgRB *l = RB_LEFT_GET(a); 55 ASSERT(l != RB_NULL); 56 RB_LEFT_SET(cap, a, RB_RIGHT_GET(l)); 57 RB_RIGHT_SET(cap, l, a); 58 return l; 59 } 60 61 int 62 checkRBTree(StgRB *r, int forceBlack, int count, int expected) 63 { 64 if (r == RB_NULL) { 65 ASSERT(expected == -1 || count == expected); 66 ASSERT(count != 0); 67 return count; 68 } 69 ASSERT(LOOKS_LIKE_CLOSURE_PTR(RB_LEFT_GET(r))); 70 ASSERT(LOOKS_LIKE_CLOSURE_PTR(RB_RIGHT_GET(r))); 71 ASSERT(LOOKS_LIKE_CLOSURE_PTR(RB_HEAD_GET(r))); 72 ASSERT(LOOKS_LIKE_CLOSURE_PTR(RB_TAIL_GET(r))); 73 if (count == 0) { 74 // this corresponds to "fast path" empty node 75 if (RB_HEAD_GET(r) == END_TSO_QUEUE) { 76 ASSERT(RB_LEFT_GET(r) == RB_NULL); 77 ASSERT(RB_RIGHT_GET(r) == RB_NULL); 78 ASSERT(RB_TAIL_GET(r) == END_TSO_QUEUE); 79 ASSERT(RB_IS_BLACK(r)); 80 } 81 return 0; 82 } else { 83 ASSERT(RB_HEAD_GET(r) != END_TSO_QUEUE); 84 ASSERT(RB_TAIL_GET(r) != END_TSO_QUEUE); 85 } 86 ASSERT(LOOKS_LIKE_CLOSURE_PTR(RB_COLOR_GET(r))); 87 ASSERT(RB_COLOR_GET(r) == RB_RED || RB_COLOR_GET(r) == RB_BLACK); 88 if (forceBlack) { 89 ASSERT(RB_IS_BLACK(r)); 90 } 91 if (RB_IS_BLACK(r)) { 92 count++; 93 } 94 expected = checkRBTree(RB_LEFT_GET(r), RB_IS_RED(r), count, expected); 95 checkRBTree(RB_RIGHT_GET(r), RB_IS_RED(r), count, expected); 96 return expected; 97 } 98 99 void 100 rbEvacuate(evac_fn evac, void *user, StgRB* r) { 101 if (r == RB_NULL) return; 102 evac(user, (StgClosure **)(void *)&r->head); 103 evac(user, (StgClosure **)(void *)&r->tail); 104 rbEvacuate(evac, user, r->left); 105 rbEvacuate(evac, user, r->right); // XXX maybe manually TCO this 106 } 107 108 // this function assumes you've checked for fastpath 109 StgRB* 110 rbInsert(Capability *cap, StgRB **root, StgTSO *tso) { 111 struct { 112 StgRB *node; 113 int cmp; 114 } path[sizeof(void *) << 4], *pathp; 115 path->node = *root; 116 int found = 0; 117 // Wind 118 for (pathp = path; pathp->node != RB_NULL; pathp++) { 119 ASSERT(RB_HEAD_GET(pathp->node) != END_TSO_QUEUE); // e.g. root is NOT a fast path empty node 120 ASSERT(RB_TAIL_GET(pathp->node) != END_TSO_QUEUE); 121 StgWord64 pass = RB_HEAD_GET(pathp->node)->ss_pass; 122 int cmp = pathp->cmp = (tso->ss_pass > pass) - (tso->ss_pass < pass); 123 if (cmp < 0) { 124 pathp[1].node = RB_LEFT_GET(pathp->node); 125 } else if (cmp == 0) { 126 found = 1; 127 break; 128 } else { 129 pathp[1].node = RB_RIGHT_GET(pathp->node); 130 } 131 } 132 if (found) { // O(n log n) pointer traverals but only O(1) edits 133 ASSERT(pathp->node != RB_NULL); 134 ASSERT(RB_HEAD_GET(pathp->node) != END_TSO_QUEUE); 135 setTSOLink(cap, RB_TAIL_GET(pathp->node), tso); 136 RB_TAIL_SET(cap, pathp->node, tso); 137 return pathp->node; 138 } 139 StgRB *node = rbCreateNode(cap, tso); 140 pathp->node = node; 141 // Unwind 142 for (pathp--; (void*)pathp >= (void*)path; pathp--) { 143 StgRB *cnode = pathp->node; 144 ASSERT(cnode != RB_NULL); 145 ASSERT(pathp->cmp != 0); 146 if (pathp->cmp < 0) { 147 StgRB *left = pathp[1].node; 148 RB_LEFT_SET(cap, cnode, left); 149 if (RB_IS_RED(left)) { 150 StgRB *leftleft = RB_LEFT_GET(left); 151 if (RB_IS_RED(leftleft)) { 152 // fix up 4-node 153 StgRB *tnode; 154 RB_COLOR_BLACK(cap, leftleft); 155 tnode = rbRotateRight(cap, cnode); 156 cnode = tnode; 157 } 158 } else { 159 return node; 160 } 161 } else { 162 StgRB *right = pathp[1].node; 163 RB_RIGHT_SET(cap, cnode, right); 164 if (RB_IS_RED(right)) { 165 StgRB *left = RB_LEFT_GET(cnode); 166 if (RB_IS_RED(left)) { 167 // split 4-node 168 RB_COLOR_BLACK(cap, left); 169 RB_COLOR_BLACK(cap, right); 170 RB_COLOR_RED(cap, cnode); 171 } else { 172 // lean left 173 StgRB *tnode; 174 StgClosure *tred = RB_COLOR_GET(cnode); 175 tnode = rbRotateLeft(cap, cnode); 176 RB_COLOR_SET(cap, tnode, tred); 177 RB_COLOR_RED(cap, cnode); 178 cnode = tnode; 179 } 180 } else { 181 return node; 182 } 183 } 184 pathp->node = cnode; 185 } 186 *root = path->node; 187 RB_COLOR_BLACK(cap, *root); 188 return node; 189 } 190 191 // this function assumes you've checked for fastpath 192 StgTSO* 193 rbRemoveMin(Capability *cap, StgRB **root) { 194 struct { 195 StgRB *node; 196 int cmp; 197 } path[sizeof(void *) << 4], *pathp, *nodep; 198 // Wind 199 nodep = NULL; 200 path->node = *root; 201 for (pathp = path; pathp->node != RB_NULL; pathp++) { 202 ASSERT(RB_HEAD_GET(pathp->node) != END_TSO_QUEUE); // e.g. fast path is NOT in effect 203 ASSERT(RB_TAIL_GET(pathp->node) != END_TSO_QUEUE); 204 StgRB *left = RB_LEFT_GET(pathp->node); 205 if (left != RB_NULL) { 206 pathp->cmp = -1; 207 pathp[1].node = left; 208 } else { 209 // Find node's successor, in preparation for swap 210 // XXX defer this for later 211 pathp[1].node = RB_RIGHT_GET(pathp->node); 212 pathp->cmp = 1; 213 nodep = pathp; 214 for (pathp++; pathp->node != RB_NULL; pathp++) { 215 pathp->cmp = -1; 216 pathp[1].node = RB_LEFT_GET(pathp->node); 217 } 218 break; 219 } 220 } 221 ASSERT(nodep->node != RB_NULL); 222 StgTSO *tso = RB_HEAD_GET(nodep->node); 223 if (tso->_link != END_TSO_QUEUE) { // fast-path! 224 RB_HEAD_SET(cap, nodep->node, tso->_link); 225 tso->_link = END_TSO_QUEUE; 226 return tso; // not to cleanup: no free node! 227 } 228 tso->_link = END_TSO_QUEUE; 229 pathp--; 230 StgRB *node = nodep->node; // the node to be deleted 231 if (pathp->node != node) { 232 ASSERT(pathp->node != RB_NULL); 233 // swap node with its successor 234 StgClosure *tred = RB_COLOR_GET(pathp->node); 235 RB_COLOR_SET(cap, pathp->node, RB_COLOR_GET(node)); 236 ASSERT(RB_HEAD_GET(pathp->node) != END_TSO_QUEUE); // e.g. fast path is NOT in effect 237 ASSERT(RB_TAIL_GET(pathp->node) != END_TSO_QUEUE); 238 RB_LEFT_SET(cap, pathp->node, RB_LEFT_GET(node)); 239 // if node's successor is its right child, the following code 240 // will do the wrong thing for the right child pointer. 241 // However, it doesn't matter, because the pointer will be 242 // properly set when the successor is pruned. 243 RB_RIGHT_SET(cap, pathp->node, RB_RIGHT_GET(node)); 244 RB_COLOR_SET(cap, node, tred); 245 // the pruned leaf node's child pointers are never accessed 246 // again, so don't bother setting them to nil 247 nodep->node = pathp->node; 248 pathp->node = node; 249 if (nodep == path) { 250 *root = nodep->node; 251 } else { 252 if (nodep[-1].cmp < 0) { 253 RB_LEFT_SET(cap, nodep[-1].node, nodep->node); 254 } else { 255 RB_RIGHT_SET(cap, nodep[-1].node, nodep->node); 256 } 257 } 258 } else { 259 StgRB *left = RB_LEFT_GET(node); 260 if (left != RB_NULL) { 261 // node has no successor, but it has a left child. 262 // splice node out, without losing the left child 263 ASSERT(RB_IS_BLACK(node)); 264 ASSERT(RB_IS_RED(left)); 265 RB_COLOR_BLACK(cap, left); 266 if (pathp == path) { 267 *root = left; 268 } else { 269 if (pathp[-1].cmp < 0) { 270 RB_LEFT_SET(cap, pathp[-1].node, left); 271 } else { 272 RB_RIGHT_SET(cap, pathp[-1].node, left); 273 } 274 } 275 goto cleanup; 276 } else if (pathp == path) { 277 // the tree contained only one node 278 // *root = RB_NULL; 279 // instead, setup fastpath 280 RB_HEAD_SET(cap, *root, END_TSO_QUEUE); 281 RB_TAIL_SET(cap, *root, END_TSO_QUEUE); 282 ASSERT(RB_LEFT_GET(*root) == RB_NULL); 283 ASSERT(RB_RIGHT_GET(*root) == RB_NULL); 284 // do NOT add to free list 285 return tso; 286 } 287 } 288 if (RB_IS_RED(pathp->node)) { 289 // prune red node, which requires no fixup 290 ASSERT(pathp[-1].cmp < 0); 291 RB_LEFT_SET(cap, pathp[-1].node, RB_NULL); 292 goto cleanup; 293 } 294 // the node to be pruned is black, so unwind until balance 295 // is restored 296 pathp->node = RB_NULL; 297 for (pathp--; (void*)pathp >= (void*)path; pathp--) { 298 ASSERT(pathp->node != RB_NULL); 299 ASSERT(pathp->cmp != 0); 300 if (pathp->cmp < 0) { 301 RB_LEFT_SET(cap, pathp->node, pathp[1].node); 302 ASSERT(RB_IS_BLACK(pathp[1].node)); 303 if (RB_IS_RED(pathp->node)) { 304 StgRB *right = RB_RIGHT_GET(pathp->node); 305 ASSERT(right != RB_NULL); 306 StgRB *rightleft = RB_LEFT_GET(right); 307 StgRB *tnode; 308 if (RB_IS_RED(rightleft)) { 309 /* In the following diagrams, ||, //, and \\ */ 310 /* indicate the path to the removed node. */ 311 /* */ 312 /* || */ 313 /* pathp(r) */ 314 /* // \ */ 315 /* (b) (b) */ 316 /* / */ 317 /* (r) */ 318 /* */ 319 RB_COLOR_BLACK(cap, pathp->node); 320 tnode = rbRotateRight(cap, right); 321 RB_RIGHT_SET(cap, pathp->node, tnode); 322 tnode = rbRotateLeft(cap, pathp->node); 323 } else { 324 /* || */ 325 /* pathp(r) */ 326 /* // \ */ 327 /* (b) (b) */ 328 /* / */ 329 /* (b) */ 330 /* */ 331 tnode = rbRotateLeft(cap, pathp->node); 332 } 333 // balance restored, but rotation modified subtree root. 334 ASSERT(pathp > path); 335 if (pathp[-1].cmp < 0) { 336 RB_LEFT_SET(cap, pathp[-1].node, tnode); 337 } else { 338 RB_RIGHT_SET(cap, pathp[-1].node, tnode); 339 } 340 goto cleanup; 341 } else { 342 StgRB *right = RB_RIGHT_GET(pathp->node); 343 ASSERT(right != RB_NULL); 344 StgRB *rightleft = RB_LEFT_GET(right); 345 if (RB_IS_RED(rightleft)) { 346 /* || */ 347 /* pathp(b) */ 348 /* // \ */ 349 /* (b) (b) */ 350 /* / */ 351 /* (r) */ 352 StgRB *tnode; 353 RB_COLOR_BLACK(cap, rightleft); 354 tnode = rbRotateRight(cap, right); 355 RB_RIGHT_SET(cap, pathp->node, tnode); 356 tnode = rbRotateLeft(cap, pathp->node); 357 // balance restored, but rotation modified 358 // subtree root, which may actually be the tree root 359 if (pathp == path) { 360 // set root 361 *root = tnode; 362 } else { 363 if (pathp[-1].cmp < 0) { 364 RB_LEFT_SET(cap, pathp[-1].node, tnode); 365 } else { 366 RB_RIGHT_SET(cap, pathp[-1].node, tnode); 367 } 368 } 369 goto cleanup; 370 } else { 371 /* || */ 372 /* pathp(b) */ 373 /* // \ */ 374 /* (b) (b) */ 375 /* / */ 376 /* (b) */ 377 StgRB *tnode; 378 RB_COLOR_RED(cap, pathp->node); 379 tnode = rbRotateLeft(cap, pathp->node); 380 pathp->node = tnode; 381 } 382 } 383 } else { 384 StgRB *left; 385 RB_RIGHT_SET(cap, pathp->node, pathp[1].node); 386 left = RB_LEFT_GET(pathp->node); 387 ASSERT(left != RB_NULL); 388 if (RB_IS_RED(left)) { 389 StgRB *tnode; 390 StgRB *leftright = RB_RIGHT_GET(left); 391 ASSERT(leftright != RB_NULL); 392 StgRB *leftrightleft = RB_LEFT_GET(leftright); 393 if (RB_IS_RED(leftrightleft)) { 394 /* || */ 395 /* pathp(b) */ 396 /* / \\ */ 397 /* (r) (b) */ 398 /* \ */ 399 /* (b) */ 400 /* / */ 401 /* (r) */ 402 StgRB *unode; 403 RB_COLOR_BLACK(cap, leftrightleft); 404 unode = rbRotateRight(cap, pathp->node); 405 tnode = rbRotateRight(cap, pathp->node); 406 RB_RIGHT_SET(cap, unode, tnode); 407 tnode = rbRotateLeft(cap, unode); 408 } else { 409 /* || */ 410 /* pathp(b) */ 411 /* / \\ */ 412 /* (r) (b) */ 413 /* \ */ 414 /* (b) */ 415 /* / */ 416 /* (b) */ 417 ASSERT(leftright != RB_NULL); 418 RB_COLOR_RED(cap, leftright); 419 tnode = rbRotateRight(cap, pathp->node); 420 RB_COLOR_BLACK(cap, tnode); 421 } 422 /* Balance restored, but rotation modified subtree */ 423 /* root, which may actually be the tree root. */ 424 if (pathp == path) { 425 /* Set root. */ 426 *root = tnode; 427 } else { 428 if (pathp[-1].cmp < 0) { 429 RB_LEFT_SET(cap, pathp[-1].node, tnode); 430 } else { 431 RB_RIGHT_SET(cap, pathp[-1].node, tnode); 432 } 433 } 434 goto cleanup; 435 } else if (RB_IS_RED(pathp->node)) { 436 StgRB *leftleft = RB_LEFT_GET(left); 437 if (RB_IS_RED(leftleft)) { 438 /* || */ 439 /* pathp(r) */ 440 /* / \\ */ 441 /* (b) (b) */ 442 /* / */ 443 /* (r) */ 444 StgRB *tnode; 445 RB_COLOR_BLACK(cap, pathp->node); 446 RB_COLOR_RED(cap, left); 447 RB_COLOR_BLACK(cap, leftleft); 448 tnode = rbRotateRight(cap, pathp->node); 449 /* Balance restored, but rotation modified */ 450 /* subtree root. */ 451 ASSERT(pathp > path); 452 if (pathp[-1].cmp < 0) { 453 RB_LEFT_SET(cap, pathp[-1].node, tnode); 454 } else { 455 RB_RIGHT_SET(cap, pathp[-1].node, tnode); 456 } 457 goto cleanup; 458 } else { 459 /* || */ 460 /* pathp(r) */ 461 /* / \\ */ 462 /* (b) (b) */ 463 /* / */ 464 /* (b) */ 465 RB_COLOR_RED(cap, left); 466 RB_COLOR_BLACK(cap, pathp->node); 467 /* Balance restored. */ 468 goto cleanup; 469 } 470 } else { 471 StgRB *leftleft = RB_LEFT_GET(left); 472 if (RB_IS_RED(leftleft)) { 473 /* || */ 474 /* pathp(b) */ 475 /* / \\ */ 476 /* (b) (b) */ 477 /* / */ 478 /* (r) */ 479 StgRB *tnode; 480 RB_COLOR_BLACK(cap, leftleft); 481 tnode = rbRotateRight(cap, pathp->node); 482 /* Balance restored, but rotation modified */ 483 /* subtree root, which may actually be the tree */ 484 /* root. */ 485 if (pathp == path) { 486 /* Set root. */ 487 *root = tnode; 488 } else { 489 if (pathp[-1].cmp < 0) { 490 RB_LEFT_SET(cap, pathp[-1].node, tnode); 491 } else { 492 RB_RIGHT_SET(cap, pathp[-1].node, tnode); 493 } 494 } 495 goto cleanup; 496 } else { 497 /* || */ 498 /* pathp(b) */ 499 /* / \\ */ 500 /* (b) (b) */ 501 /* / */ 502 /* (b) */ 503 RB_COLOR_RED(cap, left); 504 } 505 } 506 } 507 } 508 // set root 509 *root = path->node; 510 ASSERT(RB_IS_BLACK(*root)); 511 cleanup: 512 // return node to the free list 513 RB_LEFT_SET(cap, node, cap->rb_free_list); 514 #ifdef DEBUG 515 RB_RIGHT_SET(cap, node, RB_NULL); 516 RB_HEAD_SET(cap, node, END_TSO_QUEUE); 517 RB_TAIL_SET(cap, node, END_TSO_QUEUE); 518 #endif 519 cap->rb_free_list = node; 520 return tso; 521 } -
(a) /dev/null vs. (b) b/rts/RBTree.h
diff --git a/rts/RBTree.h b/rts/RBTree.h new file mode 100644 index 0000000..df5fbaa
a b 1 #ifndef RBTREE_H 2 #define RBTREE_H 3 4 #include "Capability.h" 5 #include "RtsUtils.h" 6 7 // http://www.canonware.com/rb/ 8 9 struct StgRB_ { 10 StgRB *left, *right; 11 StgTSO *head, *tail; 12 int color; 13 }; // typedef in RtsAPI 14 15 #define RB_RED 0 16 #define RB_BLACK 1 17 18 #define RB_LEFT_GET(n) ((n)->left) 19 #define RB_LEFT_SET(cap, n,p) (n)->left = p; 20 #define RB_RIGHT_GET(n) ((n)->right) 21 #define RB_RIGHT_SET(cap, n,p) (n)->right = p; 22 #define RB_HEAD_GET(n) ((n)->head) 23 #define RB_HEAD_SET(cap, n,p) (n)->head = p; 24 #define RB_TAIL_GET(n) ((n)->tail) 25 #define RB_TAIL_SET(cap, n,p) (n)->tail = p; 26 // alternatively, setup a static RB_NULL closure with the appropriate 27 // structure and do the normal thing 28 #define RB_IS_RED(n) ((n) != RB_NULL && (n)->color == RB_RED) 29 #define RB_IS_BLACK(n) ((n) == RB_NULL || (n)->color == RB_BLACK) 30 #define RB_COLOR_GET(n) ((n) == RB_NULL ? RB_BLACK : (n)->color) 31 #define RB_COLOR_RED(cap, n) (n)->color = RB_RED; 32 #define RB_COLOR_BLACK(cap, n) (n)->color = RB_BLACK; 33 #define RB_COLOR_SET(cap, n,c) (n)->color = c; 34 35 int checkRBTree(StgRB *r, int forceBlack, int count, int expected); 36 StgRB* rbInsert(Capability *cap, StgRB **root, StgTSO *tso); 37 StgTSO* rbRemoveMin(Capability *cap, StgRB **root); 38 void rbEvacuate(evac_fn evac, void *user, StgRB* r); 39 40 // maintain a free list of nodes so we don't have to continually 41 // allocate them 42 43 #define RB_CHUNK (1024 * sizeof(W_) / sizeof(StgRB)) 44 45 INLINE_HEADER StgRB* 46 rbCreateNode(Capability *cap, StgTSO *tso) { 47 StgRB *n; 48 if (cap->rb_free_list != RB_NULL) { 49 n = cap->rb_free_list; 50 cap->rb_free_list = RB_LEFT_GET(n); 51 } else { 52 n = (StgRB*) stgMallocBytes(RB_CHUNK * sizeof(StgRB), "rbCreateNode"); 53 cap->rb_free_list = n + 1; 54 StgRB *p; 55 for (p = cap->rb_free_list; p < n + RB_CHUNK - 1; p++) { 56 p->left = p + 1; 57 } 58 p->left = RB_NULL; 59 } 60 RB_LEFT_SET(cap, n, RB_NULL); 61 RB_RIGHT_SET(cap, n, RB_NULL); 62 RB_HEAD_SET(cap, n, tso); 63 RB_TAIL_SET(cap, n, tso); 64 if (tso == END_TSO_QUEUE) { 65 RB_COLOR_BLACK(cap, n); 66 } else { 67 RB_COLOR_RED(cap, n); 68 } 69 return n; 70 } 71 72 EXTERN_INLINE StgRB* 73 rbFirst(StgRB *n); 74 75 EXTERN_INLINE StgRB* 76 rbFirst(StgRB *n) { 77 for (; RB_LEFT_GET(n) != RB_NULL; n = RB_LEFT_GET(n)) {} 78 return n; 79 } 80 81 EXTERN_INLINE StgRB* 82 rbLast(StgRB *n); 83 84 EXTERN_INLINE StgRB* 85 rbLast(StgRB *n) { 86 for (; RB_RIGHT_GET(n) != RB_NULL; n = RB_RIGHT_GET(n)) {} 87 return n; 88 } 89 90 #endif /* RBTREE_H */ -
rts/Schedule.c
diff --git a/rts/Schedule.c b/rts/Schedule.c index 3de00c5..8e93fbc 100644
a b 560 560 * -------------------------------------------------------------------------- */ 561 561 562 562 void 563 removeFromRunQueue (Capability *cap , StgTSO *tso)563 removeFromRunQueue (Capability *cap STG_UNUSED, StgTSO *tso STG_UNUSED) 564 564 { 565 if (tso->block_info.prev == END_TSO_QUEUE) { 566 ASSERT(cap->run_queue_hd == tso); 567 cap->run_queue_hd = tso->_link; 568 } else { 569 setTSOLink(cap, tso->block_info.prev, tso->_link); 570 } 571 if (tso->_link == END_TSO_QUEUE) { 572 ASSERT(cap->run_queue_tl == tso); 573 cap->run_queue_tl = tso->block_info.prev; 574 } else { 575 setTSOPrev(cap, tso->_link, tso->block_info.prev); 576 } 577 tso->_link = tso->block_info.prev = END_TSO_QUEUE; 578 579 IF_DEBUG(sanity, checkRunQueue(cap)); 565 //barf("removeFromRunQueue"); 580 566 } 581 567 582 568 void 583 promoteInRunQueue (Capability *cap , StgTSO *tso)569 promoteInRunQueue (Capability *cap STG_UNUSED, StgTSO *tso STG_UNUSED) 584 570 { 585 removeFromRunQueue(cap, tso);586 pushOnRunQueue(cap, tso);571 //removeFromRunQueue(cap, tso); 572 //pushOnRunQueue(cap, tso); 587 573 } 588 574 589 575 /* ---------------------------------------------------------------------------- … … 742 728 // - giving low priority to moving long-lived threads 743 729 744 730 if (n_free_caps > 0) { 745 StgTSO *prev, *t, *next;731 //StgTSO *prev, *t, *next; 746 732 #ifdef SPARK_PUSHING 747 733 rtsBool pushed_to_all; 748 734 #endif … … 759 745 pushed_to_all = rtsFalse; 760 746 #endif 761 747 748 /* 762 749 if (cap->run_queue_hd != END_TSO_QUEUE) { 763 750 prev = cap->run_queue_hd; 764 751 t = prev->_link; … … 795 782 796 783 IF_DEBUG(sanity, checkRunQueue(cap)); 797 784 } 785 */ 798 786 799 787 #ifdef SPARK_PUSHING 800 788 /* JB I left this code in place, it would work but is not necessary */ -
rts/Schedule.h
diff --git a/rts/Schedule.h b/rts/Schedule.h index e4425af..65457c0 100644
a b 13 13 #include "rts/OSThreads.h" 14 14 #include "Capability.h" 15 15 #include "Trace.h" 16 #include "RBTree.h" 16 17 17 18 #include "BeginPrivate.h" 18 19 … … 140 141 EXTERN_INLINE void 141 142 appendToRunQueue (Capability *cap, StgTSO *tso) 142 143 { 143 ASSERT(tso->_link == END_TSO_QUEUE); 144 if (cap->run_queue_hd == END_TSO_QUEUE) { 145 cap->run_queue_hd = tso; 146 tso->block_info.prev = END_TSO_QUEUE; 144 tso->block_info.closure = (StgClosure*)END_TSO_QUEUE; 145 tso->ss_pass += STRIDE1 / tso->ss_tickets; 146 StgTSO *cur = RB_HEAD_GET(cap->run_active); 147 cap->run_count++; 148 if (cur == END_TSO_QUEUE) { 149 // fastpath 150 ASSERT(RB_IS_BLACK(cap->run_active)); 151 ASSERT(RB_LEFT_GET(cap->run_active) == RB_NULL); 152 ASSERT(RB_RIGHT_GET(cap->run_active) == RB_NULL); 153 RB_HEAD_SET(cap, cap->run_active, tso); 154 RB_TAIL_SET(cap, cap->run_active, tso); 147 155 } else { 148 setTSOLink(cap, cap->run_queue_tl, tso); 149 setTSOPrev(cap, tso, cap->run_queue_tl); 156 StgWord64 pass = cur->ss_pass; 157 if (tso->ss_pass < pass) { 158 cap->run_active = rbInsert(cap, &cap->run_rbtree, tso); 159 } else if (tso->ss_pass == pass) { 160 setTSOLink(cap, RB_TAIL_GET(cap->run_active), tso); 161 RB_TAIL_SET(cap, cap->run_active, tso); 162 } else { 163 rbInsert(cap, &cap->run_rbtree, tso); 164 } 165 IF_DEBUG(sanity, checkRBTree(cap->run_rbtree, 1, 0, -1)); 150 166 } 151 cap->run_queue_tl = tso;152 167 } 153 168 154 169 EXTERN_INLINE void … … 157 172 EXTERN_INLINE void 158 173 joinRunQueue(Capability *cap, StgTSO *tso) 159 174 { 175 tso->ss_pass = cap->ss_pass - STRIDE1 / tso->ss_tickets; 160 176 appendToRunQueue(cap, tso); 161 177 } 162 178 … … 169 185 EXTERN_INLINE void 170 186 pushOnRunQueue (Capability *cap, StgTSO *tso) 171 187 { 172 setTSOLink(cap, tso, cap->run_queue_hd); 173 tso->block_info.prev = END_TSO_QUEUE; 174 if (cap->run_queue_hd != END_TSO_QUEUE) { 175 setTSOPrev(cap, cap->run_queue_hd, tso); 176 } 177 cap->run_queue_hd = tso; 178 if (cap->run_queue_tl == END_TSO_QUEUE) { 179 cap->run_queue_tl = tso; 188 tso->block_info.closure = (StgClosure*)END_TSO_QUEUE; 189 tso->ss_pass += STRIDE1 / tso->ss_tickets; 190 cap->run_count++; 191 if (cap->promoted_run_queue_hd == END_TSO_QUEUE) { 192 } else { 193 setTSOLink(cap, tso, cap->promoted_run_queue_hd); 180 194 } 195 cap->promoted_run_queue_hd = tso; 181 196 } 182 197 183 198 EXTERN_INLINE void … … 186 201 EXTERN_INLINE void 187 202 fastJoinRunQueue(Capability *cap, StgTSO *tso) 188 203 { 204 tso->ss_pass = cap->ss_pass - STRIDE1 / tso->ss_tickets; 189 205 pushOnRunQueue(cap, tso); 190 206 } 191 207 … … 194 210 INLINE_HEADER StgTSO * 195 211 popRunQueue (Capability *cap) 196 212 { 197 StgTSO *t = cap->run_queue_hd; 198 ASSERT(t != END_TSO_QUEUE); 199 cap->run_queue_hd = t->_link; 200 if (t->_link != END_TSO_QUEUE) { 201 t->_link->block_info.prev = END_TSO_QUEUE; 202 } 203 t->_link = END_TSO_QUEUE; // no write barrier req'd 204 if (cap->run_queue_hd == END_TSO_QUEUE) { 205 cap->run_queue_tl = END_TSO_QUEUE; 213 cap->run_count--; 214 if (cap->promoted_run_queue_hd == END_TSO_QUEUE) { 215 StgTSO *candidate = RB_HEAD_GET(cap->run_active); 216 if (candidate == END_TSO_QUEUE) { 217 barf("popRunQueue: queue is empty"); 218 } 219 if ( // unconditional fastpath (no-rb tree) 220 (candidate->_link != END_TSO_QUEUE) || 221 // fastpath for a queue that just got emptied 222 (cap->run_rbtree == cap->run_active && RB_RIGHT_GET(cap->run_active) == RB_NULL) 223 ) { 224 RB_HEAD_SET(cap, cap->run_active, candidate->_link); 225 if (candidate->_link == END_TSO_QUEUE) { 226 RB_TAIL_SET(cap, cap->run_active, END_TSO_QUEUE); 227 } 228 candidate->_link = END_TSO_QUEUE; 229 return candidate; 230 } 231 StgTSO *t = rbRemoveMin(cap, &cap->run_rbtree); 232 IF_DEBUG(sanity, checkRBTree(cap->run_rbtree, 1, 0, -1)); 233 // XXX maybe this can be folded into removeMin 234 cap->run_active = rbFirst(cap->run_rbtree); 235 if (RB_HEAD_GET(cap->run_active) != END_TSO_QUEUE) { 236 StgWord64 npass = RB_HEAD_GET(cap->run_active)->ss_pass; 237 if (npass > cap->ss_pass) { 238 cap->ss_pass = npass; 239 } 240 } 241 return t; 242 } else { 243 StgTSO *t = cap->promoted_run_queue_hd; 244 cap->promoted_run_queue_hd = t->_link; 245 t->_link = END_TSO_QUEUE; // no write barrier req'd 246 return t; 206 247 } 207 return t;208 248 } 209 249 210 250 INLINE_HEADER StgTSO * 211 251 peekRunQueue (Capability *cap) 212 252 { 213 return cap->run_queue_hd; 253 if (cap->promoted_run_queue_hd == END_TSO_QUEUE) { 254 return RB_HEAD_GET(cap->run_active); 255 } else { 256 return cap->promoted_run_queue_hd; 257 } 214 258 } 215 259 216 260 EXTERN_INLINE void 217 261 leaveRunQueue (Capability *cap, StgTSO *tso); 218 262 219 263 EXTERN_INLINE void 220 leaveRunQueue (Capability *cap , StgTSO *tso)264 leaveRunQueue (Capability *cap STG_UNUSED, StgTSO *tso STG_UNUSED) 221 265 { 222 266 // XXX implement me 223 267 } … … 233 277 { 234 278 ASSERT(tso->_link == END_TSO_QUEUE); 235 279 if (blocked_queue_hd == END_TSO_QUEUE) { 236 blocked_queue_hd = tso;280 blocked_queue_hd = tso; 237 281 } else { 238 setTSOLink(&MainCapability, blocked_queue_tl, tso);282 setTSOLink(&MainCapability, blocked_queue_tl, tso); 239 283 } 240 284 blocked_queue_tl = tso; 241 285 } … … 252 296 INLINE_HEADER rtsBool 253 297 emptyRunQueue(Capability *cap) 254 298 { 255 return emptyQueue(cap->run_queue_hd);299 return cap->run_count == 0; 256 300 } 257 301 258 /* assumes that the queue is not empty; so combine this with259 * an emptyRunQueue check! */260 302 INLINE_HEADER rtsBool 261 303 singletonRunQueue(Capability *cap) 262 304 { 263 ASSERT(!emptyRunQueue(cap)); 264 return cap->run_queue_hd->_link == END_TSO_QUEUE; 305 return cap->run_count == 1; 265 306 } 266 307 267 308 INLINE_HEADER void 268 309 truncateRunQueue(Capability *cap) 269 310 { 270 cap->run_queue_hd = END_TSO_QUEUE; 271 cap->run_queue_tl = END_TSO_QUEUE; 311 cap->promoted_run_queue_hd = END_TSO_QUEUE; 312 // XXX leak 313 cap->run_rbtree = cap->run_active = rbCreateNode(cap, END_TSO_QUEUE); 314 cap->run_count = 0; 272 315 } 273 316 274 317 #if !defined(THREADED_RTS) -
rts/StgMiscClosures.cmm
diff --git a/rts/StgMiscClosures.cmm b/rts/StgMiscClosures.cmm index 4341013..bb33229 100644
a b 546 546 547 547 CLOSURE(stg_END_TSO_QUEUE_closure,stg_END_TSO_QUEUE); 548 548 549 INFO_TABLE_CONSTR(stg_RB_NULL,0,0,0,CONSTR_NOCAF_STATIC,"RB_NULL","RB_NULL") 550 { foreign "C" barf("RB_NULL object entered!") never returns; } 551 552 CLOSURE(stg_RB_NULL_closure,stg_RB_NULL); 553 549 554 /* ---------------------------------------------------------------------------- 550 555 Arrays 551 556 -
rts/Threads.c
diff --git a/rts/Threads.c b/rts/Threads.c index 758d368..5701e4b 100644
a b 113 113 tso->trec = NO_TREC; 114 114 115 115 tso->ss_tickets = DEFAULT_TICKETS; 116 tso->ss_pass = cap->ss_pass; 116 117 117 118 #ifdef PROFILING 118 119 tso->prof.cccs = CCS_MAIN; … … 844 845 for (i = 0; i < n_capabilities; i++) { 845 846 cap = &capabilities[i]; 846 847 debugBelch("threads on capability %d:\n", cap->no); 848 /* 847 849 for (t = cap->run_queue_hd; t != END_TSO_QUEUE; t = t->_link) { 848 850 printThreadStatus(t); 849 851 } 852 */ 850 853 } 851 854 852 855 debugBelch("other threads:\n"); -
rts/sm/Sanity.c
diff --git a/rts/sm/Sanity.c b/rts/sm/Sanity.c index f0e1659..fbc0971 100644
a b 787 787 } 788 788 789 789 void 790 checkRunQueue(Capability *cap )790 checkRunQueue(Capability *cap STG_UNUSED) 791 791 { 792 /* 792 793 StgTSO *prev, *tso; 793 794 prev = END_TSO_QUEUE; 794 795 for (tso = cap->run_queue_hd; tso != END_TSO_QUEUE; … … 797 798 ASSERT(tso->block_info.prev == prev); 798 799 } 799 800 ASSERT(cap->run_queue_tl == prev); 801 */ 800 802 } 801 803 802 804 /* -----------------------------------------------------------------------------
