blob: 6adcfec9f6f0a3cca169634f406ce1f79e443c98 [file] [log] [blame]
Sebastien Hertzd2fe10a2014-01-15 10:20:56 +01001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "transaction.h"
18
19#include "base/stl_util.h"
20#include "base/logging.h"
21#include "gc/accounting/card_table-inl.h"
22#include "intern_table.h"
23#include "mirror/object-inl.h"
24#include "mirror/object_array-inl.h"
25
26#include <list>
27
28namespace art {
29
30// TODO: remove (only used for debugging purpose).
31static constexpr bool kEnableTransactionStats = false;
32
33Transaction::Transaction() : log_lock_("transaction log lock", kTransactionLogLock) {
34 CHECK(Runtime::Current()->IsCompiler());
35}
36
37Transaction::~Transaction() {
38 if (kEnableTransactionStats) {
39 MutexLock mu(Thread::Current(), log_lock_);
40 size_t objects_count = object_logs_.size();
41 size_t field_values_count = 0;
42 for (auto it : object_logs_) {
43 field_values_count += it.second.Size();
44 }
45 size_t array_count = array_logs_.size();
46 size_t array_values_count = 0;
47 for (auto it : array_logs_) {
48 array_values_count += it.second.Size();
49 }
50 size_t string_count = intern_string_logs_.size();
51 LOG(INFO) << "Transaction::~Transaction"
52 << ": objects_count=" << objects_count
53 << ", field_values_count=" << field_values_count
54 << ", array_count=" << array_count
55 << ", array_values_count=" << array_values_count
56 << ", string_count=" << string_count;
57 }
58}
59
60void Transaction::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
61 bool is_volatile) {
62 DCHECK(obj != nullptr);
63 MutexLock mu(Thread::Current(), log_lock_);
64 ObjectLog& object_log = object_logs_[obj];
65 object_log.Log32BitsValue(field_offset, value, is_volatile);
66}
67
68void Transaction::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
69 bool is_volatile) {
70 DCHECK(obj != nullptr);
71 MutexLock mu(Thread::Current(), log_lock_);
72 ObjectLog& object_log = object_logs_[obj];
73 object_log.Log64BitsValue(field_offset, value, is_volatile);
74}
75
76void Transaction::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
77 mirror::Object* value, bool is_volatile) {
78 DCHECK(obj != nullptr);
79 MutexLock mu(Thread::Current(), log_lock_);
80 ObjectLog& object_log = object_logs_[obj];
81 object_log.LogReferenceValue(field_offset, value, is_volatile);
82}
83
84void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) {
85 DCHECK(array != nullptr);
86 DCHECK(array->IsArrayInstance());
87 MutexLock mu(Thread::Current(), log_lock_);
88 ArrayLog& array_log = array_logs_[array];
89 array_log.LogValue(index, value);
90}
91
92void Transaction::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) {
93 DCHECK(s != nullptr);
94 InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kInsert);
95 LogInternedString(log);
96}
97
98void Transaction::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) {
99 DCHECK(s != nullptr);
100 InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kInsert);
101 LogInternedString(log);
102}
103
104void Transaction::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) {
105 InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kRemove);
106 LogInternedString(log);
107}
108
109void Transaction::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) {
110 InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kRemove);
111 LogInternedString(log);
112}
113
114void Transaction::LogInternedString(InternStringLog& log) {
115 Locks::intern_table_lock_->AssertExclusiveHeld(Thread::Current());
116 MutexLock mu(Thread::Current(), log_lock_);
117 intern_string_logs_.push_front(log);
118}
119
120void Transaction::Abort() {
121 CHECK(!Runtime::Current()->IsActiveTransaction());
122 Thread* self = Thread::Current();
123 self->AssertNoPendingException();
124 MutexLock mu1(self, *Locks::intern_table_lock_);
125 MutexLock mu2(self, log_lock_);
126 UndoObjectModifications();
127 UndoArrayModifications();
128 UndoInternStringTableModifications();
129}
130
131void Transaction::UndoObjectModifications() {
132 // TODO we may not need to restore objects allocated during this transaction. Or we could directly
133 // remove them from the heap.
134 for (auto it : object_logs_) {
135 it.second.Undo(it.first);
136 }
137 object_logs_.clear();
138}
139
140void Transaction::UndoArrayModifications() {
141 // TODO we may not need to restore array allocated during this transaction. Or we could directly
142 // remove them from the heap.
143 for (auto it : array_logs_) {
144 it.second.Undo(it.first);
145 }
146 array_logs_.clear();
147}
148
149void Transaction::UndoInternStringTableModifications() {
150 InternTable* const intern_table = Runtime::Current()->GetInternTable();
151 // We want to undo each operation from the most recent to the oldest. List has been filled so the
152 // most recent operation is at list begin so just have to iterate over it.
153 for (InternStringLog& string_log : intern_string_logs_) {
154 string_log.Undo(intern_table);
155 }
156 intern_string_logs_.clear();
157}
158
159void Transaction::VisitRoots(RootCallback* callback, void* arg) {
160 LOG(INFO) << "Transaction::VisitRoots";
161 MutexLock mu(Thread::Current(), log_lock_);
162 VisitObjectLogs(callback, arg);
163 VisitArrayLogs(callback, arg);
164 VisitStringLogs(callback, arg);
165}
166
167void Transaction::VisitObjectLogs(RootCallback* callback, void* arg) {
168 // List of moving roots.
169 typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair;
170 std::list<ObjectPair> moving_roots;
171
172 // Visit roots.
173 for (auto it : object_logs_) {
174 it.second.VisitRoots(callback, arg);
175 mirror::Object* old_root = it.first;
176 mirror::Object* new_root = callback(old_root, arg, 0, kRootUnknown);
177 if (new_root != old_root) {
178 moving_roots.push_back(std::make_pair(old_root, new_root));
179 }
180 }
181
182 // Update object logs with moving roots.
183 for (const ObjectPair& pair : moving_roots) {
184 mirror::Object* old_root = pair.first;
185 mirror::Object* new_root = pair.second;
186 auto old_root_it = object_logs_.find(old_root);
187 CHECK(old_root_it != object_logs_.end());
188 CHECK(object_logs_.find(new_root) == object_logs_.end());
189 object_logs_.insert(std::make_pair(new_root, old_root_it->second));
190 object_logs_.erase(old_root_it);
191 }
192}
193
194void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) {
195 // List of moving roots.
196 typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
197 std::list<ArrayPair> moving_roots;
198
199 for (auto it : array_logs_) {
200 mirror::Array* old_root = it.first;
201 if (old_root->IsObjectArray()) {
202 it.second.VisitRoots(callback, arg);
203 }
204 mirror::Array* new_root = down_cast<mirror::Array*>(callback(old_root, arg, 0, kRootUnknown));
205 if (new_root != old_root) {
206 moving_roots.push_back(std::make_pair(old_root, new_root));
207 }
208 }
209
210 // Update array logs with moving roots.
211 for (const ArrayPair& pair : moving_roots) {
212 mirror::Array* old_root = pair.first;
213 mirror::Array* new_root = pair.second;
214 auto old_root_it = array_logs_.find(old_root);
215 CHECK(old_root_it != array_logs_.end());
216 CHECK(array_logs_.find(new_root) == array_logs_.end());
217 array_logs_.insert(std::make_pair(new_root, old_root_it->second));
218 array_logs_.erase(old_root_it);
219 }
220}
221
222void Transaction::VisitStringLogs(RootCallback* callback, void* arg) {
223 for (InternStringLog& log : intern_string_logs_) {
224 log.VisitRoots(callback, arg);
225 }
226}
227
228void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
229 auto it = field_values_.find(offset.Uint32Value());
230 if (it == field_values_.end()) {
231 ObjectLog::FieldValue field_value;
232 field_value.value = value;
233 field_value.is_volatile = is_volatile;
234 field_value.kind = ObjectLog::k32Bits;
235 field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
236 }
237}
238
239void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
240 auto it = field_values_.find(offset.Uint32Value());
241 if (it == field_values_.end()) {
242 ObjectLog::FieldValue field_value;
243 field_value.value = value;
244 field_value.is_volatile = is_volatile;
245 field_value.kind = ObjectLog::k64Bits;
246 field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
247 }
248}
249
250void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
251 auto it = field_values_.find(offset.Uint32Value());
252 if (it == field_values_.end()) {
253 ObjectLog::FieldValue field_value;
254 field_value.value = reinterpret_cast<uintptr_t>(obj);
255 field_value.is_volatile = is_volatile;
256 field_value.kind = ObjectLog::kReference;
257 field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
258 }
259}
260
261void Transaction::ObjectLog::Undo(mirror::Object* obj) {
262 for (auto& it : field_values_) {
263 // Garbage collector needs to access object's class and array's length. So we don't rollback
264 // these values.
265 MemberOffset field_offset(it.first);
266 if (field_offset.Uint32Value() == mirror::Class::ClassOffset().Uint32Value()) {
267 // Skip Object::class field.
268 continue;
269 }
270 if (obj->IsArrayInstance() &&
271 field_offset.Uint32Value() == mirror::Array::LengthOffset().Uint32Value()) {
272 // Skip Array::length field.
273 continue;
274 }
275 FieldValue& field_value = it.second;
276 UndoFieldWrite(obj, field_offset, field_value);
277 }
278}
279
280void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
281 const FieldValue& field_value) {
282 // TODO We may want to abort a transaction while still being in transaction mode. In this case,
283 // we'd need to disable the check.
284 constexpr bool kCheckTransaction = true;
285 switch (field_value.kind) {
286 case k32Bits:
287 obj->SetField32<false, kCheckTransaction>(field_offset, static_cast<uint32_t>(field_value.value),
288 field_value.is_volatile);
289 break;
290 case k64Bits:
291 obj->SetField64<false, kCheckTransaction>(field_offset, field_value.value,
292 field_value.is_volatile);
293 break;
294 case kReference:
295 obj->SetFieldObject<false, kCheckTransaction>(field_offset,
296 reinterpret_cast<mirror::Object*>(field_value.value),
297 field_value.is_volatile);
298 break;
299 default:
300 LOG(FATAL) << "Unknown value kind " << field_value.kind;
301 break;
302 }
303}
304
305void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) {
306 for (auto it : field_values_) {
307 FieldValue& field_value = it.second;
308 if (field_value.kind == ObjectLog::kReference) {
309 mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
310 field_value.value = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
311 }
312 }
313}
314
315void Transaction::InternStringLog::Undo(InternTable* intern_table) {
316 DCHECK(intern_table != nullptr);
317 switch (string_op_) {
318 case InternStringLog::kInsert: {
319 switch (string_kind_) {
320 case InternStringLog::kStrongString:
321 intern_table->RemoveStrongFromTransaction(str_, hash_code_);
322 break;
323 case InternStringLog::kWeakString:
324 intern_table->RemoveWeakFromTransaction(str_, hash_code_);
325 break;
326 default:
327 LOG(FATAL) << "Unknown interned string kind";
328 break;
329 }
330 break;
331 }
332 case InternStringLog::kRemove: {
333 switch (string_kind_) {
334 case InternStringLog::kStrongString:
335 intern_table->InsertStrongFromTransaction(str_, hash_code_);
336 break;
337 case InternStringLog::kWeakString:
338 intern_table->InsertWeakFromTransaction(str_, hash_code_);
339 break;
340 default:
341 LOG(FATAL) << "Unknown interned string kind";
342 break;
343 }
344 break;
345 }
346 default:
347 LOG(FATAL) << "Unknown interned string op";
348 break;
349 }
350}
351
352void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
353 str_ = down_cast<mirror::String*>(callback(str_, arg, 0, kRootInternedString));
354}
355
356void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
357 auto it = array_values_.find(index);
358 if (it == array_values_.end()) {
359 array_values_.insert(std::make_pair(index, value));
360 }
361}
362
363void Transaction::ArrayLog::Undo(mirror::Array* array) {
364 DCHECK(array != nullptr);
365 DCHECK(array->IsArrayInstance());
366 Primitive::Type type = array->GetClass()->GetComponentType()->GetPrimitiveType();
367 for (auto it : array_values_) {
368 UndoArrayWrite(array, type, it.first, it.second);
369 }
370}
371
372void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Primitive::Type array_type,
373 size_t index, uint64_t value) {
374 // TODO We may want to abort a transaction while still being in transaction mode. In this case,
375 // we'd need to disable the check.
376 switch (array_type) {
377 case Primitive::kPrimBoolean:
378 array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value));
379 break;
380 case Primitive::kPrimByte:
381 array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value));
382 break;
383 case Primitive::kPrimChar:
384 array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value));
385 break;
386 case Primitive::kPrimShort:
387 array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value));
388 break;
389 case Primitive::kPrimInt:
390 array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value));
391 break;
392 case Primitive::kPrimFloat:
393 array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value));
394 break;
395 case Primitive::kPrimLong:
396 array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value));
397 break;
398 case Primitive::kPrimDouble:
399 array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value));
400 break;
401 case Primitive::kPrimNot: {
402 mirror::ObjectArray<mirror::Object>* obj_array = array->AsObjectArray<mirror::Object>();
403 obj_array->SetWithoutChecks<false>(index, reinterpret_cast<mirror::Object*>(
404 static_cast<uintptr_t>(value)));
405 break;
406 }
407 default:
408 LOG(FATAL) << "Unsupported type " << array_type;
409 }
410}
411
412void Transaction::ArrayLog::VisitRoots(RootCallback* callback, void* arg) {
413 for (auto& it : array_values_) {
414 mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(it.second));
415 it.second = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
416 }
417}
418
419} // namespace art