package org.jerlang; import java.util.Arrays; import java.util.Stack; import java.util.concurrent.LinkedBlockingQueue; import org.jerlang.type.Atom; import org.jerlang.type.BitString; import org.jerlang.type.Float; import org.jerlang.type.Fun; import org.jerlang.type.Integer; import org.jerlang.type.List; import org.jerlang.type.PID; import org.jerlang.type.Term; import org.jerlang.type.Tuple; import org.jerlang.type.stack.ExceptionHandler; /** * Erlang is designed for massive concurrency. * Erlang processes are lightweight (grow and shrink dynamically) * with small memory footprint, fast to create and terminate, * and the scheduling overhead is low. * * Source: * http://www.erlang.org/doc/reference_manual/processes.html */ public class Process extends ProcessOrPort { // See "erts/emulator/beam/erl_vm.h": private final int MAX_REG = 1024; private final int MAX_FREG = 32; private final LinkedBlockingQueue<Term> mailbox; private final ProcessDictionary dictionary; private Term[] registers = null; private Float[] fregisters = new Float[MAX_FREG]; // Continuation Pointer private int cp = -1; private Stack<java.lang.Integer> cpStack = new Stack<>(); // Stack / Y register private Term[] stack = new Term[16]; private int sp = 0; // Signatures // We need a stack for the interpreter to find the correct labels private Stack<FunctionSignature> signatures = new Stack<>(); // Used by `put_tuple/2` and `put/1`: private Tuple tuple; private int tupleIndex; // BitString, used by `bs_put_integer/5` private BitString bitString; // Exception handling private ExceptionHandler exceptionHandler = null; // Timeout private long timeout = 0; public Process(PID pid) { super(pid); dictionary = new ProcessDictionary(); mailbox = new LinkedBlockingQueue<>(); } public Process(PID pid, Fun fun) { this(pid); } public Process(PID pid, Atom module, Atom fun, List args) { this(pid); FunctionSignature s = new FunctionSignature(module, fun, Integer.of(args.length())); signatures.push(s); int index = 0; while (args.length() > 0) { setX(index++, args.head()); args = args.tail(); } } public void allocate(int size, int keep) { Term[] newStack = new Term[stack.length + size]; if (stack.length > 0) { System.arraycopy(stack, 0, newStack, 0, stack.length); } stack = newStack; sp += size; } public void allocate_heap(int stack, int heap, int live) { if (stack > 0) { allocate(stack, live); } } public void allocate_heap_zero(int stack, int heap, int live) { if (stack > 0) { allocate_zero(stack, live); } } public void allocate_zero(int size, int keep) { if (size > 0) { allocate(size, keep); Arrays.fill(stack, stack.length - size, stack.length, List.nil); } } public BitString bitString() { return bitString; } public void deallocate(int size) { if (size > 0) { Term[] newStack = new Term[stack.length - size]; System.arraycopy(stack, 0, newStack, 0, newStack.length); stack = newStack; sp -= size; } } public ProcessDictionary dictionary() { return dictionary; } public boolean hasMessage() { return !mailbox.isEmpty(); } public int getCP() { return cp; } public Float getFR(int index) { return fregisters[index]; } public Term nextMessage() { return mailbox.peek(); } public Tuple getTuple() { return tuple; } public int getTupleIndex() { return tupleIndex; } public Term getX(int index) { return registers()[index]; } public Term getX(Integer index) { return getX(index.toInt()); } public Term getY(int index) { return stack[sp - index]; } public Term getY(Integer index) { return getY(index.toInt()); } public void incrementTupleIndex() { tupleIndex++; } public Term popStack() { return stack[--sp]; } public void pushStack(Term term) { stack[sp++] = term; } public void send(Term message) { try { mailbox.put(message); if (state() == ProcessState.WAITING) { setState(ProcessState.RUNNABLE); scheduler().add(this); } } catch (InterruptedException e) { e.printStackTrace(); } } public void setCP(int cp) { this.cp = cp; } public void setFR(Integer index, Float value) { fregisters[index.toInt()] = value; } public void setTuple(Tuple tuple) { this.tuple = tuple; this.tupleIndex = 0; } public void setX(int index, Term term) { // System.err.println("" + this + ": X" + index + " = " + term); registers()[index] = term; } public void setX(Integer index, Term term) { setX(index.toInt(), term); } public void setY(Integer index, Term term) { int pos = sp - index.toInt(); stack[sp - index.toInt()] = term; } public FunctionSignature signature() { return signatures.peek(); } public void signature(FunctionSignature signature) { signatures.pop(); signatures.push(signature); } public void restoreCP() { cp = cpStack.pop(); } public void storeCP() { cpStack.push(cp); } public ExceptionHandler exceptionHandler() { return exceptionHandler; } @Override public void execute() { switch (state()) { case WAITING: if (hasMessage()) { setState(ProcessState.RUNNING); Interpreter.continueExecution(signatures.peek(), cp); } break; case RUNNABLE: Interpreter.continueExecution(signatures.peek(), cp); break; default: System.out.println("Skipping process " + pid() + " in state " + state()); break; } } public void loop_rec_end() { // TODO: this is technically not correct // TODO: we need to advance the message index instead mailbox.remove(); } public Term[] registers() { if (registers == null) { registers = new Term[MAX_REG]; } return registers; } public void setBitString(BitString bitString) { this.bitString = bitString; } public void setExceptionHandler(ExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; } // TODO: Only for debugging, should be removed before 1.0 public void printStack() { System.out.print("REG: [" + registers()[0] + ", "); System.out.print("" + registers()[1] + ", "); System.out.print("" + registers()[2] + ", "); System.out.print("" + registers()[3] + "] "); System.out.print("STACK(" + stack.length + "): "); for (Term t : stack) { System.out.print("" + t + ", "); } System.out.println(); } public void removeMessage() { Term message = mailbox.remove(); setX(0, message); // TODO: remove timer } public void popSignature() { signatures.pop(); } public void pushSignature(FunctionSignature signature) { signatures.push(signature); } public void clearTimeout() { timeout = 0; } public void setTimeout() { timeout = System.currentTimeMillis(); } @Override public Process toProcess() { return this; } @Override public String toString() { return pid().toString(); } public void resetMailbox() { // TODO } }