summaryrefslogtreecommitdiffstats
path: root/runtime/transaction.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/transaction.cc')
-rw-r--r--runtime/transaction.cc419
1 files changed, 419 insertions, 0 deletions
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
new file mode 100644
index 0000000000..6adcfec9f6
--- /dev/null
+++ b/runtime/transaction.cc
@@ -0,0 +1,419 @@
+/*
+ * 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 "transaction.h"
+
+#include "base/stl_util.h"
+#include "base/logging.h"
+#include "gc/accounting/card_table-inl.h"
+#include "intern_table.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+
+#include <list>
+
+namespace art {
+
+// TODO: remove (only used for debugging purpose).
+static constexpr bool kEnableTransactionStats = false;
+
+Transaction::Transaction() : log_lock_("transaction log lock", kTransactionLogLock) {
+ CHECK(Runtime::Current()->IsCompiler());
+}
+
+Transaction::~Transaction() {
+ if (kEnableTransactionStats) {
+ MutexLock mu(Thread::Current(), log_lock_);
+ size_t objects_count = object_logs_.size();
+ size_t field_values_count = 0;
+ for (auto it : object_logs_) {
+ field_values_count += it.second.Size();
+ }
+ size_t array_count = array_logs_.size();
+ size_t array_values_count = 0;
+ for (auto it : array_logs_) {
+ array_values_count += it.second.Size();
+ }
+ size_t string_count = intern_string_logs_.size();
+ LOG(INFO) << "Transaction::~Transaction"
+ << ": objects_count=" << objects_count
+ << ", field_values_count=" << field_values_count
+ << ", array_count=" << array_count
+ << ", array_values_count=" << array_values_count
+ << ", string_count=" << string_count;
+ }
+}
+
+void Transaction::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
+ bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.Log32BitsValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
+ bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.Log64BitsValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+ mirror::Object* value, bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.LogReferenceValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) {
+ DCHECK(array != nullptr);
+ DCHECK(array->IsArrayInstance());
+ MutexLock mu(Thread::Current(), log_lock_);
+ ArrayLog& array_log = array_logs_[array];
+ array_log.LogValue(index, value);
+}
+
+void Transaction::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) {
+ DCHECK(s != nullptr);
+ InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kInsert);
+ LogInternedString(log);
+}
+
+void Transaction::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) {
+ DCHECK(s != nullptr);
+ InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kInsert);
+ LogInternedString(log);
+}
+
+void Transaction::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) {
+ InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kRemove);
+ LogInternedString(log);
+}
+
+void Transaction::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) {
+ InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kRemove);
+ LogInternedString(log);
+}
+
+void Transaction::LogInternedString(InternStringLog& log) {
+ Locks::intern_table_lock_->AssertExclusiveHeld(Thread::Current());
+ MutexLock mu(Thread::Current(), log_lock_);
+ intern_string_logs_.push_front(log);
+}
+
+void Transaction::Abort() {
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ Thread* self = Thread::Current();
+ self->AssertNoPendingException();
+ MutexLock mu1(self, *Locks::intern_table_lock_);
+ MutexLock mu2(self, log_lock_);
+ UndoObjectModifications();
+ UndoArrayModifications();
+ UndoInternStringTableModifications();
+}
+
+void Transaction::UndoObjectModifications() {
+ // TODO we may not need to restore objects allocated during this transaction. Or we could directly
+ // remove them from the heap.
+ for (auto it : object_logs_) {
+ it.second.Undo(it.first);
+ }
+ object_logs_.clear();
+}
+
+void Transaction::UndoArrayModifications() {
+ // TODO we may not need to restore array allocated during this transaction. Or we could directly
+ // remove them from the heap.
+ for (auto it : array_logs_) {
+ it.second.Undo(it.first);
+ }
+ array_logs_.clear();
+}
+
+void Transaction::UndoInternStringTableModifications() {
+ InternTable* const intern_table = Runtime::Current()->GetInternTable();
+ // We want to undo each operation from the most recent to the oldest. List has been filled so the
+ // most recent operation is at list begin so just have to iterate over it.
+ for (InternStringLog& string_log : intern_string_logs_) {
+ string_log.Undo(intern_table);
+ }
+ intern_string_logs_.clear();
+}
+
+void Transaction::VisitRoots(RootCallback* callback, void* arg) {
+ LOG(INFO) << "Transaction::VisitRoots";
+ MutexLock mu(Thread::Current(), log_lock_);
+ VisitObjectLogs(callback, arg);
+ VisitArrayLogs(callback, arg);
+ VisitStringLogs(callback, arg);
+}
+
+void Transaction::VisitObjectLogs(RootCallback* callback, void* arg) {
+ // List of moving roots.
+ typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair;
+ std::list<ObjectPair> moving_roots;
+
+ // Visit roots.
+ for (auto it : object_logs_) {
+ it.second.VisitRoots(callback, arg);
+ mirror::Object* old_root = it.first;
+ mirror::Object* new_root = callback(old_root, arg, 0, kRootUnknown);
+ if (new_root != old_root) {
+ moving_roots.push_back(std::make_pair(old_root, new_root));
+ }
+ }
+
+ // Update object logs with moving roots.
+ for (const ObjectPair& pair : moving_roots) {
+ mirror::Object* old_root = pair.first;
+ mirror::Object* new_root = pair.second;
+ auto old_root_it = object_logs_.find(old_root);
+ CHECK(old_root_it != object_logs_.end());
+ CHECK(object_logs_.find(new_root) == object_logs_.end());
+ object_logs_.insert(std::make_pair(new_root, old_root_it->second));
+ object_logs_.erase(old_root_it);
+ }
+}
+
+void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) {
+ // List of moving roots.
+ typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
+ std::list<ArrayPair> moving_roots;
+
+ for (auto it : array_logs_) {
+ mirror::Array* old_root = it.first;
+ if (old_root->IsObjectArray()) {
+ it.second.VisitRoots(callback, arg);
+ }
+ mirror::Array* new_root = down_cast<mirror::Array*>(callback(old_root, arg, 0, kRootUnknown));
+ if (new_root != old_root) {
+ moving_roots.push_back(std::make_pair(old_root, new_root));
+ }
+ }
+
+ // Update array logs with moving roots.
+ for (const ArrayPair& pair : moving_roots) {
+ mirror::Array* old_root = pair.first;
+ mirror::Array* new_root = pair.second;
+ auto old_root_it = array_logs_.find(old_root);
+ CHECK(old_root_it != array_logs_.end());
+ CHECK(array_logs_.find(new_root) == array_logs_.end());
+ array_logs_.insert(std::make_pair(new_root, old_root_it->second));
+ array_logs_.erase(old_root_it);
+ }
+}
+
+void Transaction::VisitStringLogs(RootCallback* callback, void* arg) {
+ for (InternStringLog& log : intern_string_logs_) {
+ log.VisitRoots(callback, arg);
+ }
+}
+
+void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
+ auto it = field_values_.find(offset.Uint32Value());
+ if (it == field_values_.end()) {
+ ObjectLog::FieldValue field_value;
+ field_value.value = value;
+ field_value.is_volatile = is_volatile;
+ field_value.kind = ObjectLog::k32Bits;
+ field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+ }
+}
+
+void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
+ auto it = field_values_.find(offset.Uint32Value());
+ if (it == field_values_.end()) {
+ ObjectLog::FieldValue field_value;
+ field_value.value = value;
+ field_value.is_volatile = is_volatile;
+ field_value.kind = ObjectLog::k64Bits;
+ field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+ }
+}
+
+void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
+ auto it = field_values_.find(offset.Uint32Value());
+ if (it == field_values_.end()) {
+ ObjectLog::FieldValue field_value;
+ field_value.value = reinterpret_cast<uintptr_t>(obj);
+ field_value.is_volatile = is_volatile;
+ field_value.kind = ObjectLog::kReference;
+ field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+ }
+}
+
+void Transaction::ObjectLog::Undo(mirror::Object* obj) {
+ for (auto& it : field_values_) {
+ // Garbage collector needs to access object's class and array's length. So we don't rollback
+ // these values.
+ MemberOffset field_offset(it.first);
+ if (field_offset.Uint32Value() == mirror::Class::ClassOffset().Uint32Value()) {
+ // Skip Object::class field.
+ continue;
+ }
+ if (obj->IsArrayInstance() &&
+ field_offset.Uint32Value() == mirror::Array::LengthOffset().Uint32Value()) {
+ // Skip Array::length field.
+ continue;
+ }
+ FieldValue& field_value = it.second;
+ UndoFieldWrite(obj, field_offset, field_value);
+ }
+}
+
+void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
+ const FieldValue& field_value) {
+ // TODO We may want to abort a transaction while still being in transaction mode. In this case,
+ // we'd need to disable the check.
+ constexpr bool kCheckTransaction = true;
+ switch (field_value.kind) {
+ case k32Bits:
+ obj->SetField32<false, kCheckTransaction>(field_offset, static_cast<uint32_t>(field_value.value),
+ field_value.is_volatile);
+ break;
+ case k64Bits:
+ obj->SetField64<false, kCheckTransaction>(field_offset, field_value.value,
+ field_value.is_volatile);
+ break;
+ case kReference:
+ obj->SetFieldObject<false, kCheckTransaction>(field_offset,
+ reinterpret_cast<mirror::Object*>(field_value.value),
+ field_value.is_volatile);
+ break;
+ default:
+ LOG(FATAL) << "Unknown value kind " << field_value.kind;
+ break;
+ }
+}
+
+void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) {
+ for (auto it : field_values_) {
+ FieldValue& field_value = it.second;
+ if (field_value.kind == ObjectLog::kReference) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
+ field_value.value = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
+ }
+ }
+}
+
+void Transaction::InternStringLog::Undo(InternTable* intern_table) {
+ DCHECK(intern_table != nullptr);
+ switch (string_op_) {
+ case InternStringLog::kInsert: {
+ switch (string_kind_) {
+ case InternStringLog::kStrongString:
+ intern_table->RemoveStrongFromTransaction(str_, hash_code_);
+ break;
+ case InternStringLog::kWeakString:
+ intern_table->RemoveWeakFromTransaction(str_, hash_code_);
+ break;
+ default:
+ LOG(FATAL) << "Unknown interned string kind";
+ break;
+ }
+ break;
+ }
+ case InternStringLog::kRemove: {
+ switch (string_kind_) {
+ case InternStringLog::kStrongString:
+ intern_table->InsertStrongFromTransaction(str_, hash_code_);
+ break;
+ case InternStringLog::kWeakString:
+ intern_table->InsertWeakFromTransaction(str_, hash_code_);
+ break;
+ default:
+ LOG(FATAL) << "Unknown interned string kind";
+ break;
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unknown interned string op";
+ break;
+ }
+}
+
+void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
+ str_ = down_cast<mirror::String*>(callback(str_, arg, 0, kRootInternedString));
+}
+
+void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
+ auto it = array_values_.find(index);
+ if (it == array_values_.end()) {
+ array_values_.insert(std::make_pair(index, value));
+ }
+}
+
+void Transaction::ArrayLog::Undo(mirror::Array* array) {
+ DCHECK(array != nullptr);
+ DCHECK(array->IsArrayInstance());
+ Primitive::Type type = array->GetClass()->GetComponentType()->GetPrimitiveType();
+ for (auto it : array_values_) {
+ UndoArrayWrite(array, type, it.first, it.second);
+ }
+}
+
+void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Primitive::Type array_type,
+ size_t index, uint64_t value) {
+ // TODO We may want to abort a transaction while still being in transaction mode. In this case,
+ // we'd need to disable the check.
+ switch (array_type) {
+ case Primitive::kPrimBoolean:
+ array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value));
+ break;
+ case Primitive::kPrimByte:
+ array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value));
+ break;
+ case Primitive::kPrimChar:
+ array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value));
+ break;
+ case Primitive::kPrimShort:
+ array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value));
+ break;
+ case Primitive::kPrimInt:
+ array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value));
+ break;
+ case Primitive::kPrimFloat:
+ array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value));
+ break;
+ case Primitive::kPrimLong:
+ array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value));
+ break;
+ case Primitive::kPrimDouble:
+ array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value));
+ break;
+ case Primitive::kPrimNot: {
+ mirror::ObjectArray<mirror::Object>* obj_array = array->AsObjectArray<mirror::Object>();
+ obj_array->SetWithoutChecks<false>(index, reinterpret_cast<mirror::Object*>(
+ static_cast<uintptr_t>(value)));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unsupported type " << array_type;
+ }
+}
+
+void Transaction::ArrayLog::VisitRoots(RootCallback* callback, void* arg) {
+ for (auto& it : array_values_) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(it.second));
+ it.second = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
+ }
+}
+
+} // namespace art