Package erjang.m.ets

Source Code of erjang.m.ets.ETable

/**
* 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.m.ets;

import java.lang.ref.WeakReference;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;

import kilim.Pausable;

import com.trifork.clj_ds.APersistentMap;
import com.trifork.clj_ds.IMapEntry;
import com.trifork.clj_ds.IPersistentMap;
import com.trifork.clj_ds.ISeq;
import com.trifork.clj_ds.PersistentTreeMap;

import erjang.EAtom;
import erjang.EInteger;
import erjang.EInternalPID;
import erjang.EObject;
import erjang.EProc;
import erjang.ERT;
import erjang.ESeq;
import erjang.ETuple;
import erjang.ETuple2;
import erjang.ETuple3;
import erjang.ErlangError;
import erjang.ExitHook;
import erjang.NotImplemented;

/**
* Abstract super class for an ETS table.  We implement ETS by using
* Clojure's persistent data types. 
*
* set:           PersistentHashMap [key, value]
* ordered_set:   PersistentTreeMap [key, value]
* bag        PersistentHashMap [key, PersistentSet [value]]
* duplicate_bag: PersistentHashMap [key, PersistentList [value]]
*
*/
abstract class ETable implements ExitHook {
  public static final EAtom am_stm = EAtom.intern("stm");
 
  protected WeakReference<EProc> owner;
  protected EAtom access;
  protected final int keypos1;
  protected EInternalPID heirPID;
  protected EObject heirData;
  protected final EInteger tid;
  protected final EAtom aname;
  protected final boolean is_named;
  protected final EAtom type;
  protected final APersistentMap<EObject,Object> empty;
  private AtomicReference<IPersistentMap<EObject, Object>> mapRef;
  private boolean is_fixed;

  ETable(EProc owner, EAtom type, EInteger tid, EAtom aname, EAtom access, int keypos,
      boolean is_named, EInternalPID heir_pid, EObject heir_data, APersistentMap<EObject,Object> map) {
    this.type = type;
    this.is_named = is_named;
    this.owner = new WeakReference<EProc>(owner);
    this.tid = tid;
    this.aname = aname;
    this.access = access;
    this.keypos1 = keypos;
    this.heirPID = heir_pid;
    this.heirData = heir_data;
    try {
      this.mapRef = new AtomicReference<IPersistentMap<EObject,Object>>(map);
    } catch (Exception e) {
      throw new ErlangError(am_stm);
    }
    empty = map;
    owner.add_exit_hook(this);
  }

  /**
   * Allocate table with specified configuration
   */
  public static ETable allocate(EProc proc, EInteger tid, EAtom aname,
      EAtom type, EAtom access, int keypos, boolean write_concurrency,
      boolean is_named, EInternalPID heir_pid, EObject heir_data) {

    if (type == Native.am_set || type == Native.am_ordered_set) {
      return new ETableSet(proc, type, tid, aname, access, keypos,
          write_concurrency, is_named, heir_pid, heir_data);
    }

    if (type == Native.am_bag || type == Native.am_duplicate_bag) {
      return new ETableBag(proc, type, tid, aname, access, keypos,
          write_concurrency, is_named, heir_pid, heir_data);
    }

    throw new NotImplemented("ets type=" + type + "; access=" + access
        + "; keypos=" + keypos + "; write_concurrency="
        + write_concurrency + "; heir_pid=" + heir_pid);
  }

  /**
   * @param caller
   * @param access
   * @return
   */
  public final boolean allow_access(EProc caller, boolean write_access) {
    if (access == Native.am_protected) {
      if (write_access) {
        return (caller == owner.get());
      } else {
        return true;
      }
    } else if (access == Native.am_public) {
      return true;
    } else if (access == Native.am_private) {
      return (caller == owner.get());
    } else {
      throw new InternalError("invalid access mode");
    }
  }

  EInternalPID owner_pid() {
    EProc o = owner.get();
    if (o == null)
      throw ERT.badarg();
    return o.self_handle();
  }

