Erlang Port drivers and Erl_interface

Casper casper2000a@REDACTED
Fri Jan 28 11:49:26 CET 2005


Raimo,

Thanks for the valuable information. I will try those methods you advised.

- Eranga




-----Original Message-----
From: owner-erlang-questions@REDACTED
[mailto:owner-erlang-questions@REDACTED] On Behalf Of Raimo Niskanen
Sent: Friday, January 28, 2005 3:28 PM
To: erlang-questions@REDACTED
Subject: Re: Erlang Port drivers and Erl_interface

I quite understand that you are not satisfied with the documentation,
especially concerning ErlIOVec. You can of course read the source
code e.g for the efile_drv.c 
($ERL_TOP/erts/emulator/drivers/common/efile_drv.c), but it is not
very readable.

You can not send anything else than I/O lists to a port from 
an Erlang process. An I/O list is a binary or a (possibly deep)
list of integers (0..255) or binaries where the last tail
may be a binary. You can use erlang:term_to_binary/1 to pack
arbitrary terms into binaries, and then use some erl_interface
functions in the driver to decode the data (this I know
little about).

Here are the typedefs involved:

typedef struct {
    char* iov_base;
    size_t iov_len;
} SysIOVec;

typedef struct erl_drv_binary {
    long orig_size;        /* total length of binary */
    long refc;             /* number of references to this binary */
    char orig_bytes[1];   /* the data (char instead of byte!) */
} ErlDrvBinary;

typedef struct erl_io_vec {
    int vsize;			/* length of vectors */
    int size;			/* total size in bytes */
    SysIOVec* iov;
    ErlDrvBinary** binv;
} ErlIOVec;

ErlIOVec:vsize gives the size of ErlIOVec:iov and ErlIOVec:binv.
SysIOVec defines one chunk of data. The corresponding
ErlDrvBinary defines the binary the data is stored in.
If you want to hang on to the binaries in the driver after
returning from ->ouputv you will have to increment 
ErlDrvBinary:refc and decrement it later when you do not
need it.

But basically, if you just want to decode the data within
the ->outputv callback, just look at the ErlIOVec:vsize to
find the number of chunks, and then decode
ErlIOVec:iov[0..N-1] data chunks.

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB



casper2000a@REDACTED (Casper) writes:

