Commits

jer...@jeremylatt.com  committed b387b33

send data to port with erlang bin format

  • Participants
  • Parent commits d48151c

Comments (0)

Files changed (6)

 EI_LIB += -L/usr/local/lib/erlang/lib/erl_interface-3.5.9/lib
 EI_LIB += -L/opt/local/lib/erlang/lib/erl_interface-3.5.9/lib
 else
+EI_ROOT = ${ERL_ROOT}/lib/erl_interface-3.5.9
+EI_INC = ${EI_ROOT}/include
 ERL_INC = -I${ERL_ROOT}/usr/include
-EI_LIB = -L${ERL_ROOT}/lib/erl_interface-3.5.9/lib
+EI_LIB = -L${EI_ROOT}/lib
 endif
 
 #Is V8_ROOT defined?  If not we have to guess....
 JSC_TARGETS = $(addprefix js/,$(JSC_FILES))
 
 #Compile flags
-CXXFLAGS  = -I./include $(V8_INC) $(ERL_INC) -g -Wall -m32
+CXXFLAGS  = -I./include $(V8_INC) $(ERL_INC) -g -Wall -m32 -I$(EI_INC)
 
 default: all
 
 	$(CPP) -c $(CXXFLAGS) -o $@ $<
 
 lib/v8erl.so: CXXFLAGS += -fPIC
-lib/v8erl.so: $(SRC_DIR)/driver.o $(SRC_DIR)/js2erl.o libv8.a
-	$(CPP) $(LDFLAGS) $^ -o $@
+lib/v8erl.so: $(SRC_DIR)/driver.o $(SRC_DIR)/js2erl.o
+	$(CPP) $(LDFLAGS) $(EI_LIB) $(V8_LIB) -lv8 -lerl_interface -lei $^ -o $@
 
 $(EBIN_DIR)/%.beam: $(ERL_DIR)/%.erl
 	erlc -W -o $(EBIN_DIR)/ $<
 -module(v8).
 -export([load/0,
          context/0,
-         script/2]).
+         execute/2]).
 -export([test/0]).
 -define(LIB, "v8erl").
--define(DRV_RUN_SCRIPT, $R).
+-define(DEFAULT_TIMEOUT, 3000). % ms
 
 load() ->
     case whereis(v8erl) of
     end.
 
 context() ->
-    open_port({spawn, ?LIB}, [binary]).
+    erlang:open_port({spawn, ?LIB}, [binary]).
 
 destroy(Context) when is_port(Context) ->
-    port_close(Context).
+    erlang:port_close(Context).
 
-script(Context, Script) when is_port(Context) ->
-    erlang:port_command(Context, Script),
-    receive
-	Result ->
-	    Result
-    after
-	3000 ->
-	    {error, timeout}
-    end.
+execute(Context, Script) when is_port(Context) andalso
+			      is_binary(Script) ->
+    Key = erlang:phash2(make_ref()),
+    erlang:port_command(Context, term_to_binary({Key, Script})),
+    Key.
 
 print_script_result(Context, Script) ->
     io:format("[erl] script: ~s~n", [Script]),
-    case script(Context, Script) of
-	{ok, Result} ->
-	    io:format("[erl] result: ~w~n", [Result]);
-	{error, Error} ->
-	    io:format("[erl] error: ~s~n", [Error])
+    Key = execute(Context, Script),
+    receive
+	{Key, Response} ->
+	    case Response of
+		{ok, Result} ->
+		    io:format("[erl] result: ~w~n", [Result]);
+		{error, Exception} ->
+		    {struct, PList} = Exception,
+		    io:format("[erl] error: ~s~n", [proplists:get_value(<<"message">>, PList)])
+	    end
+    after
+	3000 ->
+	    io:format("[erl] timeout~n", [])
     end.
 
 test() ->

File include/js2erl.h

 // -*- mode: c++ -*-
 #include <list>
 #include <v8.h>
+
 #include "erl_driver.h"
+#include "erl_interface.h"
+#include "ei.h"
 
 using namespace std;
 using namespace v8;

File src/driver.cc

 #include <string>
 #include "js2erl.h"
 
-#define DRV_RUN_SCRIPT 'R'
+#ifdef DEBUG
+#define LOG(X) printf(X)
+#else
+#define LOG(X)
+#endif
 
-// driver symbols
-static ErlDrvData start(ErlDrvPort, char *);
-static void stop(ErlDrvData);
-static void outputv(ErlDrvData, ErlIOVec *);
-static void ready_async(ErlDrvData, ErlDrvThreadData);
+static ErlDrvTermData v8erl_driver_mk_atom(const char *atom) {
+  return driver_mk_atom(const_cast<char *>(atom));
+}
 
-// local
-static void run_script(void *);
-static void async_free(void *);
+static char DRIVER_NAME[] = "v8erl";
+static const ErlDrvTermData OK = v8erl_driver_mk_atom("ok");
+static const ErlDrvTermData ERROR = v8erl_driver_mk_atom("error");
 
