• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Thread-safe Skiplist Using Integer Identifiers
4  * Copyright 1998-2000 Scott Shambarger (scott@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 
readLock(skipList list)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 
readUnlock(skipList list,skipItem x,void ** plock)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 
readBlock(skipList list)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 
readUnblock(skipList list)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 
writeLock(skipList list)136 static void writeLock(skipList list) {
137 
138   mLock(&list->write);
139 
140   return;
141 }
142 
writeUnlock(skipList list)143 static void writeUnlock(skipList list) {
144 
145   mUnlock(&list->write);
146 
147   return;
148 }
149 
skipAllocItem(skipItem * item,UINT32 level,UINT32 key,void * value)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 
skipFreeItem(skipList list,skipItem item)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 
skipFlushDeleted(skipList list,int force)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 
skipWriteUnlock(skipList list)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 
skipFind(skipList list,UINT32 key)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 
skipSearch(skipList list,UINT32 key,void ** plock)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 
skipNext(skipList list,UINT32 * pkey,void ** plock)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 
skipRelease(skipList list,void * lock)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 */
skipNewItem(skipList list,skipItem * item,UINT32 key,void * value)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 */
skipDeleteItem(skipList list,skipItem item)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 
skipNewList(skipList * skip,int threaded,int root,int maxLevel,int flushLimit,skipFreeValue freeValue,void * ctx)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 */
skipFreeAllItems(skipList list)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 
skipFreeList(skipList list)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> */
skipClosest(skipItem x,UINT32 key,UINT32 level)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 
skipLock(skipList list,UINT32 key,skipItem * save,INT32 top)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 
skipInsert(skipList list,UINT32 key,void * value,int allowUpdate)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 
skipDelete(skipList list,UINT32 key)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