> Hi Robert,
> 
> Thanks for the advice.
> 
> Let's say I sent the {Binary1, Atom1, {Atom2, Atom3, Binary2}, List1,
Atom4}
> tuple to the Port driver. The emulator call outputv callback function. How
> will be the ErlIOVec vector I receive in the C side?
> 
> I went through the document you mentioned below. But still I'm not
satisfied
> with the info I got regarding event callback function and how I can use
> ErlIOVec. I would like to know if I can use ei_encode/ei_decode to handle
> ErlIOVec and when using other callback functions.
> 
> - Eranga
> 
> 
> 
> -----Original Message-----
> From: owner-erlang-questions@REDACTED
> [mailto:owner-erlang-questions@REDACTED] On Behalf Of Raimo Niskanen
> Sent: Friday, January 28, 2005 2:02 PM
> To: erlang-questions@REDACTED
> Subject: Re: Erlang Port drivers and Erl_interface
> 
> casper2000a@REDACTED writes:
> 
> > Hi Robert,
> > 
> > This is really good information. Thanks a lot.
> > 
> > Have you used Port Driver call back functions outputv, event, etc. I
have
> 2 concerns about them. 
> > Unfortunately the documentation I found on them are not enough to absorb
> the full working scenario 
> > of them.
> > 
> > 1) In outputv, the erlang emulator sends the message as a ErlIOVec. I
> wonder how I can use 
> > ei_decode to extract the message.
> > 
> 
> You can probably not. If the driver has got an outputv callback, it will
> be used, and then the emulator does not have to copy binaries that are
> sent to the port. If you send a list of binaries mixed with bytes, one
> binary will be created to store the bytes, then an ErlIOVec is created
> that contains pointers into the binaries - either to the common binary
> for the odd bytes, or to the original binaries sent to the port. This
> is a way to minimize copying of large amounts of data between the
> erlang process and the driver.
> 
> > 2) I didn't find a good document on event call back function. What're
the
> information pass in as 
> > ErlDrvEvent and ErlDrvEventData?
> > 
> 
> Have a look at the Erts reference manual, there is some documentation
> there.
> http://www.erlang.se/doc/doc-5.4.3/erts-5.4.3/doc/html/part_frame.html
> sections 3 and 6.
> 
> > 3) Have you used anync or enq/deq functions in Port Driver? If you can
> give me some help or sample 
> > would be excellent.
> > 
> > 4) Please confirm you are discussing above a Port driver, but not a C
Node
> or Erl_Interface based 
> > port.
> > 
> > 5) In your below example, in which call back functions can you call
> handle_regular_message to 
> > decode the function? Could you provide an example call back function.
> > 
> > Thanks in advance!
> > Eranga
> > 
> > 
> > 
> > 
> > Quoting Robert Raschke : > Quoting Robert Raschke :
> > 
> > > Hi Eranga,
> > > 
> > > you wrote:
> > > > Let\'s say I want to pass a complex structure back and forth. And I
> opened
> > > > the linked-in driver port using binary. Then can I use
term_to_binary
> and
> > > > binary_to_term in the Erlang side and erl_decode, erl_encode and
other
> erl
> > > > interface functions in the C side?
> > > 
> > > I have been using the term_to_binary and binary_to_term functions as
> > > well. In my C port program I use the ei_decode...() and
> > > ei_encode...() functions. It took me a little bit to get used to the
> > > way these functions handle the message buffer, but with a little bit
> > > of experimentation you will get up and running quickly.
> > > 
> > > For example I send my requests from erlang in a predefined format
> > > (obviously), and then I unpack it in my C program like this, with the
> > > x_in buffer holding a message. It is very verbose, as I was learning
> > > how the ei functions work while writing it.
> > > 
> > > static void
> > > handle_regular_message(ei_x_buff *x_in, ei_x_buff *x_out)
> > > {
> > > int valid = 1;
> > > int version;
> > > int arity;
> > > char call_atom[MAXATOMLEN];
> > > erlang_pid from;
> > > char fn_atom[MAXATOMLEN];
> > > 
> > > /*
> > > Incoming message has to be
> > > {call, Caller_Pid, Fn, [Params]}
> > > with
> > > call - the atom \'call\'
> > > Caller_Pid - erlang pid of process that sent the message
> > > Fn - name as atom of the function to invoke
> > > Params - parameters to pass to Fn
> > > 
> > > For an invalid request message the return message has the form
> > > {error, badmsg}
> > > 
> > > For a valid request message the return message has the form
> > > {Caller_Pid, Result}
> > > with
> > > Result = ok | {ok, Value} | {error, internal, error} | {error, badarg,
> > > args}
> > > */
> > > 
> > > if (valid && ei_decode_version(x_in->buff, &x_in->index, &version) !=
0)
> {
> > > fprintf(stderr, \"Warning: Ignoring received malformed message (bad
> > > version).\\n\");
> > > valid = 0;
> > > }
> > > 
> > > if (valid && ei_decode_tuple_header(x_in->buff, &x_in->index, &arity)
!=
> 0)
> > > {
> > > fprintf(stderr, \"Warning: Ignoring received malformed message (not
> > > tuple).\\n\");
> > > valid = 0;
> > > }
> > > if (valid && arity != 4) {
> > > fprintf(stderr, \"Warning: Ignoring received malformed message (not
> 4-arity
> > > tuple).\\n\");
> > > valid = 0;
> > > }
> > > 
> > > if (valid && ei_decode_atom(x_in->buff, &x_in->index, call_atom) != 0)
{
> > > fprintf(stderr, \"Warning: Ignoring received malformed message (first
> tuple
> > > element not atom).\\n\");
> > > valid = 0;
> > > }
> > > if (valid && strcmp(call_atom, \"call\") != 0) {
> > > fprintf(stderr, \"Warning: Ignoring received malformed message\"
> > > \" (first tuple element not atom \'call\').\\n\");
> > > valid = 0;
> > > }
> > > 
> > > if (valid && ei_decode_pid(x_in->buff, &x_in->index, &from) != 0) {
> > > fprintf(stderr, \"Warning: Ignoring received malformed message (Second
> tuple
> > > element not pid).\\n\");
> > > valid = 0;
> > > }
> > > 
> > > if (valid && ei_decode_atom(x_in->buff, &x_in->index, fn_atom) != 0) {
> > > fprintf(stderr, \"Warning: Ignoring received malformed message (Third
> tuple
> > > element not atom).\\n\");
> > > valid = 0;
> > > }
> > > 
> > > if (valid) {
> > > x_out->index = 0;
> > > ei_x_encode_version(x_out);
> > > ei_x_encode_tuple_header(x_out, 2);
> > > ei_x_encode_pid(x_out, &from);
> > > 
> > > fprintf(stderr, \"Attempting to call e_%s().\\n\", fn_atom);
> > > 
> > > if (strcmp(fn_atom, \"my_fun_1\") == 0) my_fun_1(x_in, x_out);
> > > else if (strcmp(fn_atom, \"my_fun_2\") == 0) my_fun_2(x_in, x_out);
> > > else if (strcmp(fn_atom, \"my_fun_3\") == 0) my_fun_3(x_in, x_out);
> > > else {
> > > fprintf(stderr, \"Warning: Ignoring received malformed message\"
> > > \" (Third tuple element not recognised function).\\n\");
> > > valid = 0;
> > > }
> > > if (valid) {
> > > fprintf(stderr, \"Returned from my_fun().\\n\");
> > > }
> > > }
> > > 
> > > if (! valid) {
> > > x_out->index = 0;
> > > ei_x_encode_version(x_out);
> > > ei_x_encode_tuple_header(x_out, 2);
> > > ei_x_encode_atom(x_out, \"error\");
> > > ei_x_encode_atom(x_out, \"badmsg\");
> > > }
> > > }
> > > 
> > > Each function I invoke (my_fun()) takes apart the rest of the x_in
> > > buffer, and completes the result buffer x_out (which gets sent back to
> > > Erlang).
> > > 
> > > I hope this gets you further along,
> > > Robby
> > > 
> > > 
> > 
> > --------------This mail sent through OmniBIS.com--------------
> > 
> 
> -- 
> 
> / Raimo Niskanen, Erlang/OTP, Ericsson AB
> 




More information about the erlang-questions mailing list