void Transaction::Abort(const std::string& abort_message) { // We may abort more than once if the exception thrown at the time of the // previous abort has been caught during execution of a class initializer. // We just keep the message of the first abort because it will cause the // transaction to be rolled back anyway. if (!aborted_) {
aborted_ = true;
abort_message_ = abort_message;
}
}
void Transaction::ThrowAbortError(Thread* self, const std::string* abort_message) { constbool rethrow = (abort_message == nullptr); if (kIsDebugBuild && rethrow) {
CHECK(IsAborted()) << "Rethrow " << DescriptorToDot(kTransactionAbortErrorDescriptor)
<< " while transaction is not aborted";
} if (rethrow) { // Rethrow an exception with the earlier abort message stored in the transaction.
self->ThrowNewWrappedException(kTransactionAbortErrorDescriptor, GetAbortMessage().c_str());
} else { // Throw an exception with the given abort message.
self->ThrowNewWrappedException(kTransactionAbortErrorDescriptor, abort_message->c_str());
}
}
// Prevent changes in boot image spaces for app or boot image extension. // For boot image there are no boot image spaces and this condition evaluates to false. if (heap_->ObjectIsInBootImageSpace(obj)) { returntrue;
}
// For apps, also prevent writing to other classes. return IsStrict() &&
obj->IsClass() && // no constraint updating instances or arrays
obj != root_; // modifying other classes' static field, fail
}
bool Transaction::WriteValueConstraint(ObjPtr<mirror::Object> value) const { if (value == nullptr) { returnfalse; // We can always store null values.
} if (IsStrict()) { // TODO: Should we restrict writes the same way as for boot image extension? returnfalse;
} elseif (heap_->GetBootImageSpaces().empty()) { returnfalse; // No constraints for boot image.
} else { // Boot image extension.
ObjPtr<mirror::Class> klass = value->IsClass() ? value->AsClass() : value->GetClass(); return !AotClassLinker::CanReferenceInBootImageExtensionOrAppImage(klass, heap_);
}
}
bool Transaction::ReadConstraint(ObjPtr<mirror::Object> obj) const { // Read constraints are checked only for static field reads as there are // no constraints on reading instance fields and array elements.
DCHECK(obj->IsClass()); if (IsStrict()) { return obj != root_; // fail if not self-updating
} else { // For boot image and boot image extension, allow reading any field. returnfalse;
}
}
void Transaction::RecordNewArray(ObjPtr<mirror::Array> array) { if (array->IsObjectArray()) { // `ObjectArray<T>::SetWithoutChecks()` uses `SetFieldObject()` which records value // changes in `object_log_`, so we need to record new object arrays as normal objects.
RecordNewObject(array); return;
}
last_allocated_object_ = array.Ptr();
ArrayLog log(&allocator_);
log.MarkAsNewArray();
array_logs_.Put(array.Ptr(), std::move(log));
}
bool Transaction::ObjectNeedsTransactionRecords(ObjPtr<mirror::Object> obj) { if (obj == last_allocated_object_) { returnfalse;
} auto it = object_logs_.find(obj.Ptr()); return it == object_logs_.end() || !it->second.IsNewObject();
}
bool Transaction::ArrayNeedsTransactionRecords(ObjPtr<mirror::Array> array) { if (array == last_allocated_object_) { returnfalse;
} auto it = array_logs_.find(array.Ptr()); return it == array_logs_.end() || !it->second.IsNewArray();
}
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 (constauto& 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 (constauto& 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 (const InternStringLog& string_log : intern_string_logs_) {
string_log.Undo(intern_table);
}
intern_string_logs_.clear();
}
void Transaction::VisitRoots(RootVisitor* visitor) { // Transactions are used for single-threaded initialization. // This is the only function that should be called from a different thread, // namely the GC thread, and it is called with the mutator lock held exclusively, // so the data structures in the `Transaction` are protected from concurrent use.
DCHECK(Locks::mutator_lock_->IsExclusiveHeld(Thread::Current()));
visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&root_), RootInfo(kRootUnknown));
visitor->VisitRoot(&last_allocated_object_, RootInfo(kRootUnknown));
{ // Create a separate `ArenaStack` for this thread.
ArenaStack arena_stack(Runtime::Current()->GetArenaPool());
VisitObjectLogs(visitor, &arena_stack);
VisitArrayLogs(visitor, &arena_stack);
}
VisitInternStringLogs(visitor);
VisitResolveStringLogs(visitor);
VisitResolveMethodTypeLogs(visitor);
}
void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) { if (is_new_array_) { return;
} // Add a mapping if there is none yet.
array_values_.FindOrAdd(index, value);
}
void Transaction::ArrayLog::Undo(mirror::Array* array) const {
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) const { // 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 = false; switch (array_type) { case Primitive::kPrimBoolean:
array->AsBooleanArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<uint8_t>(value)); break; case Primitive::kPrimByte:
array->AsByteArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<int8_t>(value)); break; case Primitive::kPrimChar:
array->AsCharArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<uint16_t>(value)); break; case Primitive::kPrimShort:
array->AsShortArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<int16_t>(value)); break; case Primitive::kPrimInt:
array->AsIntArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<int32_t>(value)); break; case Primitive::kPrimFloat:
array->AsFloatArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<float>(value)); break; case Primitive::kPrimLong:
array->AsLongArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<int64_t>(value)); break; case Primitive::kPrimDouble:
array->AsDoubleArray()->SetWithoutChecks<false, kCheckTransaction>(
index, static_cast<double>(value)); break; case Primitive::kPrimNot:
LOG(FATAL) << "ObjectArray should be treated as Object";
UNREACHABLE(); default:
LOG(FATAL) << "Unsupported type " << array_type;
UNREACHABLE();
}
}
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.