1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_RUNTIME_MONITOR_POOL_H_
#define ART_RUNTIME_MONITOR_POOL_H_
#include "monitor.h"
#include "base/allocator.h"
#ifdef __LP64__
#include <stdint.h>
#include "base/atomic.h"
#include "runtime.h"
#else
#include "base/stl_util.h" // STLDeleteElements
#endif
namespace art {
// Abstraction to keep monitors small enough to fit in a lock word (32bits). On 32bit systems the
// monitor id loses the alignment bits of the Monitor*.
class MonitorPool {
public:
static MonitorPool* Create() {
#ifndef __LP64__
return nullptr;
#else
return new MonitorPool();
#endif
}
static Monitor* CreateMonitor(Thread* self,
Thread* owner,
ObjPtr<mirror::Object> obj,
int32_t hash_code)
REQUIRES_SHARED(Locks::mutator_lock_) {
#ifndef __LP64__
Monitor* mon = new Monitor(self, owner, obj, hash_code);
DCHECK_ALIGNED(mon, LockWord::kMonitorIdAlignment);
return mon;
#else
return GetMonitorPool()->CreateMonitorInPool(self, owner, obj, hash_code);
#endif
}
static void ReleaseMonitor(Thread* self, Monitor* monitor) {
#ifndef __LP64__
UNUSED(self);
delete monitor;
#else
GetMonitorPool()->ReleaseMonitorToPool(self, monitor);
#endif
}
static void ReleaseMonitors(Thread* self, MonitorList::Monitors* monitors) {
#ifndef __LP64__
UNUSED(self);
STLDeleteElements(monitors);
#else
GetMonitorPool()->ReleaseMonitorsToPool(self, monitors);
#endif
}
static Monitor* MonitorFromMonitorId(MonitorId mon_id) {
#ifndef __LP64__
return reinterpret_cast<Monitor*>(mon_id << LockWord::kMonitorIdAlignmentShift);
#else
return GetMonitorPool()->LookupMonitor(mon_id);
#endif
}
static MonitorId MonitorIdFromMonitor(Monitor* mon) {
#ifndef __LP64__
return reinterpret_cast<MonitorId>(mon) >> LockWord::kMonitorIdAlignmentShift;
#else
return mon->GetMonitorId();
#endif
}
static MonitorId ComputeMonitorId(Monitor* mon, Thread* self) {
#ifndef __LP64__
UNUSED(self);
return MonitorIdFromMonitor(mon);
#else
return GetMonitorPool()->ComputeMonitorIdInPool(mon, self);
#endif
}
static MonitorPool* GetMonitorPool() {
#ifndef __LP64__
return nullptr;
#else
return Runtime::Current()->GetMonitorPool();
#endif
}
~MonitorPool() {
#ifdef __LP64__
FreeInternal();
#endif
}
private:
#ifdef __LP64__
// When we create a monitor pool, threads have not been initialized, yet, so ignore thread-safety
// analysis.
MonitorPool() NO_THREAD_SAFETY_ANALYSIS;
void AllocateChunk() REQUIRES(Locks::allocated_monitor_ids_lock_);
// Release all chunks and metadata. This is done on shutdown, where threads have been destroyed,
// so ignore thead-safety analysis.
void FreeInternal() NO_THREAD_SAFETY_ANALYSIS;
Monitor* CreateMonitorInPool(Thread* self,
Thread* owner,
ObjPtr<mirror::Object> obj,
int32_t hash_code)
REQUIRES_SHARED(Locks::mutator_lock_);
void ReleaseMonitorToPool(Thread* self, Monitor* monitor);
void ReleaseMonitorsToPool(Thread* self, MonitorList::Monitors* monitors);
// Note: This is safe as we do not ever move chunks. All needed entries in the monitor_chunks_
// data structure are read-only once we get here. Updates happen-before this call because
// the lock word was stored with release semantics and we read it with acquire semantics to
// retrieve the id.
Monitor* LookupMonitor(MonitorId mon_id) {
size_t offset = MonitorIdToOffset(mon_id);
size_t index = offset / kChunkSize;
size_t top_index = index / kMaxListSize;
size_t list_index = index % kMaxListSize;
size_t offset_in_chunk = offset % kChunkSize;
uintptr_t base = monitor_chunks_[top_index][list_index];
return reinterpret_cast<Monitor*>(base + offset_in_chunk);
}
static bool IsInChunk(uintptr_t base_addr, Monitor* mon) {
uintptr_t mon_ptr = reinterpret_cast<uintptr_t>(mon);
return base_addr <= mon_ptr && (mon_ptr - base_addr < kChunkSize);
}
MonitorId ComputeMonitorIdInPool(Monitor* mon, Thread* self) {
MutexLock mu(self, *Locks::allocated_monitor_ids_lock_);
for (size_t i = 0; i <= current_chunk_list_index_; ++i) {
for (size_t j = 0; j < ChunkListCapacity(i); ++j) {
if (j >= num_chunks_ && i == current_chunk_list_index_) {
break;
}
uintptr_t chunk_addr = monitor_chunks_[i][j];
if (IsInChunk(chunk_addr, mon)) {
return OffsetToMonitorId(
reinterpret_cast<uintptr_t>(mon) - chunk_addr
+ i * (kMaxListSize * kChunkSize) + j * kChunkSize);
}
}
}
LOG(FATAL) << "Did not find chunk that contains monitor.";
return 0;
}
static constexpr size_t MonitorIdToOffset(MonitorId id) {
return id << 3;
}
static constexpr MonitorId OffsetToMonitorId(size_t offset) {
return static_cast<MonitorId>(offset >> 3);
}
static constexpr size_t ChunkListCapacity(size_t index) {
return kInitialChunkStorage << index;
}
// TODO: There are assumptions in the code that monitor addresses are 8B aligned (>>3).
static constexpr size_t kMonitorAlignment = 8;
// Size of a monitor, rounded up to a multiple of alignment.
static constexpr size_t kAlignedMonitorSize = (sizeof(Monitor) + kMonitorAlignment - 1) &
-kMonitorAlignment;
// As close to a page as we can get seems a good start.
static constexpr size_t kChunkCapacity = kPageSize / kAlignedMonitorSize;
// Chunk size that is referenced in the id. We can collapse this to the actually used storage
// in a chunk, i.e., kChunkCapacity * kAlignedMonitorSize, but this will mean proper divisions.
static constexpr size_t kChunkSize = kPageSize;
static_assert(IsPowerOfTwo(kChunkSize), "kChunkSize must be power of 2");
// The number of chunks of storage that can be referenced by the initial chunk list.
// The total number of usable monitor chunks is typically 255 times this number, so it
// should be large enough that we don't run out. We run out of address bits if it's > 512.
// Currently we set it a bit smaller, to save half a page per process. We make it tiny in
// debug builds to catch growth errors. The only value we really expect to tune.
static constexpr size_t kInitialChunkStorage = kIsDebugBuild ? 1U : 256U;
static_assert(IsPowerOfTwo(kInitialChunkStorage), "kInitialChunkStorage must be power of 2");
// The number of lists, each containing pointers to storage chunks.
static constexpr size_t kMaxChunkLists = 8; // Dictated by 3 bit index. Don't increase above 8.
static_assert(IsPowerOfTwo(kMaxChunkLists), "kMaxChunkLists must be power of 2");
static constexpr size_t kMaxListSize = kInitialChunkStorage << (kMaxChunkLists - 1);
// We lose 3 bits in monitor id due to 3 bit monitor_chunks_ index, and gain it back from
// the 3 bit alignment constraint on monitors:
static_assert(kMaxListSize * kChunkSize < (1 << LockWord::kMonitorIdSize),
"Monitor id bits don't fit");
static_assert(IsPowerOfTwo(kMaxListSize), "kMaxListSize must be power of 2");
// Array of pointers to lists (again arrays) of pointers to chunks containing monitors.
// Zeroth entry points to a list (array) of kInitialChunkStorage pointers to chunks.
// Each subsequent list as twice as large as the preceding one.
// Monitor Ids are interpreted as follows:
// Top 3 bits (of 28): index into monitor_chunks_.
// Next 16 bits: index into the chunk list, i.e. monitor_chunks_[i].
// Last 9 bits: offset within chunk, expressed as multiple of kMonitorAlignment.
// If we set kInitialChunkStorage to 512, this would allow us to use roughly 128K chunks of
// monitors, which is 0.5GB of monitors. With this maximum setting, the largest chunk list
// contains 64K entries, and we make full use of the available index space. With a
// kInitialChunkStorage value of 256, this is proportionately reduced to 0.25GB of monitors.
// Updates to monitor_chunks_ are guarded by allocated_monitor_ids_lock_ .
// No field in this entire data structure is ever updated once a monitor id whose lookup
// requires it has been made visible to another thread. Thus readers never race with
// updates, in spite of the fact that they acquire no locks.
uintptr_t* monitor_chunks_[kMaxChunkLists]; // uintptr_t is really a Monitor* .
// Highest currently used index in monitor_chunks_ . Used for newly allocated chunks.
size_t current_chunk_list_index_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
// Number of chunk pointers stored in monitor_chunks_[current_chunk_list_index_] so far.
size_t num_chunks_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
// After the initial allocation, this is always equal to
// ChunkListCapacity(current_chunk_list_index_).
size_t current_chunk_list_capacity_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
typedef TrackingAllocator<uint8_t, kAllocatorTagMonitorPool> Allocator;
Allocator allocator_;
// Start of free list of monitors.
// Note: these point to the right memory regions, but do *not* denote initialized objects.
Monitor* first_free_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
#endif
};
} // namespace art
#endif // ART_RUNTIME_MONITOR_POOL_H_
|