Package org.jnode.vm

Source Code of org.jnode.vm.VmSystem

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.ByteOrder;
import java.util.Locale;
import java.util.Properties;
import javax.naming.NameNotFoundException;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.jnode.annotation.Internal;
import org.jnode.annotation.KernelSpace;
import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.PrivilegedActionPragma;
import org.jnode.annotation.SharedStatics;
import org.jnode.annotation.Uninterruptible;
import org.jnode.bootlog.BootLogInstance;
import org.jnode.naming.InitialNaming;
import org.jnode.permission.JNodePermission;
import org.jnode.plugin.PluginManager;
import org.jnode.system.resource.MemoryResource;
import org.jnode.system.resource.ResourceManager;
import org.jnode.system.resource.ResourceNotFreeException;
import org.jnode.system.resource.ResourceOwner;
import org.jnode.system.resource.SimpleResourceOwner;
import org.jnode.vm.classmgr.AbstractExceptionHandler;
import org.jnode.vm.classmgr.VmArray;
import org.jnode.vm.classmgr.VmByteCode;
import org.jnode.vm.classmgr.VmClassLoader;
import org.jnode.vm.classmgr.VmCompiledCode;
import org.jnode.vm.classmgr.VmCompiledExceptionHandler;
import org.jnode.vm.classmgr.VmConstClass;
import org.jnode.vm.classmgr.VmConstantPool;
import org.jnode.vm.classmgr.VmMethod;
import org.jnode.vm.classmgr.VmStaticField;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.VmArchitecture;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.facade.VmWriteBarrier;
import org.jnode.vm.isolate.VmIsolate;
import org.jnode.vm.scheduler.VmProcessor;
import org.jnode.vm.scheduler.VmThread;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Offset;
import sun.nio.ch.Interruptible;
import sun.reflect.annotation.AnnotationType;

