blob: 019a3227ee8bc206ceee2d70ffe8890f032fa415 [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;
Mathieu Chartier815873e2014-02-13 18:02:13 -0800176 mirror::Object* new_root = old_root;
177 callback(&new_root, arg, 0, kRootUnknown);
Sebastien Hertzd2fe10a2014-01-15 10:20:56 +0100178 if (new_root != old_root) {
179 moving_roots.push_back(std::make_pair(old_root, new_root));
180 }
181 }
182
183 // Update object logs with moving roots.
184 for (const ObjectPair& pair : moving_roots) {
185 mirror::Object* old_root = pair.first;
186 mirror::Object* new_root = pair.second;
187 auto old_root_it = object_logs_.find(old_root);
188 CHECK(old_root_it != object_logs_.end());
189 CHECK(object_logs_.find(new_root) == object_logs_.end());
190 object_logs_.insert(std::make_pair(new_root, old_root_it->second));
191 object_logs_.erase(old_root_it);
192 }
193}
194
195void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) {
196 // List of moving roots.
197 typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
198 std::list<ArrayPair> moving_roots;
199
200 for (auto it : array_logs_) {
201 mirror::Array* old_root = it.first;
202 if (old_root->IsObjectArray()) {
203 it.second.VisitRoots(callback, arg);
204 }
Mathieu Chartier815873e2014-02-13 18:02:13 -0800205 mirror::Array* new_root = old_root;
206 callback(reinterpret_cast<mirror::Object**>(&new_root), arg, 0, kRootUnknown);
Sebastien Hertzd2fe10a2014-01-15 10:20:56 +0100207 if (new_root != old_root) {
208 moving_roots.push_back(std::make_pair(old_root, new_root));
209 }
210 }
211
212 // Update array logs with moving roots.
213 for (const ArrayPair& pair : moving_roots) {
214 mirror::Array* old_root = pair.first;
215 mirror::Array* new_root = pair.second;
216 auto old_root_it = array_logs_.find(old_root);
217 CHECK(old_root_it != array_logs_.end());
218 CHECK(array_logs_.find(new_root) == array_logs_.end());
219 array_logs_.insert(std::make_pair(new_root, old_root_it->second));
220 array_logs_.erase(old_root_it);
221 }
222}
223
224void Transaction::VisitStringLogs(RootCallback* callback, void* arg) {
225 for (InternStringLog& log : intern_string_logs_) {
226 log.VisitRoots(callback, arg);
227 }
228}
229
230void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
231 auto it = field_values_.find(offset.Uint32Value());
232 if (it == field_values_.end()) {
233 ObjectLog::FieldValue field_value;
234 field_value.value = value;
235 field_value.is_volatile = is_volatile;
236 field_value.kind = ObjectLog::k32Bits;
237 field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
238 }
239}
240
241void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
242 auto it = field_values_.find(offset.Uint32Value());
243 if (it == field_values_.end()) {
244 ObjectLog::FieldValue field_value;
245 field_value.value = value;
246 field_value.is_volatile = is_volatile;
247 field_value.kind = ObjectLog::k64Bits;
248 field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
249 }
250}
251
252void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
253 auto it = field_values_.find(offset.Uint32Value());
254 if (it == field_values_.end()) {
255 ObjectLog::FieldValue field_value;
256 field_value.value = reinterpret_cast<uintptr_t>(obj);
257 field_value.is_volatile = is_volatile;
258 field_value.kind = ObjectLog::kReference;
259 field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
260 }
261}
262
263void Transaction::ObjectLog::Undo(mirror::Object* obj) {
264 for (auto& it : field_values_) {
265 // Garbage collector needs to access object's class and array's length. So we don't rollback
266 // these values.
267 MemberOffset field_offset(it.first);
268 if (field_offset.Uint32Value() == mirror::Class::ClassOffset().Uint32Value()) {
269 // Skip Object::class field.
270 continue;
271 }
272 if (obj->IsArrayInstance() &&
273 field_offset.Uint32Value() == mirror::Array::LengthOffset().Uint32Value()) {
274 // Skip Array::length field.
275 continue;
276 }
277 FieldValue& field_value = it.second;
278 UndoFieldWrite(obj, field_offset, field_value);
279 }
280}
281
282void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
283 const FieldValue& field_value) {
284 // TODO We may want to abort a transaction while still being in transaction mode. In this case,
285 // we'd need to disable the check.
286 constexpr bool kCheckTransaction = true;
287 switch (field_value.kind) {
288 case k32Bits:
289 obj->SetField32<false, kCheckTransaction>(field_offset, static_cast<uint32_t>(field_value.value),
290 field_value.is_volatile);
291 break;
292 case k64Bits:
293 obj->SetField64<false, kCheckTransaction>(field_offset, field_value.value,
294 field_value.is_volatile);
295 break;
296 case kReference:
297 obj->SetFieldObject<false, kCheckTransaction>(field_offset,
298 reinterpret_cast<mirror::Object*>(field_value.value),
299 field_value.is_volatile);
300 break;
301 default:
302 LOG(FATAL) << "Unknown value kind " << field_value.kind;
303 break;
304 }
305}
306
307void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) {
308 for (auto it : field_values_) {
309 FieldValue& field_value = it.second;
310 if (field_value.kind == ObjectLog::kReference) {
Mathieu Chartier815873e2014-02-13 18:02:13 -0800311 mirror::Object* obj =
312 reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
313 callback(&obj, arg, 0, kRootUnknown);
314 field_value.value = reinterpret_cast<uintptr_t>(obj);
Sebastien Hertzd2fe10a2014-01-15 10:20:56 +0100315 }
316 }
317}
318
319void Transaction::InternStringLog::Undo(InternTable* intern_table) {
320 DCHECK(intern_table != nullptr);
321 switch (string_op_) {
322 case InternStringLog::kInsert: {
323 switch (string_kind_) {
324 case InternStringLog::kStrongString:
325 intern_table->RemoveStrongFromTransaction(str_, hash_code_);
326 break;
327 case InternStringLog::kWeakString:
328 intern_table->RemoveWeakFromTransaction(str_, hash_code_);
329 break;
330 default:
331 LOG(FATAL) << "Unknown interned string kind";
332 break;
333 }
334 break;
335 }
336 case InternStringLog::kRemove: {
337 switch (string_kind_) {
338 case InternStringLog::kStrongString:
339 intern_table->InsertStrongFromTransaction(str_, hash_code_);
340 break;
341 case InternStringLog::kWeakString:
342 intern_table->InsertWeakFromTransaction(str_, hash_code_);
343 break;
344 default:
345 LOG(FATAL) << "Unknown interned string kind";
346 break;
347 }
348 break;
349 }
350 default:
351 LOG(FATAL) << "Unknown interned string op";
352 break;
353 }
354}
355
356void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
Mathieu Chartier815873e2014-02-13 18:02:13 -0800357 callback(reinterpret_cast<mirror::Object**>(&str_), arg, 0, kRootInternedString);
Sebastien Hertzd2fe10a2014-01-15 10:20:56 +0100358}
359
360void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
361 auto it = array_values_.find(index);
362 if (it == array_values_.end()) {
363 array_values_.insert(std::make_pair(index, value));
364 }
365}
366
367void Transaction::ArrayLog::Undo(mirror::Array* array) {
368 DCHECK(array != nullptr);
369 DCHECK(array->IsArrayInstance());
370 Primitive::Type type = array->GetClass()->GetComponentType()->GetPrimitiveType();
371 for (auto it : array_values_) {
372 UndoArrayWrite(array, type, it.first, it.second);
373 }
374}
375
376void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Primitive::Type array_type,
377 size_t index, uint64_t value) {
378 // TODO We may want to abort a transaction while still being in transaction mode. In this case,
379 // we'd need to disable the check.
380 switch (array_type) {
381 case Primitive::kPrimBoolean:
382 array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value));
383 break;
384 case Primitive::kPrimByte:
385 array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value));
386 break;
387 case Primitive::kPrimChar:
388 array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value));
389 break;
390 case Primitive::kPrimShort:
391 array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value));
392 break;
393 case Primitive::kPrimInt:
394 array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value));
395 break;
396 case Primitive::kPrimFloat:
397 array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value));
398 break;
399 case Primitive::kPrimLong:
400 array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value));
401 break;
402 case Primitive::kPrimDouble:
403 array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value));
404 break;
405 case Primitive::kPrimNot: {
406 mirror::ObjectArray<mirror::Object>* obj_array = array->AsObjectArray<mirror::Object>();
407 obj_array->SetWithoutChecks<false>(index, reinterpret_cast<mirror::Object*>(
408 static_cast<uintptr_t>(value)));
409 break;
410 }
411 default:
412 LOG(FATAL) << "Unsupported type " << array_type;
413 }
414}
415
416void Transaction::ArrayLog::VisitRoots(RootCallback* callback, void* arg) {
417 for (auto& it : array_values_) {
418 mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(it.second));
Mathieu Chartier815873e2014-02-13 18:02:13 -0800419 callback(&obj, arg, 0, kRootUnknown);
420 it.second = reinterpret_cast<uintptr_t>(obj);
Sebastien Hertzd2fe10a2014-01-15 10:20:56 +0100421 }
422}
423
424} // namespace art