1*1fb62fb0SOlivier Houchard /*
2*1fb62fb0SOlivier Houchard * Copyright 2010-2015 Samy Al Bahra.
3*1fb62fb0SOlivier Houchard * All rights reserved.
4*1fb62fb0SOlivier Houchard *
5*1fb62fb0SOlivier Houchard * Redistribution and use in source and binary forms, with or without
6*1fb62fb0SOlivier Houchard * modification, are permitted provided that the following conditions
7*1fb62fb0SOlivier Houchard * are met:
8*1fb62fb0SOlivier Houchard * 1. Redistributions of source code must retain the above copyright
9*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer.
10*1fb62fb0SOlivier Houchard * 2. Redistributions in binary form must reproduce the above copyright
11*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer in the
12*1fb62fb0SOlivier Houchard * documentation and/or other materials provided with the distribution.
13*1fb62fb0SOlivier Houchard *
14*1fb62fb0SOlivier Houchard * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*1fb62fb0SOlivier Houchard * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*1fb62fb0SOlivier Houchard * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*1fb62fb0SOlivier Houchard * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*1fb62fb0SOlivier Houchard * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*1fb62fb0SOlivier Houchard * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*1fb62fb0SOlivier Houchard * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*1fb62fb0SOlivier Houchard * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*1fb62fb0SOlivier Houchard * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*1fb62fb0SOlivier Houchard * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*1fb62fb0SOlivier Houchard * SUCH DAMAGE.
25*1fb62fb0SOlivier Houchard */
26*1fb62fb0SOlivier Houchard
27*1fb62fb0SOlivier Houchard /*
28*1fb62fb0SOlivier Houchard * (c) Copyright 2008, IBM Corporation.
29*1fb62fb0SOlivier Houchard * Licensed under the Apache License, Version 2.0 (the "License");
30*1fb62fb0SOlivier Houchard * you may not use this file except in compliance with the License.
31*1fb62fb0SOlivier Houchard * You may obtain a copy of the License at
32*1fb62fb0SOlivier Houchard *
33*1fb62fb0SOlivier Houchard * http://www.apache.org/licenses/LICENSE-2.0
34*1fb62fb0SOlivier Houchard *
35*1fb62fb0SOlivier Houchard * Unless required by applicable law or agreed to in writing, software
36*1fb62fb0SOlivier Houchard * distributed under the License is distributed on an "AS IS" BASIS,
37*1fb62fb0SOlivier Houchard * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38*1fb62fb0SOlivier Houchard * See the License for the specific language governing permissions and
39*1fb62fb0SOlivier Houchard * limitations under the License.
40*1fb62fb0SOlivier Houchard */
41*1fb62fb0SOlivier Houchard
42*1fb62fb0SOlivier Houchard /*
43*1fb62fb0SOlivier Houchard * This is an implementation of hazard pointers as detailed in:
44*1fb62fb0SOlivier Houchard * http://www.research.ibm.com/people/m/michael/ieeetpds-2004.pdf
45*1fb62fb0SOlivier Houchard *
46*1fb62fb0SOlivier Houchard * This API provides a publishing mechanism that defers destruction of
47*1fb62fb0SOlivier Houchard * hazard pointers until it is safe to do so. Preventing arbitrary re-use
48*1fb62fb0SOlivier Houchard * protects against the ABA problem and provides safe memory reclamation.
49*1fb62fb0SOlivier Houchard * The implementation was derived from the Hazard Pointers implementation
50*1fb62fb0SOlivier Houchard * from the Amino CBBS project. It has been heavily modified for Concurrency
51*1fb62fb0SOlivier Houchard * Kit.
52*1fb62fb0SOlivier Houchard */
53*1fb62fb0SOlivier Houchard
54*1fb62fb0SOlivier Houchard #include <ck_backoff.h>
55*1fb62fb0SOlivier Houchard #include <ck_cc.h>
56*1fb62fb0SOlivier Houchard #include <ck_hp.h>
57*1fb62fb0SOlivier Houchard #include <ck_pr.h>
58*1fb62fb0SOlivier Houchard #include <ck_stack.h>
59*1fb62fb0SOlivier Houchard #include <ck_stdbool.h>
60*1fb62fb0SOlivier Houchard #include <ck_stddef.h>
61*1fb62fb0SOlivier Houchard #include <ck_stdlib.h>
62*1fb62fb0SOlivier Houchard #include <ck_string.h>
63*1fb62fb0SOlivier Houchard
CK_STACK_CONTAINER(struct ck_hp_record,global_entry,ck_hp_record_container)64*1fb62fb0SOlivier Houchard CK_STACK_CONTAINER(struct ck_hp_record, global_entry, ck_hp_record_container)
65*1fb62fb0SOlivier Houchard CK_STACK_CONTAINER(struct ck_hp_hazard, pending_entry, ck_hp_hazard_container)
66*1fb62fb0SOlivier Houchard
67*1fb62fb0SOlivier Houchard void
68*1fb62fb0SOlivier Houchard ck_hp_init(struct ck_hp *state,
69*1fb62fb0SOlivier Houchard unsigned int degree,
70*1fb62fb0SOlivier Houchard unsigned int threshold,
71*1fb62fb0SOlivier Houchard ck_hp_destructor_t destroy)
72*1fb62fb0SOlivier Houchard {
73*1fb62fb0SOlivier Houchard
74*1fb62fb0SOlivier Houchard state->threshold = threshold;
75*1fb62fb0SOlivier Houchard state->degree = degree;
76*1fb62fb0SOlivier Houchard state->destroy = destroy;
77*1fb62fb0SOlivier Houchard state->n_subscribers = 0;
78*1fb62fb0SOlivier Houchard state->n_free = 0;
79*1fb62fb0SOlivier Houchard ck_stack_init(&state->subscribers);
80*1fb62fb0SOlivier Houchard ck_pr_fence_store();
81*1fb62fb0SOlivier Houchard
82*1fb62fb0SOlivier Houchard return;
83*1fb62fb0SOlivier Houchard }
84*1fb62fb0SOlivier Houchard
85*1fb62fb0SOlivier Houchard void
ck_hp_set_threshold(struct ck_hp * state,unsigned int threshold)86*1fb62fb0SOlivier Houchard ck_hp_set_threshold(struct ck_hp *state, unsigned int threshold)
87*1fb62fb0SOlivier Houchard {
88*1fb62fb0SOlivier Houchard
89*1fb62fb0SOlivier Houchard ck_pr_store_uint(&state->threshold, threshold);
90*1fb62fb0SOlivier Houchard return;
91*1fb62fb0SOlivier Houchard }
92*1fb62fb0SOlivier Houchard
93*1fb62fb0SOlivier Houchard struct ck_hp_record *
ck_hp_recycle(struct ck_hp * global)94*1fb62fb0SOlivier Houchard ck_hp_recycle(struct ck_hp *global)
95*1fb62fb0SOlivier Houchard {
96*1fb62fb0SOlivier Houchard struct ck_hp_record *record;
97*1fb62fb0SOlivier Houchard ck_stack_entry_t *entry;
98*1fb62fb0SOlivier Houchard int state;
99*1fb62fb0SOlivier Houchard
100*1fb62fb0SOlivier Houchard if (ck_pr_load_uint(&global->n_free) == 0)
101*1fb62fb0SOlivier Houchard return NULL;
102*1fb62fb0SOlivier Houchard
103*1fb62fb0SOlivier Houchard CK_STACK_FOREACH(&global->subscribers, entry) {
104*1fb62fb0SOlivier Houchard record = ck_hp_record_container(entry);
105*1fb62fb0SOlivier Houchard
106*1fb62fb0SOlivier Houchard if (ck_pr_load_int(&record->state) == CK_HP_FREE) {
107*1fb62fb0SOlivier Houchard ck_pr_fence_load();
108*1fb62fb0SOlivier Houchard state = ck_pr_fas_int(&record->state, CK_HP_USED);
109*1fb62fb0SOlivier Houchard if (state == CK_HP_FREE) {
110*1fb62fb0SOlivier Houchard ck_pr_dec_uint(&global->n_free);
111*1fb62fb0SOlivier Houchard return record;
112*1fb62fb0SOlivier Houchard }
113*1fb62fb0SOlivier Houchard }
114*1fb62fb0SOlivier Houchard }
115*1fb62fb0SOlivier Houchard
116*1fb62fb0SOlivier Houchard return NULL;
117*1fb62fb0SOlivier Houchard }
118*1fb62fb0SOlivier Houchard
119*1fb62fb0SOlivier Houchard void
ck_hp_unregister(struct ck_hp_record * entry)120*1fb62fb0SOlivier Houchard ck_hp_unregister(struct ck_hp_record *entry)
121*1fb62fb0SOlivier Houchard {
122*1fb62fb0SOlivier Houchard
123*1fb62fb0SOlivier Houchard entry->n_pending = 0;
124*1fb62fb0SOlivier Houchard entry->n_peak = 0;
125*1fb62fb0SOlivier Houchard entry->n_reclamations = 0;
126*1fb62fb0SOlivier Houchard ck_stack_init(&entry->pending);
127*1fb62fb0SOlivier Houchard ck_pr_fence_store();
128*1fb62fb0SOlivier Houchard ck_pr_store_int(&entry->state, CK_HP_FREE);
129*1fb62fb0SOlivier Houchard ck_pr_inc_uint(&entry->global->n_free);
130*1fb62fb0SOlivier Houchard return;
131*1fb62fb0SOlivier Houchard }
132*1fb62fb0SOlivier Houchard
133*1fb62fb0SOlivier Houchard void
ck_hp_register(struct ck_hp * state,struct ck_hp_record * entry,void ** pointers)134*1fb62fb0SOlivier Houchard ck_hp_register(struct ck_hp *state,
135*1fb62fb0SOlivier Houchard struct ck_hp_record *entry,
136*1fb62fb0SOlivier Houchard void **pointers)
137*1fb62fb0SOlivier Houchard {
138*1fb62fb0SOlivier Houchard
139*1fb62fb0SOlivier Houchard entry->state = CK_HP_USED;
140*1fb62fb0SOlivier Houchard entry->global = state;
141*1fb62fb0SOlivier Houchard entry->pointers = pointers;
142*1fb62fb0SOlivier Houchard entry->n_pending = 0;
143*1fb62fb0SOlivier Houchard entry->n_peak = 0;
144*1fb62fb0SOlivier Houchard entry->n_reclamations = 0;
145*1fb62fb0SOlivier Houchard memset(pointers, 0, state->degree * sizeof(void *));
146*1fb62fb0SOlivier Houchard ck_stack_init(&entry->pending);
147*1fb62fb0SOlivier Houchard ck_pr_fence_store();
148*1fb62fb0SOlivier Houchard ck_stack_push_upmc(&state->subscribers, &entry->global_entry);
149*1fb62fb0SOlivier Houchard ck_pr_inc_uint(&state->n_subscribers);
150*1fb62fb0SOlivier Houchard return;
151*1fb62fb0SOlivier Houchard }
152*1fb62fb0SOlivier Houchard
153*1fb62fb0SOlivier Houchard static int
hazard_compare(const void * a,const void * b)154*1fb62fb0SOlivier Houchard hazard_compare(const void *a, const void *b)
155*1fb62fb0SOlivier Houchard {
156*1fb62fb0SOlivier Houchard void * const *x;
157*1fb62fb0SOlivier Houchard void * const *y;
158*1fb62fb0SOlivier Houchard
159*1fb62fb0SOlivier Houchard x = a;
160*1fb62fb0SOlivier Houchard y = b;
161*1fb62fb0SOlivier Houchard return ((*x > *y) - (*x < *y));
162*1fb62fb0SOlivier Houchard }
163*1fb62fb0SOlivier Houchard
164*1fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_hp_member_scan(ck_stack_entry_t * entry,unsigned int degree,void * pointer)165*1fb62fb0SOlivier Houchard ck_hp_member_scan(ck_stack_entry_t *entry, unsigned int degree, void *pointer)
166*1fb62fb0SOlivier Houchard {
167*1fb62fb0SOlivier Houchard struct ck_hp_record *record;
168*1fb62fb0SOlivier Houchard unsigned int i;
169*1fb62fb0SOlivier Houchard void *hazard;
170*1fb62fb0SOlivier Houchard
171*1fb62fb0SOlivier Houchard do {
172*1fb62fb0SOlivier Houchard record = ck_hp_record_container(entry);
173*1fb62fb0SOlivier Houchard if (ck_pr_load_int(&record->state) == CK_HP_FREE)
174*1fb62fb0SOlivier Houchard continue;
175*1fb62fb0SOlivier Houchard
176*1fb62fb0SOlivier Houchard if (ck_pr_load_ptr(&record->pointers) == NULL)
177*1fb62fb0SOlivier Houchard continue;
178*1fb62fb0SOlivier Houchard
179*1fb62fb0SOlivier Houchard for (i = 0; i < degree; i++) {
180*1fb62fb0SOlivier Houchard hazard = ck_pr_load_ptr(&record->pointers[i]);
181*1fb62fb0SOlivier Houchard if (hazard == pointer)
182*1fb62fb0SOlivier Houchard return (true);
183*1fb62fb0SOlivier Houchard }
184*1fb62fb0SOlivier Houchard } while ((entry = CK_STACK_NEXT(entry)) != NULL);
185*1fb62fb0SOlivier Houchard
186*1fb62fb0SOlivier Houchard return (false);
187*1fb62fb0SOlivier Houchard }
188*1fb62fb0SOlivier Houchard
189*1fb62fb0SOlivier Houchard CK_CC_INLINE static void *
ck_hp_member_cache(struct ck_hp * global,void ** cache,unsigned int * n_hazards)190*1fb62fb0SOlivier Houchard ck_hp_member_cache(struct ck_hp *global, void **cache, unsigned int *n_hazards)
191*1fb62fb0SOlivier Houchard {
192*1fb62fb0SOlivier Houchard struct ck_hp_record *record;
193*1fb62fb0SOlivier Houchard ck_stack_entry_t *entry;
194*1fb62fb0SOlivier Houchard unsigned int hazards = 0;
195*1fb62fb0SOlivier Houchard unsigned int i;
196*1fb62fb0SOlivier Houchard void *pointer;
197*1fb62fb0SOlivier Houchard
198*1fb62fb0SOlivier Houchard CK_STACK_FOREACH(&global->subscribers, entry) {
199*1fb62fb0SOlivier Houchard record = ck_hp_record_container(entry);
200*1fb62fb0SOlivier Houchard if (ck_pr_load_int(&record->state) == CK_HP_FREE)
201*1fb62fb0SOlivier Houchard continue;
202*1fb62fb0SOlivier Houchard
203*1fb62fb0SOlivier Houchard if (ck_pr_load_ptr(&record->pointers) == NULL)
204*1fb62fb0SOlivier Houchard continue;
205*1fb62fb0SOlivier Houchard
206*1fb62fb0SOlivier Houchard for (i = 0; i < global->degree; i++) {
207*1fb62fb0SOlivier Houchard if (hazards > CK_HP_CACHE)
208*1fb62fb0SOlivier Houchard break;
209*1fb62fb0SOlivier Houchard
210*1fb62fb0SOlivier Houchard pointer = ck_pr_load_ptr(&record->pointers[i]);
211*1fb62fb0SOlivier Houchard if (pointer != NULL)
212*1fb62fb0SOlivier Houchard cache[hazards++] = pointer;
213*1fb62fb0SOlivier Houchard }
214*1fb62fb0SOlivier Houchard }
215*1fb62fb0SOlivier Houchard
216*1fb62fb0SOlivier Houchard *n_hazards = hazards;
217*1fb62fb0SOlivier Houchard return (entry);
218*1fb62fb0SOlivier Houchard }
219*1fb62fb0SOlivier Houchard
220*1fb62fb0SOlivier Houchard void
ck_hp_reclaim(struct ck_hp_record * thread)221*1fb62fb0SOlivier Houchard ck_hp_reclaim(struct ck_hp_record *thread)
222*1fb62fb0SOlivier Houchard {
223*1fb62fb0SOlivier Houchard struct ck_hp_hazard *hazard;
224*1fb62fb0SOlivier Houchard struct ck_hp *global = thread->global;
225*1fb62fb0SOlivier Houchard unsigned int n_hazards;
226*1fb62fb0SOlivier Houchard void **cache, *marker, *match;
227*1fb62fb0SOlivier Houchard ck_stack_entry_t *previous, *entry, *next;
228*1fb62fb0SOlivier Houchard
229*1fb62fb0SOlivier Houchard /* Store as many entries as possible in local array. */
230*1fb62fb0SOlivier Houchard cache = thread->cache;
231*1fb62fb0SOlivier Houchard marker = ck_hp_member_cache(global, cache, &n_hazards);
232*1fb62fb0SOlivier Houchard
233*1fb62fb0SOlivier Houchard /*
234*1fb62fb0SOlivier Houchard * In theory, there is an n such that (n * (log n) ** 2) < np.
235*1fb62fb0SOlivier Houchard */
236*1fb62fb0SOlivier Houchard qsort(cache, n_hazards, sizeof(void *), hazard_compare);
237*1fb62fb0SOlivier Houchard
238*1fb62fb0SOlivier Houchard previous = NULL;
239*1fb62fb0SOlivier Houchard CK_STACK_FOREACH_SAFE(&thread->pending, entry, next) {
240*1fb62fb0SOlivier Houchard hazard = ck_hp_hazard_container(entry);
241*1fb62fb0SOlivier Houchard match = bsearch(&hazard->pointer, cache, n_hazards,
242*1fb62fb0SOlivier Houchard sizeof(void *), hazard_compare);
243*1fb62fb0SOlivier Houchard if (match != NULL) {
244*1fb62fb0SOlivier Houchard previous = entry;
245*1fb62fb0SOlivier Houchard continue;
246*1fb62fb0SOlivier Houchard }
247*1fb62fb0SOlivier Houchard
248*1fb62fb0SOlivier Houchard if (marker != NULL &&
249*1fb62fb0SOlivier Houchard ck_hp_member_scan(marker, global->degree, hazard->pointer)) {
250*1fb62fb0SOlivier Houchard previous = entry;
251*1fb62fb0SOlivier Houchard continue;
252*1fb62fb0SOlivier Houchard }
253*1fb62fb0SOlivier Houchard
254*1fb62fb0SOlivier Houchard thread->n_pending -= 1;
255*1fb62fb0SOlivier Houchard
256*1fb62fb0SOlivier Houchard /* Remove from the pending stack. */
257*1fb62fb0SOlivier Houchard if (previous)
258*1fb62fb0SOlivier Houchard CK_STACK_NEXT(previous) = CK_STACK_NEXT(entry);
259*1fb62fb0SOlivier Houchard else
260*1fb62fb0SOlivier Houchard CK_STACK_FIRST(&thread->pending) = CK_STACK_NEXT(entry);
261*1fb62fb0SOlivier Houchard
262*1fb62fb0SOlivier Houchard /* The entry is now safe to destroy. */
263*1fb62fb0SOlivier Houchard global->destroy(hazard->data);
264*1fb62fb0SOlivier Houchard thread->n_reclamations++;
265*1fb62fb0SOlivier Houchard }
266*1fb62fb0SOlivier Houchard
267*1fb62fb0SOlivier Houchard return;
268*1fb62fb0SOlivier Houchard }
269*1fb62fb0SOlivier Houchard
270*1fb62fb0SOlivier Houchard void
ck_hp_retire(struct ck_hp_record * thread,struct ck_hp_hazard * hazard,void * data,void * pointer)271*1fb62fb0SOlivier Houchard ck_hp_retire(struct ck_hp_record *thread,
272*1fb62fb0SOlivier Houchard struct ck_hp_hazard *hazard,
273*1fb62fb0SOlivier Houchard void *data,
274*1fb62fb0SOlivier Houchard void *pointer)
275*1fb62fb0SOlivier Houchard {
276*1fb62fb0SOlivier Houchard
277*1fb62fb0SOlivier Houchard ck_pr_store_ptr(&hazard->pointer, pointer);
278*1fb62fb0SOlivier Houchard ck_pr_store_ptr(&hazard->data, data);
279*1fb62fb0SOlivier Houchard ck_stack_push_spnc(&thread->pending, &hazard->pending_entry);
280*1fb62fb0SOlivier Houchard
281*1fb62fb0SOlivier Houchard thread->n_pending += 1;
282*1fb62fb0SOlivier Houchard if (thread->n_pending > thread->n_peak)
283*1fb62fb0SOlivier Houchard thread->n_peak = thread->n_pending;
284*1fb62fb0SOlivier Houchard
285*1fb62fb0SOlivier Houchard return;
286*1fb62fb0SOlivier Houchard }
287*1fb62fb0SOlivier Houchard
288*1fb62fb0SOlivier Houchard void
ck_hp_free(struct ck_hp_record * thread,struct ck_hp_hazard * hazard,void * data,void * pointer)289*1fb62fb0SOlivier Houchard ck_hp_free(struct ck_hp_record *thread,
290*1fb62fb0SOlivier Houchard struct ck_hp_hazard *hazard,
291*1fb62fb0SOlivier Houchard void *data,
292*1fb62fb0SOlivier Houchard void *pointer)
293*1fb62fb0SOlivier Houchard {
294*1fb62fb0SOlivier Houchard struct ck_hp *global;
295*1fb62fb0SOlivier Houchard
296*1fb62fb0SOlivier Houchard global = ck_pr_load_ptr(&thread->global);
297*1fb62fb0SOlivier Houchard ck_pr_store_ptr(&hazard->data, data);
298*1fb62fb0SOlivier Houchard ck_pr_store_ptr(&hazard->pointer, pointer);
299*1fb62fb0SOlivier Houchard ck_stack_push_spnc(&thread->pending, &hazard->pending_entry);
300*1fb62fb0SOlivier Houchard
301*1fb62fb0SOlivier Houchard thread->n_pending += 1;
302*1fb62fb0SOlivier Houchard if (thread->n_pending > thread->n_peak)
303*1fb62fb0SOlivier Houchard thread->n_peak = thread->n_pending;
304*1fb62fb0SOlivier Houchard
305*1fb62fb0SOlivier Houchard if (thread->n_pending >= global->threshold)
306*1fb62fb0SOlivier Houchard ck_hp_reclaim(thread);
307*1fb62fb0SOlivier Houchard
308*1fb62fb0SOlivier Houchard return;
309*1fb62fb0SOlivier Houchard }
310*1fb62fb0SOlivier Houchard
311*1fb62fb0SOlivier Houchard void
ck_hp_purge(struct ck_hp_record * thread)312*1fb62fb0SOlivier Houchard ck_hp_purge(struct ck_hp_record *thread)
313*1fb62fb0SOlivier Houchard {
314*1fb62fb0SOlivier Houchard ck_backoff_t backoff = CK_BACKOFF_INITIALIZER;
315*1fb62fb0SOlivier Houchard
316*1fb62fb0SOlivier Houchard while (thread->n_pending > 0) {
317*1fb62fb0SOlivier Houchard ck_hp_reclaim(thread);
318*1fb62fb0SOlivier Houchard if (thread->n_pending > 0)
319*1fb62fb0SOlivier Houchard ck_backoff_eb(&backoff);
320*1fb62fb0SOlivier Houchard }
321*1fb62fb0SOlivier Houchard
322*1fb62fb0SOlivier Houchard return;
323*1fb62fb0SOlivier Houchard }
324