A bit further along the track of multiple threads.
This also makes valgrind happy, and tsan mostly happy. (The heap, it turns out,
doesn't have a lock yet.)
The DexVerifier changes are just to make valgrind happy, and the code a little
less unidiomatic.
Change-Id: Ic5d436b4863b9d7088eb0b8fe9d32308919899d8
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 14f4883..b533a11 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -633,7 +633,7 @@
} else {
klass = AllocClass(SizeOfClass(dex_file, dex_class_def));
}
- if (!klass->IsLinked()) {
+ if (!klass->IsResolved()) {
klass->SetDexCache(dex_cache);
LoadClass(dex_file, dex_class_def, klass, class_loader);
// Check for a pending exception during load
@@ -663,27 +663,27 @@
}
CHECK(klass->IsLoaded());
// Link the class (if necessary)
- CHECK(!klass->IsLinked());
+ CHECK(!klass->IsResolved());
if (!LinkClass(klass)) {
// Linking failed.
// TODO: CHECK(self->IsExceptionPending());
lock.NotifyAll();
return NULL;
}
- CHECK(klass->IsLinked());
+ CHECK(klass->IsResolved());
}
}
}
// Link the class if it has not already been linked.
- if (!klass->IsLinked() && !klass->IsErroneous()) {
+ if (!klass->IsResolved() && !klass->IsErroneous()) {
ObjectLock lock(klass);
// Check for circular dependencies between classes.
- if (!klass->IsLinked() && klass->GetClinitThreadId() == self->GetTid()) {
+ if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) {
self->ThrowNewException("Ljava/lang/ClassCircularityError;", NULL); // TODO: detail
return NULL;
}
// Wait for the pending initialization to complete.
- while (!klass->IsLinked() && !klass->IsErroneous()) {
+ while (!klass->IsResolved() && !klass->IsErroneous()) {
lock.Wait();
}
}
@@ -692,7 +692,7 @@
return NULL;
}
// Return the loaded class. No exceptions should be pending.
- CHECK(klass->IsLinked());
+ CHECK(klass->IsResolved());
CHECK(!self->IsExceptionPending());
return klass;
}
@@ -1428,6 +1428,19 @@
return klass;
}
+void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+ Class* c, std::map<int, Field*>& field_map) {
+ const ClassLoader* cl = c->GetClassLoader();
+ const byte* class_data = dex_file.GetClassData(dex_class_def);
+ DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data);
+ uint32_t last_idx = 0;
+ for (size_t i = 0; i < header.static_fields_size_; ++i) {
+ DexFile::Field dex_field;
+ dex_file.dexReadClassDataField(&class_data, &dex_field, &last_idx);
+ field_map[i] = ResolveField(dex_file, dex_field.field_idx_, c->GetDexCache(), cl, true);
+ }
+}
+
void ClassLinker::InitializeStaticFields(Class* klass) {
size_t num_static_fields = klass->NumStaticFields();
if (num_static_fields == 0) {
@@ -1442,6 +1455,11 @@
const DexFile& dex_file = FindDexFile(dex_cache);
const DexFile::ClassDef* dex_class_def = dex_file.FindClassDef(descriptor);
CHECK(dex_class_def != NULL);
+
+ // We reordered the fields, so we need to be able to map the field indexes to the right fields.
+ std::map<int, Field*> field_map;
+ ConstructFieldMap(dex_file, *dex_class_def, klass, field_map);
+
const byte* addr = dex_file.GetEncodedArray(*dex_class_def);
if (addr == NULL) {
// All this class' static fields have default values.
@@ -1449,7 +1467,7 @@
}
size_t array_size = DecodeUnsignedLeb128(&addr);
for (size_t i = 0; i < array_size; ++i) {
- Field* field = klass->GetStaticField(i);
+ Field* field = field_map[i];
JValue value;
DexFile::ValueType type = dex_file.ReadEncodedValue(&addr, &value);
switch (type) {
@@ -1573,7 +1591,7 @@
#ifndef NDEBUG
// Ensure super classes are fully resolved prior to resolving fields..
while (super != NULL) {
- CHECK(super->IsLinked());
+ CHECK(super->IsResolved());
super = super->GetSuperClass();
}
#endif
@@ -1818,7 +1836,7 @@
return success;
}
-bool ClassLinker::LinkFields(Class *klass, bool instance) {
+bool ClassLinker::LinkFields(Class* klass, bool instance) {
size_t num_fields =
instance ? klass->NumInstanceFields() : klass->NumStaticFields();
@@ -1833,7 +1851,7 @@
if (instance) {
Class* super_class = klass->GetSuperClass();
if (super_class != NULL) {
- CHECK(super_class->IsLinked());
+ CHECK(super_class->IsResolved());
field_offset = MemberOffset(super_class->GetObjectSize());
if (field_offset.Uint32Value() == 0u) {
field_offset = OFFSET_OF_OBJECT_MEMBER(DataObject, fields_);
diff --git a/src/class_linker.h b/src/class_linker.h
index 4f4ffc7..fe375c2 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -185,6 +185,9 @@
void AppendToBootClassPath(const DexFile& dex_file);
void AppendToBootClassPath(const DexFile& dex_file, DexCache* dex_cache);
+ void ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+ Class* c, std::map<int, Field*>& field_map);
+
size_t SizeOfClass(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def);
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 71cb22e..607382b 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -40,7 +40,7 @@
EXPECT_TRUE(primitive->GetStatus() == Class::kStatusInitialized);
EXPECT_FALSE(primitive->IsErroneous());
EXPECT_TRUE(primitive->IsVerified());
- EXPECT_TRUE(primitive->IsLinked());
+ EXPECT_TRUE(primitive->IsResolved());
EXPECT_FALSE(primitive->IsArrayInstance());
EXPECT_FALSE(primitive->IsArrayClass());
EXPECT_EQ(0, primitive->GetArrayRank());
@@ -81,7 +81,7 @@
EXPECT_TRUE(array->GetStatus() == Class::kStatusInitialized);
EXPECT_FALSE(array->IsErroneous());
EXPECT_TRUE(array->IsVerified());
- EXPECT_TRUE(array->IsLinked());
+ EXPECT_TRUE(array->IsResolved());
EXPECT_FALSE(array->IsArrayInstance());
EXPECT_TRUE(array->IsArrayClass());
EXPECT_LE(1, array->GetArrayRank());
@@ -138,10 +138,9 @@
EXPECT_TRUE(klass->GetSuperClass() != NULL);
}
EXPECT_TRUE(klass->GetDexCache() != NULL);
- EXPECT_EQ(Class::kStatusResolved, klass->GetStatus());
+ EXPECT_TRUE(klass->IsResolved());
EXPECT_FALSE(klass->IsErroneous());
- EXPECT_FALSE(klass->IsVerified());
- EXPECT_TRUE(klass->IsLinked());
+ EXPECT_TRUE(klass->IsResolved());
EXPECT_TRUE(klass->IsLoaded());
EXPECT_FALSE(klass->IsArrayClass());
EXPECT_EQ(0, klass->GetArrayRank());
@@ -309,8 +308,8 @@
EXPECT_FALSE(JavaLangObject->HasSuperClass());
EXPECT_TRUE(JavaLangObject->GetClassLoader() == NULL);
EXPECT_FALSE(JavaLangObject->IsErroneous());
- EXPECT_FALSE(JavaLangObject->IsVerified());
- EXPECT_TRUE(JavaLangObject->IsLinked());
+ EXPECT_TRUE(JavaLangObject->IsVerified());
+ EXPECT_TRUE(JavaLangObject->IsResolved());
EXPECT_FALSE(JavaLangObject->IsArrayInstance());
EXPECT_FALSE(JavaLangObject->IsArrayClass());
EXPECT_EQ(0, JavaLangObject->GetArrayRank());
@@ -339,7 +338,7 @@
EXPECT_TRUE(MyClass->GetStatus() == Class::kStatusResolved);
EXPECT_FALSE(MyClass->IsErroneous());
EXPECT_FALSE(MyClass->IsVerified());
- EXPECT_TRUE(MyClass->IsLinked());
+ EXPECT_TRUE(MyClass->IsResolved());
EXPECT_FALSE(MyClass->IsArrayInstance());
EXPECT_FALSE(MyClass->IsArrayClass());
EXPECT_EQ(0, JavaLangObject->GetArrayRank());
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index df9d7cb..0a93109 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -12,7 +12,6 @@
#include "logging.h"
#include "runtime.h"
#include "stringpiece.h"
-#include "UniquePtr.h"
namespace art {
@@ -125,7 +124,7 @@
const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
- const DexFile::CodeItem *code_item =
+ const DexFile::CodeItem* code_item =
dex_file.GetCodeItem(method->GetCodeItemOffset());
/*
@@ -158,8 +157,7 @@
/*
* Allocate and initialize an array to hold instruction data.
*/
- UniquePtr<InsnFlags> insn_flags(new InsnFlags[code_item->insns_size_]());
- vdata.insn_flags_ = insn_flags.get();
+ vdata.insn_flags_.reset(new InsnFlags[code_item->insns_size_]());
/*
* Run through the instructions and see if the width checks out.
@@ -194,7 +192,7 @@
bool DexVerifier::VerifyInstructions(VerifierData* vdata) {
const DexFile::CodeItem* code_item = vdata->code_item_;
- InsnFlags* insn_flags = vdata->insn_flags_;
+ InsnFlags* insn_flags = vdata->insn_flags_.get();
const byte* ptr = reinterpret_cast<const byte*>(code_item->insns_);
const Instruction* inst = Instruction::At(ptr);
@@ -227,7 +225,7 @@
const Instruction* inst, uint32_t code_offset) {
const DexFile* dex_file = vdata->dex_file_;
const DexFile::CodeItem* code_item = vdata->code_item_;
- InsnFlags* insn_flags = vdata->insn_flags_;
+ InsnFlags* insn_flags = vdata->insn_flags_.get();
Instruction::DecodedInstruction dec_insn(inst);
bool result = true;
@@ -330,14 +328,13 @@
return false;
}
- vdata->register_lines_ = reg_table.register_lines_;
+ vdata->register_lines_ = reg_table.register_lines_.get();
/* Allocate a map to hold the classes of uninitialized instances. */
- UniquePtr<UninitInstanceMap> uninit_map(CreateUninitInstanceMap(vdata));
- vdata->uninit_map_ = uninit_map.get();
+ vdata->uninit_map_.reset(CreateUninitInstanceMap(vdata));
/* Initialize register types of method arguments. */
- if (!SetTypesFromSignature(vdata, reg_table.register_lines_[0].reg_types_)) {
+ if (!SetTypesFromSignature(vdata, reg_table.register_lines_[0].reg_types_.get())) {
LOG(ERROR) << "VFY: bad signature '"
<< method->GetSignature()->ToModifiedUtf8() << "' for "
<< method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
@@ -359,7 +356,7 @@
bool DexVerifier::ComputeWidthsAndCountOps(VerifierData* vdata) {
const uint16_t* insns = vdata->code_item_->insns_;
uint32_t insns_size = vdata->code_item_->insns_size_;
- InsnFlags* insn_flags = vdata->insn_flags_;
+ InsnFlags* insn_flags = vdata->insn_flags_.get();
const byte* ptr = reinterpret_cast<const byte*>(insns);
const Instruction* inst = Instruction::At(ptr);
size_t new_instance_count = 0;
@@ -392,7 +389,7 @@
bool DexVerifier::ScanTryCatchBlocks(VerifierData* vdata) {
const DexFile::CodeItem* code_item = vdata->code_item_;
- InsnFlags* insn_flags = vdata->insn_flags_;
+ InsnFlags* insn_flags = vdata->insn_flags_.get();
uint32_t insns_size = code_item->insns_size_;
uint32_t tries_size = code_item->tries_size_;
@@ -821,7 +818,7 @@
bool DexVerifier::InitRegisterTable(VerifierData* vdata,
RegisterTable* reg_table, RegisterTrackingMode track_regs_for) {
const DexFile::CodeItem* code_item = vdata->code_item_;
- InsnFlags* insn_flags = vdata->insn_flags_;
+ InsnFlags* insn_flags = vdata->insn_flags_.get();
uint16_t registers_size = code_item->registers_size_;
uint32_t insns_size = code_item->insns_size_;
uint32_t i;
@@ -832,7 +829,7 @@
* indirection.
*/
reg_table->insn_reg_count_plus_ = registers_size + kExtraRegs;
- reg_table->register_lines_ = new RegisterLine[insns_size]();
+ reg_table->register_lines_.reset(new RegisterLine[insns_size]());
assert(insns_size > 0);
@@ -1050,7 +1047,7 @@
Method* method = vdata->method_;
const DexFile* dex_file = vdata->dex_file_;
const DexFile::CodeItem* code_item = vdata->code_item_;
- UninitInstanceMap* uninit_map = vdata->uninit_map_;
+ UninitInstanceMap* uninit_map = vdata->uninit_map_.get();
int arg_start = code_item->registers_size_ - code_item->ins_size_;
int expected_args = code_item->ins_size_; /* long/double count as two */
@@ -1243,7 +1240,7 @@
RegisterTable* reg_table) {
const Method* method = vdata->method_;
const DexFile::CodeItem* code_item = vdata->code_item_;
- InsnFlags* insn_flags = vdata->insn_flags_;
+ InsnFlags* insn_flags = vdata->insn_flags_.get();
const uint16_t* insns = code_item->insns_;
uint32_t insns_size = code_item->insns_size_;
size_t insn_idx, start_guess;
@@ -1296,7 +1293,7 @@
* a full table) and make sure it actually matches.
*/
RegisterLine* register_line = GetRegisterLine(reg_table, insn_idx);
- if (register_line->reg_types_ != NULL && CompareLineToTable(reg_table,
+ if (register_line->reg_types_.get() != NULL && CompareLineToTable(reg_table,
insn_idx, ®_table->work_line_) != 0) {
Class* klass = method->GetDeclaringClass();
LOG(ERROR) << "HUH? work_line diverged in "
@@ -1378,9 +1375,9 @@
const Method* method = vdata->method_;
Class* klass = method->GetDeclaringClass();
const DexFile::CodeItem* code_item = vdata->code_item_;
- InsnFlags* insn_flags = vdata->insn_flags_;
+ InsnFlags* insn_flags = vdata->insn_flags_.get();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- UninitInstanceMap* uninit_map = vdata->uninit_map_;
+ UninitInstanceMap* uninit_map = vdata->uninit_map_.get();
const uint16_t* insns = code_item->insns_ + insn_idx;
uint32_t insns_size = code_item->insns_size_;
uint32_t registers_size = code_item->registers_size_;
@@ -1437,7 +1434,7 @@
reg_table->insn_reg_count_plus_);
} else {
#ifndef NDEBUG
- memset(reg_table->saved_line_.reg_types_, 0xdd,
+ memset(reg_table->saved_line_.reg_types_.get(), 0xdd,
reg_table->insn_reg_count_plus_ * sizeof(RegType));
#endif
}
@@ -1681,7 +1678,7 @@
* we skip them here); if we can't, then the code path could be
* "live" so we still need to check it.
*/
- if (work_line->monitor_entries_ != NULL)
+ if (work_line->monitor_entries_.get() != NULL)
opcode_flag &= ~Instruction::kThrow;
HandleMonitorExit(work_line, dec_insn.vA_, insn_idx, &failure);
break;
@@ -3450,7 +3447,7 @@
if (!CheckMoveException(code_item->insns_, insn_idx + insn_width))
return false;
- if (GetRegisterLine(reg_table, insn_idx + insn_width)->reg_types_ != NULL) {
+ if (GetRegisterLine(reg_table, insn_idx + insn_width)->reg_types_.get() != NULL) {
/*
* Merge registers into what we have for the next instruction,
* and set the "changed" flag if needed.
@@ -3732,7 +3729,7 @@
return;
}
- if (work_line->monitor_entries_ == NULL) {
+ if (work_line->monitor_entries_.get() == NULL) {
return;
}
@@ -3760,7 +3757,7 @@
return;
}
- if (work_line->monitor_entries_ == NULL) {
+ if (work_line->monitor_entries_.get() == NULL) {
return;
}
@@ -3801,7 +3798,7 @@
int field_idx, VerifyError* failure) {
Method* method = vdata->method_;
const DexFile* dex_file = vdata->dex_file_;
- UninitInstanceMap* uninit_map = vdata->uninit_map_;
+ UninitInstanceMap* uninit_map = vdata->uninit_map_.get();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
const ClassLoader* class_loader =
@@ -4013,7 +4010,7 @@
void DexVerifier::SetRegisterType(RegisterLine* register_line, uint32_t vdst,
RegType new_type) {
- RegType* insn_regs = register_line->reg_types_;
+ RegType* insn_regs = register_line->reg_types_.get();
switch (new_type) {
case kRegTypeUnknown:
@@ -4077,13 +4074,13 @@
/*
* Clear the monitor entry bits for this register.
*/
- if (register_line->monitor_entries_ != NULL)
+ if (register_line->monitor_entries_.get() != NULL)
register_line->monitor_entries_[vdst] = 0;
}
void DexVerifier::VerifyRegisterType(RegisterLine* register_line, uint32_t vsrc,
RegType check_type, VerifyError* failure) {
- const RegType* insn_regs = register_line->reg_types_;
+ const RegType* insn_regs = register_line->reg_types_.get();
RegType src_type = insn_regs[vsrc];
switch (check_type) {
@@ -4189,7 +4186,7 @@
void DexVerifier::MarkRefsAsInitialized(RegisterLine* register_line,
int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type,
VerifyError* failure) {
- RegType* insn_regs = register_line->reg_types_;
+ RegType* insn_regs = register_line->reg_types_.get();
Class* klass = GetUninitInstance(uninit_map,
RegTypeToUninitIndex(uninit_type));
@@ -4216,12 +4213,12 @@
void DexVerifier::MarkUninitRefsAsInvalid(RegisterLine* register_line,
int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type) {
- RegType* insn_regs = register_line->reg_types_;
+ RegType* insn_regs = register_line->reg_types_.get();
for (int i = 0; i < insn_reg_count; i++) {
if (insn_regs[i] == uninit_type) {
insn_regs[i] = kRegTypeConflict;
- if (register_line->monitor_entries_ != NULL)
+ if (register_line->monitor_entries_.get() != NULL)
register_line->monitor_entries_[i] = 0;
}
}
@@ -4237,7 +4234,7 @@
<< " cat=" << (int) cat;
} else {
SetRegisterType(register_line, vdst, type);
- if (cat == kTypeCategoryRef && register_line->monitor_entries_ != NULL) {
+ if (cat == kTypeCategoryRef && register_line->monitor_entries_.get() != NULL) {
register_line->monitor_entries_[vdst] =
register_line->monitor_entries_[vsrc];
}
@@ -4489,7 +4486,7 @@
RegisterTable* reg_table, int next_insn, const RegisterLine* work_line) {
const size_t insn_reg_count_plus = reg_table->insn_reg_count_plus_;
assert(work_line != NULL);
- const RegType* work_regs = work_line->reg_types_;
+ const RegType* work_regs = work_line->reg_types_.get();
if (!InsnIsVisitedOrChanged(insn_flags, next_insn)) {
/*
@@ -4504,9 +4501,9 @@
} else {
/* Merge registers, set Changed only if different */
RegisterLine* target_line = GetRegisterLine(reg_table, next_insn);
- RegType* target_regs = target_line->reg_types_;
- MonitorEntries* work_mon_ents = work_line->monitor_entries_;
- MonitorEntries* target_mon_ents = target_line->monitor_entries_;
+ RegType* target_regs = target_line->reg_types_.get();
+ MonitorEntries* work_mon_ents = work_line->monitor_entries_.get();
+ MonitorEntries* target_mon_ents = target_line->monitor_entries_.get();
bool changed = false;
unsigned int idx;
@@ -4520,7 +4517,7 @@
<< std::hex << next_insn << std::dec;
return false;
}
- if (memcmp(target_line->monitor_stack_, work_line->monitor_stack_,
+ if (memcmp(target_line->monitor_stack_.get(), work_line->monitor_stack_.get(),
target_line->monitor_stack_top_ * sizeof(uint32_t)) != 0) {
LOG(ERROR) << "VFY: mismatched monitor stacks at 0x" << std::hex
<< next_insn << std::dec;
@@ -4667,7 +4664,7 @@
bool DexVerifier::CheckConstructorReturn(const Method* method,
const RegisterLine* register_line, const int insn_reg_count) {
- const RegType* insn_regs = register_line->reg_types_;
+ const RegType* insn_regs = register_line->reg_types_.get();
if (!IsInitMethod(method))
return true;
@@ -4955,7 +4952,7 @@
Method* method = vdata->method_;
const DexFile* dex_file = vdata->dex_file_;
const DexFile::CodeItem* code_item = vdata->code_item_;
- UninitInstanceMap* uninit_map = vdata->uninit_map_;
+ UninitInstanceMap* uninit_map = vdata->uninit_map_.get();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
const ClassLoader* class_loader =
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index b6fbd46..c89dfaf 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -7,6 +7,7 @@
#include "dex_instruction.h"
#include "macros.h"
#include "object.h"
+#include "UniquePtr.h"
namespace art {
@@ -235,32 +236,21 @@
* to the GC).
*/
struct RegisterLine {
- RegType* reg_types_;
- MonitorEntries* monitor_entries_;
- uint32_t* monitor_stack_;
- uint32_t monitor_stack_top_;
+ UniquePtr<RegType[]> reg_types_;
+ UniquePtr<MonitorEntries[]> monitor_entries_;
+ UniquePtr<uint32_t[]> monitor_stack_;
+ uint32_t monitor_stack_top_;
- /* Default constructor. */
- RegisterLine() {
- reg_types_ = NULL;
- monitor_entries_ = NULL;
- monitor_stack_ = NULL;
- monitor_stack_top_ = 0;
- }
-
- /* Default destructor. */
- ~RegisterLine() {
- delete reg_types_;
- delete monitor_entries_;
- delete monitor_stack_;
+ RegisterLine()
+ : reg_types_(NULL), monitor_entries_(NULL), monitor_stack_(NULL), monitor_stack_top_(0) {
}
/* Allocate space for the fields. */
void Alloc(size_t size, bool track_monitors) {
- reg_types_ = new RegType[size]();
+ reg_types_.reset(new RegType[size]());
if (track_monitors) {
- monitor_entries_ = new MonitorEntries[size];
- monitor_stack_ = new uint32_t[kMaxMonitorStackDepth];
+ monitor_entries_.reset(new MonitorEntries[size]);
+ monitor_stack_.reset(new uint32_t[kMaxMonitorStackDepth]);
}
}
};
@@ -272,7 +262,7 @@
* set the pointers for certain addresses, based on instruction widths
* and what we're trying to accomplish.
*/
- RegisterLine* register_lines_;
+ UniquePtr<RegisterLine[]> register_lines_;
/*
* Number of registers we track for each instruction. This is equal
@@ -286,15 +276,7 @@
/* Storage for a register line we're saving for later. */
RegisterLine saved_line_;
- /* Default constructor. */
- RegisterTable() {
- register_lines_ = NULL;
- insn_reg_count_plus_ = 0;
- }
-
- /* Default destructor. */
- ~RegisterTable() {
- delete [] register_lines_;
+ RegisterTable() : register_lines_(NULL), insn_reg_count_plus_(0) {
}
};
@@ -313,17 +295,11 @@
*/
struct UninitInstanceMap {
int num_entries_;
- UninitInstanceMapEntry* map_;
+ UniquePtr<UninitInstanceMapEntry[]> map_;
- /* Basic constructor */
- UninitInstanceMap(int num_entries) {
- num_entries_ = num_entries;
- map_ = new UninitInstanceMapEntry[num_entries]();
- }
-
- /* Default destructor */
- ~UninitInstanceMap() {
- delete map_;
+ UninitInstanceMap(int num_entries)
+ : num_entries_(num_entries),
+ map_(new UninitInstanceMapEntry[num_entries]()) {
}
};
#define kUninitThisArgAddr (-1)
@@ -341,13 +317,13 @@
const DexFile::CodeItem* code_item_;
/* Instruction widths and flags, one entry per code unit. */
- InsnFlags* insn_flags_;
+ UniquePtr<InsnFlags[]> insn_flags_;
/*
* Uninitialized instance map, used for tracking the movement of
* objects that have been allocated but not initialized.
*/
- UninitInstanceMap* uninit_map_;
+ UniquePtr<UninitInstanceMap> uninit_map_;
/*
* Array of RegisterLine structs, one entry per code unit. We only need
@@ -361,7 +337,6 @@
size_t new_instance_count_;
size_t monitor_enter_count_;
- /* Basic constructor. */
VerifierData(Method* method, const DexFile* dex_file,
const DexFile::CodeItem* code_item)
: method_(method), dex_file_(dex_file), code_item_(code_item),
@@ -461,9 +436,9 @@
}
/* Get the class object at the specified index. */
- static inline Class* GetUninitInstance(const UninitInstanceMap* uninit_map,
- int idx) {
- assert(idx >= 0 && idx < uninit_map->num_entries_);
+ static inline Class* GetUninitInstance(const UninitInstanceMap* uninit_map, int idx) {
+ DCHECK_GE(idx, 0);
+ DCHECK_LT(idx, uninit_map->num_entries_);
return uninit_map->map_[idx].klass_;
}
@@ -483,7 +458,7 @@
* (does not expect uninit ref types or "zero").
*/
static Class* RegTypeInitializedReferenceToClass(RegType type) {
- assert(RegTypeIsReference(type) && type != kRegTypeZero);
+ DCHECK(RegTypeIsReference(type) && type != kRegTypeZero);
if ((type & 0x01) == 0) {
return (Class*) type;
} else {
@@ -494,16 +469,16 @@
/* Extract the index into the uninitialized instance map table. */
static inline int RegTypeToUninitIndex(RegType type) {
- assert(RegTypeIsUninitReference(type));
+ DCHECK(RegTypeIsUninitReference(type));
return (type & ~kRegTypeUninitMask) >> kRegTypeUninitShift;
}
/* Convert the reference "type" to a Class pointer. */
static Class* RegTypeReferenceToClass(RegType type,
const UninitInstanceMap* uninit_map) {
- assert(RegTypeIsReference(type) && type != kRegTypeZero);
+ DCHECK(RegTypeIsReference(type) && type != kRegTypeZero);
if (RegTypeIsUninitReference(type)) {
- assert(uninit_map != NULL);
+ DCHECK(uninit_map != NULL);
return GetUninitInstance(uninit_map, RegTypeToUninitIndex(type));
} else {
return (Class*) type;
@@ -755,15 +730,15 @@
/* Copy a register line. */
static inline void CopyRegisterLine(RegisterLine* dst,
const RegisterLine* src, size_t num_regs) {
- memcpy(dst->reg_types_, src->reg_types_, num_regs * sizeof(RegType));
+ memcpy(dst->reg_types_.get(), src->reg_types_.get(), num_regs * sizeof(RegType));
- assert((src->monitor_entries_ == NULL && dst->monitor_entries_ == NULL) ||
- (src->monitor_entries_ != NULL && dst->monitor_entries_ != NULL));
- if (dst->monitor_entries_ != NULL) {
- assert(dst->monitor_stack_ != NULL);
- memcpy(dst->monitor_entries_, src->monitor_entries_,
+ DCHECK((src->monitor_entries_.get() == NULL && dst->monitor_entries_.get() == NULL) ||
+ (src->monitor_entries_.get() != NULL && dst->monitor_entries_.get() != NULL));
+ if (dst->monitor_entries_.get() != NULL) {
+ DCHECK(dst->monitor_stack_.get() != NULL);
+ memcpy(dst->monitor_entries_.get(), src->monitor_entries_.get(),
num_regs * sizeof(MonitorEntries));
- memcpy(dst->monitor_stack_, src->monitor_stack_,
+ memcpy(dst->monitor_stack_.get(), src->monitor_stack_.get(),
kMaxMonitorStackDepth * sizeof(uint32_t));
dst->monitor_stack_top_ = src->monitor_stack_top_;
}
@@ -773,7 +748,7 @@
static inline void CopyLineToTable(RegisterTable* reg_table, int insn_idx,
const RegisterLine* src) {
RegisterLine* dst = GetRegisterLine(reg_table, insn_idx);
- assert(dst->reg_types_ != NULL);
+ DCHECK(dst->reg_types_.get() != NULL);
CopyRegisterLine(dst, src, reg_table->insn_reg_count_plus_);
}
@@ -781,7 +756,7 @@
static inline void CopyLineFromTable(RegisterLine* dst,
const RegisterTable* reg_table, int insn_idx) {
RegisterLine* src = GetRegisterLine(reg_table, insn_idx);
- assert(src->reg_types_ != NULL);
+ DCHECK(src->reg_types_.get() != NULL);
CopyRegisterLine(dst, src, reg_table->insn_reg_count_plus_);
}
@@ -795,12 +770,12 @@
static inline int CompareLineToTable(const RegisterTable* reg_table,
int insn_idx, const RegisterLine* line2) {
const RegisterLine* line1 = GetRegisterLine(reg_table, insn_idx);
- if (line1->monitor_entries_ != NULL) {
+ if (line1->monitor_entries_.get() != NULL) {
int result;
- if (line2->monitor_entries_ == NULL)
+ if (line2->monitor_entries_.get() == NULL)
return 1;
- result = memcmp(line1->monitor_entries_, line2->monitor_entries_,
+ result = memcmp(line1->monitor_entries_.get(), line2->monitor_entries_.get(),
reg_table->insn_reg_count_plus_ * sizeof(MonitorEntries));
if (result != 0) {
LOG(ERROR) << "monitor_entries_ mismatch";
@@ -811,14 +786,14 @@
LOG(ERROR) << "monitor_stack_top_ mismatch";
return result;
}
- result = memcmp(line1->monitor_stack_, line2->monitor_stack_,
+ result = memcmp(line1->monitor_stack_.get(), line2->monitor_stack_.get(),
line1->monitor_stack_top_);
if (result != 0) {
LOG(ERROR) << "monitor_stack_ mismatch";
return result;
}
}
- return memcmp(line1->reg_types_, line2->reg_types_,
+ return memcmp(line1->reg_types_.get(), line2->reg_types_.get(),
reg_table->insn_reg_count_plus_ * sizeof(RegType));
}
#endif
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 8e24e47..2ea6f76 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -442,7 +442,8 @@
CHECK_GE(args.version, JNI_VERSION_1_2);
Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
- runtime->AttachCurrentThread(args.name, p_env, as_daemon);
+ runtime->AttachCurrentThread(args.name, as_daemon);
+ *p_env = Thread::Current()->GetJniEnv();
return JNI_OK;
}
@@ -618,11 +619,17 @@
static jclass FindClass(JNIEnv* env, const char* name) {
ScopedJniThreadState ts(env);
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
std::string descriptor(NormalizeJniClassDescriptor(name));
- // TODO: need to get the appropriate ClassLoader.
- const ClassLoader* cl = ts.Self()->GetClassLoaderOverride();
- Class* c = class_linker->FindClass(descriptor, cl);
+ Class* c = NULL;
+ if (runtime->IsStarted()) {
+ // TODO: need to get the appropriate ClassLoader.
+ const ClassLoader* cl = ts.Self()->GetClassLoaderOverride();
+ c = class_linker->FindClass(descriptor, cl);
+ } else {
+ c = class_linker->FindSystemClass(descriptor);
+ }
return AddLocalReference<jclass>(env, c);
}
diff --git a/src/logging.cc b/src/logging.cc
index c59a099..03788c3 100644
--- a/src/logging.cc
+++ b/src/logging.cc
@@ -17,26 +17,43 @@
#include "logging.h"
#include "runtime.h"
+#include "thread.h"
#include "utils.h"
+namespace {
+
+art::Mutex* GetLoggingLock() {
+ static art::Mutex* lock = art::Mutex::Create("LogMessage lock");
+ return lock;
+}
+
+}
+
LogMessage::~LogMessage() {
+ // Finish constructing the message.
if (errno_ != -1) {
buffer_ << ": " << strerror(errno_);
}
std::string msg(buffer_.str());
- if (msg.find('\n') == std::string::npos) {
- LogLine(msg.c_str());
- } else {
- msg += '\n';
- size_t i = 0;
- while (i < msg.size()) {
- size_t nl = msg.find('\n', i);
- msg[nl] = '\0';
- LogLine(&msg[i]);
- i = nl + 1;
+
+ // Do the actual logging with the lock held.
+ {
+ art::MutexLock mu(GetLoggingLock());
+ if (msg.find('\n') == std::string::npos) {
+ LogLine(msg.c_str());
+ } else {
+ msg += '\n';
+ size_t i = 0;
+ while (i < msg.size()) {
+ size_t nl = msg.find('\n', i);
+ msg[nl] = '\0';
+ LogLine(&msg[i]);
+ i = nl + 1;
+ }
}
}
+ // Abort if necessary.
if (severity_ == FATAL) {
art::Runtime::Abort(file_, line_number_);
}
diff --git a/src/object.cc b/src/object.cc
index d7b6024..7c27ec7 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -196,7 +196,7 @@
}
void Field::SetInt(Object* object, int32_t i) const {
- DCHECK(GetType()->IsPrimitiveInt());
+ DCHECK(GetType()->IsPrimitiveInt()) << PrettyField(this);
Set32(object, i);
}
@@ -273,7 +273,7 @@
}
Class* Method::GetReturnType() const {
- DCHECK(GetDeclaringClass()->IsLinked());
+ DCHECK(GetDeclaringClass()->IsResolved());
// Short-cut
Class* result = GetDexCacheResolvedTypes()->Get(GetReturnTypeIdx());
if (result == NULL) {
diff --git a/src/object.h b/src/object.h
index c0e725c..23ce3c4 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1275,7 +1275,7 @@
}
// Returns true if the class has been linked.
- bool IsLinked() const {
+ bool IsResolved() const {
return GetStatus() >= kStatusResolved;
}
@@ -1630,7 +1630,7 @@
}
Method* GetVirtualMethod(uint32_t i) const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
return GetVirtualMethods()->Get(i);
}
@@ -1647,7 +1647,7 @@
}
ObjectArray<Method>* GetVTable() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
return GetFieldObject<ObjectArray<Method>*>(
OFFSET_OF_OBJECT_MEMBER(Class, vtable_), false);
}
@@ -1745,7 +1745,7 @@
}
size_t GetIFTableCount() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
DCHECK(sizeof(size_t) == sizeof(int32_t));
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, iftable_count_), false);
}
@@ -1757,7 +1757,7 @@
}
InterfaceEntry* GetIFTable() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
return GetFieldPtr<InterfaceEntry*>(
OFFSET_OF_OBJECT_MEMBER(Class, iftable_), false);
}
@@ -1768,7 +1768,7 @@
}
size_t GetIfviPoolCount() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
CHECK(sizeof(size_t) == sizeof(int32_t));
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, ifvi_pool_count_), false);
}
@@ -1780,7 +1780,7 @@
}
uint32_t* GetIfviPool() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
return GetFieldPtr<uint32_t*>(
OFFSET_OF_OBJECT_MEMBER(Class, ifvi_pool_), false);
}
@@ -1820,7 +1820,7 @@
// Returns the number of instance fields containing reference types.
size_t NumReferenceInstanceFields() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
DCHECK(sizeof(size_t) == sizeof(int32_t));
return GetField32(
OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false);
@@ -1840,7 +1840,7 @@
}
uint32_t GetReferenceInstanceOffsets() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
return GetField32(
OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), false);
}
@@ -1854,7 +1854,7 @@
// Returns the number of static fields containing reference types.
size_t NumReferenceStaticFields() const {
- DCHECK(IsLinked());
+ DCHECK(IsResolved());
DCHECK(sizeof(size_t) == sizeof(int32_t));
return GetField32(
OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false);
@@ -2208,7 +2208,7 @@
}
inline uint32_t Method::GetReturnTypeIdx() const {
- DCHECK(GetDeclaringClass()->IsLinked());
+ DCHECK(GetDeclaringClass()->IsResolved());
return GetField32(OFFSET_OF_OBJECT_MEMBER(Method, java_return_type_idx_),
false);
}
@@ -2527,7 +2527,7 @@
}
inline MemberOffset Field::GetOffset() const {
- DCHECK(GetDeclaringClass()->IsLinked());
+ DCHECK(GetDeclaringClass()->IsResolved());
return MemberOffset(
GetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_), false));
}
@@ -2595,7 +2595,7 @@
}
inline uint16_t Method::GetMethodIndex() const {
- DCHECK(GetDeclaringClass()->IsLinked());
+ DCHECK(GetDeclaringClass()->IsResolved());
return GetField16(OFFSET_OF_OBJECT_MEMBER(Method, method_index_), false);
}
diff --git a/src/runtime.cc b/src/runtime.cc
index 2acf2ec..c7ae9c1 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -36,10 +36,11 @@
}
Runtime::~Runtime() {
- // TODO: use smart pointers instead. (we'll need the pimpl idiom.)
+ // Make sure our internal threads are dead before we start tearing down things they're using.
+ delete signal_catcher_;
+
delete class_linker_;
Heap::Destroy();
- delete signal_catcher_;
delete thread_list_;
delete intern_table_;
delete java_vm_;
@@ -336,6 +337,11 @@
void Runtime::Start() {
started_ = true;
+
+ // Finish attaching the main thread.
+ Thread* main_thread = Thread::Current();
+ main_thread->CreatePeer("main", false);
+
instance_->InitLibraries();
instance_->signal_catcher_ = new SignalCatcher;
}
@@ -378,7 +384,9 @@
return false;
}
- thread_list_->Register(Thread::Attach(this, "main", false));
+ // ClassLinker needs an attached thread, but we can't fully attach a thread
+ // without creating objects.
+ Thread::Attach(this, "main", false);
class_linker_ = ClassLinker::Create(options->boot_class_path_,
options->class_path_,
@@ -466,9 +474,8 @@
CHECK_EQ(sigprocmask(SIG_BLOCK, &sigset, NULL), 0);
}
-void Runtime::AttachCurrentThread(const char* name, JNIEnv** penv, bool as_daemon) {
- Thread* t = Thread::Attach(instance_, name, as_daemon);
- thread_list_->Register(t);
+void Runtime::AttachCurrentThread(const char* name, bool as_daemon) {
+ Thread::Attach(instance_, name, as_daemon);
}
void Runtime::DetachCurrentThread() {
diff --git a/src/runtime.h b/src/runtime.h
index db86a96..96513e6 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -85,7 +85,7 @@
static void Abort(const char* file, int line);
// Attaches the current native thread to the runtime.
- void AttachCurrentThread(const char* name, JNIEnv** jni_env, bool as_daemon);
+ void AttachCurrentThread(const char* name, bool as_daemon);
void CallExitHook(jint status);
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index e2e2360..1552180 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -29,11 +29,12 @@
namespace art {
-bool SignalCatcher::halt_ = false;
-
SignalCatcher::SignalCatcher() {
+ lock_ = Mutex::Create("SignalCatcher lock");
+ SetHaltFlag(false);
+
// Create a raw pthread; its start routine will attach to the runtime.
- errno = pthread_create(&thread_, NULL, &Run, NULL);
+ errno = pthread_create(&thread_, NULL, &Run, this);
if (errno != 0) {
PLOG(FATAL) << "pthread_create failed for signal catcher thread";
}
@@ -42,11 +43,21 @@
SignalCatcher::~SignalCatcher() {
// Since we know the thread is just sitting around waiting for signals
// to arrive, send it one.
- halt_ = true;
+ SetHaltFlag(true);
pthread_kill(thread_, SIGQUIT);
pthread_join(thread_, NULL);
}
+void SignalCatcher::SetHaltFlag(bool new_value) {
+ MutexLock mu(lock_);
+ halt_ = new_value;
+}
+
+bool SignalCatcher::ShouldHalt() {
+ MutexLock mu(lock_);
+ return halt_;
+}
+
void SignalCatcher::HandleSigQuit() {
// TODO: suspend all threads
@@ -100,8 +111,11 @@
return signal_number;
}
-void* SignalCatcher::Run(void*) {
- Runtime::Current()->AttachCurrentThread("Signal Catcher", NULL, true);
+void* SignalCatcher::Run(void* arg) {
+ SignalCatcher* signal_catcher = reinterpret_cast<SignalCatcher*>(arg);
+ CHECK(signal_catcher != NULL);
+
+ Runtime::Current()->AttachCurrentThread("Signal Catcher", true);
Thread* self = Thread::Current();
CHECK(self != NULL);
@@ -113,7 +127,7 @@
while (true) {
int signal_number = WaitForSignal(self, mask);
- if (halt_) {
+ if (signal_catcher->ShouldHalt()) {
Runtime::Current()->DetachCurrentThread();
return NULL;
}
diff --git a/src/signal_catcher.h b/src/signal_catcher.h
index a31b9f1..8d91a94 100644
--- a/src/signal_catcher.h
+++ b/src/signal_catcher.h
@@ -21,6 +21,7 @@
namespace art {
+class Mutex;
class Runtime;
class Thread;
@@ -40,7 +41,11 @@
static void* Run(void* arg);
static void HandleSigUsr1();
- static bool halt_;
+ void SetHaltFlag(bool new_value);
+ bool ShouldHalt();
+
+ Mutex* lock_;
+ bool halt_;
pthread_t thread_;
};
diff --git a/src/thread.cc b/src/thread.cc
index 7d909c9..3081d91 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -205,10 +205,26 @@
}
void Mutex::Unlock() {
- DCHECK(HaveLock());
+#ifndef NDEBUG
+ Thread* self = Thread::Current();
+ std::stringstream os;
+ os << "owner=";
+ if (owner_ != NULL) {
+ os << *owner_;
+ } else {
+ os << "NULL";
+ }
+ os << " self=";
+ if (self != NULL) {
+ os << *self;
+ } else {
+ os << "NULL";
+ }
+ DCHECK(HaveLock()) << os.str();
+#endif
+ SetOwner(NULL);
int result = pthread_mutex_unlock(&lock_impl_);
CHECK_EQ(result, 0);
- SetOwner(NULL);
}
bool Mutex::HaveLock() {
@@ -244,7 +260,6 @@
size_t stack_size = runtime->GetStackSize();
Thread* new_thread = new Thread;
- new_thread->InitCpu();
pthread_attr_t attr;
errno = pthread_attr_init(&attr);
@@ -279,25 +294,52 @@
}
Thread* Thread::Attach(const Runtime* runtime, const char* name, bool as_daemon) {
- Thread* thread = new Thread;
- thread->InitCpu();
+ Thread* self = new Thread;
- thread->tid_ = ::art::GetTid();
- thread->handle_ = pthread_self();
- thread->is_daemon_ = as_daemon;
+ self->tid_ = ::art::GetTid();
+ self->handle_ = pthread_self();
+ self->is_daemon_ = as_daemon;
- thread->state_ = kRunnable;
+ self->state_ = kRunnable;
SetThreadName(name);
- errno = pthread_setspecific(Thread::pthread_key_self_, thread);
+ errno = pthread_setspecific(Thread::pthread_key_self_, self);
if (errno != 0) {
PLOG(FATAL) << "pthread_setspecific failed";
}
- thread->jni_env_ = new JNIEnvExt(thread, runtime->GetJavaVM());
+ self->jni_env_ = new JNIEnvExt(self, runtime->GetJavaVM());
- return thread;
+ runtime->GetThreadList()->Register(self);
+
+ // If we're the main thread, ClassLinker won't be created until after we're attached,
+ // so that thread needs a two-stage attach. Regular threads don't need this hack.
+ if (self->thin_lock_id_ != ThreadList::kMainId) {
+ self->CreatePeer(name, as_daemon);
+ }
+
+ return self;
+}
+
+void Thread::CreatePeer(const char* name, bool as_daemon) {
+ ScopedThreadStateChange tsc(Thread::Current(), Thread::kNative);
+
+ JNIEnv* env = jni_env_;
+
+ jobject thread_group = NULL;
+ jobject thread_name = env->NewStringUTF(name);
+ jint thread_priority = 123;
+ jboolean thread_is_daemon = as_daemon;
+
+ jclass c = env->FindClass("java/lang/Thread");
+ LOG(INFO) << "java/lang/Thread=" << (void*)c;
+ jmethodID mid = env->GetMethodID(c, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+ LOG(INFO) << "java/lang/Thread.<init>=" << (void*)mid;
+ jobject o = env->NewObject(c, mid, thread_group, thread_name, thread_priority, thread_is_daemon);
+ LOG(INFO) << "Created new java.lang.Thread " << (void*) o << " decoded=" << (void*) DecodeJObject(o);
+
+ peer_ = DecodeJObject(o);
}
void Thread::Dump(std::ostream& os) const {
@@ -494,6 +536,7 @@
exception_(NULL),
suspend_count_(0),
class_loader_override_(NULL) {
+ InitCpu();
{
ThreadListLock mu;
thin_lock_id_ = Runtime::Current()->GetThreadList()->AllocThreadId();
diff --git a/src/thread.h b/src/thread.h
index b255d6b..df162b9 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -448,6 +448,9 @@
~Thread();
friend class ThreadList; // For ~Thread.
+ void CreatePeer(const char* name, bool as_daemon);
+ friend class Runtime; // For CreatePeer.
+
void DumpState(std::ostream& os) const;
void DumpStack(std::ostream& os) const;
@@ -528,9 +531,9 @@
class ThreadList {
public:
- static const int kMaxThreadId = 0xFFFF;
- static const int kInvalidId = 0;
- static const int kMainId = 1;
+ static const uint32_t kMaxThreadId = 0xFFFF;
+ static const uint32_t kInvalidId = 0;
+ static const uint32_t kMainId = 1;
static ThreadList* Create();