-static const char OK[] = "ok";
-static const char ERROR[] = "error";
-static char DRIVER_NAME[] = "v8erl";
+static Persistent<ObjectTemplate> global_template;
 
 class PortContext {
 public:
 
   PortContext(ErlDrvPort);
   ~PortContext();
-  ErlDrvData castOut();
-  static PortContext *castIn(void *);
 };
 
 PortContext::PortContext(ErlDrvPort p) {
   port = p;
 
   HandleScope handle_scope;
-  Handle<ObjectTemplate> global = ObjectTemplate::New();
-  context = Context::New(NULL, global);
+  context = Context::New(NULL, global_template);
 };
 
 PortContext::~PortContext() {
   context.Dispose();
 };
 
-ErlDrvData PortContext::castOut() { 
-  return reinterpret_cast<ErlDrvData>(this); 
-};
-
-PortContext *PortContext::castIn(void *data) {
-  return reinterpret_cast<PortContext *>(data);
-};
-
-class AsyncData {
+class ScriptRunner {
 public:
   // input
   ErlDrvPort port;
   Persistent<Context> context;
-  char *buf;
-  int len;
+  const char *buf;
+  long len;
+  long key;
   // output
-  const char *type;
+  ErlDrvTermData type;
   Persistent<Value> result;
 
-  AsyncData(ErlDrvPort, Persistent<Context>, char *, int);
-  void setResult(const char *, Handle<Value>);
-  void send();
-  ~AsyncData();
-  void *castOut();
-  static AsyncData *castIn(void *);
+  ScriptRunner(ErlDrvPort, Persistent<Context>, const char *, long, long);
+  void setResult(ErlDrvTermData atom, Handle<Value>);
+  int send();
+  ~ScriptRunner();
 };
 
-AsyncData::AsyncData(ErlDrvPort p, Persistent<Context> c, char *b, int l) {
+ScriptRunner::ScriptRunner(ErlDrvPort p, Persistent<Context> c, const char *b, long l, long k) {
   port = p;
   context = c;
   buf = b;
   len = l;
+  key = k;
 };
 
-AsyncData::~AsyncData() {
+ScriptRunner::~ScriptRunner() {
+  delete buf;
   result.Dispose();
 };
 
-void AsyncData::setResult(const char *t, Handle<Value> r) {
+void ScriptRunner::setResult(ErlDrvTermData t, Handle<Value> r) {
   type = t;
   result = Persistent<Value>::New(r);
 };
 
-void AsyncData::send() {
+int ScriptRunner::send() {
   Context::Scope context_scope(context);
   HandleScope handle_scope;
 
-  unsigned int tlen = terms_length(result) + 4;
+  unsigned int tlen = terms_length(result) + 8;
   ErlDrvTermData *terms = reinterpret_cast<ErlDrvTermData *>(driver_alloc(sizeof(ErlDrvTermData) * tlen));
-  terms[0] = ERL_DRV_ATOM;
-  terms[1] = driver_mk_atom(const_cast<char *>(type));
+
+  terms[0] = ERL_DRV_UINT;
+  terms[1] = key;
+  terms[2] = ERL_DRV_ATOM;
+  terms[3] = type;
 
   list<void *> pl;
-  value_to_terms(result, terms + 2, &pl);
+  value_to_terms(result, terms + 4, &pl);
 
+  terms[tlen - 4] = ERL_DRV_TUPLE;
+  terms[tlen - 3] = 2;
   terms[tlen - 2] = ERL_DRV_TUPLE;
   terms[tlen - 1] = 2;
 
-  driver_output_term(port, terms, tlen);
+  int ret = driver_output_term(port, terms, tlen);
 
   for (list<void *>::iterator iter = pl.begin() ; iter != pl.end(); ++iter) {
     driver_free(*iter);
   }
   driver_free(terms);
+
+  return ret;
 };
 
-void *AsyncData::castOut() {
-  return reinterpret_cast<void *>(this); 
-};
+static int v8erl_init() {
+  LOG("[drv] init\n");
 
-AsyncData *AsyncData::castIn(void *data) {
-  return reinterpret_cast<AsyncData *>(data);
-};
+  HandleScope handle_scope;
+  global_template = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+  return 0;
+}
+
+static void v8erl_finish() {
+  global_template.Dispose();
+}
+
+static ErlDrvData v8erl_start(ErlDrvPort port, char *cmd) {
+  LOG("[drv] start\n");
+  return reinterpret_cast<ErlDrvData>(new PortContext(port));
+}
+
+static void v8erl_stop(ErlDrvData driver_data) {
+  delete reinterpret_cast<PortContext *>(driver_data);
+}
+
+void run_script(void *async_data) {
+  ScriptRunner *script_runner = reinterpret_cast<ScriptRunner *>(async_data);
+ 
+  Context::Scope context_scope(script_runner->context);
+  HandleScope handle_scope;
+  TryCatch try_catch;
+
+  Handle<String> script = String::New(script_runner->buf, script_runner->len);
+  Handle<Script> compiled = Script::Compile(script);
+  if (compiled.IsEmpty()) {
+    script_runner->setResult(ERROR, try_catch.Exception());
+  } else {
+    Handle<Value> value = compiled->Run();
+    if (value.IsEmpty()) {
+      script_runner->setResult(ERROR, try_catch.Exception());
+    } else {
+      script_runner->setResult(OK, value);
+    }
+  }
+}
+
+void cancel_script_runner(void *async_data) {
+  delete reinterpret_cast<ScriptRunner *>(async_data);
+}
+
+static void v8erl_output(ErlDrvData driver_data, char *buf, int len) {
+  LOG("[drv] v8erl_output\n");
+  PortContext *data = reinterpret_cast<PortContext *>(driver_data);
+
+  int index = 0;
+  int version;
+  if (ei_decode_version(buf, &index, &version)) {
+    LOG("[drv] bad version\n");
+    return;
+  }
+
+  int arity;
+  if (ei_decode_tuple_header(buf, &index, &arity) || (arity != 2)) {
+    LOG("[drv] no tuple or wrong arity\n");
+    return;
+  }
+
+  long key;
+  if (ei_decode_long(buf, &index, &key)) {
+    LOG("[drv] no key or wrong type\n");
+    return;
+  }
+
+  char *script;
+  int type;
+  long script_length;
+  if (ei_get_type(buf, &index, &type, reinterpret_cast<int *>(&script_length))) {
+    LOG("[drv] cannot read size\n");
+    return;
+  }
+  script = new char[script_length];
+  if (ei_decode_binary(buf, &index, script, &script_length)) {
+    LOG("[drv] bad script binary\n");
+    delete script;
+    return;
+  }
+  
+  ScriptRunner *script_runner = new ScriptRunner(data->port, data->context, script, script_length, key);
+  driver_async(data->port, NULL, run_script, script_runner, cancel_script_runner);
+}
+
+static void v8erl_ready_async(ErlDrvData driver_data, ErlDrvThreadData thread_data) {
+  LOG("[drv] v8erl_ready_async\n");
+  ScriptRunner *script_runner = reinterpret_cast<ScriptRunner *>(thread_data);
+  script_runner->send();
+  delete script_runner;
+}
 
 static ErlDrvEntry driver_entry = {
-  NULL, // init driver
-  start, // open_port
-  stop, // close_port
-  NULL, // output		
+  v8erl_init, // init driver
+  v8erl_start, // open_port
+  v8erl_stop, // close_port
+  v8erl_output, // output		
   NULL, // ready_input
   NULL, // ready_output
   DRIVER_NAME, // driver name
-  NULL, // unload dynamic driver
+  v8erl_finish, // unload dynamic driver
   NULL, // handle: EMULATOR USE ONLY
   NULL, // control
   NULL, // timeout
-  outputv, // outputv
-  ready_async, // async event done
+  NULL, // outputv
+  v8erl_ready_async, // async event done
   NULL, // flush
   NULL, // call
   NULL // event
 extern "C" DRIVER_INIT(v8erl) {
   return &driver_entry;
 }
-
-static ErlDrvData start(ErlDrvPort port, char *cmd) {
-  PortContext *data = new PortContext(port);
-  return data->castOut(); 
-}
-
-static void stop(ErlDrvData edata) {
-  PortContext *data = PortContext::castIn(edata);
-  delete data;
-}
-
-static void outputv(ErlDrvData edata, ErlIOVec *ev) {
-  //printf("[drv] outputv\n");
-  ErlDrvBinary* input = ev->binv[1];
-
-  PortContext *data = PortContext::castIn(edata);
-  AsyncData *adata = new AsyncData(data->port, data->context, input->orig_bytes, input->orig_size);
-  driver_async(data->port, NULL, run_script, adata, async_free);
-}
-
-static void ready_async(ErlDrvData edata, ErlDrvThreadData tdata) {
-  //printf("[drv] ready_async\n");
-  AsyncData *adata = AsyncData::castIn(tdata);
-  adata->send();
-  delete adata;
-}
-
-static void run_script(void *tdata) {
-  AsyncData *adata = AsyncData::castIn(tdata);
- 
-  Context::Scope context_scope(adata->context);
-  HandleScope handle_scope;
-  TryCatch try_catch;
-
-  Handle<String> script = String::New(adata->buf, adata->len);
-  //String::AsciiValue script_str(script);
-  //printf("[drv] run_script \"%s\"\n", *script_str);
-
-  Handle<Script> compiled = Script::Compile(script);
-  if (compiled.IsEmpty()) {
-    adata->setResult(ERROR, try_catch.Message()->Get());
-  } else {
-    Handle<Value> value = compiled->Run();
-    if (value.IsEmpty()) {
-      adata->setResult(ERROR, try_catch.Message()->Get());
-    } else {
-      adata->setResult(OK, value);
-    }
-  }
-}
-
-static void async_free(void *tdata) {
-  AsyncData *adata = AsyncData::castIn(tdata);
-  delete adata;
-}

File src/js2erl.cc

 }
 
 int object_to_terms(Handle<Object> obj, ErlDrvTermData *terms, list<void *> *pl) {
-  printf("[v2t] object_to_terms\n");
+  //printf("[v2t] object_to_terms\n");
   int tlen = 0;
 
   tlen += atom("struct", terms != NULL ? (terms + tlen) : NULL);