1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
/*
* Copyright (C) 2023 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 "startup_completed_task.h"
#include "base/systrace.h"
#include "class_linker.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
#include "handle_scope-inl.h"
#include "linear_alloc-inl.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
#include "obj_ptr.h"
#include "runtime_image.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "thread_list.h"
namespace art HIDDEN {
class UnlinkStartupDexCacheVisitor : public DexCacheVisitor {
public:
UnlinkStartupDexCacheVisitor() {}
void Visit(ObjPtr<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_) override {
dex_cache->UnlinkStartupCaches();
}
};
void StartupCompletedTask::Run(Thread* self) {
Runtime* const runtime = Runtime::Current();
if (runtime->NotifyStartupCompleted()) {
// Maybe generate a runtime app image. If the runtime is debuggable, boot
// classpath classes can be dynamically changed, so don't bother generating an
// image.
if (!runtime->IsJavaDebuggable()) {
std::string compiler_filter;
std::string compilation_reason;
std::string primary_apk_path = runtime->GetAppInfo()->GetPrimaryApkPath();
runtime->GetAppInfo()->GetPrimaryApkOptimizationStatus(&compiler_filter, &compilation_reason);
CompilerFilter::Filter filter;
if (CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter) &&
!CompilerFilter::IsAotCompilationEnabled(filter) &&
!runtime->GetHeap()->HasAppImageSpaceFor(primary_apk_path)) {
std::string error_msg;
if (!RuntimeImage::WriteImageToDisk(&error_msg)) {
LOG(DEBUG) << "Could not write temporary image to disk " << error_msg;
}
}
}
ScopedObjectAccess soa(self);
DeleteStartupDexCaches(self, /* called_by_gc= */ false);
}
// Delete the thread pool used for app image loading since startup is assumed to be completed.
ScopedTrace trace2("Delete thread pool");
Runtime::Current()->DeleteThreadPool();
}
void StartupCompletedTask::DeleteStartupDexCaches(Thread* self, bool called_by_gc) {
VLOG(startup) << "StartupCompletedTask running";
Runtime* const runtime = Runtime::Current();
ScopedTrace trace("Releasing dex caches and app image spaces metadata");
static struct EmptyClosure : Closure {
void Run([[maybe_unused]] Thread* thread) override {}
} closure;
// Fetch the startup linear alloc so no other thread tries to allocate there.
std::unique_ptr<LinearAlloc> startup_linear_alloc(runtime->ReleaseStartupLinearAlloc());
// No thread could be allocating arrays or accessing dex caches when this
// thread has mutator-lock held exclusively.
bool run_checkpoints = !Locks::mutator_lock_->IsExclusiveHeld(self);
// Request a checkpoint to make sure all threads see we have started up and
// won't allocate in the startup linear alloc. Without this checkpoint what
// could happen is (T0 == self):
// 1) T1 fetches startup alloc, allocates an array there.
// 2) T0 goes over the dex caches, clear dex cache arrays in the startup alloc.
// 3) T1 sets the dex cache array from startup alloc in a dex cache.
// 4) T0 releases startup alloc.
//
// With this checkpoint, 3) cannot happen as T0 waits for T1 to reach the
// checkpoint.
if (run_checkpoints) {
runtime->GetThreadList()->RunCheckpoint(&closure);
}
{
UnlinkStartupDexCacheVisitor visitor;
ReaderMutexLock mu(self, *Locks::dex_lock_);
runtime->GetClassLinker()->VisitDexCaches(&visitor);
}
// Request a checkpoint to make sure no threads are:
// - accessing the image space metadata section when we madvise it
// - accessing dex caches when we free them
if (run_checkpoints) {
runtime->GetThreadList()->RunCheckpoint(&closure);
}
// If this isn't the GC calling `DeleteStartupDexCaches` and a GC may be
// running, wait for it to be complete. We don't want it to see these dex
// caches. Since we are runnable, a GC started after this cannot get far.
if (!called_by_gc) {
runtime->GetHeap()->WaitForGcToComplete(gc::kGcCauseDeletingDexCacheArrays, self);
}
// At this point, we know no other thread can see the arrays, nor the GC. So
// we can safely release them.
for (gc::space::ContinuousSpace* space : runtime->GetHeap()->GetContinuousSpaces()) {
if (space->IsImageSpace()) {
gc::space::ImageSpace* image_space = space->AsImageSpace();
if (image_space->GetImageHeader().IsAppImage()) {
image_space->ReleaseMetadata();
}
}
}
if (startup_linear_alloc != nullptr) {
ScopedTrace trace2("Delete startup linear alloc");
ArenaPool* arena_pool = startup_linear_alloc->GetArenaPool();
startup_linear_alloc.reset();
arena_pool->TrimMaps();
}
}
} // namespace art
|