blob: df9a09894c6fb9be42fc60af990000fb9986bb7d [file] [log] [blame]
Vijay Pai005e3052015-07-10 15:18:45 -07001/*
2 *
3 * Copyright 2015, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/support/stack_lockfree.h"
35
36#include <stdlib.h>
37#include <string.h>
38
39#include <grpc/support/port_platform.h>
40#include <grpc/support/alloc.h>
41#include <grpc/support/atm.h>
42#include <grpc/support/log.h>
43
44/* The lockfree node structure is a single architecture-level
45 word that allows for an atomic CAS to set it up. */
Craig Tillera82950e2015-09-22 12:33:20 -070046struct lockfree_node_contents {
Vijay Pai005e3052015-07-10 15:18:45 -070047 /* next thing to look at. Actual index for head, next index otherwise */
48 gpr_uint16 index;
49#ifdef GPR_ARCH_64
50 gpr_uint16 pad;
51 gpr_uint32 aba_ctr;
52#else
53#ifdef GPR_ARCH_32
54 gpr_uint16 aba_ctr;
55#else
56#error Unsupported bit width architecture
57#endif
58#endif
59};
60
61/* Use a union to make sure that these are in the same bits as an atm word */
Craig Tillera82950e2015-09-22 12:33:20 -070062typedef union lockfree_node {
Vijay Pai005e3052015-07-10 15:18:45 -070063 gpr_atm atm;
64 struct lockfree_node_contents contents;
65} lockfree_node;
66
Craig Tillera82950e2015-09-22 12:33:20 -070067#define ENTRY_ALIGNMENT_BITS 3 /* make sure that entries aligned to 8-bytes */
Craig Tillere0981df2015-07-16 08:45:15 -070068#define INVALID_ENTRY_INDEX \
Craig Tillera82950e2015-09-22 12:33:20 -070069 ((1 << 16) - 1) /* reserve this entry as invalid \
Craig Tiller565b18b2015-09-23 10:09:42 -070070 */
Vijay Pai005e3052015-07-10 15:18:45 -070071
Craig Tillera82950e2015-09-22 12:33:20 -070072struct gpr_stack_lockfree {
Vijay Pai005e3052015-07-10 15:18:45 -070073 lockfree_node *entries;
Craig Tillera82950e2015-09-22 12:33:20 -070074 lockfree_node head; /* An atomic entry describing curr head */
vjpai6c226192015-07-22 14:34:58 -070075
76#ifndef NDEBUG
77 /* Bitmap of pushed entries to check for double-push or pop */
Craig Tillera82950e2015-09-22 12:33:20 -070078 gpr_atm pushed[(INVALID_ENTRY_INDEX + 1) / (8 * sizeof(gpr_atm))];
vjpai6c226192015-07-22 14:34:58 -070079#endif
Vijay Pai005e3052015-07-10 15:18:45 -070080};
81
Craig Tillera82950e2015-09-22 12:33:20 -070082gpr_stack_lockfree *gpr_stack_lockfree_create(size_t entries) {
Vijay Pai005e3052015-07-10 15:18:45 -070083 gpr_stack_lockfree *stack;
Craig Tillera82950e2015-09-22 12:33:20 -070084 stack = gpr_malloc(sizeof(*stack));
Vijay Pai005e3052015-07-10 15:18:45 -070085 /* Since we only allocate 16 bits to represent an entry number,
86 * make sure that we are within the desired range */
87 /* Reserve the highest entry number as a dummy */
Craig Tillera82950e2015-09-22 12:33:20 -070088 GPR_ASSERT(entries < INVALID_ENTRY_INDEX);
89 stack->entries = gpr_malloc_aligned(entries * sizeof(stack->entries[0]),
90 ENTRY_ALIGNMENT_BITS);
Vijay Pai005e3052015-07-10 15:18:45 -070091 /* Clear out all entries */
Craig Tillera82950e2015-09-22 12:33:20 -070092 memset(stack->entries, 0, entries * sizeof(stack->entries[0]));
93 memset(&stack->head, 0, sizeof(stack->head));
vjpai6c226192015-07-22 14:34:58 -070094#ifndef NDEBUG
Craig Tillera82950e2015-09-22 12:33:20 -070095 memset(&stack->pushed, 0, sizeof(stack->pushed));
vjpai6c226192015-07-22 14:34:58 -070096#endif
Vijay Pai005e3052015-07-10 15:18:45 -070097
Craig Tillera82950e2015-09-22 12:33:20 -070098 GPR_ASSERT(sizeof(stack->entries->atm) == sizeof(stack->entries->contents));
Craig Tiller42b6c932015-07-30 09:59:25 -070099
Vijay Pai005e3052015-07-10 15:18:45 -0700100 /* Point the head at reserved dummy entry */
101 stack->head.contents.index = INVALID_ENTRY_INDEX;
102 return stack;
103}
104
Craig Tillera82950e2015-09-22 12:33:20 -0700105void gpr_stack_lockfree_destroy(gpr_stack_lockfree *stack) {
106 gpr_free_aligned(stack->entries);
107 gpr_free(stack);
Vijay Pai005e3052015-07-10 15:18:45 -0700108}
109
Craig Tillera82950e2015-09-22 12:33:20 -0700110int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) {
Vijay Pai005e3052015-07-10 15:18:45 -0700111 lockfree_node head;
112 lockfree_node newhead;
Craig Tiller42b6c932015-07-30 09:59:25 -0700113 lockfree_node curent;
114 lockfree_node newent;
Vijay Pai005e3052015-07-10 15:18:45 -0700115
116 /* First fill in the entry's index and aba ctr for new head */
Craig Tillera82950e2015-09-22 12:33:20 -0700117 newhead.contents.index = (gpr_uint16)entry;
Vijay Pai005e3052015-07-10 15:18:45 -0700118 /* Also post-increment the aba_ctr */
Craig Tillera82950e2015-09-22 12:33:20 -0700119 curent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm);
Craig Tiller42b6c932015-07-30 09:59:25 -0700120 newhead.contents.aba_ctr = ++curent.contents.aba_ctr;
Craig Tillera82950e2015-09-22 12:33:20 -0700121 gpr_atm_no_barrier_store(&stack->entries[entry].atm, curent.atm);
Vijay Pai005e3052015-07-10 15:18:45 -0700122
vjpai6c226192015-07-22 14:34:58 -0700123#ifndef NDEBUG
124 /* Check for double push */
125 {
Craig Tillera82950e2015-09-22 12:33:20 -0700126 int pushed_index = entry / (int)(8 * sizeof(gpr_atm));
127 int pushed_bit = entry % (int)(8 * sizeof(gpr_atm));
vjpai6c226192015-07-22 14:34:58 -0700128 gpr_atm old_val;
129
Craig Tillera82950e2015-09-22 12:33:20 -0700130 old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index],
131 (gpr_atm)(1UL << pushed_bit));
132 GPR_ASSERT((old_val & (gpr_atm)(1UL << pushed_bit)) == 0);
vjpai6c226192015-07-22 14:34:58 -0700133 }
134#endif
135
Craig Tillera82950e2015-09-22 12:33:20 -0700136 do {
137 /* Atomically get the existing head value for use */
138 head.atm = gpr_atm_no_barrier_load(&(stack->head.atm));
139 /* Point to it */
140 newent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm);
141 newent.contents.index = head.contents.index;
142 gpr_atm_no_barrier_store(&stack->entries[entry].atm, newent.atm);
143 } while (!gpr_atm_rel_cas(&(stack->head.atm), head.atm, newhead.atm));
Vijay Pai005e3052015-07-10 15:18:45 -0700144 /* Use rel_cas above to make sure that entry index is set properly */
Craig Tillerffe27b92015-07-13 16:37:15 -0700145 return head.contents.index == INVALID_ENTRY_INDEX;
Vijay Pai005e3052015-07-10 15:18:45 -0700146}
147
Craig Tillera82950e2015-09-22 12:33:20 -0700148int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) {
Vijay Pai005e3052015-07-10 15:18:45 -0700149 lockfree_node head;
150 lockfree_node newhead;
vjpai6c226192015-07-22 14:34:58 -0700151
Craig Tillera82950e2015-09-22 12:33:20 -0700152 do {
153 head.atm = gpr_atm_acq_load(&(stack->head.atm));
154 if (head.contents.index == INVALID_ENTRY_INDEX) {
155 return -1;
Craig Tiller45724b32015-09-22 10:42:19 -0700156 }
Craig Tillera82950e2015-09-22 12:33:20 -0700157 newhead.atm =
158 gpr_atm_no_barrier_load(&(stack->entries[head.contents.index].atm));
159
160 } while (!gpr_atm_no_barrier_cas(&(stack->head.atm), head.atm, newhead.atm));
vjpai6c226192015-07-22 14:34:58 -0700161#ifndef NDEBUG
162 /* Check for valid pop */
163 {
Craig Tillera82950e2015-09-22 12:33:20 -0700164 int pushed_index = head.contents.index / (8 * sizeof(gpr_atm));
165 int pushed_bit = head.contents.index % (8 * sizeof(gpr_atm));
vjpai6c226192015-07-22 14:34:58 -0700166 gpr_atm old_val;
167
Craig Tillera82950e2015-09-22 12:33:20 -0700168 old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index],
169 -(gpr_atm)(1UL << pushed_bit));
170 GPR_ASSERT((old_val & (gpr_atm)(1UL << pushed_bit)) != 0);
vjpai6c226192015-07-22 14:34:58 -0700171 }
172#endif
173
Vijay Pai887f86b2015-07-10 17:12:10 -0700174 return head.contents.index;
Vijay Pai005e3052015-07-10 15:18:45 -0700175}