  ESeq info() {
    ESeq rep = ERT.NIL;
    ETable table = this;
   
    rep = rep.cons(new ETuple2(Native.am_owner, table.owner_pid()));
    rep = rep.cons(new ETuple2(Native.am_named_table, ERT.box(table.is_named)));
    rep = rep.cons(new ETuple2(Native.am_name, table.aname));
    if (table.heirPID != null)
      rep = rep.cons(new ETuple2(Native.am_heir, table.heirPID));
    else
      rep = rep.cons(new ETuple2(Native.am_heir, Native.am_none));
    rep = rep.cons(new ETuple2(Native.am_size, ERT.box(table.size())));
    rep = rep.cons(new ETuple2(Native.am_node, ERT.getLocalNode().node()));
    rep = rep.cons(new ETuple2(Native.am_type, table.type));
    rep = rep.cons(new ETuple2(Native.am_keypos, ERT.box(table.keypos1)));
    rep = rep.cons(new ETuple2(Native.am_protection, table.access));
    rep = rep.cons(new ETuple2(Native.am_fixed, ERT.box(is_fixed)));

    return rep;
  }

 
  public void on_exit(EInternalPID dyingPID)
    throws Pausable
  {
    if (dyingPID == owner_pid()) {
     
      EInternalPID heirPID = this.heirPID;
            if (heirPID != null && heirPID != dyingPID) {
                transfer_ownership_to(heirPID,
                        this.heirData == null ? ERT.NIL : this.heirData);
            } else {
                //System.err.println("received exit from owner "+dyingPID+" => delete");
        delete();
      }
     
    } else {
      Native.log.warning("table "+aname+" ("+tid+") received exit from unrelated "+dyingPID);
    }
  }

    public void transfer_ownership_to(EInternalPID new_owner, EObject transfer_data) throws Pausable {
        EInternalPID former_owner = owner_pid();
        System.err.println("DB| transfer ownership of "+tid+" from "+former_owner+" to "+new_owner+" with tag "+transfer_data);
        EProc new_owner_task;
        if ((new_owner_task = new_owner.task()) != null) {
            //System.err.println("received exit from owner "+former_owner
            //          +" => transfer to "+new_owner_task);

            ETuple msg = ETuple.make(EAtom.intern("ETS-TRANSFER"),
                    this.is_named ? this.aname : this.tid,
                    former_owner,
                    transfer_data);

            this.owner = new WeakReference<EProc>(new_owner_task);
            new_owner_task.add_exit_hook(this);
            former_owner.remove_exit_hook(this);

            new_owner.send(former_owner, msg);
        } else {
            delete();
        }
    }

    void delete() {
    EInternalPID p = owner_pid();
    if (p != null) p.remove_exit_hook(this);
   
    Native.tid_to_table.remove(tid);
    if (is_named) {
      Native.name_to_tid.remove(aname);
    }
  }
 
  abstract int size();

  /** utility for subclasses */
  EObject get_key(ETuple value) {
    if (keypos1 > value.arity()) {
      // TODO: return full list of args
      throw ERT.badarg(ERT.NIL.cons(value));
    }
    return value.elm(keypos1);
  }
 
  IPersistentMap<EObject,Object> deref() {
    return mapRef.get();
  }
 

  abstract class WithMap<T> implements Callable<T> {
   
    private IPersistentMap<EObject,Object> orig;
   
    @Override
    public final T call() {
      return run(orig = mapRef.get());
    }

    protected abstract T run(IPersistentMap<EObject,Object> map);

    protected void set(IPersistentMap<EObject,Object> map) {
      mapRef.compareAndSet(orig, map);
    }
   
  }
 
  <X> X in_tx(WithMap<X> run) {
    try {
      synchronized (ETable.this) {
        return run.call();
      }
    } catch (ErlangError e) {
      throw e;
    } catch (Exception e) {
      e.printStackTrace();
      // STM Failure
      throw new ErlangError(am_stm);
    }
  }
 
 


  protected abstract void insert_one(ETuple value);

  protected abstract void insert_many(ESeq values);

  protected abstract boolean insert_new_one(ETuple value);

  protected abstract boolean insert_new_many(ESeq values);

  protected abstract ESeq lookup(EObject key);

  protected abstract ESeq match(EPattern matcher);

  protected abstract ESeq match_object(EPattern matcher);

  protected abstract void delete(EObject key);

  protected abstract EInteger select_delete(EMatchSpec matcher);

  protected void delete_all_objects() {
    in_tx(new WithMap<Object>() {

      @Override
      protected Object run(IPersistentMap<EObject,Object> map) {
        set(empty);
        return null;
      }
    });
  }

  protected abstract EObject first();

