Home | History | Annotate | Download | only in util
      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