erl_interface
Serge Aleynikov
serge@REDACTED
Wed May 28 02:02:44 CEST 2003
I wonder if someone could share erl_interface-based examples with me.
I've been trying to write some code that passes either a tuple/list, or
an integer from C to Erlang, and I am having a problem that in case of
integers (see iterate() and finalize() below) Erlangs gets a response
correctly, but in case of tuple/list (see initialize()), looks like
Erlang is waiting for something in addition to what the port replies. I
can see in debugger that the Port sends everything normally, and then
blocks on the read() function as expected.
Serge
--------------- C CODE -----------------
#include <erl_interface.h>
#include <ei.h>
#include <io.h>
#define OPT_INITIALIZE 1
#define OPT_ITERATE 2
#define OPT_FINALIZE 3
typedef unsigned char byte;
int read_cmd(byte *buf);
int write_cmd(byte *buf, int len);
int read_exact(byte *buf, int len);
int write_exact(byte *buf, int len);
//-----------------------------------------------------------------
// Interface routines
//-----------------------------------------------------------------
int initialize(int nGatewayID, ETERM *demand, ETERM *supply, ETERM *rates) {
int nDemandLen = erl_length(demand);
int nSupplyLen = erl_length(supply);
int nRatesLen = erl_length(rates);
ETERM *list, *tuple, *arg1, *arg2;
if (ERL_IS_LIST(demand)) {
for (list = demand; ! ERL_IS_EMPTY_LIST(list);
list=ERL_CONS_TAIL(list)) {
tuple = ERL_CONS_HEAD(list); // Fetch a record from the list
arg1 = erl_element(1, tuple);
arg2 = erl_element(2, tuple);
// Check ERL_TUPLE_SIZE(tuple) == 2
fprintf(stderr, "Gateway: %3d, Demand: %4d = %d\n",
nGatewayID, ERL_INT_VALUE(arg1), ERL_INT_VALUE(arg2));
erl_free_term(arg1);
erl_free_term(arg2);
}
}
return nDemandLen;
}
int iterate(int y) {
return y*2;
}
int finalize(int z) {
return z*3;
}
int main(int argc, char *argv[]) {
ETERM *tuple, *result;
ETERM *fun, *arg1, *arg2, *arg3, *arg4;
ETERM *array[10];
int res, n;
byte buf[1024];
long allocated, freed;
erl_init(NULL, 0); // Initialize Erlang interface library
while (read_cmd(buf) > 0) {
// The parameters are coming encoded as a tuple
tuple = erl_decode(buf);
// Get the first element in the tuple.
fun = erl_element(1, tuple);
switch (ERL_INT_VALUE(fun)) {
case OPT_INITIALIZE:
if (ERL_TUPLE_SIZE(tuple) != 5) {
result = erl_format("{error, {demand, tuple_size, ~i}}",
ERL_TUPLE_SIZE(tuple));
// Note: The function above doesn't return.
} else {
// {init, GtwyID, Demand, Supply, Rates}
arg1 = erl_element(2, tuple);
arg2 = erl_element(3, tuple);
arg3 = erl_element(4, tuple);
arg4 = erl_element(5, tuple);
res = initialize(ERL_INT_VALUE(arg1), // GtwyID
arg2, arg3, arg4);
erl_free_term(arg1);
erl_free_term(arg2);
erl_free_term(arg3);
erl_free_term(arg4);
result = erl_format("{ok, ~i}", res);
}
break;
case OPT_ITERATE:
res = 0; // iterate(ERL_INT_VALUE(argp));
result = erl_mk_int(res);
break;
case OPT_FINALIZE:
res = 0; // finalize(ERL_INT_VALUE(argp));
result = erl_mk_int(res);
break;
default:
res = -1;
}
if (!erl_encode(result, buf))
erl_err_ret("Error in erl_encode(): %d!", erl_errno);
else {
n = erl_term_len(result);
write_cmd(buf, n);
}
erl_free_compound(tuple);
erl_free_term(fun);
erl_free_compound((ETERM*)result);
}
return 0;
}
//-----------------------------------------------------------------
// Data marshaling routines
//-----------------------------------------------------------------
int read_cmd(byte *buf)
{
int len;
if (read_exact(buf, 2) != 2)
return(-1);
len = (buf[0] << 8) | buf[1];
return read_exact(buf, len);
}
int write_cmd(byte *buf, int len)
{
byte li;
li = (len >> 8) & 0xff;
write_exact(&li, 1);
li = len & 0xff;
write_exact(&li, 1);
return write_exact(buf, len);
}
int read_exact(byte *buf, int len)
{
int i, got=0;
do {
if ((i = read(0, buf+got, len-got)) <= 0)
return(i);
got += i;
} while (got<len);
return(len);
}
int write_exact(byte *buf, int len)
{
int i, wrote = 0;
do {
if ((i = write(1, buf+wrote, len-wrote)) <= 0)
return (i);
wrote += i;
} while (wrote<len);
return (len);
}
--------------- Erlang CODE -----------------
-module(opt_local).
-export([start/1, stop/1]).
-export([initialize/5, iterate/2, finalize/2]).
% testing
-export([start/0, test/1]).
% internal
-export([init/1, loop/1]).
%%====================================================================
%%===================== TEST CASES ==================================
%%====================================================================
start() ->
start("../port/opt_model").
test(P) ->
R = initialize(P, 1, [{1, 2}, {2, 3}], [], []),
R1 = stop(P),
{R, R1}.
%%======================================================================
%%================================= API ================================
%%======================================================================
start(ExtPrg) ->
spawn_link(?MODULE, init, [ExtPrg]).
stop(Pid) ->
Pid ! stop.
%%---------------------------------------------------------------------------
-define(opt_initialize, 1).
-define(opt_iterate, 2).
-define(opt_finalize, 3).
%%---------------------------------------------------------------------------
initialize(Pid, GtwyID, Demand, Supply, Rates) ->
call_port(Pid, {?opt_initialize, GtwyID, Demand, Supply, Rates}).
iterate(Pid, GtwyID) ->
call_port(Pid, {?opt_iterate, GtwyID}).
finalize(Pid, GtwyID) ->
call_port(Pid, {?opt_finalize, GtwyID}).
%%======================================================================
%%===================== INTERNAL ROUTINES ==============================
%%======================================================================
init(ExtPrg) ->
process_flag(trap_exit, true),
Port = open_port({spawn, ExtPrg}, [{packet, 2}, binary]),
loop(Port).
call_port(Pid, Msg) ->
Pid ! {call, self(), Msg},
receive
Result -> Result
end.
loop(Port) ->
receive
{call, Caller, Msg} ->
io:format("Msg to port: ~p~n", [Msg]),
Port ! {self(), {command, term_to_binary(Msg)}},
receive
{Port, {data, Data}} ->
io:format("Response received: ", []),
io:format("~p~n", [binary_to_term(Data)]),
Caller ! binary_to_term(Data);
{'EXIT', Pid, Reason} ->
io:format("Port ~p crashed with reason: ~p~n",
[Pid, Reason]),
Caller ! {'EXIT', Pid, Reason},
exit(Reason)
end,
apply(?MODULE, loop, [Port]);
stop ->
Port ! {self(), close},
receive
{Port, closed} -> exit(normal)
end;
{'EXIT', Port, Reason} ->
exit({port_terminated, Reason})
end.
More information about the erlang-questions
mailing list