Overhaul driver to avoid use of pipes and simplify memory management

This commit is contained in:
Dave Smith 2008-12-15 09:23:19 -07:00
parent f20e46d756
commit f56351e1dc
2 changed files with 212 additions and 215 deletions

View file

@ -39,10 +39,11 @@ static int alloc_dbref();
static void* zalloc(unsigned int size); static void* zalloc(unsigned int size);
static void signal_port(PortData* d);
static void* deadlock_check(void* arg); static void* deadlock_check(void* arg);
static void* trickle_write(void* arg); static void* trickle_write(void* arg);
static void send_ok_or_error(ErlDrvPort port, ErlDrvTermData pid, int rc);
/** /**
* Global instance of DB_ENV; only a single one exists per O/S process. * Global instance of DB_ENV; only a single one exists per O/S process.
*/ */
@ -123,6 +124,18 @@ static TPool* G_TPOOL_TXNS;
bin_helper_push_int32(&bh, val); \ bin_helper_push_int32(&bh, val); \
RETURN_BH(bh, outbuf); } RETURN_BH(bh, outbuf); }
#define FAIL_IF_ASYNC_PENDING(d, outbuf) { \
erl_drv_mutex_lock(d->port_lock); \
if (d->async_op != CMD_NONE) { \
erl_drv_mutex_unlock(d->port_lock); \
RETURN_INT(ERROR_ASYNC_PENDING, outbuf); \
} else { \
erl_drv_mutex_unlock(d->port_lock); \
}}
DRIVER_INIT(bdberl_drv) DRIVER_INIT(bdberl_drv)
{ {
printf("DRIVER INIT\n"); printf("DRIVER INIT\n");
@ -210,12 +223,15 @@ static ErlDrvData bdberl_drv_start(ErlDrvPort port, char* buffer)
// Save handle to the port // Save handle to the port
d->port = port; d->port = port;
// Setup a pair of pipes for notification purposes // Allocate a mutex for the port
assert(pipe(d->pipe_fds) == 0); d->port_lock = erl_drv_mutex_create("bdberl_port_lock");
// Make sure both pipes are configured non-blocking // Save the caller/owner PID
assert(fcntl(d->pipe_fds[0], F_SETFL, O_NONBLOCK) == 0); d->port_owner = driver_connected(port);
assert(fcntl(d->pipe_fds[1], F_SETFL, O_NONBLOCK) == 0);
// Allocate an initial buffer for work purposes
d->work_buffer = driver_alloc(4096);
d->work_buffer_sz = 4096;
// Make sure port is running in binary mode // Make sure port is running in binary mode
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
@ -226,20 +242,31 @@ static ErlDrvData bdberl_drv_start(ErlDrvPort port, char* buffer)
static void bdberl_drv_stop(ErlDrvData handle) static void bdberl_drv_stop(ErlDrvData handle)
{ {
PortData* d = (PortData*)handle; PortData* d = (PortData*)handle;
// printf("%p: stop\n", d->port);
// Grab the port lock, in case we have an async job running
erl_drv_mutex_lock(d->port_lock);
// If there is an async job pending, we need to cancel it. The cancel operation will // If there is an async job pending, we need to cancel it. The cancel operation will
// block until the job has either been removed or has run // block until the job has either been removed or has run
if (d->async_job) if (d->async_job)
{ {
// Drop the lock prior to starting the wait for the async process
erl_drv_mutex_unlock(d->port_lock);
printf("Cancelling async job for port: %p\n", d->port); printf("Cancelling async job for port: %p\n", d->port);
bdberl_tpool_cancel(d->async_pool, d->async_job); bdberl_tpool_cancel(d->async_pool, d->async_job);
driver_select(d->port, (ErlDrvEvent)(size_t)d->pipe_fds[0], DO_READ, 0);
printf("Canceled async job for port: %p\n", d->port); printf("Canceled async job for port: %p\n", d->port);
} }
else
{
// If there was no async job, drop the lock -- not needed
erl_drv_mutex_unlock(d->port_lock);
}
// If a txn is currently active, terminate it. We _must_ do it synchronously (unfortunately) as // Cleanup the port lock
// there doesn't seem to be a to do an async op while stopping the driver. erl_drv_mutex_destroy(d->port_lock);
// If a txn is currently active, terminate it.
if (d->txn) if (d->txn)
{ {
d->txn->abort(d->txn); d->txn->abort(d->txn);
@ -251,12 +278,10 @@ static void bdberl_drv_stop(ErlDrvData handle)
close_database(d->dbrefs->dbref, 0, d); close_database(d->dbrefs->dbref, 0, d);
} }
close(d->pipe_fds[0]);
close(d->pipe_fds[1]);
printf("Stopped port: %p\n", d->port); printf("Stopped port: %p\n", d->port);
// Release the port instance data // Release the port instance data
driver_free(d->work_buffer);
driver_free(handle); driver_free(handle);
} }
@ -322,7 +347,13 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
} }
case CMD_CLOSE_DB: case CMD_CLOSE_DB:
{ {
// TODO: If data is inflight, fail. Abort any open txns. FAIL_IF_ASYNC_PENDING(d, outbuf);
// Fail if a txn is open
if (d->txn != 0)
{
RETURN_INT(ERROR_TXN_OPEN, outbuf);
}
// Take the provided dbref and attempt to close it // Take the provided dbref and attempt to close it
// Inbuf is: <<DbRef:32, Flags:32/unsigned>> // Inbuf is: <<DbRef:32, Flags:32/unsigned>>
@ -336,11 +367,7 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
} }
case CMD_TXN_BEGIN: case CMD_TXN_BEGIN:
{ {
// If an async operation is pending, fail FAIL_IF_ASYNC_PENDING(d, outbuf);
if (d->async_op != CMD_NONE)
{
RETURN_INT(ERROR_ASYNC_PENDING, outbuf);
}
// If we already have a txn open, fail // If we already have a txn open, fail
if (d->txn != 0) if (d->txn != 0)
@ -358,11 +385,7 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
case CMD_TXN_COMMIT: case CMD_TXN_COMMIT:
case CMD_TXN_ABORT: case CMD_TXN_ABORT:
{ {
// If an async operation is pending, fail FAIL_IF_ASYNC_PENDING(d, outbuf);
if (d->async_op != CMD_NONE)
{
RETURN_INT(ERROR_ASYNC_PENDING, outbuf);
}
// If we don't already have a txn open, fail // If we don't already have a txn open, fail
if (d->txn == 0) if (d->txn == 0)
@ -370,26 +393,14 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
RETURN_INT(ERROR_NO_TXN, outbuf); RETURN_INT(ERROR_NO_TXN, outbuf);
} }
// Allocate operation structure // Setup async command and schedule it on the txns threadpool
AsyncData* adata = zalloc(sizeof(AsyncData)); d->async_op = cmd;
adata->port = d;
if (cmd == CMD_TXN_COMMIT) if (cmd == CMD_TXN_COMMIT)
{ {
adata->payload = (void*)(size_t)UNPACK_INT(inbuf, 0); d->async_flags = UNPACK_INT(inbuf, 0);
} }
// Update port data to indicate we have an operation in progress
d->async_op = cmd;
// Schedule async operation to execute the commit/abort
d->async_data = adata;
d->async_pool = G_TPOOL_TXNS; d->async_pool = G_TPOOL_TXNS;
d->async_job = bdberl_tpool_run(G_TPOOL_TXNS, &do_async_txnop, adata, 0); d->async_job = bdberl_tpool_run(G_TPOOL_TXNS, &do_async_txnop, d, 0);
// Watch for events on the output pipe
// TODO: Can we do this just once ?!
driver_select(d->port, (ErlDrvEvent)(size_t)d->pipe_fds[0], DO_READ, 1);
// Outbuf is <<Rc:32>> // Outbuf is <<Rc:32>>
RETURN_INT(0, outbuf); RETURN_INT(0, outbuf);
@ -397,11 +408,7 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
case CMD_PUT: case CMD_PUT:
case CMD_GET: case CMD_GET:
{ {
// If another async op is pending, fail FAIL_IF_ASYNC_PENDING(d, outbuf);
if (d->async_op != CMD_NONE)
{
RETURN_INT(ERROR_ASYNC_PENDING, outbuf);
}
// Inbuf is: << DbRef:32, Rest/binary>> // Inbuf is: << DbRef:32, Rest/binary>>
int dbref = UNPACK_INT(inbuf, 0); int dbref = UNPACK_INT(inbuf, 0);
@ -411,19 +418,20 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
// the underlying handle disappearing since we have a reference. // the underlying handle disappearing since we have a reference.
if (has_dbref(d, dbref)) if (has_dbref(d, dbref))
{ {
// Allocate operation structure // If the working buffer is large enough, copy the data to put/get into it. Otherwise, realloc
void* thread_data = zalloc(sizeof(AsyncData) + inbuf_sz); // until it is large enough
AsyncData* adata = (AsyncData*)thread_data; if (d->work_buffer_sz < inbuf_sz)
adata->port = d; {
adata->db = G_DATABASES[dbref].db; d->work_buffer = driver_realloc(d->work_buffer, inbuf_sz);
adata->payload = thread_data + sizeof(AsyncData); d->work_buffer_sz = inbuf_sz;
}
// Copy the payload into place // Copy the payload into place
memcpy(adata->payload, inbuf, inbuf_sz); memcpy(d->work_buffer, inbuf, inbuf_sz);
d->work_buffer_offset = inbuf_sz;
// Mark the port as busy and then schedule the appropriate async operation // Mark the port as busy and then schedule the appropriate async operation
d->async_op = cmd; d->async_op = cmd;
TPoolJobFunc fn; TPoolJobFunc fn;
if (cmd == CMD_PUT) if (cmd == CMD_PUT)
{ {
@ -433,14 +441,8 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
{ {
fn = &do_async_get; fn = &do_async_get;
} }
d->async_data = adata;
d->async_pool = G_TPOOL_GENERAL; d->async_pool = G_TPOOL_GENERAL;
d->async_job = bdberl_tpool_run(G_TPOOL_GENERAL, fn, adata, 0); d->async_job = bdberl_tpool_run(G_TPOOL_GENERAL, fn, d, 0);
// Watch for events on the output pipe
// TODO: Can we do this just once ?!
driver_select(d->port, (ErlDrvEvent)(size_t)d->pipe_fds[0], DO_READ, 1);
// Let caller know that the operation is in progress // Let caller know that the operation is in progress
// Outbuf is: <<0:32>> // Outbuf is: <<0:32>>
@ -471,100 +473,6 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
return 0; return 0;
} }
static void bdberl_drv_ready_input(ErlDrvData handle, ErlDrvEvent event)
{
PortData* d = (PortData*)handle;
// printf("%p: ready_input; cmd = %d; rc = %d\n", d->port, d->async_op,
// ((AsyncData*)d->async_data)->rc);
// Empty out the queue
int readbuf;
while (read((size_t)event, &readbuf, sizeof(readbuf)) > 0) { ; }
driver_select(d->port, event, DO_READ, 0);
// The async op has completed running on the thread pool -- process the results
switch (d->async_op)
{
case CMD_PUT:
case CMD_TXN_COMMIT:
case CMD_TXN_ABORT:
{
AsyncData* adata = (AsyncData*)d->async_data;
// Extract return code == if it's zero, send back "ok" to driver process; otherwise
// send a {error, Reason} tuple
if (adata->rc == 0)
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("ok") };
driver_output_term(d->port, response, sizeof(response) / sizeof(response[0]));
}
else
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("error"),
ERL_DRV_INT, adata->rc,
ERL_DRV_TUPLE, 2};
driver_output_term(d->port, response, sizeof(response) / sizeof(response[0]));
}
// If this was a commit/abort, or a deadlock occurred while in a transaction,
// clear out the handle -- it's already invalid
if (d->async_op == CMD_TXN_COMMIT || d->async_op == CMD_TXN_ABORT ||
(d->txn && (adata->rc == DB_LOCK_NOTGRANTED || adata->rc == DB_LOCK_DEADLOCK)))
{
d->txn = 0;
}
// Cleanup async data and mark the port as not busy
driver_free(d->async_data);
d->async_data = 0;
d->async_op = CMD_NONE;
d->async_job = 0;
d->async_pool = 0;
break;
}
case CMD_GET:
{
// Extract return code == if it's zero, send back {ok, Payload} or not_found to driver
// process; otherwise send a {error, Reason} tuple
AsyncData* adata = (AsyncData*)d->async_data;
if (adata->rc == 0)
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("ok"),
ERL_DRV_BUF2BINARY, (ErlDrvTermData)adata->payload, (ErlDrvUInt)adata->payload_sz,
ERL_DRV_TUPLE, 2};
driver_output_term(d->port, response, sizeof(response) / sizeof(response[0]));
}
else if (adata->rc == DB_NOTFOUND)
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("not_found") };
driver_output_term(d->port, response, sizeof(response) / sizeof(response[0]));
}
else
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("error"),
ERL_DRV_INT, adata->rc,
ERL_DRV_TUPLE, 2};
driver_output_term(d->port, response, sizeof(response) / sizeof(response[0]));
}
// If a deadlock occurred while in a transaction, clear out the handle -- it's
// already invalid
if (d->txn && (adata->rc == DB_LOCK_DEADLOCK || adata->rc == DB_LOCK_NOTGRANTED))
{
d->txn = 0;
}
// Cleanup async data and mark the port as not busy
driver_free(adata->payload);
driver_free(d->async_data);
d->async_data = 0;
d->async_op = CMD_NONE;
d->async_job = 0;
d->async_pool = 0;
break;
}
}
}
static int open_database(const char* name, DBTYPE type, unsigned int flags, PortData* data, int* dbref_res) static int open_database(const char* name, DBTYPE type, unsigned int flags, PortData* data, int* dbref_res)
{ {
@ -773,8 +681,12 @@ static void tune_system(int target, void* values, BinHelper* bh)
static void do_async_put(void* arg) static void do_async_put(void* arg)
{ {
// Payload is: <<DbRef:32, Flags:32, KeyLen:32, Key:KeyLen, ValLen:32, Val:ValLen>> // Payload is: <<DbRef:32, Flags:32, KeyLen:32, Key:KeyLen, ValLen:32, Val:ValLen>>
AsyncData* adata = (AsyncData*)arg; PortData* d = (PortData*)arg;
unsigned flags = UNPACK_INT(adata->payload, 4);
// Get the database reference and flags from the payload
int dbref = UNPACK_INT(d->work_buffer, 0);
DB* db = G_DATABASES[dbref].db;
unsigned int flags = UNPACK_INT(d->work_buffer, 4);
// Setup DBTs // Setup DBTs
DBT key; DBT key;
@ -783,31 +695,55 @@ static void do_async_put(void* arg)
memset(&value, '\0', sizeof(DBT)); memset(&value, '\0', sizeof(DBT));
// Parse payload into DBTs // Parse payload into DBTs
key.size = UNPACK_INT(adata->payload, 8); key.size = UNPACK_INT(d->work_buffer, 8);
key.data = UNPACK_BLOB(adata->payload, 12); key.data = UNPACK_BLOB(d->work_buffer, 12);
value.size = UNPACK_INT(adata->payload, 12 + key.size); value.size = UNPACK_INT(d->work_buffer, 12 + key.size);
value.data = UNPACK_BLOB(adata->payload, 12 + key.size + 4); value.data = UNPACK_BLOB(d->work_buffer, 12 + key.size + 4);
// Execute the actual put -- we'll process the result back in the driver_async_ready function // Execute the actual put. All databases are opened with AUTO_COMMIT, so if msg->port->txn
// All databases are opened with AUTO_COMMIT, so if msg->port->txn is NULL, the put will still // is NULL, the put will still be atomic
// be atomic int rc = db->put(db, d->txn, &key, &value, flags);
adata->rc = adata->db->put(adata->db, adata->port->txn, &key, &value, flags);
// If any error occurs while we have a txn action, abort it // If any error occurs while we have a txn action, abort it
if (adata->port->txn && adata->rc) if (d->txn && rc)
{ {
adata->port->txn->abort(adata->port->txn); d->txn->abort(d->txn);
d->txn = 0;
} }
// Enqueue a signal for the port to know that the operation is complete // Save the port and pid references -- we need copies independent from the PortData
signal_port(adata->port); // structure. Once we release the port_lock after clearing the cmd, it's possible that
// the port could go away without waiting on us to finish. This is acceptable, but we need
// to be certain that there is no overlap of data between the two threads. driver_send_term
// is safe to use from a thread, even if the port you're sending from has already expired.
ErlDrvPort port = d->port;
ErlDrvTermData port_owner = d->port_owner;
// Release the port for another operation
d->async_pool = 0;
d->async_job = 0;
d->work_buffer_offset = 0;
erl_drv_mutex_lock(d->port_lock);
d->async_op = CMD_NONE;
erl_drv_mutex_unlock(d->port_lock);
// TODO: May need to tag the messages a bit more explicitly so that if another async
// job runs to completion before the message gets delivered we don't mis-interpret this
// response code.
send_ok_or_error(port, port_owner, rc);
} }
static void do_async_get(void* arg) static void do_async_get(void* arg)
{ {
// Payload is: << DbRef:32, Flags:32, KeyLen:32, Key:KeyLen >> // Payload is: << DbRef:32, Flags:32, KeyLen:32, Key:KeyLen >>
AsyncData* adata = (AsyncData*)arg; PortData* d = (PortData*)arg;
unsigned flags = UNPACK_INT(adata->payload, 4);
// Get the database object, using the provided ref
int dbref = UNPACK_INT(d->work_buffer, 0);
DB* db = G_DATABASES[dbref].db;
// Extract operation flags
unsigned flags = UNPACK_INT(d->work_buffer, 4);
// Setup DBTs // Setup DBTs
DBT key; DBT key;
@ -816,54 +752,109 @@ static void do_async_get(void* arg)
memset(&value, '\0', sizeof(DBT)); memset(&value, '\0', sizeof(DBT));
// Parse payload into DBT // Parse payload into DBT
key.size = UNPACK_INT(adata->payload, 8); key.size = UNPACK_INT(d->work_buffer, 8);
key.data = UNPACK_BLOB(adata->payload, 12); key.data = UNPACK_BLOB(d->work_buffer, 12);
// Allocate memory to hold the value -- hard code initial size to 4k // Allocate a buffer for the output value
// TODO: Make this smarter! value.data = driver_alloc(4096);
value.data = zalloc(4096);
value.ulen = 4096; value.ulen = 4096;
value.flags = DB_DBT_USERMEM; value.flags = DB_DBT_USERMEM;
int rc = adata->db->get(adata->db, adata->port->txn, &key, &value, flags); int rc = db->get(db, d->txn, &key, &value, flags);
while (rc == DB_BUFFER_SMALL) while (rc == DB_BUFFER_SMALL)
{ {
// Grow our value buffer and try again // Grow our value buffer and try again
value.data = driver_realloc(value.data, value.size); value.data = driver_realloc(value.data, value.size);
value.ulen = value.size; value.ulen = value.size;
rc = adata->db->get(adata->db, adata->port->txn, &key, &value, flags); rc = db->get(db, d->txn, &key, &value, flags);
} }
adata->payload = value.data; // Cleanup transaction as necessary
adata->payload_sz = value.size; // Not ulen -- we want the actual data size if (rc && rc != DB_NOTFOUND && d->txn)
adata->rc = rc;
// If any error occurs while we have a txn action, abort it
if (adata->rc != DB_NOTFOUND && adata->port->txn && adata->rc)
{ {
adata->port->txn->abort(adata->port->txn); d->txn->abort(d->txn);
d->txn = 0;
} }
signal_port(adata->port); // Save the port and pid references -- we need copies independent from the PortData
// structure. Once we release the port_lock after clearing the cmd, it's possible that
// the port could go away without waiting on us to finish. This is acceptable, but we need
// to be certain that there is no overlap of data between the two threads. driver_send_term
// is safe to use from a thread, even if the port you're sending from has already expired.
ErlDrvPort port = d->port;
ErlDrvTermData port_owner = d->port_owner;
// Release the port for another operation
d->async_pool = 0;
d->async_job = 0;
d->work_buffer_offset = 0;
erl_drv_mutex_lock(d->port_lock);
d->async_op = CMD_NONE;
erl_drv_mutex_unlock(d->port_lock);
// Notify port of result
if (rc == 0)
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("ok"),
ERL_DRV_BUF2BINARY, (ErlDrvTermData)value.data, (ErlDrvUInt)value.size,
ERL_DRV_TUPLE, 2};
driver_send_term(port, port_owner, response, sizeof(response) / sizeof(response[0]));
}
else if (rc == DB_NOTFOUND)
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("not_found") };
driver_send_term(port, port_owner, response, sizeof(response) / sizeof(response[0]));
}
else
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("error"),
ERL_DRV_INT, rc,
ERL_DRV_TUPLE, 2};
driver_send_term(port, port_owner, response, sizeof(response) / sizeof(response[0]));
}
// Finally, clean up value buffer (driver_send_term made a copy)
driver_free(value.data);
} }
static void do_async_txnop(void* arg) static void do_async_txnop(void* arg)
{ {
AsyncData* adata = (AsyncData*)arg; PortData* d = (PortData*)arg;
// printf("%p: do_async_txnop\n", adata->port->port);
// Execute the actual commit/abort // Execute the actual commit/abort
if (adata->port->async_op == CMD_TXN_COMMIT) int rc = 0;
if (d->async_op == CMD_TXN_COMMIT)
{ {
unsigned flags = (unsigned)(size_t)adata->payload; rc = d->txn->commit(d->txn, d->async_flags);
adata->rc = adata->port->txn->commit(adata->port->txn, flags);
} }
else else
{ {
adata->rc = adata->port->txn->abort(adata->port->txn); rc = d->txn->abort(d->txn);
} }
signal_port(adata->port); // The transaction is now invalid, regardless of the outcome.
d->txn = 0;
// Save the port and pid references -- we need copies independent from the PortData
// structure. Once we release the port_lock after clearing the cmd, it's possible that
// the port could go away without waiting on us to finish. This is acceptable, but we need
// to be certain that there is no overlap of data between the two threads. driver_send_term
// is safe to use from a thread, even if the port you're sending from has already expired.
ErlDrvPort port = d->port;
ErlDrvTermData port_owner = d->port_owner;
// Release the port for another operation
d->async_pool = 0;
d->async_job = 0;
d->work_buffer_offset = 0;
erl_drv_mutex_lock(d->port_lock);
d->async_op = CMD_NONE;
erl_drv_mutex_unlock(d->port_lock);
// TODO: May need to tag the messages a bit more explicitly so that if another async
// job runs to completion before the message gets delivered we don't mis-interpret this
// response code.
send_ok_or_error(port, port_owner, rc);
} }
@ -1047,11 +1038,6 @@ static int alloc_dbref()
return -1; return -1;
} }
static void signal_port(PortData* d)
{
int flag = 1;
write(d->pipe_fds[1], &flag, sizeof(flag));
}
/** /**
* Thread function that runs the deadlock checker periodically * Thread function that runs the deadlock checker periodically
@ -1105,3 +1091,19 @@ static void* trickle_write(void* arg)
printf("Trickle writer exiting.\n"); printf("Trickle writer exiting.\n");
return 0; return 0;
} }
static void send_ok_or_error(ErlDrvPort port, ErlDrvTermData pid, int rc)
{
if (rc == 0)
{
ErlDrvTermData response[] = {ERL_DRV_ATOM, driver_mk_atom("ok")};
driver_send_term(port, pid, response, sizeof(response) / sizeof(response[0]));
}
else
{
ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("error"),
ERL_DRV_INT, rc,
ERL_DRV_TUPLE, 2};
driver_send_term(port, pid, response, sizeof(response) / sizeof(response[0]));
}
}

View file

@ -24,8 +24,6 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
char* inbuf, int inbuf_sz, char* inbuf, int inbuf_sz,
char** outbuf, int outbuf_sz); char** outbuf, int outbuf_sz);
static void bdberl_drv_ready_input(ErlDrvData handle, ErlDrvEvent ev);
/** /**
* Command codes * Command codes
@ -85,7 +83,7 @@ ErlDrvEntry bdberl_drv_entry =
bdberl_drv_start, /* L_PTR start, called when port is opened */ bdberl_drv_start, /* L_PTR start, called when port is opened */
bdberl_drv_stop, /* F_PTR stop, called when port is closed */ bdberl_drv_stop, /* F_PTR stop, called when port is closed */
NULL, /* F_PTR output, called when erlang has sent */ NULL, /* F_PTR output, called when erlang has sent */
bdberl_drv_ready_input, /* F_PTR ready_input, called when input descriptor ready */ NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */ NULL, /* F_PTR ready_output, called when output descriptor ready */
"bdberl_drv", /* driver_name */ "bdberl_drv", /* driver_name */
bdberl_drv_finish, /* F_PTR finish, called when unloaded */ bdberl_drv_finish, /* F_PTR finish, called when unloaded */
@ -134,33 +132,30 @@ typedef struct
{ {
ErlDrvPort port; ErlDrvPort port;
ErlDrvMutex* port_lock; /* Mutex for this port (to permit async jobs to safely update this
* structure) */
ErlDrvTermData port_owner; /* Pid of the port owner */
DbRefList* dbrefs; /* List of databases that this port has opened */ DbRefList* dbrefs; /* List of databases that this port has opened */
DB_TXN* txn; /* Transaction handle for this port; each port may only have 1 txn DB_TXN* txn; /* Transaction handle for this port; each port may only have 1 txn
* active */ * active */
int pipe_fds[2]; /* Array of pipe fds for signaling purposes */
int async_op; /* Value indicating what async op is pending */ int async_op; /* Value indicating what async op is pending */
void* async_data; /* Opaque point to data used during async op */ int async_flags; /* Flags for the async op command */
TPoolJob* async_job; /* Active job on the thread pool */ TPoolJob* async_job; /* Active job on the thread pool */
TPool* async_pool; /* Pool the async job is running on */ TPool* async_pool; /* Pool the async job is running on */
void* work_buffer;
unsigned int work_buffer_sz;
unsigned int work_buffer_offset;
} PortData; } PortData;
typedef struct
{
PortData* port; /* Port that originated this request -- READ ONLY! */
int rc; /* Return code from operation */
DB* db; /* Database to use for data storage/retrieval */
void* payload; /* Packed key/value data */
int payload_sz; /* Size of payload */
} AsyncData;
#endif #endif