Package erjang.driver

Source Code of erjang.driver.EDriverInstance

/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

package erjang.driver;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.logging.Level;
import java.util.logging.Logger;

import kilim.Pausable;
import kilim.ReentrantLock;
import erjang.EAtom;
import erjang.EBinList;
import erjang.EBinary;
import erjang.EHandle;
import erjang.EInternalPort;
import erjang.EObject;
import erjang.EPID;
import erjang.EPeer;
import erjang.ERT;
import erjang.ERef;
import erjang.EString;
import erjang.ETask;
import erjang.ETuple;
import erjang.ErlangException;
import erjang.NotImplemented;
import erjang.driver.efile.Posix;
import erjang.m.erlang.ErlProc;

/**
*
*/
public abstract class EDriverInstance extends EDriverControl {
  protected static Logger log = Logger.getLogger("erjang.driver");

  EDriver driver;
  protected EDriverTask task;
  Lock pdl;
 
  public EDriverInstance(EDriver driver) {
    this.driver = driver;
  }

  public EDriver getDriver() {
    return driver;
  }

  public EInternalPort port() {
    return task.self_handle();
  }

  @Override
  void setTask(EDriverTask task) {
    this.task = task;
  }
 
  protected static final int ERL_DRV_READ = SelectionKey.OP_READ;
  protected static final int ERL_DRV_WRITE = SelectionKey.OP_WRITE;
  protected static final int ERL_DRV_ACCEPT = SelectionKey.OP_ACCEPT;
  protected static final int ERL_DRV_CONNECT = SelectionKey.OP_CONNECT;
  protected static final int ERL_DRV_USE = 1 << 5;

  static private final int ALL_OPS = ERL_DRV_READ | ERL_DRV_WRITE
      | ERL_DRV_ACCEPT | ERL_DRV_CONNECT;

  /**
   * Register selector
   *
   * @param ch
   *            {@link SelectableChannel} for which to perform select
   * @param mode
   *            bit-or of ERL_DRV_{READ,WRITE,ACCEPT,CONNECT}
   * @param onOff
   *            one of {@link SelectMode}.SET or {@link SelectMode}.CLEAR
   */
  public void select(SelectableChannel ch, int mode, SelectMode onOff) {

    int selectOps = mode & ALL_OPS;
    if (onOff == SelectMode.SET) {
      NIOSelector.setInterest(ch, selectOps, task);
    } else if (onOff == SelectMode.CLEAR) {
      boolean releaseNotify = (mode & ERL_DRV_USE) == ERL_DRV_USE;
      NIOSelector.clearInterest(ch, selectOps, releaseNotify, task);
    }
  }

  protected void driver_exit(int i) {
    this.task.exit(i==0?ETask.am_normal:EAtom.intern(Posix.errno_id(i)));
  }


  protected void driver_async(EAsync job) {
    ERT.run_async(job, task);
  }

  protected void driver_outputv(ByteBuffer hdr, ByteBuffer[] ev) throws Pausable {
   
    EObject resp = ERT.NIL;
   
    if (task.send_binary_data) {
   
    if (ev.length > 0) {
      ev[ev.length-1].flip();
      resp = EBinary.make(ev[ev.length-1]);
   
      for (int i = ev.length-2; i >= 0; i--) {
        ev[i].flip();
        resp = resp.cons( EBinary.make(ev[i]) );
      }
    }
   
    } else {
      throw new NotImplemented();
    }
   
    hdr.flip();
    EBinList res = new EBinList(hdr, resp);

    task.output_from_driver(res);
  }
 
  public void driver_output2(ByteBuffer header, ByteBuffer buf) throws Pausable {

    int status = task.status;
   
    if ((status & EDriverTask.ERTS_PORT_SFLG_CLOSING) != 0) {
      return;
    }
   
    header.flip();
    if (buf != null)
      buf.flip();
   

    if ((status & EDriverTask.ERTS_PORT_SFLG_DISTRIBUTION) != 0) {
      task.node().net_message(task.self_handle(), null, buf);
      return;
    }
   
    if ((status & EDriverTask.ERTS_PORT_SFLG_LINEBUF_IO) != 0) {
      throw new NotImplemented();
    }
   
    EObject tail = null;
    if (buf == null || !buf.hasRemaining()) {
      tail = ERT.NIL;
    } else if (task.send_binary_data) {
      tail = EBinary.make(buf);
    } else {
      tail = EString.make(buf);
    }

   
    EBinList out = new EBinList(header, tail);
    task.output_from_driver(out);
  }
 
