1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
 
21
package org.jnode.vm;
22
23
import java.nio.ByteBuffer;
24
import java.security.ProtectionDomain;
25
import java.util.ArrayList;
26
27
import org.jnode.assembler.ObjectResolver;
28
import org.jnode.annotation.Inline;
29
import org.jnode.annotation.Internal;
30
import org.jnode.annotation.KernelSpace;
31
import org.jnode.annotation.MagicPermission;
32
import org.jnode.annotation.SharedStatics;
33
import org.jnode.vm.classmgr.ClassDecoder;
34
import org.jnode.vm.classmgr.VmClassLoader;
35
import org.jnode.vm.classmgr.VmMethod;
36
import org.jnode.vm.classmgr.VmType;
37
import org.jnode.vm.compiler.NativeCodeCompiler;
38
39
/**
40
 * Service used to load classes and compile methods.
41
 *
42
 * @author Ewout Prangsma (epr@users.sourceforge.net)
43
 */
44
@MagicPermission
45
@SharedStatics
46
public final class LoadCompileService {
47
48
    private static LoadCompileService service;
49
50
    private final ArrayList<Request> requestQueue = new ArrayList<Request>();
51
52
    private final ObjectResolver resolver;
53
54
    private final NativeCodeCompiler[] compilers;
55
56
    private final NativeCodeCompiler[] testCompilers;
57
58
    private static boolean started = false;
59
60
    private static final int threadCount = 2; //4
61
62
    /**
63
     * Default ctor
64
     */
65
    public LoadCompileService(ObjectResolver resolver) {
66
        this.resolver = resolver;
67
        final VmArchitecture arch = VmMagic.currentProcessor()
68
            .getArchitecture();
69
        this.compilers = arch.getCompilers();
70
        this.testCompilers = arch.getTestCompilers();
71
    }
72
73
    /**
74
     * Recompile the given method with the given optimization level
75
     *
76
     * @param method
77
     * @param optLevel
78
     * @param enableTestCompilers
79
     */
80
    public static final void compile(VmMethod method, int optLevel,
81
                                     boolean enableTestCompilers) {
82
        if (service == null) {
83
            service = new LoadCompileService(new Unsafe.UnsafeObjectResolver());
84
        }
85
86
        if ((!started) || (Thread.currentThread() instanceof LoadCompileThread)) {
87
            // Compile now
88
            service.doCompile(method, optLevel, enableTestCompilers);
89
        } else {
90
            // Put request in queue
91
            service.enqueAndWait(new CompileRequest(method, optLevel,
92
                enableTestCompilers));
93
        }
94
    }
95
96
    /**
97
     * Get the highest supported optimization level for the regular or test compilers.
98
     */
99
    public static int getHighestOptimizationLevel(boolean test) {
100
        if (test) {
101
            return service.testCompilers == null ? -1 : service.testCompilers.length - 1;
102
        } else {
103
            return service.compilers == null ? -1 : service.compilers.length - 1;
104
        }
105
    }
106
107
    /**
108
     * @see org.jnode.vm.classmgr.VmClassLoader#defineClass(java.lang.String,
109
     *      ByteBuffer, java.security.ProtectionDomain)
110
     */
111
    public static final VmType<?> defineClass(String name, ByteBuffer data,
112
                                              ProtectionDomain protDomain, VmClassLoader loader) {
113
        initService();
114
        /* TODO review it: if(true || ... disables class definition on the load-compile thread.*/
115
        if (true || (!started) || (Thread.currentThread() instanceof LoadCompileThread)) {
116
            // Load now
117
            return service.doDefineClass(name, data, protDomain, loader);
118
        } else {
119
            // Put request in queue
120
            LoadRequest request = new LoadRequest(name, data, protDomain,
121
                loader);
122
            service.enqueAndWait(request);
123
            return request.getDefinedType();
124
        }
125
    }
126
127
    /**
128
     * Start this service.
129
     */
130
    static final void start() {
131
        initService();
132
        /*
133
        Disabled the startup of the LoadCompile thread service because multithreaded
134
        class loading creates deadlock in URLClassLoader and subclasses.
135
        Loading and compilation will happen onthe caller thread.
136
        - load-compile thread are enabled again but only used for compilation
137
        TODO review this: investigate class loading in dedicated threads (probably will not work)
138
        TODO and method compilation on dedicated threads (probably will work)
139
        */
140
        if (!started) {
141
            started = true;
142
            for (int i = 0; i < threadCount; i++) {
143
                LoadCompileThread thread = new LoadCompileThread(service,
144
                    "LoadCompile-" + i);
145
                thread.start();
146
            }
147
        }
148
149
    }
150
151
    @KernelSpace
152
    @Internal
153
    public static final void showInfo() {
154
        Unsafe.debug(" #loadcompile requests: ");
155
        Unsafe.debug((service != null) ? service.requestQueue.size() : 0);
156
    }
157
158
    /**
159
     * Initialize the service if needed.
160
     */
161
    @Inline
162
    private static void initService() {
163
        if (service == null) {
164
            service = new LoadCompileService(new Unsafe.UnsafeObjectResolver());
165
        }
166
    }
167
168
    /**
169
     * Put request in queue and wait for request to finish.
170
     *
171
     * @param request
172
     */
173
    private void enqueAndWait(Request request) {
174
        // Put request in queue
175
        synchronized (requestQueue) {
176
            requestQueue.add(request);
177
            requestQueue.notify();
178
        }
179
        // Wait for request to finish
180
        request.waitUntilFinished();
181
    }
182
183
    /**
184
     * Wait for a request in the queue and process it.
185
     */
186
    final void processNextRequest() {
187
        // Get the first request
188
        final Request request;
189
        synchronized (requestQueue) {
190
            while (requestQueue.isEmpty()) {
191
                try {
192
                    requestQueue.wait();
193
                } catch (InterruptedException ex) {
194
                    // Ignore
195
                }
196
            }
197
            request = requestQueue.get(0);
198
            requestQueue.remove(0);
199
        }
200
        try {
201
            // Process request
202
            request.execute();
203
        } finally {
204
            // Notify waiting threads
205
            request.setFinished();
206
        }
207
    }
208
209
    /**
210
     * Compile the given method
211
     *
212
     * @param vmMethod The method to compile
213
     * @param optLevel The optimization level
214
     */
215
    private void doCompile(VmMethod vmMethod, int optLevel,
216
                           boolean enableTestCompilers) {
217
        final NativeCodeCompiler cmps[];
218
        int index;
219
        if (enableTestCompilers) {
220
            index = optLevel;
221
            optLevel += compilers.length;
222
            cmps = testCompilers;
223
        } else {
224
            index = optLevel;
225
            cmps = compilers;
226
        }
227
228
        final NativeCodeCompiler cmp;
229
        if (index < 0) {
230
            index = 0;
231
        } else if (index >= cmps.length) {
232
            index = cmps.length - 1;
233
        }
234
        if (vmMethod.getNativeCodeOptLevel() < optLevel) {
235
            cmp = cmps[index];
236
            cmp.compileRuntime(vmMethod, resolver, optLevel, null);
237
        }
238
    }
239
240
    /**
241
     * @see org.jnode.vm.classmgr.VmClassLoader#defineClass(java.lang.String,
242
     *      ByteBuffer, java.security.ProtectionDomain)
243
     */
244
    final VmType<?> doDefineClass(String name, ByteBuffer data,
245
                                  ProtectionDomain protDomain, VmClassLoader loader) {
246
        return ClassDecoder.defineClass(name, data, true, loader, protDomain);
247
    }
248
249
    private abstract static class Request {
250
251
        private boolean finished = false;
252
        private Throwable exception;
253
254
        /**
255
         * Wait until this request is finished.
256
         */
257
        final synchronized void waitUntilFinished() {
258
            while (!finished) {
259
                try {
260
                    wait(5000);
261
                } catch (InterruptedException ex) {
262
                    // Ignore
263
                }
264
            }
265
            if (exception != null) {
266
                //todo debug
267
                exception.printStackTrace();
268
                throw new RuntimeException(errorMessage(), exception);
269
            }
270
        }
271
272
        final synchronized void setFinished() {
273
            finished = true;
274
            notifyAll();
275
        }
276
277
        final void execute() {
278
            try {
279
                doExecute();
280
            } catch (Throwable ex) {
281
                this.exception = ex;
282
            }
283
        }
284
285
        /**
286
         * Execute this request.
287
         */
288
        abstract void doExecute();
289
290
        /**
291
         * Gets request specific error message.
292
         *
293
         * @return
294
         */
295
        abstract String errorMessage();
296
    }
297
298
    static final class CompileRequest extends Request {
299
300
        private final VmMethod method;
301
302
        private final int optLevel;
303
304
        private final boolean enableTestCompilers;
305
306
        /**
307
         * @param method
308
         * @param optLevel
309
         * @param enableTestCompilers
310
         */
311
        CompileRequest(final VmMethod method, final int optLevel,
312
                       final boolean enableTestCompilers) {
313
            this.method = method;
314
            this.optLevel = optLevel;
315
            this.enableTestCompilers = enableTestCompilers;
316
        }
317
318
        /**
319
         * Execute this request.
320
         *
321
         * @see org.jnode.vm.LoadCompileService.Request#execute()
322
         */
323
        void doExecute() {
324
            service.doCompile(method, optLevel, enableTestCompilers);
325
        }
326
327
        /**
328
         * @see org.jnode.vm.LoadCompileService.Request#errorMessage()
329
         */
330
        @Override
331
        String errorMessage() {
332
            return "Error in compilation: ";
333
        }
334
    }
335
336
    static final class LoadRequest extends Request {
337
        private final String name;
338
339
        private final ByteBuffer data;
340
341
        private final ProtectionDomain protDomain;
342
343
        private final VmClassLoader loader;
344
345
        private VmType<?> definedType;
346
347
        /**
348
         * @param name
349
         * @param data
350
         * @param protDomain
351
         * @param loader
352
         */
353
        LoadRequest(final String name, final ByteBuffer data,
354
                    final ProtectionDomain protDomain, final VmClassLoader loader) {
355
            this.name = name;
356
            this.data = data;
357
            this.protDomain = protDomain;
358
            this.loader = loader;
359
        }
360
361
        /**
362
         * Execute this request.
363
         *
364
         * @see org.jnode.vm.LoadCompileService.Request#execute()
365
         */
366
        void doExecute() {
367
            definedType = service.doDefineClass(name, data, protDomain, loader);
368
        }
369
370
        /**
371
         * @return the definedType
372
         */
373
        final VmType<?> getDefinedType() {
374
            return definedType;
375
        }
376
377
        /**
378
         * @see org.jnode.vm.LoadCompileService.Request#errorMessage()
379
         */
380
        @Override
381
        String errorMessage() {
382
            return "Error in class loading: ";
383
        }
384
    }
385
}