OpenFPGA/libs/EXTERNAL/tcl8.6.12/pkgs/thread2.8.7/generic/psLmdb.c

546 lines
11 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This file implements wrappers for persistent lmdb storage for the
* shared variable arrays.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
* ----------------------------------------------------------------------------
*/
#ifdef HAVE_LMDB
#include "threadSvCmd.h"
#include <lmdb.h>
/*
* Structure keeping the lmdb environment context
*/
typedef struct {
MDB_env * env; // Environment
MDB_txn * txn; // Last active read transaction
MDB_cursor * cur; // Cursor used for ps_lmdb_first and ps_lmdb_next
MDB_dbi dbi; // Open database (default db)
int err; // Last error (used in ps_lmdb_geterr)
} * LmdbCtx;
/*
* Transaction and DB open mode
*/
enum LmdbOpenMode { LmdbRead, LmdbWrite };
// Initialize or renew a transaction.
static void LmdbTxnGet(LmdbCtx ctx, enum LmdbOpenMode mode);
// Commit a transaction.
static void LmdbTxnCommit(LmdbCtx ctx);
// Abort a transaction
static void LmdbTxnAbort(LmdbCtx ctx);
void LmdbTxnGet(LmdbCtx ctx, enum LmdbOpenMode mode)
{
// Read transactions are reused, if possible
if (ctx->txn && mode == LmdbRead)
{
ctx->err = mdb_txn_renew(ctx->txn);
if (ctx->err)
{
ctx->txn = NULL;
}
}
else if (ctx->txn && mode == LmdbWrite)
{
LmdbTxnAbort(ctx);
}
if (ctx->txn == NULL)
{
ctx->err = mdb_txn_begin(ctx->env, NULL, 0, &ctx->txn);
}
if (ctx->err)
{
ctx->txn = NULL;
return;
}
// Given the setup above, and the arguments given, this won't fail.
mdb_dbi_open(ctx->txn, NULL, 0, &ctx->dbi);
}
void LmdbTxnCommit(LmdbCtx ctx)
{
ctx->err = mdb_txn_commit(ctx->txn);
ctx->txn = NULL;
}
void LmdbTxnAbort(LmdbCtx ctx)
{
mdb_txn_abort(ctx->txn);
ctx->txn = NULL;
}
/*
* Functions implementing the persistent store interface
*/
static ps_open_proc ps_lmdb_open;
static ps_close_proc ps_lmdb_close;
static ps_get_proc ps_lmdb_get;
static ps_put_proc ps_lmdb_put;
static ps_first_proc ps_lmdb_first;
static ps_next_proc ps_lmdb_next;
static ps_delete_proc ps_lmdb_delete;
static ps_free_proc ps_lmdb_free;
static ps_geterr_proc ps_lmdb_geterr;
/*
* This structure collects all the various pointers
* to the functions implementing the lmdb store.
*/
const PsStore LmdbStore = {
"lmdb",
NULL,
ps_lmdb_open,
ps_lmdb_get,
ps_lmdb_put,
ps_lmdb_first,
ps_lmdb_next,
ps_lmdb_delete,
ps_lmdb_close,
ps_lmdb_free,
ps_lmdb_geterr,
NULL
};
/*
*-----------------------------------------------------------------------------
*
* Sv_RegisterLmdbStore --
*
* Register the lmdb store with shared variable implementation.
*
* Results:
* None.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
void
Sv_RegisterLmdbStore(void)
{
Sv_RegisterPsStore(&LmdbStore);
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_open --
*
* Opens the lmdb-based persistent storage.
*
* Results:
* Opaque handle for LmdbCtx.
*
* Side effects:
* The lmdb file might be created if not found.
*
*-----------------------------------------------------------------------------
*/
static ClientData
ps_lmdb_open(
const char *path)
{
LmdbCtx ctx;
char *ext;
Tcl_DString toext;
ctx = (LmdbCtx)ckalloc(sizeof(*ctx));
if (ctx == NULL)
{
return NULL;
}
ctx->env = NULL;
ctx->txn = NULL;
ctx->cur = NULL;
ctx->dbi = 0;
ctx->err = mdb_env_create(&ctx->env);
if (ctx->err)
{
ckfree(ctx);
return NULL;
}
Tcl_DStringInit(&toext);
ext = Tcl_UtfToExternalDString(NULL, path, strlen(path), &toext);
ctx->err = mdb_env_open(ctx->env, ext, MDB_NOSUBDIR|MDB_NOLOCK, 0666);
Tcl_DStringFree(&toext);
if (ctx->err)
{
ckfree(ctx);
return NULL;
}
return ctx;
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_close --
*
* Closes the lmdb-based persistent storage.
*
* Results:
* 0 - ok
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
static int
ps_lmdb_close(
ClientData handle)
{
LmdbCtx ctx = (LmdbCtx)handle;
if (ctx->cur)
{
mdb_cursor_close(ctx->cur);
}
if (ctx->txn)
{
LmdbTxnAbort(ctx);
}
mdb_env_close(ctx->env);
ckfree(ctx);
return 0;
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_get --
*
* Retrieves data for the key from the lmdb storage.
*
* Results:
* 1 - no such key
* 0 - ok
*
* Side effects:
* Data returned must be copied, then psFree must be called.
*
*-----------------------------------------------------------------------------
*/
static int
ps_lmdb_get(
ClientData handle,
const char *keyptr,
char **dataptrptr,
size_t *lenptr)
{
LmdbCtx ctx = (LmdbCtx)handle;
MDB_val key, data;
LmdbTxnGet(ctx, LmdbRead);
if (ctx->err)
{
return 1;
}
key.mv_data = (void *)keyptr;
key.mv_size = strlen(keyptr) + 1;
ctx->err = mdb_get(ctx->txn, ctx->dbi, &key, &data);
if (ctx->err)
{
mdb_txn_reset(ctx->txn);
return 1;
}
*dataptrptr = (char *)data.mv_data;
*lenptr = data.mv_size;
/*
* Transaction is left open at this point, so that the caller can get ahold
* of the data and make a copy of it. Afterwards, it will call ps_lmdb_free
* to free the data, and we'll catch the chance to reset the transaction
* there.
*/
return 0;
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_first --
*
* Starts the iterator over the lmdb file and returns the first record.
*
* Results:
* 1 - no more records in the iterator
* 0 - ok
*
* Side effects:
* Data returned must be copied, then psFree must be called.
*
*-----------------------------------------------------------------------------
*/
static int
ps_lmdb_first(
ClientData handle,
char **keyptrptr,
char **dataptrptr,
size_t *lenptr)
{
LmdbCtx ctx = (LmdbCtx)handle;
MDB_val key, data;
LmdbTxnGet(ctx, LmdbRead);
if (ctx->err)
{
return 1;
}
ctx->err = mdb_cursor_open(ctx->txn, ctx->dbi, &ctx->cur);
if (ctx->err)
{
return 1;
}
ctx->err = mdb_cursor_get(ctx->cur, &key, &data, MDB_FIRST);
if (ctx->err)
{
mdb_txn_reset(ctx->txn);
mdb_cursor_close(ctx->cur);
ctx->cur = NULL;
return 1;
}
*dataptrptr = (char *)data.mv_data;
*lenptr = data.mv_size;
*keyptrptr = (char *)key.mv_data;
return 0;
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_next --
*
* Uses the iterator over the lmdb file and returns the next record.
*
* Results:
* 1 - no more records in the iterator
* 0 - ok
*
* Side effects:
* Data returned must be copied, then psFree must be called.
*
*-----------------------------------------------------------------------------
*/
static int ps_lmdb_next(
ClientData handle,
char **keyptrptr,
char **dataptrptr,
size_t *lenptr)
{
LmdbCtx ctx = (LmdbCtx)handle;
MDB_val key, data;
ctx->err = mdb_cursor_get(ctx->cur, &key, &data, MDB_NEXT);
if (ctx->err)
{
mdb_txn_reset(ctx->txn);
mdb_cursor_close(ctx->cur);
ctx->cur = NULL;
return 1;
}
*dataptrptr = (char *)data.mv_data;
*lenptr = data.mv_size;
*keyptrptr = (char *)key.mv_data;
return 0;
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_put --
*
* Stores used data bound to a key in lmdb storage.
*
* Results:
* 0 - ok
* -1 - error; use ps_lmdb_geterr to retrieve the error message
*
* Side effects:
* If the key is already associated with some user data, this will
* be replaced by the new data chunk.
*
*-----------------------------------------------------------------------------
*/
static int
ps_lmdb_put(
ClientData handle,
const char *keyptr,
char *dataptr,
size_t len)
{
LmdbCtx ctx = (LmdbCtx)handle;
MDB_val key, data;
LmdbTxnGet(ctx, LmdbWrite);
if (ctx->err)
{
return -1;
}
key.mv_data = (void*)keyptr;
key.mv_size = strlen(keyptr) + 1;
data.mv_data = dataptr;
data.mv_size = len;
ctx->err = mdb_put(ctx->txn, ctx->dbi, &key, &data, 0);
if (ctx->err)
{
LmdbTxnAbort(ctx);
}
else
{
LmdbTxnCommit(ctx);
}
return ctx->err ? -1 : 0;
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_delete --
*
* Deletes the key and associated data from the lmdb storage.
*
* Results:
* 0 - ok
* -1 - error; use ps_lmdb_geterr to retrieve the error message
*
* Side effects:
* If the key is already associated with some user data, this will
* be replaced by the new data chunk.
*
*-----------------------------------------------------------------------------
*/
static int
ps_lmdb_delete(
ClientData handle,
const char *keyptr)
{
LmdbCtx ctx = (LmdbCtx)handle;
MDB_val key;
LmdbTxnGet(ctx, LmdbWrite);
if (ctx->err)
{
return -1;
}
key.mv_data = (void*)keyptr;
key.mv_size = strlen(keyptr) + 1;
ctx->err = mdb_del(ctx->txn, ctx->dbi, &key, NULL);
if (ctx->err)
{
LmdbTxnAbort(ctx);
}
else
{
LmdbTxnCommit(ctx);
}
ctx->txn = NULL;
return ctx->err ? -1 : 0;
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_free --
*
* This function is called to free data returned by the persistent store
* after calls to psFirst, psNext, or psGet. Lmdb doesn't need to free any
* data, as the data returned is owned by lmdb. On the other hand, this
* method is required to reset the read transaction. This is done only
* when iteration is over (ctx->cur == NULL).
*
* Results:
* None.
*
* Side effects:
* Memory gets reclaimed.
*
*-----------------------------------------------------------------------------
*/
static void
ps_lmdb_free(
ClientData handle,
void *data)
{
LmdbCtx ctx = (LmdbCtx)handle;
(void)data;
if (ctx->cur == NULL)
{
mdb_txn_reset(ctx->txn);
}
}
/*
*-----------------------------------------------------------------------------
*
* ps_lmdb_geterr --
*
* Retrieves the textual representation of the error caused
* by the last lmdb command.
*
* Results:
* Pointer to the string message.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
static const char*
ps_lmdb_geterr(
ClientData handle)
{
LmdbCtx ctx = (LmdbCtx)handle;
return mdb_strerror(ctx->err);
}
#endif /* HAVE_LMDB */
/* EOF $RCSfile*/
/* Emacs Setup Variables */
/* Local Variables: */
/* mode: C */
/* indent-tabs-mode: nil */
/* c-basic-offset: 4 */
/* End: */