非同期ドライバーは非同期スレッドで使いたくてやっているわけだが、
erl_interfaceは使えない。
すべて、eiライブラリだけでやりとりする必要がある。よって、ETERMも使えない。
この結論にいたるまでずいぶんと無駄をした。
それで、前回のコードをeiを使用して書いてみた。
まずは、erlang側のコード
注意点や前回との違いは
erl_ddll:load_driver(".", SharedLib) はspawn してから行う
open_portは binary でオープンする(use_stdioがあるとデバックに便利)
データを渡すときは、term_to_binary、受けとるときは、binary_to_termを使う
サンプルは、整数を扱っているのでリストではなくタプルで渡す。
(サンプルの場合、リストで渡すと、C側で-1と255の区別ができない)
start(SharedLib) ->
spawn(?MODULE, init, [SharedLib]).
init(SharedLib) ->
register(complex, self()),
erl_ddll:load_driver(".", SharedLib),
Port = open_port({spawn_driver, SharedLib}, [binary, use_stdio]),
loop(Port).
stop() ->
complex ! stop.
foo(X) ->
call_port({foo, X}).
bar(Y) ->
call_port({bar, Y}).
call_port(Msg) ->
complex ! {call, self(), Msg},
receive
{complex, Result} ->
Result
end.
loop(Port) ->
receive
{call, Caller, Msg} ->
%Port ! {self(), {command, encode(Msg)}},
Port ! {self(), {command, term_to_binary(encode(Msg))}},
receive
{Port, {data, Data}} ->
Caller ! {complex, decode(Data)}
end,
loop(Port);
stop ->decode([Int]) -> IC側のコードnt;
decode(Res) when is_binary(Res)->
binary_to_term(Res).
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
{'EXIT', Port, Reason} ->C側のコード
io:format("~p ~n", [Reason]),
exit(port_terminated)
end.
encode({foo, X}) -> {1, X};
encode({bar, Y}) -> {2, Y}.
decode([Int]) -> Int;
decode(Res) when is_binary(Res)->
binary_to_term(Res).
examplebar.cも変更
C側のコード
long bar(int a) {
return a+1;
}
long foo(int b) {
return b*2;
}
ドライバーの方は抜粋。
いろいろ大変だったが、何とか動いた。
ei_decode_XXX を行う度にindexが書き換わる。
...
typedef struct {
char fn;
int arg;
long res;
} our_async_data;
...
static void example_drv_foobar(void * async_data)
{
our_async_data* d = (our_async_data*)async_data;
if (d->fn == 1) {
d->res = foo(d->arg);
} else if (d->fn == 2) {
d->res = bar(d->arg);
}else{
d->res = 0;
}
}
static void example_drv_error(void * async_data){
our_async_data* d = (our_async_data*)async_data;
d->res = 255;
}
static void example_drv_output(ErlDrvData handle, char* buff, int bufflen)
{
example_data* d = (example_data*)handle;
our_async_data* a = (our_async_data*)malloc(sizeof(our_async_data));
int i, ver, size, type, arity;
int val;
long long_val;
unsigned char str[3];
// indexの初期化を忘れないように
i = 0;
if(0 != ei_decode_version(buff, &i, &ver))printf("error\n\r");
// printf("version is %d.\n\r", ver);
ei_get_type(buff, &i, &type, &size);
printf("term type is %d,(%c).\n\r", type,type);
printf("size is %d.\n\r", size);
if( type == ERL_SMALL_TUPLE_EXT ){
ei_decode_tuple_header(buff, &i, &arity);
printf("list size is %d .\n\r", arity);
ei_get_type(buff, &i, &type, &size);
printf("i1 type is %d,(%c).\n\r", type,type);
if( type == ERL_SMALL_INTEGER_EXT){
ei_decode_char(buff, &i, str);
printf("function is %d\n\r",str[0]);
a->fn = str[0];
ei_get_type(buff, &i, &type, &size);
printf("i2 type is %d,(%c).\n\r", type,type);
if( type == ERL_SMALL_INTEGER_EXT){
ei_decode_char(buff, &i, str);
a->arg = (unsigned int)*str;
printf("arg is %d\n\r",a->arg);
driver_async(d->port, NULL, example_drv_foobar, a, free);
}else if( type == ERL_INTEGER_EXT){
ei_decode_long(buff, &i, &val);
a->arg = val;
printf("arg is %d\n\r",val);
driver_async(d->port, NULL, example_drv_foobar, a, free);
}else{
driver_async(d->port, NULL, example_drv_error, a, free);
}
}else{
driver_async(d->port, NULL, example_drv_error, a, free);
}
}
static void ready_async(ErlDrvData handle, ErlDrvThreadData async_data)
{
ei_x_buff x_buff;
ei_x_new_with_version(&x_buff);
example_data* d = (example_data*)handle;
our_async_data* a = (our_async_data*)async_data;
if( a->res <>res >= 0 ){
ei_x_encode_char(&x_buff, a->res);
}else{
ei_x_encode_long(&x_buff, a->res);
}
driver_output(d->port, x_buff.buff, x_buff.index);
/* not thread safe */
// driver_output_term(d->port, bin, sizeof(bin)/sizeof(bin[0]));
ei_x_free(&x_buff);
free(a);
}
...