1 /* 2 * 3 * Thread-safe Skiplist Using Integer Identifiers 4 * Copyright 1998-2000 Scott Shambarger (scott (at) shambarger.net) 5 * 6 * This software is open source. Permission to use, copy, modify, and 7 * distribute this software for any purpose and without fee is hereby granted, 8 * provided that the above copyright notice appear in all copies. No 9 * warranty of any kind is expressed or implied. Use at your own risk. 10 * 11 * 1/14/2001 blong 12 * Made it use neo errs... probably need to check locking functions 13 * for error returns... 14 * 15 */ 16 17 #include "cs_config.h" 18 19 #include <stdlib.h> 20 #include <assert.h> 21 #include <string.h> 22 23 #include "neo_misc.h" 24 #include "neo_err.h" 25 #include "skiplist.h" 26 #include "ulocks.h" 27 28 typedef struct skipItem *skipItem; 29 30 /* structure is sized on allocation based on its level */ 31 struct skipItem { 32 UINT32 locks; /* count of locks on value */ 33 UINT32 key; /* item's key */ 34 void *value; /* item's value */ 35 INT32 level; /* item level */ 36 skipItem next[1]; /* array of next items */ 37 }; 38 39 #define SIZEOFITEM(max) (sizeof(struct skipItem) + \ 40 ((max+1) * sizeof(skipItem))) 41 42 struct skipList_struct { 43 INT32 topLevel; /* current max level in any item */ 44 INT32 levelHint; /* hint at level to start search */ 45 skipItem header; /* header item (has all levels) */ 46 skipItem tail; /* tail item (has all levels) */ 47 48 /* elements to handle cached deleted items */ 49 skipItem deleted; /* cached deleted items (linked by level+1 next entries) */ 50 UINT32 cached; /* number of cached deleted items */ 51 52 int flushing; /* TRUE if thread waiting to flush cached items */ 53 UINT32 readers; /* number of current readers */ 54 int block; /* TRUE if readers should wait */ 55 56 pthread_mutex_t read; /* readers count/cond wait mutex */ 57 pthread_mutex_t write; /* writer mutex */ 58 pthread_cond_t resume; /* condition to wait on to resume reads */ 59 pthread_cond_t flush; /* condition to wait on for flush */ 60 61 /* list constants */ 62 int threaded; /* TRUE if list needs to be thread safe */ 63 UINT32 flushLimit; /* max number of cached deleted items before flush */ 64 INT32 maxLevel; /* max level list can reach */ 65 double randLimit; /* min random value to jump levels */ 66 skipFreeValue freeValue; /* free value callback */ 67 void *freeValueCtx; /* context to pass to <freeValue> callback */ 68 }; 69 70 static void readLock(skipList list) { 71 72 mLock(&list->read); 73 74 if(list->block) 75 cWait(&list->resume, &list->read); 76 77 list->readers++; 78 79 mUnlock(&list->read); 80 81 return; 82 } 83 84 static void readUnlock(skipList list, skipItem x, void **plock) { 85 86 int startFlush = FALSE; 87 88 if(list->threaded) 89 mLock(&list->read); 90 91 if(plock) { 92 x->locks++; 93 *plock = x; 94 } 95 96 if(! list->threaded) 97 return; 98 99 list->readers--; 100 101 if((list->readers == 0) && list->block) 102 startFlush = TRUE; 103 104 mUnlock(&list->read); 105 106 if(startFlush) 107 cSignal(&list->flush); 108 109 return; 110 } 111 112 static void readBlock(skipList list) { 113 114 mLock(&list->read); 115 116 list->block = TRUE; 117 118 if(list->readers) 119 cWait(&list->flush, &list->read); /* wait until reader locks released */ 120 121 return; 122 } 123 124 static void readUnblock(skipList list) { 125 126 list->block = FALSE; 127 128 mUnlock(&list->read); 129 130 cBroadcast(&list->resume); 131 132 return; 133 } 134 135 136 static void writeLock(skipList list) { 137 138 mLock(&list->write); 139 140 return; 141 } 142 143 static void writeUnlock(skipList list) { 144 145 mUnlock(&list->write); 146 147 return; 148 } 149 150 static NEOERR *skipAllocItem(skipItem *item, UINT32 level, UINT32 key, 151 void *value) 152 { 153 154 if(! (*item = malloc(SIZEOFITEM(level)))) 155 return nerr_raise(NERR_NOMEM, "Unable to allocate space for skipItem"); 156 157 /* init new item */ 158 (*item)->locks = 0; 159 (*item)->key = key; 160 (*item)->value = value; 161 (*item)->level = level; 162 163 return STATUS_OK; 164 } 165 166 static void skipFreeItem(skipList list, skipItem item) { 167 168 if(list->freeValue) 169 list->freeValue(item->value, list->freeValueCtx); /* free value */ 170 171 free(item); /* release item */ 172 173 return; 174 } 175 176 static void skipFlushDeleted(skipList list, int force) { 177 178 skipItem x, y, next; 179 180 x = list->deleted; 181 y = x->next[x->level + 1]; 182 183 while(y != list->tail) { 184 185 next = y->next[y->level + 1]; 186 187 if(force || (! y->locks)) { /* check if value currently locked */ 188 189 x->next[x->level + 1] = next; /* set previous item's next link */ 190 skipFreeItem(list, y); /* free item */ 191 192 list->cached--; /* update cached count */ 193 } 194 else { 195 x = y; /* make this item the previous item */ 196 } 197 198 y = next; /* advance to next item */ 199 } 200 201 return; 202 } 203 204 static void skipWriteUnlock(skipList list) { 205 206 int flush; 207 208 if(! list->threaded) 209 return; 210 211 if((list->cached > list->flushLimit) && (! list->flushing)) { 212 list->flushing = TRUE; 213 flush = TRUE; 214 } 215 else { 216 flush = FALSE; 217 } 218 219 writeUnlock(list); /* let any pending writes complete */ 220 readUnlock(list, NULL, NULL); /* no longer reading */ 221 222 if(flush) { 223 /* we are now flushing deleted items */ 224 225 readBlock(list); /* acquire all read locks */ 226 227 /* at this point no readers/writers are active */ 228 229 skipFlushDeleted(list, FALSE); /* flush deleted items */ 230 231 list->flushing = FALSE; /* done flushing */ 232 233 readUnblock(list); /* let everyone continue */ 234 } 235 236 return; 237 } 238 239 static skipItem skipFind(skipList list, UINT32 key) { 240 241 skipItem x, y = NULL; 242 INT32 i; 243 244 if(list->threaded) 245 readLock(list); 246 247 x = list->header; /* header contains all levels */ 248 249 for(i = list->levelHint; /* loop from levelHint level down to level 0 */ 250 i >= 0; 251 i--) { 252 253 y = x->next[i]; /* get next item at new level */ 254 255 while(y->key < key) { /* if y has a smaller key, try the next item */ 256 x = y; /* save x in case we overshoot */ 257 y = x->next[i]; /* get next item */ 258 } 259 } 260 261 return y; 262 } 263 264 void *skipSearch(skipList list, UINT32 key, void **plock) { 265 266 skipItem y; 267 void *value; 268 269 y = skipFind(list, key); /* find item */ 270 271 if(y->key == key) { /* y has our key, or it isn't here */ 272 value = y->value; 273 } 274 else { /* didn't find item, don't allow locks */ 275 value = NULL; 276 plock = NULL; 277 } 278 279 readUnlock(list, y, plock); 280 281 return value; 282 } 283 284 void *skipNext(skipList list, UINT32 *pkey, void **plock) { 285 286 skipItem y; 287 void *value; 288 289 y = skipFind(list, *pkey); /* find item */ 290 291 if((y->key == *pkey) && (y != list->tail)) /* skip to next if found y */ 292 y = y->next[0]; 293 294 if(y != list->tail) { /* reset key to next, return value */ 295 *pkey = y->key; 296 value = y->value; 297 } 298 else { /* no next item, don't allow locks */ 299 value = NULL; 300 plock = NULL; 301 } 302 303 readUnlock(list, y, plock); 304 305 return value; 306 } 307 308 void skipRelease(skipList list, void *lock) { 309 310 skipItem x; 311 312 mLock(&list->read); 313 314 x = lock; 315 x->locks--; 316 317 mUnlock(&list->read); 318 319 return; 320 } 321 322 /* list is write locked */ 323 static NEOERR *skipNewItem(skipList list, skipItem *item, UINT32 key, 324 void *value) 325 { 326 327 INT32 level = 0; 328 329 while((drand48() < list->randLimit) && (level < list->maxLevel)) 330 level++; 331 332 if(level > list->topLevel) { 333 334 if(list->topLevel < list->maxLevel) 335 list->topLevel++; 336 337 level = list->topLevel; 338 } 339 340 return skipAllocItem(item, level, key, value); 341 } 342 343 /* list is write locked */ 344 static void skipDeleteItem(skipList list, skipItem item) { 345 346 if(list->threaded) { 347 item->next[item->level + 1] = list->deleted->next[1]; 348 list->cached++; 349 list->deleted->next[1] = item; 350 } 351 else { 352 skipFreeItem(list, item); 353 } 354 355 return; 356 } 357 358 NEOERR *skipNewList(skipList *skip, int threaded, int root, int maxLevel, 359 int flushLimit, skipFreeValue freeValue, void *ctx) 360 { 361 NEOERR *err; 362 skipList list; 363 UINT32 i; 364 365 *skip = NULL; 366 if(! (list = calloc(1, sizeof(struct skipList_struct)))) 367 return nerr_raise(NERR_NOMEM, "Unable to allocate memore for skiplist"); 368 369 if (maxLevel == 0) 370 return nerr_raise(NERR_ASSERT, "maxLevel must be greater than 0"); 371 372 if(maxLevel >= SKIP_MAXLEVEL) /* check limits */ 373 maxLevel = SKIP_MAXLEVEL-1; 374 375 if(root > 4) 376 root = 4; 377 else if(root < 2) 378 root = 2; 379 380 list->maxLevel = maxLevel; /* init list constants */ 381 list->randLimit = 1.0 / (double)root; 382 list->threaded = threaded; 383 list->freeValue = freeValue; 384 list->freeValueCtx = ctx; 385 386 do { 387 if(threaded) { 388 389 list->flushLimit = flushLimit; 390 391 err = mCreate(&list->read); 392 if (err != STATUS_OK) break; 393 394 err = mCreate(&list->write); 395 if (err != STATUS_OK) break; 396 397 err = cCreate(&list->resume); 398 if (err != STATUS_OK) break; 399 400 err = cCreate(&list->flush); 401 if (err != STATUS_OK) break; 402 } 403 404 err = skipAllocItem(&(list->header), list->maxLevel, 0, NULL); 405 if (err != STATUS_OK) break; 406 err = skipAllocItem(&(list->tail), list->maxLevel, (UINT32)-1, NULL); 407 if (err != STATUS_OK) break; 408 err = skipAllocItem(&(list->deleted), 0, 0, NULL); 409 if (err != STATUS_OK) break; 410 411 for(i = 0; /* init header and tail */ 412 i <= list->maxLevel; 413 i++) { 414 list->tail->next[i] = NULL; 415 list->header->next[i] = list->tail; 416 } 417 418 list->deleted->next[1] = list->tail; 419 420 *skip = list; 421 return STATUS_OK; /* return new list */ 422 423 } while(FALSE); 424 425 if(list->header) /* failed to make list, bail */ 426 free(list->header); 427 free(list); 428 429 return nerr_pass(err); 430 } 431 432 /* list considered locked */ 433 static void skipFreeAllItems(skipList list) { 434 435 UINT32 i; 436 skipItem x, y; 437 438 x = list->header->next[0]; 439 440 while(x != list->tail) { 441 y = x->next[0]; /* get next item from level 0 pointer */ 442 skipFreeItem(list, x); /* release item */ 443 x = y; 444 } 445 /* clear header pointers */ 446 for(i = 0; 447 i <= list->maxLevel; 448 i++) 449 list->header->next[i] = list->tail; 450 451 return; 452 } 453 454 void skipFreeList(skipList list) { 455 456 skipFlushDeleted(list, TRUE); /* flush deleted items */ 457 458 skipFreeAllItems(list); /* free list items */ 459 460 if(list->threaded) { 461 cDestroy(&list->flush); 462 cDestroy(&list->resume); 463 mDestroy(&list->write); 464 mDestroy(&list->read); 465 } 466 467 free(list->tail); /* free list */ 468 free(list->header); 469 free(list->deleted); 470 free(list); 471 472 return; 473 } 474 475 /* <list> is locked, <x> is at least level <level>, and <x>->key < <key> */ 476 static skipItem skipClosest(skipItem x, UINT32 key, UINT32 level) { 477 478 skipItem y; 479 480 y = x->next[level]; /* get next item at this level */ 481 482 while(y->key < key) { /* ensure that we have the item before the key */ 483 x = y; 484 y = x->next[level]; 485 } 486 487 return x; 488 } 489 490 static skipItem skipLock(skipList list, UINT32 key, skipItem *save, INT32 top) { 491 492 INT32 i; 493 skipItem x, y; 494 495 if(list->threaded) 496 readLock(list); 497 498 x = list->header; /* header contains all levels */ 499 500 for(i = top; /* loop from top level down to level 0 */ 501 i >= 0; 502 i--) { 503 504 y = x->next[i]; /* get next item at this level */ 505 506 while(y->key < key) { /* if y has a smaller key, try the next item */ 507 x = y; /* save x in case we overshoot */ 508 y = x->next[i]; /* get next item */ 509 } 510 511 save[i] = x; /* preserve item with next pointer in save */ 512 } 513 514 if(list->threaded) 515 writeLock(list); /* lock list for update */ 516 517 /* validate we have the closest previous item */ 518 return skipClosest(x, key, 0); 519 } 520 521 NEOERR *skipInsert(skipList list, UINT32 key, void *value, int allowUpdate) 522 { 523 NEOERR *err; 524 INT32 i, level; 525 skipItem save[SKIP_MAXLEVEL]; 526 skipItem x, y; 527 528 if (value == 0) 529 return nerr_raise(NERR_ASSERT, "value must be non-zero"); 530 if (key == 0 || key == (UINT32)-1) 531 return nerr_raise(NERR_ASSERT, "key must not be 0 or -1"); 532 533 level = list->levelHint; 534 535 x = skipLock(list, key, save, level); /* quick search for key */ 536 537 y = x->next[0]; 538 539 if(y->key == key) { 540 541 if(!allowUpdate) 542 { 543 skipWriteUnlock(list); 544 return nerr_raise(NERR_DUPLICATE, "key %u exists in skiplist", key); 545 } 546 547 y->value = value; /* found the key, update value */ 548 skipWriteUnlock(list); 549 return STATUS_OK; 550 } 551 552 err = skipNewItem(list, &y, key, value); 553 if (err != STATUS_OK) 554 { 555 skipWriteUnlock(list); 556 return nerr_pass(err); 557 } 558 559 for(i = level + 1; /* is new item has more levels than <level> */ 560 i <= y->level; /* if so fill in save */ 561 i++) 562 save[i] = list->header; 563 564 for(i = 0; /* populate pointers for all levels */ 565 i <= y->level; 566 i++) { 567 568 if(i) /* check that save is correct for each level */ 569 x = skipClosest(save[i], key, i); 570 571 y->next[i] = x->next[i]; /* now insert the item at this level */ 572 x->next[i] = y; /* (order here important for thread-safeness) */ 573 } 574 575 while((list->levelHint < list->topLevel) /* update levelHint */ 576 && (list->header->next[list->levelHint+1] != list->tail)) 577 list->levelHint++; 578 579 skipWriteUnlock(list); 580 581 return STATUS_OK; 582 } 583 584 void skipDelete(skipList list, UINT32 key) { 585 586 INT32 i, level; 587 skipItem save[SKIP_MAXLEVEL]; 588 skipItem x, y; 589 590 assert(key && (key != (UINT32)-1)); 591 592 level = list->levelHint; 593 594 x = skipLock(list, key, save, level); /* quick search for key */ 595 596 y = x->next[0]; 597 598 /* check that we found the key, and it isn't deleted */ 599 if((y->key != key) || (y->next[0]->key < key)) { 600 skipWriteUnlock(list); 601 return; 602 } 603 604 for(i = level + 1; /* check if item has more levels than <level> */ 605 i <= y->level; /* if so fill in save */ 606 i++) 607 save[i] = list->header; 608 609 for(i = y->level; 610 i >= 0; 611 i--) { 612 613 /* check that save is correct for each level */ 614 x = skipClosest(save[i], key, i); 615 616 x->next[i] = y->next[i]; /* now remove item at this level */ 617 y->next[i] = x; /* (order here is imported for thread-safeness) */ 618 } 619 620 skipDeleteItem(list, y); /* put on deleted list */ 621 622 while((list->levelHint > 0) /* update levelHint */ 623 && (list->header->next[list->levelHint] == list->tail)) 624 list->levelHint--; 625 626 skipWriteUnlock(list); 627 628 return; 629 } 630 631 632 633