src/dbfs/trans.c
author Tero Marttila <terom@fixme.fi>
Tue, 18 Nov 2008 02:06:52 +0200
changeset 42 40a3b13ffc9d
parent 33 c71f3053c714
permissions -rw-r--r--
defer fuse_reply_err using event_base_once, interrupt happens without deadlock now

#include <stdlib.h>
#include <assert.h>

#include "trans.h"
#include "../lib/log.h"

void dbfs_trans_free (struct dbfs_trans *ctx) {
    assert(!ctx->req);
    assert(!ctx->trans);
    
    if (ctx->free_fn)
        ctx->free_fn(ctx);

    free(ctx);
}

void dbfs_trans_fail (struct dbfs_trans *ctx, int err) {
    if (ctx->req) {
        if ((err = fuse_reply_err(ctx->req, err)))
            EWARNING(err, "fuse_reply_err: request hangs");

        ctx->req = NULL;
    }

    if (ctx->trans) {
        evsql_trans_abort(ctx->trans);

        ctx->trans = NULL;
    }

    dbfs_trans_free(ctx);
}

static void dbfs_trans_error (struct evsql_trans *trans, void *arg) {
    struct dbfs_trans *ctx = arg;

    // deassociate trans
    ctx->trans = NULL;

    // log error
    INFO("\t[dbfs_trans.err %p:%p] %s", ctx, ctx->req, evsql_trans_error(trans));

    // mark
    if (ctx->err_ptr)
        *ctx->err_ptr = EIO;
    
    // fail
    dbfs_trans_fail(ctx, EIO);
}

static void dbfs_trans_ready (struct evsql_trans *trans, void *arg) {
    struct dbfs_trans *ctx = arg;

    // associate trans
    ctx->trans = trans;

    // log
    INFO("\t[dbfs_trans.ready %p:%p] -> trans=%p", ctx, ctx->req, trans);

    // trigger the callback
    ctx->begin_fn(ctx);
}

static void dbfs_trans_done (struct evsql_trans *trans, void *arg) {
    struct dbfs_trans *ctx = arg;

    // deassociate trans
    ctx->trans = NULL;

    // log
    INFO("\t[dbfs_trans.done %p:%p]", ctx, ctx->req);

    // trigger the callback
    ctx->commit_fn(ctx);
}

int dbfs_trans_init (struct dbfs_trans *ctx, struct fuse_req *req) {
    struct dbfs *dbfs_ctx = fuse_req_userdata(req);
    int err;

    // store
    ctx->req = req;

    // trans
    if ((ctx->trans = evsql_trans(dbfs_ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_trans_error, dbfs_trans_ready, dbfs_trans_done, ctx)) == NULL)
        EERROR(err = EIO, "evsql_trans");

    // good
    return 0;

error:
    return -1;
}

struct dbfs_trans *dbfs_trans_new (struct fuse_req *req) {
    struct dbfs_trans *ctx = NULL;

    // alloc
    if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
        ERROR("calloc");

    // init
    if (dbfs_trans_init(ctx, req))
        goto error;

    // good
    return ctx;
    
error:
    free(ctx);

    return NULL;
}

void dbfs_trans_commit (struct dbfs_trans *ctx) {
    int err, trans_err = 0;

    // detect errors
    ctx->err_ptr = &trans_err;
    
    // attempt commit
    if (evsql_trans_commit(ctx->trans))
        SERROR(err = EIO);
    
    // drop err_ptr
    ctx->err_ptr = NULL;

    // ok, wait for done or error
    return;

error:
    // fail if not already failed
    if (!trans_err)
        dbfs_trans_fail(ctx, err);
}