if (tdb->global_lock.count) {
return 0;
}
- if (tdb->have_transaction_lock) {
- tdb->have_transaction_lock++;
+ if (tdb->transaction_lock_count > 0) {
+ tdb->transaction_lock_count++;
return 0;
}
+
if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype,
F_SETLKW, 0, 1) == -1) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n"));
tdb->ecode = TDB_ERR_LOCK;
return -1;
}
- tdb->have_transaction_lock++;
+ tdb->transaction_lock_count++;
return 0;
}
*/
int tdb_transaction_unlock(struct tdb_context *tdb)
{
+ int ret;
if (tdb->global_lock.count) {
return 0;
}
- if (--tdb->have_transaction_lock) {
+ if (tdb->transaction_lock_count > 1) {
+ tdb->transaction_lock_count--;
return 0;
}
- return tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
+ ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
+ if (ret == 0) {
+ tdb->transaction_lock_count = 0;
+ }
+ return ret;
}
struct tdb_transaction *transaction;
int page_size;
int max_dead_records;
- int have_transaction_lock;
+ int transaction_lock_count;
int tracefd;
volatile sig_atomic_t *interrupt_sig_ptr;
};
--- /dev/null
+#include "external-transaction.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ccan/tdb/tdb.h>
+
+static volatile sig_atomic_t alarmed;
+static void do_alarm(int signum)
+{
+ alarmed++;
+}
+
+static int do_transaction(const char *name)
+{
+ TDB_DATA k = { .dptr = (void *)"a", .dsize = 1 };
+ TDB_DATA d = { .dptr = (void *)"b", .dsize = 1 };
+ struct tdb_context *tdb = tdb_open(name, 0, 0, O_RDWR, 0);
+
+ if (!tdb)
+ return -1;
+
+ alarmed = 0;
+ tdb_setalarm_sigptr(tdb, &alarmed);
+
+ alarm(1);
+ if (tdb_transaction_start(tdb) != 0)
+ goto maybe_alarmed;
+
+ if (tdb_store(tdb, k, d, 0) != 0) {
+ tdb_transaction_cancel(tdb);
+ tdb_close(tdb);
+ return -2;
+ }
+
+ if (tdb_transaction_commit(tdb) == 0) {
+ tdb_delete(tdb, k);
+ tdb_close(tdb);
+ return 1;
+ }
+
+ tdb_delete(tdb, k);
+maybe_alarmed:
+ tdb_close(tdb);
+ if (alarmed)
+ return 0;
+ return -3;
+}
+
+
+/* Do this before doing any tdb stuff. Return handle, or -1. */
+int prepare_external_agent(void)
+{
+ int pid;
+ int command[2], response[2];
+ struct sigaction act = { .sa_handler = do_alarm };
+ char name[PATH_MAX];
+
+ if (pipe(command) != 0 || pipe(response) != 0)
+ return -1;
+
+ pid = fork();
+ if (pid < 0)
+ return -1;
+
+ if (pid != 0) {
+ close(command[0]);
+ close(response[1]);
+ /* FIXME: Make fds consective. */
+ dup2(command[1]+1, response[1]);
+ return command[1];
+ }
+
+ close(command[1]);
+ close(response[0]);
+ sigaction(SIGALRM, &act, NULL);
+
+ while (read(command[0], name, sizeof(name)) != 0) {
+ int result = do_transaction(name);
+ if (write(response[1], &result, sizeof(result))
+ != sizeof(result))
+ err(1, "Writing response");
+ }
+ exit(0);
+}
+
+/* Ask the external agent to try to do a transaction. */
+bool external_agent_transaction(int handle, const char *tdbname)
+{
+ int res;
+
+ if (write(handle, tdbname, strlen(tdbname)+1)
+ != strlen(tdbname)+1)
+ err(1, "Writing to agent");
+
+ if (read(handle+1, &res, sizeof(res)) != sizeof(res))
+ err(1, "Reading from agent");
+
+ if (res > 1)
+ errx(1, "Agent returned %u\n", res);
+
+ return res;
+}
--- /dev/null
+#ifndef TDB_TEST_EXTERNAL_TRANSACTION_H
+#define TDB_TEST_EXTERNAL_TRANSACTION_H
+#include <stdbool.h>
+
+/* Do this before doing any tdb stuff. Return handle, or -1. */
+int prepare_external_agent(void);
+
+/* Ask the external agent to try to do a transaction. */
+bool external_agent_transaction(int handle, const char *tdbname);
+
+#endif /* TDB_TEST_EXTERNAL_TRANSACTION_H */
#include <stdlib.h>
#include <stdbool.h>
#include <err.h>
+#include "external-transaction.h"
+
+static int agent;
static bool correct_key(TDB_DATA key)
{
{
ok1(correct_key(key));
ok1(correct_data(data));
- ok1(tdb->have_transaction_lock);
+ ok1(!external_agent_transaction(agent, tdb_name(tdb)));
tdb_traverse(tdb, traverse2, NULL);
/* That should *not* release the transaction lock! */
- ok1(tdb->have_transaction_lock);
+ ok1(!external_agent_transaction(agent, tdb_name(tdb)));
return 0;
}
struct tdb_context *tdb;
TDB_DATA key, data;
- plan_tests(14);
- tdb = tdb_open("/tmp/test.tdb", 1024, TDB_CLEAR_IF_FIRST,
+ plan_tests(15);
+ agent = prepare_external_agent();
+ if (agent < 0)
+ err(1, "preparing agent");
+
+ tdb = tdb_open("/tmp/test3.tdb", 1024, TDB_CLEAR_IF_FIRST,
O_CREAT|O_TRUNC|O_RDWR, 0600);
ok1(tdb);
- /* Tickle bug on appending zero length buffer to zero length buffer. */
+ ok1(external_agent_transaction(agent, tdb_name(tdb)));
+
key.dsize = strlen("hi");
key.dptr = (void *)"hi";
data.dptr = (void *)"world";
#include <stdlib.h>
#include <stdbool.h>
#include <err.h>
+#include "external-transaction.h"
+
+static int agent;
static bool correct_key(TDB_DATA key)
{
struct tdb_context *tdb;
TDB_DATA key, data;
- plan_tests(12);
- tdb = tdb_open("/tmp/test.tdb", 1024, TDB_CLEAR_IF_FIRST,
+ plan_tests(13);
+ agent = prepare_external_agent();
+ if (agent < 0)
+ err(1, "preparing agent");
+
+ tdb = tdb_open("/tmp/test2.tdb", 1024, TDB_CLEAR_IF_FIRST,
O_CREAT|O_TRUNC|O_RDWR, 0600);
ok1(tdb);
- /* Tickle bug on appending zero length buffer to zero length buffer. */
key.dsize = strlen("hi");
key.dptr = (void *)"hi";
data.dptr = (void *)"world";
ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
+ ok1(external_agent_transaction(agent, tdb_name(tdb)));
+
ok1(tdb_transaction_start(tdb) == 0);
- ok(tdb->have_transaction_lock, "Transaction lock in transaction");
+ ok1(!external_agent_transaction(agent, tdb_name(tdb)));
tdb_traverse(tdb, traverse, NULL);
/* That should *not* release the transaction lock! */
- ok(tdb->have_transaction_lock, "Transaction lock after traverse");
+ ok1(!external_agent_transaction(agent, tdb_name(tdb)));
tdb_traverse_read(tdb, traverse, NULL);
/* That should *not* release the transaction lock! */
- ok(tdb->have_transaction_lock, "Transaction lock after traverse_read");
+ ok1(!external_agent_transaction(agent, tdb_name(tdb)));
ok1(tdb_transaction_commit(tdb) == 0);
- ok(!tdb->have_transaction_lock, "Transaction unlock");
+ /* Now we should be fine. */
+ ok1(external_agent_transaction(agent, tdb_name(tdb)));
tdb_close(tdb);