summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/types.h2
-rw-r--r--include/urweb.h2
-rw-r--r--src/c/urweb.c51
-rw-r--r--tests/cffi.ur15
-rw-r--r--tests/clib.urp1
-rw-r--r--tests/test.c15
-rw-r--r--tests/test.h2
-rw-r--r--tests/test.urs2
8 files changed, 89 insertions, 1 deletions
diff --git a/include/types.h b/include/types.h
index 0dbd6118..89e88b88 100644
--- a/include/types.h
+++ b/include/types.h
@@ -45,4 +45,6 @@ typedef struct input *uw_input;
#define FLOATS_MAX 100
#define TIMES_MAX 100
+typedef void (*uw_callback)(void *);
+
#endif
diff --git a/include/urweb.h b/include/urweb.h
index d2fa30da..4d176561 100644
--- a/include/urweb.h
+++ b/include/urweb.h
@@ -183,4 +183,6 @@ uw_Basis_int uw_Basis_blobSize(uw_context, uw_Basis_blob);
__attribute__((noreturn)) void uw_return_blob(uw_context, uw_Basis_blob, uw_Basis_string mimeType);
+void uw_register_transactional(uw_context, void *data, uw_callback commit, uw_callback rollback, uw_callback free);
+
#endif
diff --git a/src/c/urweb.c b/src/c/urweb.c
index 1f256681..038f2549 100644
--- a/src/c/urweb.c
+++ b/src/c/urweb.c
@@ -304,6 +304,11 @@ typedef struct input {
} data;
} input;
+typedef struct {
+ void *data;
+ uw_callback commit, rollback, free;
+} transactional;
+
struct uw_context {
char *headers, *headers_end;
@@ -332,6 +337,9 @@ struct uw_context {
client *client;
+ transactional *transactionals;
+ size_t n_transactionals, used_transactionals;
+
char error_message[ERROR_BUF_LEN];
};
@@ -377,6 +385,9 @@ uw_context uw_init() {
ctx->error_message[0] = 0;
+ ctx->transactionals = malloc(0);
+ ctx->n_transactionals = ctx->used_transactionals = 0;
+
return ctx;
}
@@ -398,6 +409,7 @@ void uw_free(uw_context ctx) {
free(ctx->inputs);
free(ctx->subinputs);
free(ctx->cleanup);
+ free(ctx->transactionals);
for (i = 0; i < ctx->n_deltas; ++i)
buf_free(&ctx->deltas[i].msgs);
@@ -419,6 +431,7 @@ void uw_reset_keep_error_message(uw_context ctx) {
ctx->used_deltas = 0;
ctx->client = NULL;
ctx->cur_container = NULL;
+ ctx->used_transactionals = 0;
}
void uw_reset_keep_request(uw_context ctx) {
@@ -2339,7 +2352,15 @@ int uw_db_rollback(uw_context);
void uw_commit(uw_context ctx) {
unsigned i;
- if (uw_db_commit(ctx))
+ for (i = 0; i < ctx->used_transactionals; ++i)
+ if (ctx->transactionals[i].rollback != NULL)
+ ctx->transactionals[i].commit(ctx->transactionals[i].data);
+
+ for (i = 0; i < ctx->used_transactionals; ++i)
+ if (ctx->transactionals[i].rollback == NULL)
+ ctx->transactionals[i].commit(ctx->transactionals[i].data);
+
+ if (uw_db_commit(ctx))
uw_error(ctx, FATAL, "Error running SQL COMMIT");
for (i = 0; i < ctx->used_deltas; ++i) {
@@ -2353,15 +2374,43 @@ void uw_commit(uw_context ctx) {
if (ctx->client)
release_client(ctx->client);
+
+ for (i = 0; i < ctx->used_transactionals; ++i)
+ ctx->transactionals[i].free(ctx->transactionals[i].data);
}
int uw_rollback(uw_context ctx) {
+ size_t i;
+
if (ctx->client)
release_client(ctx->client);
+ for (i = 0; i < ctx->used_transactionals; ++i)
+ if (ctx->transactionals[i].rollback != NULL)
+ ctx->transactionals[i].rollback(ctx->transactionals[i].data);
+
+ for (i = 0; i < ctx->used_transactionals; ++i)
+ ctx->transactionals[i].free(ctx->transactionals[i].data);
+
return uw_db_rollback(ctx);
}
+void uw_register_transactional(uw_context ctx, void *data, uw_callback commit, uw_callback rollback,
+ uw_callback free) {
+ if (commit == NULL)
+ uw_error(ctx, FATAL, "uw_register_transactional: NULL commit callback");
+
+ if (ctx->used_transactionals >= ctx->n_transactionals) {
+ ctx->transactionals = realloc(ctx->transactionals, ctx->used_transactionals+1);
+ ++ctx->n_transactionals;
+ }
+
+ ctx->transactionals[ctx->used_transactionals].data = data;
+ ctx->transactionals[ctx->used_transactionals].commit = commit;
+ ctx->transactionals[ctx->used_transactionals].rollback = rollback;
+ ctx->transactionals[ctx->used_transactionals++].free = free;
+}
+
// "Garbage collection"
diff --git a/tests/cffi.ur b/tests/cffi.ur
index 039eac55..bcb9944c 100644
--- a/tests/cffi.ur
+++ b/tests/cffi.ur
@@ -8,7 +8,22 @@ fun effect () =
<button value="Either" onclick={Test.print}/>
</body></xml>
+fun xact () =
+ Test.transactional;
+ return <xml><body>
+ All good.
+ </body></xml>
+
+fun xact2 () =
+ Test.transactional;
+ error <xml>Failure</xml>;
+ return <xml><body>
+ All gooder.
+ </body></xml>
+
fun main () = return <xml><body>
{[Test.out (Test.frob (Test.create "Hello ") "world!")]}
<form><submit action={effect}/></form>
+ <form><submit action={xact}/></form>
+ <form><submit action={xact2}/></form>
</body></xml>
diff --git a/tests/clib.urp b/tests/clib.urp
index b803a604..de89d03a 100644
--- a/tests/clib.urp
+++ b/tests/clib.urp
@@ -5,5 +5,6 @@ link test.o
effectful Test.print
serverOnly Test.foo
clientOnly Test.bar
+effectful Test.transactional
jsFunc Test.print=print
jsFunc Test.bar=bar
diff --git a/tests/test.c b/tests/test.c
index 1249721e..ef8558d7 100644
--- a/tests/test.c
+++ b/tests/test.c
@@ -25,3 +25,18 @@ uw_Basis_unit uw_Test_foo(uw_context ctx) {
printf("FOO!\n");
return uw_unit_v;
}
+
+static void commit(void *data) {
+ printf("Commit: %s\n", data);
+}
+static void rollback(void *data) {
+ printf("Rollback: %s\n", data);
+}
+static void free(void *data) {
+ printf("Free: %s\n", data);
+}
+
+uw_Basis_unit uw_Test_transactional(uw_context ctx) {
+ uw_register_transactional(ctx, "Beppo", commit, rollback, free);
+ return uw_unit_v;
+}
diff --git a/tests/test.h b/tests/test.h
index d1574e1b..c0dec379 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -8,3 +8,5 @@ uw_Test_t uw_Test_frob(uw_context, uw_Test_t, uw_Basis_string);
uw_Basis_unit uw_Test_print(uw_context);
uw_Basis_unit uw_Test_foo(uw_context);
+
+uw_Basis_unit uw_Test_transactional(uw_context);
diff --git a/tests/test.urs b/tests/test.urs
index 05efcb5b..b4ca6fb6 100644
--- a/tests/test.urs
+++ b/tests/test.urs
@@ -7,3 +7,5 @@ val print : transaction unit
val foo : transaction unit
val bar : string -> transaction unit
+
+val transactional : transaction unit