  protected void driver_output(ByteBuffer buf) throws Pausable {
   
    int status = task.status;
   
    if ((status & EDriverTask.ERTS_PORT_SFLG_CLOSING) != 0) {
      return;
    }
   
    buf.flip();
   
    if ((status & EDriverTask.ERTS_PORT_SFLG_DISTRIBUTION) != 0) {
      task.node().net_message(task.self_handle(), null, buf);
      return;
    }
   
    if ((status & EDriverTask.ERTS_PORT_SFLG_LINEBUF_IO) != 0) {
      throw new NotImplemented();
    }
   

    EObject out;
   
    if (buf == null || !buf.hasRemaining()) {
      out = ERT.NIL;
    } else if (task.send_binary_data) {
      out = EBinary.make(buf);
    } else {
      out = EString.make(buf);
    }

    task.output_from_driver(out);
  }
 
  public void driver_output_term(EObject term) throws Pausable {
    task.output_term_from_driver(term);
  }

  public void driver_send_term(EHandle caller, ETuple msg) throws Pausable {
    if (caller != null) {
      caller.send(task.self_handle(), msg);
    }
  }


  /**
   * @param fileRespOkHeader
   * @param binp
   * @throws Pausable
   */
  protected void driver_output_binary(byte[] header, ByteBuffer binp) throws Pausable {
    EObject out = EBinary.make(binp);
    if (header.length > 0) {
      out = new EBinList(header, out);
    }

    task.output_from_driver(out);
  }

  /**
   */
  protected void driver_cancel_timer() {
    task.cancel_timer(port());
  }

  protected void driver_set_timer(long howlong) {
    task.set_timer(howlong);
  }

  protected long driver_read_timer() {
    return task.read_timer();
  }

  /**
   * @return
   */
  protected Lock driver_pdl_create() {
    if (pdl == null) {
      pdl = new ReentrantLock();
    }
    return pdl;
  }

  private ByteBuffer[] queue = null;
  private EPID caller;

  /**
   * @return
   */
  protected ByteBuffer[] driver_peekq() {
    return queue;
  }
 
  protected EPID driver_caller() {
    return this.caller;
  }

  protected boolean driver_demonitor_process(ERef monitor) throws Pausable {
    try {
      return ErlProc.demonitor(task, monitor, ERT.NIL) == ERT.TRUE;
    } catch (ErlangException e) {
      EDriverTask.log.log(Level.FINE, "demonitor", e);
      return false;
    }
  }

 
  protected EHandle driver_get_monitored_process(ERef monitor) {
    return task.get_monitored_process(monitor);
  }

 
  protected ERef driver_monitor_process(EPID pid) throws Pausable {
    ERef ref = ERT.getLocalNode().createRef();
   
    if (!task.monitor(pid, pid, ref)) {
      port().send(port(), ETuple.make(ERT.am_DOWN, ref, ErlProc.am_process, pid, ERT.am_noproc));
    }
   
    return ref;
  }


  protected int driver_sizeq() {
    if (queue == null) return 0;
   
    int size = 0;
    int p = 0;
    for (p = 0; p < queue.length; p++) {
      if (queue[p] != null)
        size += queue[p].remaining();
    }
    return size;
  }
 
  protected long driver_deq(long size) {

    ByteBuffer[] queue = this.queue;
   
    if (queue == null)
      return 0;

    int p = 0;
    for (p = 0; p < queue.length && queue[p] != null && !queue[p].hasRemaining(); p++) {
      /* skip */
    }

    if (p == queue.length)
      return 0;

    long res = 0;
    for (int i = 0; (p+i) < queue.length; i++) {
      queue[i] = queue[p + i];
      if (queue[i] != null) {
       res += queue[i].remaining();
      }
    }

    for (int i = p; i < queue.length; i++) {
      queue[i] = null;
    }
   
    return res;
  }
 
  protected void driver_enqv(ByteBuffer[] q) {
    if (queue == null || (queue.length>0 && queue[0] == null))
      queue = q;
    else {
     
      ArrayList<ByteBuffer> bbs = new ArrayList<ByteBuffer>();
      for (int i = 0; i < queue.length; i++) {
        if (queue[i] != null && queue[i].hasRemaining())
          bbs.add(queue[i]);
      }
     
      for (int i = 0; i < q.length; i++) {
        if (q[i] != null && q[i].hasRemaining())
          bbs.add(q[i]);
      }
     
      queue = bbs.toArray(new ByteBuffer[bbs.size()]);
     
    }
  }

  /*
   * Called on behalf of driver_select when it is safe to release 'event'. A
   * typical unix driver would call close(event)
   */
  protected void stopSelect(SelectableChannel event) throws Pausable {
  }

  /**
   * @param out
   * @return
   */
  public static ByteBuffer flatten(ByteBuffer[] out) {
    return EDriverTask.flatten(out);
  }

  /*
   * called when port is closed, and when the emulator is halted. Default
   * behavior is to do nothing.
   */

