[erlang-questions] Wrong large 64bit return-values from Erlang Linkedin Driver

Mikael Pettersson mikpe@REDACTED
Thu Jun 17 14:40:01 CEST 2010


Thijs Terlouw writes:
 > Hello,
 > 
 > I'm trying to return signed 64bit integers (on a 32bit system) from a
 > custom Erlang linkedin driver. I try to use the ERL_DRV_INT64 type on
 > the C-side. Because ERL_DRV_INT64 requires a pointer to ErlDrvSInt64,
 > I first malloc a buffer, store it there, get a pointer to the result
 > and then return that pointer. For small values this works great.
 > 
 > Whenever I use values >134217727 (0x7FFFFFF) it will return wrong
 > values. I have gone over my own code many times and cannot find any
 > bugs. Then I looked at the Erlang code, and it seems that inside the
 > function erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64)
 > (erts/emulator/beam/utils.c) the value is checked: if smaller than
 > this it will construct a regular integer, if bigger, it will go into
 > the BigNum branch.
 > 
 > On the Erlang side I use erl_ddll to load the driver and open several
 > ports (spawn_driver). I do not believe there is any bug in my
 > Erlang-side, because I use the same gen_linkedin_driver.erl module for
 > several drivers.
 > 
 > Is there a bug in the way Erlang handles the very large integers (long
 > long in C-language)? Or am I making some mistakes? I saw they were
 > quite new (ERTS 5.7.4).
 > 
 > I am using :
 > Erlang R13B04 (erts-5.7.5) [source] [smp:4:4] [rq:4]
 > [async-threads:30] [hipe] [kernel-poll:true]
 > 
 > Below is my test driver bignum.c with hardcoded test-value:
 > 
 > ==================
 > #include <stdio.h>
 > #include <erl_driver.h>
 > 
 > #define INIT_INT64_LEN 512
 > #define INIT_TERM_LEN 4096
 > 
 > int
 > test_decode(ErlDrvPort port, char* buf, int len, char** rbuf, int rlen)
 > {
 > 	long long signed testvalue = 4294967296LL;		// 2^32, fail
 > 	//long long signed testvalue = 234LL;			// this is OK	
 > 
 > 	//buffer to store the terms
 >     ErlDrvTermData*  	term_data;
 >     int 				term_length;
 >     int 				term_used;
 > 	int 				resp;
 > 
 > 	//temp buffer for my int64 data
 >     ErlDrvSInt64*  	int64_data;
 >     int             int64_length;
 >     int             int64_used;
 > 
 > 	//init term-data
 >     term_data = (ErlDrvTermData*) malloc(INIT_TERM_LEN *
 > sizeof(ErlDrvTermData));
 >     if(term_data == NULL) goto done;
 >     term_length = INIT_TERM_LEN;
 >     term_used = 0;
 > 
 > 	//init int64-data
 >     int64_data = (ErlDrvSInt64*) malloc(INIT_INT64_LEN * sizeof(ErlDrvSInt64));
 >     if(int64_data == NULL) goto done;
 >     int64_length = INIT_INT64_LEN;
 >     int64_used = 0;
 > 
 >     int ret = -1;
 > 
 > 	//add a large integer to the output
 > 	term_data[term_used++] = ERL_DRV_INT64;
 > 	int64_data[int64_used++] = (ErlDrvSInt64) testvalue;
 > 	ErlDrvSInt64* pos = int64_data + (int64_used-1);
 > 	term_data[term_used++] = (ErlDrvTermData) pos;
 > 
 > 	//sanity check
 > 	fprintf(stderr, "testvalue: %lld, pos: %ld, *pos: %lld\n", testvalue,
 > pos, *pos);
 > 
 >     *rbuf = NULL;
 >     resp = driver_send_term(
 >         port,
 >         driver_caller(port),
 >         term_data,
 >         term_used
 >     );
 >     if(resp == 1) ret = 0;
 > 
 > 	if(term_data != NULL) free(term_data);
 > 	if(int64_data != NULL) free(int64_data);
 > done:
 >     return ret;
 > }
 > 
 > 
 > static ErlDrvData
 > bignum_start(ErlDrvPort port, char *buff)
 > {
 >     if(port == NULL) return ERL_DRV_ERROR_GENERAL;
 >     set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
 >     return (ErlDrvData) port;
 > }
 > 
 > static int
 > bignum_control(
 >         ErlDrvData drv_data,
 >         unsigned int command,
 >         char* buf,
 >         int len,
 >         char** rbuf,
 >         int rlen
 > )
 > {
 >     switch(command)
 >     {
 >         case 0:
 >             return test_decode((ErlDrvPort) drv_data, buf, len, rbuf, rlen);
 >         default:
 >             return -1;
 >     }
 > }
 > 
 > static ErlDrvEntry
 > bignum_driver_entry =
 > {
 >     NULL,               /* Init */
 >     bignum_start,
 >     NULL,               /* Stop */
 >     NULL,               /* Output */
 >     NULL,               /* Input Ready */
 >     NULL,               /* Output Ready */
 >     "bignum_drv",      /* Driver Name */
 >     NULL,               /* Finish */
 >     NULL,               /* Handle */
 >     bignum_control,    /* Control */
 >     NULL,               /* Timeout */
 >     NULL,               /* Outputv */
 >     NULL,               /* Ready Async */
 >     NULL,               /* Flush */
 >     NULL,               /* Call */
 >     NULL,               /* Event */
 >     ERL_DRV_EXTENDED_MARKER,
 >     ERL_DRV_EXTENDED_MAJOR_VERSION,
 >     ERL_DRV_EXTENDED_MINOR_VERSION,
 >     ERL_DRV_FLAG_USE_PORT_LOCKING,
 >     NULL,               /* Reserved */
 >     NULL,               /* Process Exit */
 > };
 > 
 > DRIVER_INIT(bignum_drv)    /* must match name in driver_entry */
 > {
 >     return &bignum_driver_entry;
 > }
 > 
 > ===============

Can you please provide a self-contained test? I.e., an Erlang
module that loads your driver, invokes it, and checks the result?
And whatever build instructions are needed for your driver.


More information about the erlang-questions mailing list