  protected EObject next(EObject from) {
    IPersistentMap<EObject,Object> map = deref();

    if (map instanceof PersistentTreeMap) {
      PersistentTreeMap<EObject,Object> ptm = (PersistentTreeMap<EObject,Object>) map;
      @SuppressWarnings("unchecked")
      ISeq<IMapEntry<EObject,Object>> seq = ptm.seqFrom(from, true);
      if (seq == null) return Native.am_$end_of_table;
      seq = seq.next();
      if (seq == null) return Native.am_$end_of_table;
      IMapEntry<EObject,Object> ent = (IMapEntry<EObject,Object>) seq.first();
      if (ent == null) return Native.am_$end_of_table;
      return (EObject) ent.getKey();
     
    } else {
     
      for (ISeq<IMapEntry<EObject,Object>> seq = map.seq();seq != null;seq = seq.next()) {
        IMapEntry<EObject,Object> ent = (IMapEntry<EObject,Object>) seq.first();
        if (ent == null) return Native.am_$end_of_table;
        EObject key = (EObject) ent.getKey();
        if (key.equalsExactly(from)) {
          seq = seq.next();
          if (seq == null) return Native.am_$end_of_table;
          ent = (IMapEntry<EObject,Object>) seq.first();
          if (ent == null) return Native.am_$end_of_table;
          return (EObject) ent.getKey();
        }
      }
        
      return Native.am_$end_of_table;
     
    }
  }

  protected abstract void delete_object(ETuple obj);

  public abstract ESeq slot();

  public abstract EObject select(EMatchSpec spec, int i);

  public EObject info(EObject item) {
   
    if (item == Native.am_owner) {
      return owner_pid();
    } else if (item == Native.am_named_table) {
      return ERT.box(aname != null);
    } else if (item == Native.am_name) {
      return aname;
    } else if (item == Native.am_heir) {
      if (heirPID == null) return Native.am_none;
      else return heirPID;
    } else if (item == Native.am_size) {
      return ERT.box(size());
    } else if (item == Native.am_memory) {
      return ERT.box(10*size());
    } else if (item == Native.am_node) {
      return ERT.getLocalNode().node();
    } else if (item == Native.am_type) {
      return type;
    } else if (item == Native.am_keypos) {
      return ERT.box( keypos1 );
    } else if (item == Native.am_protection) {
      return access;
    } else if (item == Native.am_fixed) {
      return ERT.box(is_fixed);
    } else if (item == Native.am_stats) {
      //
      // The OTP test suite checks these, so we simply
      // provide some values that will make it happy.
      // This is very implementation dependent.
      //
      // {Buckets,AvgLen,StdDev,ExpSD,_MinLen,_MaxLen}
      //
      return ETuple.make( ERT.box(256), // Buckets
                ERT.box(7),   // AvgLen,
                ERT.box(1),   // StdDev
                ERT.box(1),   // ExpSD
                ERT.box(1),   // MinLen
                ERT.box(10)   // MaxLen
                );
    } else if (item == Native.am_safe_fixed) {
      throw new NotImplemented();
    } else {
      return null;
    }
  }

  public void setopt(EObject head) {
   
    ETuple3 tup = ETuple3.cast(head);
    if (tup != null) {

      EInternalPID pid;
      if (tup.elem1 == Native.am_heir
          && (pid=tup.elem2.testInternalPID()) != null)
      {
        if (!pid.is_alive()) {
          this.heirPID = null;
          this.heirData = ERT.NIL;
          return;
        }

        EInternalPID old = this.heirPID;
       
        this.heirData = tup.elem3;
        this.heirPID = pid;

        pid.add_exit_hook(this);

        if (old != null) {
          old.remove_exit_hook(this);
        }
       
        return;
      }
     
    }
   
    ETuple2 tup2 = ETuple2.cast(head);
    if (tup2 != null) {
     
      if (tup2.elem1 == Native.am_protection) {
        EObject mode = tup2.elem2;
        if (mode == Native.am_private
            || mode == Native.am_public
            || mode == Native.am_protected) {
          this.access = (EAtom) mode;
          return;
        }
      } else if (tup2.elem1 == Native.am_heir
          && tup2.elem2 == Native.am_none) {

        EInternalPID old = this.heirPID;
       
        this.heirPID = null;
        this.heirData = ERT.NIL;

        if (old != null) {
          old.remove_exit_hook(this);
        }
        return;
      }
     
    }
   
    throw ERT.badarg(tid, head);
  }

  protected abstract EAtom member(EObject key);

  protected abstract EObject last();
 
}
TOP

Related Classes of erjang.m.ets.ETable

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.