/**
* System support for the Virtual Machine
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@SharedStatics
@MagicPermission
public final class VmSystem {

    public static final int RC_HANDLER = 0xFFFFFFFB;

    public static final int RC_DEFHANDLER = 0xFFFFFFF1;

    private static boolean inited;

    private static VmSystemClassLoader systemLoader;

    private static String cmdLine;

    private static volatile long currentTimeMillis;

    private static long ghz = -1;

    private static long rtcIncrement;

    private static RTCService rtcService;

    private static SystemOutputStream bootOut;

    private static PrintStream bootOutStream;

    private static MemoryResource initJar;

    private static PrintStream out;

    private static final String LAYOUT = "%-5p [%c{1}]: %m%n";

    private static boolean inShutdown = false;

    private static int exitCode = 0;

    static int debug = 0;

    /**
     * Initialize the Virtual Machine
     */
    public static void initialize() {
        if (!inited) {
            // Initialize Naming
            InitialNaming.setNameSpace(new DefaultNameSpace());

            // Initialize BootLog
            BootLogImpl.initialize();

            // Initialize resource manager
            final ResourceManager rm = ResourceManagerImpl.initialize();

            /* Initialize the system classloader */
            VmSystemClassLoader loader = (VmSystemClassLoader) (getVmClass(VmProcessor.current()).getLoader());
            systemLoader = loader;
            loader.initialize();

            VmSystem.out = getSystemOut();

            // Initialize VmThread
            VmThread.initialize();

            final org.jnode.vm.facade.Vm vm = VmUtils.getVm();

            // Initialize the monitors for the heap manager
            vm.getHeapManager().start();

            Locale.setDefault(Locale.ENGLISH);

            // Find & start all processors
            ((VmImpl) vm).initializeProcessors(rm);

            /* We're done initializing */
            inited = true;
            VmProcessor.current().systemReadyForThreadSwitch();

            // Load the command line
            final Properties props = System.getProperties();
            props.setProperty("jnode.cmdline", getCmdLine());

            // Make sure that we have the default locale,
            // otherwise String.toLowerCase fails because it needs itself
            // via Locale.getDefault.
            //Locale.getDefault();
            //Locale.setDefault(Locale.ENGLISH);

            // Calibrate the processors
            VmProcessor.current().calibrate();

            // Setup class loading & compilation service
            LoadCompileService.start();

            // Load the initial jarfile
            initJar = loadInitJar(rm);

            // Initialize log4j
            final Logger root = Logger.getRootLogger();
            final ConsoleAppender infoApp = new ConsoleAppender(new PatternLayout(LAYOUT));
            root.addAppender(infoApp);

            initOpenJDKSpecifics();
        }
    }

    private static void initOpenJDKSpecifics() {
        //todo this will be moved to java.lang.System during openjdk integration
        sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess() {
            public sun.reflect.ConstantPool getConstantPool(Class klass) {
                return new VmConstantPool(VmType.fromClass(klass));
            }

            public void setAnnotationType(Class klass, AnnotationType type) {
                klass.setAnnotationType(type);
            }

            public AnnotationType getAnnotationType(Class klass) {
                return klass.getAnnotationType();
            }

            public <E extends Enum<E>> E[] getEnumConstantsShared(Class<E> klass) {
                return klass.getEnumConstantsShared();
            }

            public void blockedOn(Thread t, Interruptible b) {
                //t.blockedOn(b);
                throw new UnsupportedOperationException();
            }
        });

        // Trigger initialization of the global environment variables.
        System.getenv();
    }

    static boolean isInitialized() {
        return inited;
    }

    /**
     * Gets the system output stream.
     *
     * @return the system output stream
     */
    public static PrintStream getSystemOut() {
        SystemOutputStream sout = null;
        if (bootOut == null) {
            // initialization trick to avoid circularity and setting bootOut twice
            //todo review when migrating java.lang.System to OpenJDK
            sout = new SystemOutputStream();
        }

        if (bootOut == null) {
            bootOut = sout;
            bootOutStream = new PrintStream(bootOut, true);
            VmIOContext.setGlobalOutStream(bootOutStream);
            VmIOContext.setGlobalErrStream(bootOutStream);
            setOut(bootOutStream);
            setErr(bootOutStream);
            return bootOutStream;
        } else if (VmIsolate.isRoot()) {
            return bootOutStream;
        } else {
            return VmIOContext.getGlobalOutStream();
        }
    }

    /**
     * Load the initial jarfile.
     *
     * @param rm the resource manager
     * @return The initial jarfile resource, or null if no initial jarfile is
     *         available.
     */
    private static MemoryResource loadInitJar(ResourceManager rm) {
        final Address start = Unsafe.getInitJarStart();
        final Address end = Unsafe.getInitJarEnd();
        final Extent size = end.toWord().sub(start.toWord()).toExtent();
        if (size.toWord().isZero()) {
            // No initial jarfile
            BootLogInstance.get().info("No initial jarfile found");
            return null;
        } else {
            BootLogInstance.get().info("Found initial jarfile of " + size.toInt() + 'b');
            try {
                final ResourceOwner owner = new SimpleResourceOwner("System");
                return rm.claimMemoryResource(owner, start, size,
                    ResourceManager.MEMMODE_NORMAL);
            } catch (ResourceNotFreeException ex) {
                BootLogInstance.get().error("Cannot claim initjar resource", ex);
                return null;
            }
        }
    }

    // ------------------------------------------
    // Information
    // ------------------------------------------

    /**
     * This method adds some default system properties
     *
     * @param res the system properties object
     */
    public static void insertSystemProperties(Properties res) {

        final org.jnode.vm.facade.Vm vm = VmUtils.getVm();
        final VmArchitecture arch = vm.getArch();

        // Standard Java properties
        res.put("file.separator", "/");
//        res.put("file.encoding", "ISO-8859-1");
        res.put("java.awt.graphicsenv", "org.jnode.awt.JNodeGraphicsEnvironment");
        //todo
//        res.put("java.awt.printerjob", "");
        //todo
        res.put("java.class.path", ":");
        res.put("java.class.version", "50.0");
        res.put("java.compiler", "Internal"); //todo is this needed?
        res.put("java.endorsed.dirs", "/jifs/lib/");
        res.put("java.ext.dirs", "/jifs/lib/");
        res.put("java.home", "/jnode");
        res.put("java.io.tmpdir", "/jnode/tmp");
        res.put("java.library.path", "/jnode/tmp"); //dummy value but needed by Runtime.loadLibrary
        res.put("java.runtime.name", "JNode");
        res.put("java.runtime.version", vm.getVersion());
        res.put("java.specification.name", "Java Platform API Specification");
        res.put("java.specification.vendor", "Sun Microsystems Inc.");
        res.put("java.specification.version", "1.6");
        res.put("java.vendor", "JNode.org");
        res.put("java.vendor.url", "http://jnode.org");
        res.put("java.vendor.url.bug", "http://jnode.org");
        res.put("java.version", "1.6");
        res.put("java.vm.info", "JNode");
        res.put("java.vm.name", "JNode");
        res.put("java.vm.specification.name", "Java Virtual Machine Specification");
        res.put("java.vm.specification.vendor", "Sun Microsystems Inc.");
        res.put("java.vm.specification.version", "1.0");
        res.put("java.vm.vendor", "JNode.org");
        res.put("java.vm.version", vm.getVersion());
        res.put("line.separator", "\n");
        res.put("os.arch", arch.getName());
        res.put("os.name", "JNode");
        res.put("os.version", vm.getVersion());
        res.put("path.separator", ":");
        //todo
//        res.put("user.country", "");
        res.put("user.dir", "/");
        res.put("user.home", "/jnode/home");
        res.put("user.language", "en");
        res.put("user.name", "admin");
        //todo
//        res.put("user.timezone", "");

        // GNU properties
        res.put("gnu.cpu.endian", (arch.getByteOrder() == ByteOrder.BIG_ENDIAN) ? "big" : "little");
        res.put("gnu.classpath.home.url", "system://");
        res.put("gnu.classpath.vm.shortname", "jnode");
        res.put("gnu.javax.swing.noGraphics2D", "true");

        //----------JNode related
        // Log4j properties
        res.put("log4j.defaultInitOverride", "true");

        // keep this property until transparency support works fine with all drivers
        res.put("org.jnode.awt.transparency", "true");

        //internal classpath for javac
        res.put("sun.boot.class.path", ":");

        res.put("swing.handleTopLevelPaint", "false");
        res.put("java.protocol.handler.pkgs", "org.jnode.protocol|gnu.java.net.protocol|gnu.inet");
        res.put("java.content.handler.pkgs", "gnu.java.net.content");
        //todo fix MethodAccessorGenerator issue with isolates
        //Setting it to max value to avoid the bugs with isolates & MethodAccessorGenerator,
        //see sun.reflect.ReflectionFactory for details
        res.put("sun.reflect.inflationThreshold", String.valueOf(Integer.MAX_VALUE));

        VmSystemSettings.insertSystemProperties(res);
    }

    /**
     * Returns the commandline appended to the kernel by the bootloader (e.g. grub)
     *
     * @return the commandline appended to the kernel
     */
    public static String getCmdLine() {
        if (cmdLine == null) {
            /* Load the command line */
            final int cmdLineSize = Unsafe.getCmdLine(null);
            final byte[] cmdLineArr = new byte[cmdLineSize];
            Unsafe.getCmdLine(cmdLineArr);
            cmdLine = new String(cmdLineArr).trim();
        }
        return cmdLine;
    }

    /**
     * Gets the log of the bootstrap phase.
     *
     * @return String
     */
    public static String getBootLog() {
        if (bootOut != null) {
            return bootOut.getData();
        } else {
            return "";
        }
    }

    // ------------------------------------------
    // java.lang.Object support
    // ------------------------------------------

    /**
     * Gets the class of the given object
     *
     * @param obj
     * @return The class
     */
    public static Class<?> getClass(Object obj) {
        return getVmClass(obj).asClass();
    }

    /**
     * Gets the VmClass of the given object.
     *
     * @param obj
     * @return VmClass
     */
    public static VmType<?> getVmClass(Object obj) {
        if (obj == null) {
            throw new NullPointerException();
        } else {
            return VmMagic.getObjectType(obj);
        }
    }

    /**
     * Clone the given object
     *
     * @param obj
     * @return Object
     */
    public static Object clone(Cloneable obj) {
        return VmUtils.getVm().getHeapManager().clone(obj);
    }

    /**
     * Gets the hashcode of the given object
     *
     * @param obj
     * @return int
     */
    public static int getHashCode(Object obj) {
        if (obj == null) {
            // According to spec, null has zero as hashcode.
            return 0;
        } else {
            return ObjectReference.fromObject(obj).toAddress().toInt();
        }
    }

    // ------------------------------------------
    // java.lang.Class support
    // ------------------------------------------

    public static Class forName(String className) throws ClassNotFoundException {
        return getContextClassLoader().asClassLoader().loadClass(className);
    }

    /**
     * Gets the first non-system classloader out of the current stacktrace, or
     * the system classloader if no other classloader is found in the current
     * stacktrace.
     *
     * @return The classloader
     */
    protected static VmClassLoader getContextClassLoader() {
        final VmStackReader reader = VmProcessor.current().getArchitecture()
            .getStackReader();
        final VmSystemClassLoader systemLoader = VmSystem.systemLoader;
        Address f = VmMagic.getCurrentFrame();
        while (reader.isValid(f)) {
            final VmMethod method = reader.getMethod(f);
            final VmClassLoader loader = method.getDeclaringClass().getLoader();
            if ((loader != null) && (loader != systemLoader)) {
                return loader;
            } else {
                f = reader.getPrevious(f);
            }
        }
        return systemLoader;
    }

    // ------------------------------------------
    // java.lang.SecurityManager support
    // ------------------------------------------

    /**
     * Gets the current stacktrace as array of classes.
     *
     * @return Class[]
     */
    public static Class[] getClassContext() {
        final VmStackReader reader = VmProcessor.current().getArchitecture()
            .getStackReader();
        final VmStackFrame[] stack = reader.getVmStackTrace(VmMagic
            .getCurrentFrame(), null, VmThread.STACKTRACE_LIMIT);
        final int count = stack.length;
        final Class[] result = new Class[count];

        for (int i = 0; i < count; i++) {
            result[i] = stack[i].getMethod().getDeclaringClass().asClass();
        }

        return result;
    }

    /**
     * Gets the current stacktrace as array of classes excluding the calls to
     * java.lang.reflect.Method.invoke() and org.jnode.vm.VmReflection.invoke().
     *
     * @return Class[]
     */
    public static Class[] getRealClassContext() {
        final VmStackReader reader = VmProcessor.current().getArchitecture()
            .getStackReader();
        final VmStackFrame[] stack = reader.getVmStackTrace(VmMagic
            .getCurrentFrame(), null, VmThread.STACKTRACE_LIMIT);
        final int count = stack.length;
        final Class[] result = new Class[count];
        int real_count = 0;
        for (int i = 0; i < count; i++) {
            VmMethod method = stack[i].getMethod();
            VmType<?> clazz = method.getDeclaringClass();
            if ((method.getName().equals("invoke") && (
                clazz.getName().equals("java.lang.reflect.Method") ||
                    clazz.getName().equals("org.jnode.vm.VmReflection"))))
                continue;

            result[real_count++] = clazz.asClass();
        }

        Class[] real_result = new Class[real_count];
        System.arraycopy(result, 0, real_result, 0, real_count);

        return real_result;
    }

    /**
     * Do nothing, until interrupted by an interrupts.
     */
    public static void idle() {
        Unsafe.idle();
    }

    @Internal
    public static final Object allocStack(int size) {
        try {
            return VmUtils.getVm().getHeapManager()
                .newInstance(
                    systemLoader.loadClass(
                        "org.jnode.vm.objects.VmSystemObject", true), size);
        } catch (ClassNotFoundException ex) {
            throw (NoClassDefFoundError) new NoClassDefFoundError()
                .initCause(ex);
        }
    }

    /**
     * Find an exception handler to handle the given exception in the given
     * frame at the given address.
     *
     * @param ex
     * @param frame
     * @param address
     * @return Object
     */
    @PrivilegedActionPragma
    public static Address findThrowableHandler(Throwable ex, Address frame,
                                               Address address) {

        try {
            debug++;

            if (ex == null) {
                Unsafe.debug("NPE");
                throw new NullPointerException();
            }
            if (frame == null) {
                Unsafe.debug("frame==null");
                return null;
            }
            final VmProcessor proc = VmProcessor.current();
            final VmStackReader reader = proc.getArchitecture()
                .getStackReader();

            final VmType exClass = VmMagic.getObjectType(ex);
            final VmMethod method = reader.getMethod(frame);
            if (method == null) {
                Unsafe.debug("Unknown method in class " + ex.getClass().getName());
                return null;
            }

            // if (interpreted) {
            /*
             * Screen.debug("{ex at pc:"); Screen.debug(pc); Screen.debug(" of " +
             * method.getBytecodeSize()); Screen.debug(method.getName());
             */
            // }
            final int count;
            final VmByteCode bc = method.getBytecode();
            final VmCompiledCode cc = reader.getCompiledCode(frame);
            if (bc != null) {
                count = bc.getNoExceptionHandlers();
            } else {
                count = 0;
            }
            // Screen.debug("eCount=" + count);
            for (int i = 0; i < count; i++) {
                final AbstractExceptionHandler eh;
                final VmCompiledExceptionHandler ceh;
                ceh = cc.getExceptionHandler(i);
                eh = ceh;
                boolean match;

                match = ceh.isInScope(address);

                if (match) {
                    final VmConstClass catchType = eh.getCatchType();

                    if (catchType == null) {
                        /* Catch all exceptions */
                        return Address.fromAddress(ceh.getHandler());
                    } else {
                        if (!catchType.isResolved()) {
                            SoftByteCodes.resolveClass(catchType);
                        }
                        final VmType handlerClass = catchType
                            .getResolvedVmClass();
                        if (handlerClass != null) {
                            if (handlerClass.isAssignableFrom(exClass)) {
                                return Address.fromAddress(ceh.getHandler());
                            }
                        } else {
                            System.err
                                .println("Warning: handler class==null in "
                                    + method.getName());
                        }
                    }
                }
            }

            if (cc.contains(address)) {
                return Address.fromAddress(cc.getDefaultExceptionHandler());
            } else {
                return null;
            }
        } catch (Throwable ex2) {
            Unsafe.debug("Exception in findThrowableHandler");
            try {
                ex2.printStackTrace();
            } finally {
                Unsafe.die("findThrowableHandler");
            }
            return null;
        } finally {
            debug--;
        }
    }

    // ------------------------------------------
    // java.lang.System support
    // ------------------------------------------

    /**
     * Copy one array to another. This is the implementation for System.arraycopy in JNode
     *
     * @param src
     * @param srcPos
     * @param dst
     * @param dstPos
     * @param length
     */
    @PrivilegedActionPragma
    public static void arrayCopy(final Object src, final int srcPos,
                                 final Object dst, final int dstPos, final int length) {
        Class<?> src_class = src.getClass();
        Class<?> dst_class = dst.getClass();

        if (!src_class.isArray()) {
            // Unsafe.debug('!');
            throw new ArrayStoreException("src is not an array");
        }

        if (!dst_class.isArray()) {
            // Unsafe.debug("dst is not an array:");
            // Unsafe.debug(dst_class.getName());
            throw new ArrayStoreException("dst is not an array");
        }

        String src_name = src_class.getName();
        String dst_name = dst_class.getName();

        char src_type = src_name.charAt(1);
        char dst_type = dst_name.charAt(1);

        if (src_type == '[') {
            src_type = 'L';
        }
        if (dst_type == '[') {
            dst_type = 'L';
        }

        if (src_type != dst_type) {
            // Unsafe.debug("invalid array types:");
            // Unsafe.debug(src_class.getName());
            // Unsafe.debug(dst_class.getName());
            throw new ArrayStoreException(
                "Incompatible array types: " + src_class.getName() + ", " + dst_class.getName());
        }

        if (srcPos < 0) {
            throw new IndexOutOfBoundsException("srcPos < 0");
        }
        if (dstPos < 0) {
            throw new IndexOutOfBoundsException("dstPos < 0");
        }
        if (length < 0) {
            throw new IndexOutOfBoundsException("length < 0");
        }

        final int slotSize = VmProcessor.current().getArchitecture()
            .getReferenceSize();
        final Offset lengthOffset = Offset
            .fromIntSignExtend(VmArray.LENGTH_OFFSET * slotSize);
        final int dataOffset = VmArray.DATA_OFFSET * slotSize;

        final Address srcAddr = ObjectReference.fromObject(src).toAddress();
        final Address dstAddr = ObjectReference.fromObject(dst).toAddress();

        final int srcLen = srcAddr.loadInt(lengthOffset);
        final int dstLen = dstAddr.loadInt(lengthOffset);

        // Calc end index (if overflow, then will be < 0 )
        final int srcEnd = srcPos + length;
        final int dstEnd = dstPos + length;

        if ((srcEnd > srcLen) || (srcEnd < 0)) {
            throw new IndexOutOfBoundsException("srcPos+length > src.length ("
                + srcPos + '+' + length + " > " + srcLen + ')');
        }
        if ((dstEnd > dstLen) || (dstEnd < 0)) {
            throw new IndexOutOfBoundsException("dstPos+length > dst.length");
        }

        final int elemsize;
        final boolean isObjectArray;
        switch (src_type) {
            case 'Z':
                // Boolean
            case 'B':
                // Byte
                elemsize = 1;
                isObjectArray = false;
                break;
            case 'C':
                // Character
            case 'S':
                // Short
                elemsize = 2;
                isObjectArray = false;
                break;
            case 'I':
                // Integer
            case 'F':
                // Float
                elemsize = 4;
                isObjectArray = false;
                break;
            case 'L':
                // Object
                elemsize = slotSize;
                isObjectArray = true;
                break;
            case 'J':
                // Long
            case 'D':
                // Double
                elemsize = 8;
                isObjectArray = false;
                break;
            default:
                // Unsafe.debug("uat:");
                // Unsafe.debug(src_type);
                // Unsafe.debug(src_name);
                throw new ArrayStoreException("Unknown array type");
        }

        final Address srcPtr = srcAddr.add(dataOffset + (srcPos * elemsize));
        final Address dstPtr = dstAddr.add(dataOffset + (dstPos * elemsize));
        final Extent size = Extent.fromIntZeroExtend(length * elemsize);


        if (isObjectArray) {
            Class dst_comp_class = dst_class.getComponentType();
            Class src_comp_class = src_class.getComponentType();
            if (!dst_comp_class.isAssignableFrom(src_comp_class)) {
                //todo optimize for speed
                Object[] srca = (Object[]) src;
                Object[] dsta = (Object[]) dst;
                for (int i = 0; i < length; i++) {
                    Object o = srca[srcPos + i];
                    if (o == null || dst_comp_class.isInstance(o)) {
                        dsta[dstPos + i] = o;
                    } else {
                        throw new ArrayStoreException();
                    }
                }
            } else {
                Unsafe.copy(srcPtr, dstPtr, size);
            }
        } else {
            Unsafe.copy(srcPtr, dstPtr, size);
        }

        if (isObjectArray) {
            final VmWriteBarrier wb = VmUtils.getVm().getHeapManager().getWriteBarrier();
            if (wb != null) {
                wb.arrayCopyWriteBarrier(src, srcPos, srcPos + length);
            }
        }
    }

    /**
     * Returns the current time in milliseconds. Note that while the unit of
     * time of the return value is a millisecond, the granularity of the value
     * depends on the underlying operating system and may be larger. For
     * example, many operating systems measure time in units of tens of
     * milliseconds. See the description of the class Date for a discussion of
     * slight discrepancies that may arise between "computer time" and
     * coordinated universal time (UTC).
     * <p/>
     * This method does call other methods and CANNOT be used in the low-level
     * system environment, where synchronization cannot be used. *
     *
     * @return the difference, measured in milliseconds, between the current
     *         time and midnight, January 1, 1970 UTC
     */
    public static long currentTimeMillis() {

        if (rtcIncrement == 0) {
            try {
                final RTCService rtcService = VmSystem.rtcService;
                if (rtcService != null) {
                    final long rtcTime = rtcService.getTime();
                    if (rtcTime == 0L) {
                        // We don't have an RTC service yet, return an invalid,
                        // but for now good enough value
                        return currentTimeMillis;
                    } else {
                        rtcIncrement = rtcTime - currentTimeMillis;
                    }
                }
            } catch (Exception ex) {
                BootLogInstance.get().error("Error getting rtcIncrement ", ex);
                rtcIncrement = 1;
            }
        }
        return currentTimeMillis + rtcIncrement;
    }

    /**
     * <p>
     * Returns the current value of a nanosecond-precise system timer.
     * The value of the timer is an offset relative to some arbitrary fixed
     * time, which may be in the future (making the value negative).  This
     * method is useful for timing events where nanosecond precision is
     * required.  This is achieved by calling this method before and after the
     * event, and taking the difference between the two times:
     * </p>
     * <p>
     * <code>long startTime = System.nanoTime();</code><br />
     * <code>... <emph>event code</emph> ...</code><br />
     * <code>long endTime = System.nanoTime();</code><br />
     * <code>long duration = endTime - startTime;</code><br />
     * </p>
     * <p>
     * Note that the value is only nanosecond-precise, and not accurate; there
     * is no guarantee that the difference between two values is really a
     * nanosecond.  Also, the value is prone to overflow if the offset
     * exceeds 2^63.
     * </p>
     *
     * @return the time of a system timer in nanoseconds.
     * @since 1.5
     */
    public static long nanoTime() {
        if (ghz == -1) {
            final long measureDuration = 1000; // in milliseconds

            long start = Unsafe.getCpuCycles();
            long ms_start = currentTimeMillis();
            long ms_end;
            try {
                Thread.sleep(measureDuration);
            } catch (InterruptedException e) {
                //ignore
            } finally {
                ms_end = currentTimeMillis();
            }
            long end = Unsafe.getCpuCycles();
            long ms = ms_end - ms_start;
            if (ms <= 0) {
                ms = measureDuration;
            }

            ghz = (end - start) / (ms * 1000000L);
            if (ghz <= 0) {
                ghz = 0;
            }
        }

        if (ghz == 0) {
            //todo these are CPUs under 1GHz, improve this case
            return currentTimeMillis() * 1000000L;
        }

        return Unsafe.getCpuCycles() / ghz;
    }

    /**
     * Returns the number of milliseconds since booting the kernel of JNode.
     * <p/>
     * This method does not call any other method and CAN be used in the
     * low-level system environment, where synchronization cannot be used.
     *
     * @return The current time of the kernel
     * @throws org.vmmagic.pragma.UninterruptiblePragma
     *
     */
    @KernelSpace
    @Uninterruptible
    public static long currentKernelMillis() {
        return currentTimeMillis;
    }

    /**
     * @return VmClassLoader
     */
    public static VmSystemClassLoader getSystemClassLoader() {
        return systemLoader;
    }

    /**
     * Returns the free memory in system ram
     *
     * @return free memory in system ram
     */
    public static long freeMemory() {
        return VmUtils.getVm().getHeapManager().getFreeMemory();
    }

    /**
     * Returns the total amount of system memory
     *
     * @return the total amount of system memory
     */
    public static long totalMemory() {
        return VmUtils.getVm().getHeapManager().getTotalMemory();
    }

    /**
     * Call the garbage collector
     */
    public static void gc() {
        VmUtils.getVm().getHeapManager().gc();
    }

    static class SystemOutputStream extends OutputStream {

        private StringBuffer data;

        /**
         * @see java.io.OutputStream#write(int)
         */
        public void write(int b) throws IOException {
            final char ch = (char) (b & 0xFF);
            Unsafe.debug(ch);
            if (data == null) {
                synchronized (this) {
                    data = new StringBuffer();
                }
            }
            data.append(ch);
        }

        /**
         * Returns the data written to the system output stream
         *
         * @return data written to the system output stream
         */
        public String getData() {
            return (data == null) ? "" : data.toString();
        }
    }

    /**
     * @param rtcService The rtcService to set.
     */
    public static final void setRtcService(RTCService rtcService) {
        if (VmSystem.rtcService == null) {
            VmSystem.rtcService = rtcService;
        }
    }

    /**
     * @param rtcService The rtcService previously set.
     */
    public static final void resetRtcService(RTCService rtcService) {
        if (VmSystem.rtcService == rtcService) {
            VmSystem.rtcService = null;
        }
    }

    /**
     * @return Returns the initJar.
     */
    public static final MemoryResource getInitJar() {
        return initJar;
    }

    /**
     * @return Returns the out.
     */
    public static final PrintStream getOut() {
        return out;
    }

    /**
     * Calculate the speed of the current processor.
     *
     * @return the speed of the current processor in "JNodeMips"
     */
    @Uninterruptible
    public static float calculateJNodeMips() {
        final long millis = currentTimeMillis % 1000;
        while (millis == (currentTimeMillis % 1000)) {
            // Wait
        }
        long count = 0;
        float dummy = 0.0f;
        while (millis != (currentTimeMillis % 1000)) {
            count++;
            dummy += 0.5f;
        }
        return count / 100000.0f;
    }

    /**
     * Is the system shutting down.
     *
     * @return if the system is shutting down
     */
    public static boolean isShuttingDown() {
        return inShutdown;
    }

    /**
     * Gets the system exit code.
     *
     * @return the system exit code
     */
    public static int getExitCode() {
        return exitCode;
    }

    /**
     * Halt the system. This method requires a JNodePermission("halt").
     *
     * @param reset
     */
    @PrivilegedActionPragma
    public static void halt(boolean reset) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new JNodePermission("halt"));
        }
        exitCode = (reset ? 1 : 0);
        inShutdown = true;
        try {
            final PluginManager pm = InitialNaming.lookup(PluginManager.NAME);
            pm.stopPlugins();
        } catch (NameNotFoundException ex) {
            System.err.println("Cannot find ServiceManager");
        }
    }

    /**
     * Set the effective System.in to a different InputStream.  The actual behavior depends
     * on whether we're in proclet mode or not.  If we are, we set the appropriate proxied stream,
     * to the new stream, depending on whether the current thread is a ProcletContext or not.
     * Otherwise, we update the System.in field.
     *
     * @param in the new InputStream
     * @see #setIn(InputStream)
     */
    @PrivilegedActionPragma
    public static void setIn(InputStream in) {
        getIOContext().setSystemIn(in);
    }

    /**
     * Set the effective System.out to a different PrintStream.  The actual behavior depends
     * on whether we're in proclet mode or not.  If we are, we set the appropriate proxied stream,
     * to the new stream, depending on whether the current thread is a ProcletContext or not.
     * Otherwise, we update the System.out field.
     *
     * @param out the new PrintStream
     * @see java.lang.System#setOut(PrintStream)
     */
    @PrivilegedActionPragma
    public static void setOut(PrintStream out) {
        getIOContext().setSystemOut(out);
    }

    /**
     * Set the effective System.err to a different PrintStream.  The actual behavior depends
     * on whether we're in proclet mode or not.  If we are, we set the appropriate proxied stream,
     * to the new stream, depending on whether the current thread is a ProcletContext or not.
     * Otherwise, we update the System.err field.
     *
     * @param err the new PrintStream
     * @see java.lang.System#setErr(PrintStream)
     */
    @PrivilegedActionPragma
    public static void setErr(PrintStream err) {
        getIOContext().setSystemErr(err);
    }

    //todo protect this method from arbitrary access
    @PrivilegedActionPragma
    public static void setStaticField(Class<?> clazz, String fieldName,
                                      Object value) {
        final VmStaticField f = (VmStaticField) VmType.fromClass((Class<?>) clazz).getField(
            fieldName);
        final Object staticsTable;
        final Offset offset;
        if (f.isShared()) {
            staticsTable = VmMagic.currentProcessor().getSharedStaticsTable();
            offset = Offset.fromIntZeroExtend(f.getSharedStaticsIndex() << 2);
        } else {
            staticsTable = VmMagic.currentProcessor().getIsolatedStaticsTable();
            offset = Offset.fromIntZeroExtend(f.getIsolatedStaticsIndex() << 2);
        }
        final Address ptr = VmMagic.getArrayData(staticsTable);
        ptr.store(ObjectReference.fromObject(value), offset);
    }

    //io context related

    public static IOContext getIOContext() {
        return VmIsolate.currentIsolate().getIOContext();
    }

    public static boolean hasVmIOContext() {
        return getIOContext() instanceof VmIOContext;
    }


    /**
     * Get the current global (i.e. non-ProcletContext) flavor of System.err.
     *
     * @return the global 'err' stream.
     */
    public static PrintStream getGlobalErrStream() {
        return VmIOContext.getGlobalErrStream();
    }

    /**
     * Get the current global (i.e. non-ProcletContext) flavor of System.in.
     *
     * @return the global 'in' stream.
     */
    public static InputStream getGlobalInStream() {
        return VmIOContext.getGlobalInStream();
    }

    /**
     * Get the current global (i.e. non-ProcletContext) flavor of System.out.
     *
     * @return the global 'out' stream.
     */
    public static PrintStream getGlobalOutStream() {
        return VmIOContext.getGlobalOutStream();
    }

    /**
     * Switch the current Isolate from the initial IOContext to an external one.
     * If the Isolate already has an external IOContext, this is a no-op.
     *
     * @param context
     */
    public static synchronized void switchToExternalIOContext(IOContext context) {
        if (hasVmIOContext()) {
            getIOContext().exitContext();
            VmIsolate.currentIsolate().setIOContext(context);
            context.enterContext();
        }
    }

    /**
     * Reset to the current Isolate to its initial IOContext.
     */
    public static synchronized void resetIOContext() {
        if (!hasVmIOContext()) {
            getIOContext().exitContext();
            VmIsolate.currentIsolate().resetIOContext();
            getIOContext().enterContext();
        } else {
            throw new RuntimeException("IO Context cannot be reset");
        }
    }

    /**
     * Wait for ms milliseconds in a busy waiting loop.
     * This method is very CPU intensive, so be carefull.
     *
     * @param ms
     */
    public static void loop(long ms) {
        final long start = currentKernelMillis();
        while (true) {
            if ((start + ms) <= currentKernelMillis()) {
                break;
            }
        }
    }
}
TOP

Related Classes of org.jnode.vm.VmSystem

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.