SquirrelJME/SquirrelJME

View on GitHub
emulators/springcoat-vm/src/main/java/cc/squirreljme/vm/springcoat/SpringTaskManager.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.vm.springcoat;

import cc.squirreljme.emulator.profiler.ProfilerSnapshot;
import cc.squirreljme.emulator.terminal.TerminalPipeManager;
import cc.squirreljme.emulator.vm.VMSuiteManager;
import cc.squirreljme.jdwp.host.JDWPHostBinding;
import cc.squirreljme.jdwp.host.JDWPHostController;
import cc.squirreljme.jdwp.host.JDWPHostState;
import cc.squirreljme.jdwp.host.views.JDWPView;
import cc.squirreljme.jdwp.host.views.JDWPViewKind;
import cc.squirreljme.jvm.mle.constants.StandardPipeType;
import cc.squirreljme.runtime.cldc.SquirrelJME;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import cc.squirreljme.vm.VMClassLibrary;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * This class manages tasks within SpringCoat and is used to launch and
 * provide access to those that are running.
 *
 * @since 2018/11/04
 */
public final class SpringTaskManager
    implements JDWPHostBinding
{
    /** The manager for suites. */
    protected final VMSuiteManager suites;
    
    /** The profiling information. */
    protected final ProfilerSnapshot profiler;
    
    /** Global state. */
    protected final GlobalState globalState;
    
    /** The machine queue. */
    private final ReferenceQueue<SpringMachine> _machineGc =
        new ReferenceQueue<>(); 
    
    /** Machines that are running on the VM. */
    private final Collection<Reference<SpringMachine>> _machines =
        new LinkedList<>();
    
    /** Controller for JDWP Connections. */
    protected JDWPHostController jdwpController;
    
    /** Next thread ID, for debugging. */
    private volatile int _nextThreadId;
    
    /**
     * Initializes the task manager.
     *
     * @param __sm The suite manager.
     * @param __ps The snapshot for profiling.
     * @throws NullPointerException On null arguments.
     * @since 2018/11/04
     */
    public SpringTaskManager(VMSuiteManager __sm, ProfilerSnapshot __ps)
        throws NullPointerException
    {
        if (__sm == null)
            throw new NullPointerException("NARG");
        
        this.suites = __sm;
        this.profiler = (__ps == null ? new ProfilerSnapshot() : __ps);
        this.globalState = new GlobalState();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/03/14
     */
    @Override
    public String[] debuggerLibraries()
    {
        return this.suites.listLibraryNames();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/04/10
     */
    @Override
    public Object[] debuggerThreadGroups()
    {
        return this.tasks();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/04/10
     */
    @Override
    public <V extends JDWPView> V debuggerView(Class<V> __type,
        JDWPViewKind __kind, Reference<JDWPHostState> __state)
        throws NullPointerException
    {
        // What do we want to view?
        switch (__kind)
        {
            case FRAME:
                return __type.cast(new DebugViewFrame(__state));
            
            case OBJECT:
                return __type.cast(new DebugViewObject(__state));
            
            case THREAD:
                return __type.cast(new DebugViewThread(__state));
            
            case THREAD_GROUP:
                return __type.cast(new DebugViewThreadGroup());
            
            case TYPE:
                return __type.cast(new DebugViewType(__state));
            
            default:
                throw Debugging.oops(__kind);
        }
    }
    
    /**
     * Return the next thread ID.
     * 
     * @return The next thread ID.
     * @since 2021/03/14
     */
    protected int nextThreadId()
    {
        synchronized (this)
        {
            return ++this._nextThreadId;
        }
    }
    
    /**
     * Spawns a new task.
     * 
     * @param __classpath The classpath to use.
     * @param __mainClass The main entry class.
     * @param __mainArgs The main arguments.
     * @param __sysProps The system properties.
     * @param __stdOutMode Standard output mode.
     * @param __stdErrMode Standard error mode.
     * @param __forkThread Should the task be started on a new thread?
     * @param __rootVm Is this the root virtual machine?
     * @return The spawned machine.
     * @throws NullPointerException On null arguments.
     * @since 2020/07/09
     */
    public SpringMachine startTask(VMClassLibrary[] __classpath,
        String __mainClass, String[] __mainArgs,
        Map<String, String> __sysProps, int __stdOutMode, int __stdErrMode,
        boolean __forkThread, boolean __rootVm)
        throws NullPointerException
    {
        if (__classpath == null || __mainClass == null || __mainArgs == null ||
            __sysProps == null)
            throw new NullPointerException("NARG");
        
        // Setup the pipe manager
        TerminalPipeManager pipes = new TerminalPipeManager();
        
        // Figure out where the pipe ends go
        pipes.registerByType(StandardPipeType.STDOUT, __stdOutMode);
        pipes.registerByType(StandardPipeType.STDERR, __stdErrMode);
        
        // Spawn the machine
        SpringClassLoader classloader = new SpringClassLoader(__classpath);
        SpringMachine machine = new SpringMachine(this.suites,
            classloader, this, __mainClass,
            this.profiler, new LinkedHashMap<>(__sysProps), this.globalState,
            pipes, __rootVm, __mainArgs);
        
        // Register the machine, use garbage collector for the weak references
        synchronized (this)
        {
            this._machines.add(new WeakReference<>(machine, this._machineGc));
        }
        
        // Fork a new thread
        if (__forkThread)
        {
            // Setup new thread
            Thread fork = new Thread(machine, String.format(
                "%s-%s-main", classloader.bootLibrary().name(),
                __mainClass));
            
            // Start executing it
            fork.start();
        }
        
        // Use the resultant machine
        return machine;
    }
    
    /**
     * Returns the active tasks.
     * 
     * @return The active tasks.
     * @since 2020/07/09
     */
    public final SpringMachine[] tasks()
    {
        Collection<SpringMachine> result = new ArrayList<>();
        
        Collection<Reference<SpringMachine>> machines = this._machines;
        synchronized (this)
        {
            // See if we can early GC the machines that may have gone away
            ReferenceQueue<SpringMachine> gc = this._machineGc;
            for (Reference<? extends SpringMachine> ref = gc.poll();
                ref != null; ref = gc.poll())
                machines.remove(ref);
            
            // Go through machines to add to the collection
            for (Iterator<Reference<SpringMachine>> it = machines.iterator();
                it.hasNext();)
            {
                Reference<SpringMachine> ref = it.next();
                SpringMachine machine = ref.get();
                
                // Machine was cleared out, so get rid of it
                if (machine == null)
                {
                    it.remove();
                    continue;
                }
                
                result.add(machine);
            }
        }
        
        return result.<SpringMachine>toArray(new SpringMachine[result.size()]);
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/03/13
     */
    @Override
    public String vmDescription()
    {
        return "SquirrelJME SpringCoat " + this.vmVersion();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/03/13
     */
    @Override
    public String vmName()
    {
        return "SquirrelJME SpringCoat";
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/03/13
     */
    @Override
    public String vmVersion()
    {
        return SquirrelJME.RUNTIME_VERSION;
    }
}