1 /*
  2  * Copyright (c) 2018, 2020, Red Hat, Inc. 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 
 25 #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHTHREADLOCALDATA_HPP
 26 #define SHARE_GC_SHENANDOAH_SHENANDOAHTHREADLOCALDATA_HPP
 27 
 28 #include "gc/shared/plab.hpp"
 29 #include "gc/shared/gcThreadLocalData.hpp"
 30 #include "gc/shared/gc_globals.hpp"
 31 #include "gc/shenandoah/shenandoahBarrierSet.hpp"
 32 #include "gc/shenandoah/shenandoahCodeRoots.hpp"
 33 #include "gc/shenandoah/shenandoahSATBMarkQueueSet.hpp"
 34 #include "runtime/thread.hpp"
 35 #include "utilities/debug.hpp"
 36 #include "utilities/sizes.hpp"
 37 
 38 class ShenandoahThreadLocalData {
 39 public:
 40   static const uint INVALID_WORKER_ID = uint(-1);
 41 
 42 private:
 43   char _gc_state;
 44   // Evacuation OOM state
 45   uint8_t                 _oom_scope_nesting_level;
 46   bool                    _oom_during_evac;
 47   SATBMarkQueue           _satb_mark_queue;
 48   PLAB* _gclab;
 49   size_t _gclab_size;
 50   uint  _worker_id;
 51   int  _disarmed_value;
 52   double _paced_time;
 53 
 54   ShenandoahThreadLocalData() :
 55     _gc_state(0),
 56     _oom_scope_nesting_level(0),
 57     _oom_during_evac(false),
 58     _satb_mark_queue(&ShenandoahBarrierSet::satb_mark_queue_set()),
 59     _gclab(NULL),
 60     _gclab_size(0),
 61     _worker_id(INVALID_WORKER_ID),
 62     _disarmed_value(0),
 63     _paced_time(0) {
 64 
 65     // At least on x86_64, nmethod entry barrier encodes _disarmed_value offset
 66     // in instruction as disp8 immed
 67     assert(in_bytes(disarmed_value_offset()) < 128, "Offset range check");
 68   }
 69 
 70   ~ShenandoahThreadLocalData() {
 71     if (_gclab != NULL) {
 72       delete _gclab;
 73     }
 74   }
 75 
 76   static ShenandoahThreadLocalData* data(Thread* thread) {
 77     assert(UseShenandoahGC, "Sanity");
 78     return thread->gc_data<ShenandoahThreadLocalData>();
 79   }
 80 
 81   static ByteSize satb_mark_queue_offset() {
 82     return Thread::gc_data_offset() + byte_offset_of(ShenandoahThreadLocalData, _satb_mark_queue);
 83   }
 84 
 85 public:
 86   static void create(Thread* thread) {
 87     new (data(thread)) ShenandoahThreadLocalData();
 88   }
 89 
 90   static void destroy(Thread* thread) {
 91     data(thread)->~ShenandoahThreadLocalData();
 92   }
 93 
 94   static SATBMarkQueue& satb_mark_queue(Thread* thread) {
 95     return data(thread)->_satb_mark_queue;
 96   }
 97 
 98   static void set_gc_state(Thread* thread, char gc_state) {
 99     data(thread)->_gc_state = gc_state;
100   }
101 
102   static char gc_state(Thread* thread) {
103     return data(thread)->_gc_state;
104   }
105 
106   static void set_worker_id(Thread* thread, uint id) {
107     assert(thread->is_Worker_thread(), "Must be a worker thread");
108     data(thread)->_worker_id = id;
109   }
110 
111   static uint worker_id(Thread* thread) {
112     assert(thread->is_Worker_thread(), "Must be a worker thread");
113     return data(thread)->_worker_id;
114   }
115 
116   static void initialize_gclab(Thread* thread) {
117     assert (thread->is_Java_thread() || thread->is_Worker_thread(), "Only Java and GC worker threads are allowed to get GCLABs");
118     assert(data(thread)->_gclab == NULL, "Only initialize once");
119     data(thread)->_gclab = new PLAB(PLAB::min_size());
120     data(thread)->_gclab_size = 0;
121   }
122 
123   static PLAB* gclab(Thread* thread) {
124     return data(thread)->_gclab;
125   }
126 
127   static size_t gclab_size(Thread* thread) {
128     return data(thread)->_gclab_size;
129   }
130 
131   static void set_gclab_size(Thread* thread, size_t v) {
132     data(thread)->_gclab_size = v;
133   }
134 
135   static void add_paced_time(Thread* thread, double v) {
136     data(thread)->_paced_time += v;
137   }
138 
139   static double paced_time(Thread* thread) {
140     return data(thread)->_paced_time;
141   }
142 
143   static void reset_paced_time(Thread* thread) {
144     data(thread)->_paced_time = 0;
145   }
146 
147   static void set_disarmed_value(Thread* thread, int value) {
148     data(thread)->_disarmed_value = value;
149   }
150 
151   // Evacuation OOM handling
152   static bool is_oom_during_evac(Thread* thread) {
153     return data(thread)->_oom_during_evac;
154   }
155 
156   static void set_oom_during_evac(Thread* thread, bool oom) {
157     data(thread)->_oom_during_evac = oom;
158   }
159 
160   static uint8_t evac_oom_scope_level(Thread* thread) {
161     return data(thread)->_oom_scope_nesting_level;
162   }
163 
164   // Push the scope one level deeper, return previous level
165   static uint8_t push_evac_oom_scope(Thread* thread) {
166     uint8_t level = evac_oom_scope_level(thread);
167     assert(level < 254, "Overflow nesting level"); // UINT8_MAX = 255
168     data(thread)->_oom_scope_nesting_level = level + 1;
169     return level;
170   }
171 
172   // Pop the scope by one level, return previous level
173   static uint8_t pop_evac_oom_scope(Thread* thread) {
174     uint8_t level = evac_oom_scope_level(thread);
175     assert(level > 0, "Underflow nesting level");
176     data(thread)->_oom_scope_nesting_level = level - 1;
177     return level;
178   }
179 
180   static bool is_evac_allowed(Thread* thread) {
181     return evac_oom_scope_level(thread) > 0;
182   }
183 
184   // Offsets
185   static ByteSize satb_mark_queue_active_offset() {
186     return satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_active();
187   }
188 
189   static ByteSize satb_mark_queue_index_offset() {
190     return satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_index();
191   }
192 
193   static ByteSize satb_mark_queue_buffer_offset() {
194     return satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_buf();
195   }
196 
197   static ByteSize gc_state_offset() {
198     return Thread::gc_data_offset() + byte_offset_of(ShenandoahThreadLocalData, _gc_state);
199   }
200 
201   static ByteSize disarmed_value_offset() {
202     return Thread::gc_data_offset() + byte_offset_of(ShenandoahThreadLocalData, _disarmed_value);
203   }
204 };
205 
206 STATIC_ASSERT(sizeof(ShenandoahThreadLocalData) <= sizeof(GCThreadLocalData));
207 
208 #endif // SHARE_GC_SHENANDOAH_SHENANDOAHTHREADLOCALDATA_HPP