diff options
Diffstat (limited to 'runtime/transaction.cc')
-rw-r--r-- | runtime/transaction.cc | 419 |
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 |