OSDN Git Service

Fix for "Compiler thread shutdown should switch out of VMWAIT"
[android-x86/dalvik.git] / vm / compiler / Compiler.c
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <sys/mman.h>
18 #include <errno.h>
19
20 #include "Dalvik.h"
21 #include "interp/Jit.h"
22 #include "CompilerInternals.h"
23
24 static inline bool workQueueLength(void)
25 {
26     return gDvmJit.compilerQueueLength;
27 }
28
29 static CompilerWorkOrder workDequeue(void)
30 {
31     assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind
32            != kWorkOrderInvalid);
33     CompilerWorkOrder work =
34         gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex];
35     gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind =
36         kWorkOrderInvalid;
37     if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) {
38         gDvmJit.compilerWorkDequeueIndex = 0;
39     }
40     gDvmJit.compilerQueueLength--;
41
42     /* Remember the high water mark of the queue length */
43     if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued)
44         gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength;
45
46     return work;
47 }
48
49 bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
50 {
51     int cc;
52     int i;
53     int numWork;
54
55     dvmLockMutex(&gDvmJit.compilerLock);
56
57     /* Queue full */
58     if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
59         gDvmJit.codeCacheFull == true) {
60         dvmUnlockMutex(&gDvmJit.compilerLock);
61         return false;
62     }
63
64     for (numWork = gDvmJit.compilerQueueLength,
65            i = gDvmJit.compilerWorkDequeueIndex;
66          numWork > 0;
67          numWork--) {
68         /* Already enqueued */
69         if (gDvmJit.compilerWorkQueue[i++].pc == pc)
70             goto done;
71         /* Wrap around */
72         if (i == COMPILER_WORK_QUEUE_SIZE)
73             i = 0;
74     }
75
76     gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].pc = pc;
77     gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].kind = kind;
78     gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].info = info;
79     gDvmJit.compilerWorkEnqueueIndex++;
80     if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
81         gDvmJit.compilerWorkEnqueueIndex = 0;
82     gDvmJit.compilerQueueLength++;
83     cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
84     assert(cc == 0);
85
86 done:
87     dvmUnlockMutex(&gDvmJit.compilerLock);
88     return true;
89 }
90
91 /* Block until queue length is 0 */
92 void dvmCompilerDrainQueue(void)
93 {
94     dvmLockMutex(&gDvmJit.compilerLock);
95     while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread) {
96         pthread_cond_wait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock);
97     }
98     dvmUnlockMutex(&gDvmJit.compilerLock);
99 }
100
101 static void *compilerThreadStart(void *arg)
102 {
103     dvmChangeStatus(NULL, THREAD_VMWAIT);
104
105     dvmLockMutex(&gDvmJit.compilerLock);
106     /*
107      * Since the compiler thread will not touch any objects on the heap once
108      * being created, we just fake its state as VMWAIT so that it can be a
109      * bit late when there is suspend request pending.
110      */
111     while (!gDvmJit.haltCompilerThread) {
112         if (workQueueLength() == 0) {
113             int cc;
114             cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
115             assert(cc == 0);
116             pthread_cond_wait(&gDvmJit.compilerQueueActivity,
117                               &gDvmJit.compilerLock);
118             continue;
119         } else {
120             do {
121                 CompilerWorkOrder work = workDequeue();
122                 dvmUnlockMutex(&gDvmJit.compilerLock);
123                 /* Check whether there is a suspend request on me */
124                 dvmCheckSuspendPending(NULL);
125                 /* Is JitTable filling up? */
126                 if (gDvmJit.jitTableEntriesUsed >
127                     (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
128                     dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
129                 }
130                 if (gDvmJit.haltCompilerThread) {
131                     LOGD("Compiler shutdown in progress - discarding request");
132                 } else {
133                     /* Compilation is successful */
134                     if (dvmCompilerDoWork(&work)) {
135                         dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
136                                           work.result.instructionSet);
137                     }
138                 }
139                 free(work.info);
140                 dvmLockMutex(&gDvmJit.compilerLock);
141             } while (workQueueLength() != 0);
142         }
143     }
144     pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
145     dvmUnlockMutex(&gDvmJit.compilerLock);
146
147     /*
148      * As part of detaching the thread we need to call into Java code to update
149      * the ThreadGroup, and we should not be in VMWAIT state while executing
150      * interpreted code.
151      */
152     dvmChangeStatus(NULL, THREAD_RUNNING);
153
154     LOGD("Compiler thread shutting down\n");
155     return NULL;
156 }
157
158 bool dvmCompilerSetupCodeCache(void)
159 {
160     extern void dvmCompilerTemplateStart(void);
161     extern void dmvCompilerTemplateEnd(void);
162
163     /* Allocate the code cache */
164     gDvmJit.codeCache = mmap(0, CODE_CACHE_SIZE,
165                           PROT_READ | PROT_WRITE | PROT_EXEC,
166                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
167     if (gDvmJit.codeCache == MAP_FAILED) {
168         LOGE("Failed to create the code cache: %s\n", strerror(errno));
169         return false;
170     }
171
172     /* Copy the template code into the beginning of the code cache */
173     int templateSize = (intptr_t) dmvCompilerTemplateEnd -
174                        (intptr_t) dvmCompilerTemplateStart;
175     memcpy((void *) gDvmJit.codeCache,
176            (void *) dvmCompilerTemplateStart,
177            templateSize);
178
179     gDvmJit.templateSize = templateSize;
180     gDvmJit.codeCacheByteUsed = templateSize;
181
182     /* Flush dcache and invalidate the icache to maintain coherence */
183     cacheflush((intptr_t) gDvmJit.codeCache,
184                (intptr_t) gDvmJit.codeCache + CODE_CACHE_SIZE, 0);
185     return true;
186 }
187
188 bool dvmCompilerStartup(void)
189 {
190     /* Make sure the BBType enum is in sane state */
191     assert(CHAINING_CELL_NORMAL == 0);
192
193     /* Architecture-specific chores to initialize */
194     if (!dvmCompilerArchInit())
195         goto fail;
196
197     /*
198      * Setup the code cache if it is not done so already. For apps it should be
199      * done by the Zygote already, but for command-line dalvikvm invocation we
200      * need to do it here.
201      */
202     if (gDvmJit.codeCache == NULL) {
203         if (!dvmCompilerSetupCodeCache())
204             goto fail;
205     }
206
207     /* Allocate the initial arena block */
208     if (dvmCompilerHeapInit() == false) {
209         goto fail;
210     }
211
212     dvmInitMutex(&gDvmJit.compilerLock);
213     pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
214     pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
215
216     dvmLockMutex(&gDvmJit.compilerLock);
217
218     gDvmJit.haltCompilerThread = false;
219
220     /* Reset the work queue */
221     memset(gDvmJit.compilerWorkQueue, 0,
222            sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
223     gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
224     gDvmJit.compilerQueueLength = 0;
225     gDvmJit.compilerHighWater =
226         COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
227
228     assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE);
229     if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
230                                  compilerThreadStart, NULL)) {
231         dvmUnlockMutex(&gDvmJit.compilerLock);
232         goto fail;
233     }
234
235     /* Track method-level compilation statistics */
236     gDvmJit.methodStatsTable =  dvmHashTableCreate(32, NULL);
237
238     dvmUnlockMutex(&gDvmJit.compilerLock);
239
240     return true;
241
242 fail:
243     return false;
244 }
245
246 void dvmCompilerShutdown(void)
247 {
248     void *threadReturn;
249
250     if (gDvmJit.compilerHandle) {
251
252         gDvmJit.haltCompilerThread = true;
253
254         dvmLockMutex(&gDvmJit.compilerLock);
255         pthread_cond_signal(&gDvmJit.compilerQueueActivity);
256         dvmUnlockMutex(&gDvmJit.compilerLock);
257
258         if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
259             LOGW("Compiler thread join failed\n");
260         else
261             LOGD("Compiler thread has shut down\n");
262     }
263 }