1 /******************************************************************************
2 * skip_lock.c (Variable-granularity Mutexes)
4 * Mutex only taken for write operations (reads are unprotected). Write
5 * mutexes come in three flavours, selected by a compile-time flag.
7 * If FAT_MTX is defined:
8 * A skip list is protected by one mutex for the entire list. Note that this
9 * differs from skip_bm.c, which takes the mutex for read operations as well.
11 * If TINY_MTX is defined:
12 * Mutex per forward pointer in each node.
14 * If neither flag is defined:
17 * Copyright (c) 2001-2003, K A Fraser
19 Redistribution and use in source and binary forms, with or without
20 modification, are permitted provided that the following conditions are
23 * Redistributions of source code must retain the above copyright
24 * notice, this list of conditions and the following disclaimer.
25 * Redistributions in binary form must reproduce the above
26 * copyright notice, this list of conditions and the following
27 * disclaimer in the documentation and/or other materials provided
28 * with the distribution. Neither the name of the Keir Fraser
29 * nor the names of its contributors may be used to endorse or
30 * promote products derived from this software without specific
31 * prior written permission.
33 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
34 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
35 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
36 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
37 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
40 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
41 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
43 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 #define __SET_IMPLEMENTATION__
51 #include "portable_defns.h"
60 typedef struct node_st node_t
;
61 typedef struct set_st set_t
;
62 typedef VOLATILE node_t
*sh_node_pt
;
64 typedef struct ptr_st ptr_t
;
67 #ifdef TINY_MTX /* mutex per forward pointer */
92 static int gc_id
[NUM_LEVELS
];
100 #define LIST_LOCK(_l,_qn) ((void)mcs_lock((void*)&(_l)->m, (_qn)))
101 #define LIST_UNLOCK(_l,_qn) ((void)mcs_unlock((void*)&(_l)->m, (_qn)))
102 #define NODE_LOCK(_x,_qn) ((void)0)
103 #define NODE_UNLOCK(_x,_qn) ((void)0)
104 #define PTR_UPDATE_LOCK(_x,_i,_qn) ((void)0)
105 #define PTR_UPDATE_UNLOCK(_x,_i,_qn) ((void)0)
106 #define PTR_DELETE_LOCK(_x,_i,_qn) ((void)0)
107 #define PTR_DELETE_UNLOCK(_x,_i,_qn) ((void)0)
111 #define LIST_LOCK(_l,_qn) ((void)0)
112 #define LIST_UNLOCK(_l,_qn) ((void)0)
114 /* We take the main node lock to get exclusive rights on insert/delete ops. */
115 #define NODE_LOCK(_x,_qn) ((void)mcs_lock((void*)&(_x)->m, (_qn)))
116 #define NODE_UNLOCK(_x,_qn) ((void)mcs_unlock((void*)&(_x)->m, (_qn)))
121 * Predecessor's pointer is locked before swinging (on delete), or
122 * replumbing (on insert).
124 #define PTR_UPDATE_LOCK(_x, _i, _qn) \
125 ((void)mcs_lock((void*)&(_x)->next[(_i)].m, (_qn)))
126 #define PTR_UPDATE_UNLOCK(_x, _i, _qn) \
127 ((void)mcs_unlock((void*)&(_x)->next[(_i)].m, (_qn)))
129 * When deleting a node, we take the lock on each of its pointers in turn,
130 * to prevent someone from inserting a new node directly after, or deleting
131 * immediate successor.
133 #define PTR_DELETE_LOCK(_x, _i, _qn) PTR_UPDATE_LOCK(_x,_i,(_qn))
134 #define PTR_DELETE_UNLOCK(_x, _i, _qn) PTR_UPDATE_UNLOCK(_x,_i,(_qn))
136 #else /* LITTLE_MTX */
139 * Predecessor must certainly be locked for insert/delete ops. So we take
140 * the only lock we can.
142 #define PTR_UPDATE_LOCK(_x, _i, _qn) NODE_LOCK(_x,(_qn))
143 #define PTR_UPDATE_UNLOCK(_x, _i, _qn) NODE_UNLOCK(_x,(_qn))
145 * We can't lock individual pointers. There's no need anyway, since we have
146 * the node's lock already (to allow us exclusive delete rights).
148 #define PTR_DELETE_LOCK(_x, _i, _qn) ((void)0)
149 #define PTR_DELETE_UNLOCK(_x, _i, _qn) ((void)0)
161 * Random level generator. Drop-off rate is 0.5 per level.
162 * Returns value 1 <= level <= NUM_LEVELS.
164 static int get_level(ptst_t
*ptst
)
166 unsigned long r
= rand_next(ptst
);
168 r
= (r
>> 4) & ((1 << (NUM_LEVELS
-1)) - 1);
169 while ( (r
& 1) ) { l
++; r
>>= 1; }
175 * Allocate a new node, and initialise its @level field.
176 * NB. Initialisation will eventually be pushed into garbage collector,
177 * because of dependent read reordering.
179 static node_t
*alloc_node(ptst_t
*ptst
)
184 n
= gc_alloc(ptst
, gc_id
[l
- 1]);
190 for ( l
= 0; l
< n
->level
; l
++ )
192 mcs_init(&n
->next
[l
].m
);
199 /* Free a node to the garbage collector. */
200 static void free_node(ptst_t
*ptst
, sh_node_pt n
)
202 gc_free(ptst
, (void *)n
, gc_id
[n
->level
- 1]);
207 * Find and lock predecessor at level @i of node with key @k. This
208 * predecessor must have key >= @x->k.
211 static sh_node_pt
get_lock(sh_node_pt x
, setkey_t k
, int i
, qnode_t
*qn
)
218 READ_FIELD(y
, x
->next
[i
].p
);
219 READ_FIELD(y_k
, y
->k
);
220 if ( y_k
>= k
) break;
225 PTR_UPDATE_LOCK(x
, i
, qn
); /* MB => no need for READ_FIELD on x or y. */
229 PTR_UPDATE_UNLOCK(x
, i
, qn
);
236 #define get_lock(_x,_k,_i,_qn) (_x)
241 * Search for first non-deleted node, N, with key >= @k at each level in @l.
243 * Array @pa: @pa[i] is non-deleted predecessor of N at level i
244 * MAIN RETURN VALUE: N at level 0.
246 static sh_node_pt
search_predecessors(set_t
*l
, setkey_t k
, sh_node_pt
*pa
)
253 for ( i
= NUM_LEVELS
- 1; i
>= 0; i
-- )
257 READ_FIELD(y
, x
->next
[i
].p
);
258 READ_FIELD(y_k
, y
->k
);
259 if ( y_k
>= k
) break;
260 x
= y
; /* remember largest predecessor so far */
274 set_t
*set_alloc(void)
280 n
= malloc(sizeof(*n
) + (NUM_LEVELS
-1)*sizeof(ptr_t
));
281 memset(n
, 0, sizeof(*n
) + (NUM_LEVELS
-1)*sizeof(ptr_t
));
282 n
->k
= SENTINEL_KEYMAX
;
284 l
= malloc(sizeof(*l
) + (NUM_LEVELS
-1)*sizeof(ptr_t
));
285 l
->head
.k
= SENTINEL_KEYMIN
;
286 l
->head
.level
= NUM_LEVELS
;
290 mcs_init(&l
->head
.m
);
292 for ( i
= 0; i
< NUM_LEVELS
; i
++ )
294 l
->head
.next
[i
].p
= n
;
296 mcs_init(&l
->head
.next
[i
].m
);
304 setval_t
set_update(set_t
*l
, setkey_t k
, setval_t v
, int overwrite
)
308 sh_node_pt update
[NUM_LEVELS
];
311 qnode_t l_qn
, x_qn
, y_qn
;
313 k
= CALLER_TO_INTERNAL_KEY(k
);
315 ptst
= critical_enter();
318 (void)search_predecessors(l
, k
, update
);
320 x
= get_lock(update
[0], k
, 0, &x_qn
);
325 if ( overwrite
) y
->v
= v
;
326 PTR_UPDATE_UNLOCK(x
, 0, &x_qn
);
330 /* Not in the list, so do the insertion. */
331 y
= alloc_node(ptst
);
336 for ( i
= 0; i
< y
->level
; i
++ )
338 if ( i
!= 0 ) x
= get_lock(update
[i
], k
, i
, &x_qn
);
339 y
->next
[i
].p
= x
->next
[i
].p
;
342 PTR_UPDATE_UNLOCK(x
, i
, &x_qn
);
345 NODE_UNLOCK(y
, &y_qn
);
348 LIST_UNLOCK(l
, &l_qn
);
354 setval_t
set_remove(set_t
*l
, setkey_t k
)
358 sh_node_pt update
[NUM_LEVELS
];
361 qnode_t l_qn
, x_qn
, y_qn
, yd_qn
;
363 k
= CALLER_TO_INTERNAL_KEY(k
);
365 ptst
= critical_enter();
368 y
= search_predecessors(l
, k
, update
);
371 if ( y
->k
!= k
) goto out
;
377 y
= y
->next
[0].p
; /* no need for READ_FIELD() */
378 READ_FIELD(y_k
, y
->k
);
379 if ( y_k
> k
) goto out
;
381 if ( (y_k
== k
) && (y_k
<= y
->next
[0].p
->k
) ) break;
382 NODE_UNLOCK(y
, &y_qn
);
386 /* @y is the correct node, and we have it locked, so now delete it. */
387 for ( i
= y
->level
- 1; i
>= 0; i
-- )
389 x
= get_lock(update
[i
], k
, i
, &x_qn
);
390 PTR_DELETE_LOCK(y
, i
, &yd_qn
);
391 x
->next
[i
].p
= y
->next
[i
].p
;
394 PTR_DELETE_UNLOCK(y
, i
, &yd_qn
);
395 PTR_UPDATE_UNLOCK(x
, i
, &x_qn
);
400 NODE_UNLOCK(y
, &y_qn
);
403 LIST_UNLOCK(l
, &l_qn
);
409 setval_t
set_lookup(set_t
*l
, setkey_t k
)
415 k
= CALLER_TO_INTERNAL_KEY(k
);
417 ptst
= critical_enter();
419 x
= search_predecessors(l
, k
, NULL
);
420 if ( x
->k
== k
) READ_FIELD(v
, x
->v
);
427 void _init_set_subsystem(void)
431 for ( i
= 0; i
< NUM_LEVELS
; i
++ )
433 gc_id
[i
] = gc_add_allocator(sizeof(node_t
) + i
*sizeof(ptr_t
));