1 /*
2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 #include "precompiled.hpp"
25 #include "gc/z/zMarkStack.inline.hpp"
26 #include "gc/z/zMarkStackAllocator.hpp"
27 #include "logging/log.hpp"
28 #include "utilities/debug.hpp"
29 #include "utilities/powerOfTwo.hpp"
30
ZMarkStripe()31 ZMarkStripe::ZMarkStripe() :
32 _published(),
33 _overflowed() {}
34
ZMarkStripeSet()35 ZMarkStripeSet::ZMarkStripeSet() :
36 _nstripes(0),
37 _nstripes_mask(0),
38 _stripes() {}
39
set_nstripes(size_t nstripes)40 void ZMarkStripeSet::set_nstripes(size_t nstripes) {
41 assert(is_power_of_2(nstripes), "Must be a power of two");
42 assert(is_power_of_2(ZMarkStripesMax), "Must be a power of two");
43 assert(nstripes >= 1, "Invalid number of stripes");
44 assert(nstripes <= ZMarkStripesMax, "Invalid number of stripes");
45
46 _nstripes = nstripes;
47 _nstripes_mask = nstripes - 1;
48
49 log_debug(gc, marking)("Using " SIZE_FORMAT " mark stripes", _nstripes);
50 }
51
is_empty() const52 bool ZMarkStripeSet::is_empty() const {
53 for (size_t i = 0; i < _nstripes; i++) {
54 if (!_stripes[i].is_empty()) {
55 return false;
56 }
57 }
58
59 return true;
60 }
61
stripe_for_worker(uint nworkers,uint worker_id)62 ZMarkStripe* ZMarkStripeSet::stripe_for_worker(uint nworkers, uint worker_id) {
63 const size_t spillover_limit = (nworkers / _nstripes) * _nstripes;
64 size_t index;
65
66 if (worker_id < spillover_limit) {
67 // Not a spillover worker, use natural stripe
68 index = worker_id & _nstripes_mask;
69 } else {
70 // Distribute spillover workers evenly across stripes
71 const size_t spillover_nworkers = nworkers - spillover_limit;
72 const size_t spillover_worker_id = worker_id - spillover_limit;
73 const double spillover_chunk = (double)_nstripes / (double)spillover_nworkers;
74 index = spillover_worker_id * spillover_chunk;
75 }
76
77 assert(index < _nstripes, "Invalid index");
78 return &_stripes[index];
79 }
80
ZMarkThreadLocalStacks()81 ZMarkThreadLocalStacks::ZMarkThreadLocalStacks() :
82 _magazine(NULL) {
83 for (size_t i = 0; i < ZMarkStripesMax; i++) {
84 _stacks[i] = NULL;
85 }
86 }
87
is_empty(const ZMarkStripeSet * stripes) const88 bool ZMarkThreadLocalStacks::is_empty(const ZMarkStripeSet* stripes) const {
89 for (size_t i = 0; i < stripes->nstripes(); i++) {
90 ZMarkStack* const stack = _stacks[i];
91 if (stack != NULL) {
92 return false;
93 }
94 }
95
96 return true;
97 }
98
allocate_stack(ZMarkStackAllocator * allocator)99 ZMarkStack* ZMarkThreadLocalStacks::allocate_stack(ZMarkStackAllocator* allocator) {
100 if (_magazine == NULL) {
101 // Allocate new magazine
102 _magazine = allocator->alloc_magazine();
103 if (_magazine == NULL) {
104 return NULL;
105 }
106 }
107
108 ZMarkStack* stack = NULL;
109
110 if (!_magazine->pop(stack)) {
111 // Magazine is empty, convert magazine into a new stack
112 _magazine->~ZMarkStackMagazine();
113 stack = new ((void*)_magazine) ZMarkStack();
114 _magazine = NULL;
115 }
116
117 return stack;
118 }
119
free_stack(ZMarkStackAllocator * allocator,ZMarkStack * stack)120 void ZMarkThreadLocalStacks::free_stack(ZMarkStackAllocator* allocator, ZMarkStack* stack) {
121 for (;;) {
122 if (_magazine == NULL) {
123 // Convert stack into a new magazine
124 stack->~ZMarkStack();
125 _magazine = new ((void*)stack) ZMarkStackMagazine();
126 return;
127 }
128
129 if (_magazine->push(stack)) {
130 // Success
131 return;
132 }
133
134 // Free and uninstall full magazine
135 allocator->free_magazine(_magazine);
136 _magazine = NULL;
137 }
138 }
139
push_slow(ZMarkStackAllocator * allocator,ZMarkStripe * stripe,ZMarkStack ** stackp,ZMarkStackEntry entry,bool publish)140 bool ZMarkThreadLocalStacks::push_slow(ZMarkStackAllocator* allocator,
141 ZMarkStripe* stripe,
142 ZMarkStack** stackp,
143 ZMarkStackEntry entry,
144 bool publish) {
145 ZMarkStack* stack = *stackp;
146
147 for (;;) {
148 if (stack == NULL) {
149 // Allocate and install new stack
150 *stackp = stack = allocate_stack(allocator);
151 if (stack == NULL) {
152 // Out of mark stack memory
153 return false;
154 }
155 }
156
157 if (stack->push(entry)) {
158 // Success
159 return true;
160 }
161
162 // Publish/Overflow and uninstall stack
163 stripe->publish_stack(stack, publish);
164 *stackp = stack = NULL;
165 }
166 }
167
pop_slow(ZMarkStackAllocator * allocator,ZMarkStripe * stripe,ZMarkStack ** stackp,ZMarkStackEntry & entry)168 bool ZMarkThreadLocalStacks::pop_slow(ZMarkStackAllocator* allocator,
169 ZMarkStripe* stripe,
170 ZMarkStack** stackp,
171 ZMarkStackEntry& entry) {
172 ZMarkStack* stack = *stackp;
173
174 for (;;) {
175 if (stack == NULL) {
176 // Try steal and install stack
177 *stackp = stack = stripe->steal_stack();
178 if (stack == NULL) {
179 // Nothing to steal
180 return false;
181 }
182 }
183
184 if (stack->pop(entry)) {
185 // Success
186 return true;
187 }
188
189 // Free and uninstall stack
190 free_stack(allocator, stack);
191 *stackp = stack = NULL;
192 }
193 }
194
flush(ZMarkStackAllocator * allocator,ZMarkStripeSet * stripes)195 bool ZMarkThreadLocalStacks::flush(ZMarkStackAllocator* allocator, ZMarkStripeSet* stripes) {
196 bool flushed = false;
197
198 // Flush all stacks
199 for (size_t i = 0; i < stripes->nstripes(); i++) {
200 ZMarkStripe* const stripe = stripes->stripe_at(i);
201 ZMarkStack** const stackp = &_stacks[i];
202 ZMarkStack* const stack = *stackp;
203 if (stack == NULL) {
204 continue;
205 }
206
207 // Free/Publish and uninstall stack
208 if (stack->is_empty()) {
209 free_stack(allocator, stack);
210 } else {
211 stripe->publish_stack(stack);
212 flushed = true;
213 }
214 *stackp = NULL;
215 }
216
217 return flushed;
218 }
219
free(ZMarkStackAllocator * allocator)220 void ZMarkThreadLocalStacks::free(ZMarkStackAllocator* allocator) {
221 // Free and uninstall magazine
222 if (_magazine != NULL) {
223 allocator->free_magazine(_magazine);
224 _magazine = NULL;
225 }
226 }
227