summaryrefslogtreecommitdiffstats
path: root/runtime/monitor_pool.cc
blob: 4a364cab62c3ce0e64191fff042612804766df61 (plain)
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
/*
 * 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.
 */

#include "monitor_pool.h"

#include "base/logging.h"
#include "base/mutex-inl.h"
#include "thread-inl.h"
#include "monitor.h"

namespace art {

namespace mirror {
  class Object;
}  // namespace mirror

MonitorPool::MonitorPool()
    : num_chunks_(0), capacity_(0), first_free_(nullptr) {
  AllocateChunk();  // Get our first chunk.
}

// Assumes locks are held appropriately when necessary.
// We do not need a lock in the constructor, but we need one when in CreateMonitorInPool.
void MonitorPool::AllocateChunk() {
  DCHECK(first_free_ == nullptr);

  // Do we need to resize?
  if (num_chunks_ == capacity_) {
    if (capacity_ == 0U) {
      // Initialization.
      capacity_ = kInitialChunkStorage;
      uintptr_t* new_backing = new uintptr_t[capacity_];
      monitor_chunks_.StoreRelaxed(new_backing);
    } else {
      size_t new_capacity = 2 * capacity_;
      uintptr_t* new_backing = new uintptr_t[new_capacity];
      uintptr_t* old_backing = monitor_chunks_.LoadRelaxed();
      memcpy(new_backing, old_backing, sizeof(uintptr_t) * capacity_);
      monitor_chunks_.StoreRelaxed(new_backing);
      capacity_ = new_capacity;
      old_chunk_arrays_.push_back(old_backing);
      VLOG(monitor) << "Resizing to capacity " << capacity_;
    }
  }

  // Allocate the chunk.
  void* chunk = allocator_.allocate(kChunkSize);
  // Check we allocated memory.
  CHECK_NE(reinterpret_cast<uintptr_t>(nullptr), reinterpret_cast<uintptr_t>(chunk));
  // Check it is aligned as we need it.
  CHECK_EQ(0U, reinterpret_cast<uintptr_t>(chunk) % kMonitorAlignment);

  // Add the chunk.
  *(monitor_chunks_.LoadRelaxed() + num_chunks_) = reinterpret_cast<uintptr_t>(chunk);
  num_chunks_++;

  // Set up the free list
  Monitor* last = reinterpret_cast<Monitor*>(reinterpret_cast<uintptr_t>(chunk) +
                                             (kChunkCapacity - 1) * kAlignedMonitorSize);
  last->next_free_ = nullptr;
  // Eagerly compute id.
  last->monitor_id_ = OffsetToMonitorId((num_chunks_ - 1) * kChunkSize +
                                        (kChunkCapacity - 1) * kAlignedMonitorSize);
  for (size_t i = 0; i < kChunkCapacity - 1; ++i) {
    Monitor* before = reinterpret_cast<Monitor*>(reinterpret_cast<uintptr_t>(last) -
                                                 kAlignedMonitorSize);
    before->next_free_ = last;
    // Derive monitor_id from last.
    before->monitor_id_ = OffsetToMonitorId(MonitorIdToOffset(last->monitor_id_) -
                                            kAlignedMonitorSize);

    last = before;
  }
  DCHECK(last == reinterpret_cast<Monitor*>(chunk));
  first_free_ = last;
}

Monitor* MonitorPool::CreateMonitorInPool(Thread* self, Thread* owner, mirror::Object* obj,
                                          int32_t hash_code)
    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  // We are gonna allocate, so acquire the writer lock.
  MutexLock mu(self, *Locks::allocated_monitor_ids_lock_);

  // Enough space, or need to resize?
  if (first_free_ == nullptr) {
    VLOG(monitor) << "Allocating a new chunk.";
    AllocateChunk();
  }

  Monitor* mon_uninitialized = first_free_;
  first_free_ = first_free_->next_free_;

  // Pull out the id which was preinitialized.
  MonitorId id = mon_uninitialized->monitor_id_;

  // Initialize it.
  Monitor* monitor = new(mon_uninitialized) Monitor(self, owner, obj, hash_code, id);

  return monitor;
}

void MonitorPool::ReleaseMonitorToPool(Thread* self, Monitor* monitor) {
  // Might be racy with allocation, so acquire lock.
  MutexLock mu(self, *Locks::allocated_monitor_ids_lock_);

  // Keep the monitor id. Don't trust it's not cleared.
  MonitorId id = monitor->monitor_id_;

  // Call the destructor.
  // TODO: Exception safety?
  monitor->~Monitor();

  // Add to the head of the free list.
  monitor->next_free_ = first_free_;
  first_free_ = monitor;

  // Rewrite monitor id.
  monitor->monitor_id_ = id;
}

void MonitorPool::ReleaseMonitorsToPool(Thread* self, MonitorList::Monitors* monitors) {
  for (Monitor* mon : *monitors) {
    ReleaseMonitorToPool(self, mon);
  }
}

}  // namespace art