  protected void stop(EObject reason) throws Pausable {
    if ((task.status & EDriverTask.ERTS_PORT_SFLG_DISTRIBUTION) != 0) {
     
      EPeer node = task.node();
     
      if (node != null) {
        node.node_going_down(port(), reason);
      }
     
      task.status &= ~EDriverTask.ERTS_PORT_SFLG_DISTRIBUTION;
    }
  }

  /*
   * called when we have output from erlang to the port
   */
  protected abstract void output(EHandle caller, ByteBuffer buf) throws IOException, Pausable;

  /*
   * called when we have output from erlang to the port, and the iodata()
   * passed in contains multiple fragments. Default behavior is to flatten the
   * input vector, and call EDriverInstance#output(ByteBuffer).
   */
  protected void outputv(EHandle caller, ByteBuffer[] ev) throws IOException, Pausable {
    output(caller, flatten(ev));
  }

  /*
   * called when we have input from one of the driver's handles)
   */
  protected void readyInput(SelectableChannel ch) throws Pausable {};

  /*
   * called when output is possible to one of the driver's handles
   */
  protected void readyOutput(SelectableChannel evt) throws Pausable {};

  /* called when "action" is possible, async job done */
  protected void readyAsync(EAsync data) throws Pausable {}

  /*
   * "ioctl" for drivers - invoked by port_control/3)
   */
  protected ByteBuffer control(EPID pid, int command, ByteBuffer cmd) throws Pausable {
    throw ERT.badarg();
  }

  /* Handling of timeout in driver */
  protected void timeout() throws Pausable {};

  /*
   * called when the port is about to be closed, and there is data in the
   * driver queue that needs to be flushed before 'stop' can be called
   */
  protected void flush() throws Pausable {};

  /*
   * Works mostly like 'control', a syncronous call into the driver.
   */
  protected EObject call(EPID caller, int command, EObject data) throws Pausable {
    throw ERT.badarg();
  }


  /**
   * @param ch
   * @throws Pausable
   */
  public void readyConnect(SelectableChannel evt) throws Pausable {
    // TODO Auto-generated method stub

  }

  /**
   * @param ch
   * @throws Pausable
   */
  public void readyAccept(SelectableChannel ch) throws Pausable {

  }

  protected void set_busy_port(boolean b) {
    throw new erjang.NotImplemented();
   
  }
 
  public static void dump_buffer(ByteBuffer[] buffer) {
    dump_buffer(log, null, buffer);
  }
 
  public static void dump_buffer(Logger log, String heading, ByteBuffer[] buffer) {
    dump_buffer(log, Level.FINEST, heading, buffer);
  }
 
  public static void dump_buffer(Logger log, Level level, String heading, ByteBuffer[] buffer) {

    if (!log.isLoggable(level)) return;
   
    StringBuilder sb = new StringBuilder();
    if (heading != null) {
      sb.append(heading);
      sb.append("\n");
    }
   
    sb.append(" vec[" + buffer.length + "]:: \n");

    for (int i = 0; i < buffer.length; i++) {

      ByteBuffer evp = buffer[i];
      int off = 0;
      boolean did_print = false;
      for (int p = evp.position(); p < evp.limit(); p++) {

        if ((off % 0x10) == 0 && off != 0)
          sb.append("\n");

        if ((off % 0x10) == 0)
          sb.append("["+i+"] 0x" + hex4(off) + " :");

        did_print = true;
        sb.append(" ");
        byte ch = evp.get(p);
        sb.append(hex2(ch&0xff));

        off += 1;

      }

      if (i < buffer.length-1 && did_print) sb.append("\n");
    }

    sb.append("---\n");

    for (int i = 0; i < buffer.length; i++) {

      ByteBuffer evp = buffer[i];
      int off = 0;
      boolean did_print = false;
      for (int p = evp.position(); p < evp.limit(); p++) {

        if ((off % 0x10) == 0 && off != 0)
          sb.append("\n");

        if ((off % 0x10) == 0)
          sb.append("["+i+"] 0x" + hex4(off) + " : ");

        did_print = true;
        byte ch = evp.get(p);
        if (ch >= 32 && ch <= 127) {
          sb.append((char)ch);
        } else {
          sb.append('.');
        }

        off += 1;

      }

      if (i < buffer.length-1 && did_print) sb.append("\n");
    }
   
    String msg = sb.toString();
    log.log(level, msg);
  }

  public static String hex2(int i) {
    if (i < 0x10) return "0" + Integer.toHexString(i);
    return Integer.toHexString(i);
  }
 
  public static String hex4(int i) {
    if (i < 0x10) return "000" + Integer.toHexString(i);
    if (i < 0x100) return "00" + Integer.toHexString(i);
    if (i < 0x1000) return "0" + Integer.toHexString(i);
    return Integer.toHexString(i);
  }


}
TOP

Related Classes of erjang.driver.EDriverInstance

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.