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