From 1e7619137f25ceb0cef59100bc5a41ffc21a1412 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Thu, 17 Apr 2014 17:41:24 -0400 Subject: uw_register_transactional() can return error codes --- doc/manual.tex | 6 +++--- include/urweb/urweb_cpp.h | 2 +- src/c/urweb.c | 7 +++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/manual.tex b/doc/manual.tex index 98ebaac5..ea866309 100644 --- a/doc/manual.tex +++ b/doc/manual.tex @@ -2459,10 +2459,10 @@ void *uw_malloc(uw_context, size_t); \item \begin{verbatim} typedef void (*uw_callback)(void *); typedef void (*uw_callback_with_retry)(void *, int will_retry); -void uw_register_transactional(uw_context, void *data, uw_callback commit, - uw_callback rollback, uw_callback_with_retry free); +int uw_register_transactional(uw_context, void *data, uw_callback commit, + uw_callback rollback, uw_callback_with_retry free); \end{verbatim} - All side effects in Ur/Web programs need to be compatible with transactions, such that any set of actions can be undone at any time. Thus, you should not perform actions with non-local side effects directly; instead, register handlers to be called when the current transaction is committed or rolled back. The arguments here give an arbitary piece of data to be passed to callbacks, a function to call on commit, a function to call on rollback, and a function to call afterward in either case to clean up any allocated resources. A rollback handler may be called after the associated commit handler has already been called, if some later part of the commit process fails. A free handler is told whether the runtime system expects to retry the current page request after rollback finishes. + All side effects in Ur/Web programs need to be compatible with transactions, such that any set of actions can be undone at any time. Thus, you should not perform actions with non-local side effects directly; instead, register handlers to be called when the current transaction is committed or rolled back. The arguments here give an arbitary piece of data to be passed to callbacks, a function to call on commit, a function to call on rollback, and a function to call afterward in either case to clean up any allocated resources. A rollback handler may be called after the associated commit handler has already been called, if some later part of the commit process fails. A free handler is told whether the runtime system expects to retry the current page request after rollback finishes. The return value of \texttt{uw\_register\_transactional()} is 0 on success and nonzero on failure (where failure currently only happens when exceeding configured limits on number of transactionals). Any of the callbacks may be \texttt{NULL}. To accommodate some stubbornly non-transactional real-world actions like sending an e-mail message, Ur/Web treats \texttt{NULL} \texttt{rollback} callbacks specially. When a transaction commits, all \texttt{commit} actions that have non-\texttt{NULL} rollback actions are tried before any \texttt{commit} actions that have \texttt{NULL} rollback actions. Furthermore, an SQL \texttt{COMMIT} is also attempted in between the two phases, so the nicely transactional actions have a chance to influence whether data are committed to the database, while \texttt{NULL}-rollback actions only get run in the first place after committing data. The reason for all this is that it is \emph{expected} that concurrency interactions will cause database commits to fail in benign ways that call for transaction restart. A truly non-undoable action should only be run after we are sure the database transaction will commit. diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h index b016f038..8e65ace3 100644 --- a/include/urweb/urweb_cpp.h +++ b/include/urweb/urweb_cpp.h @@ -280,7 +280,7 @@ uw_Basis_int uw_Basis_datetimeSecond(struct uw_context *, uw_Basis_time); uw_Basis_int uw_Basis_datetimeDayOfWeek(struct uw_context *, uw_Basis_time); extern const uw_Basis_time uw_Basis_minTime; -void uw_register_transactional(struct uw_context *, void *data, uw_callback commit, uw_callback rollback, uw_callback_with_retry free); +int uw_register_transactional(struct uw_context *, void *data, uw_callback commit, uw_callback rollback, uw_callback_with_retry free); void uw_check_heap(struct uw_context *, size_t extra); char *uw_heap_front(struct uw_context *); diff --git a/src/c/urweb.c b/src/c/urweb.c index 7417e4b7..9a1e40a7 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -3469,11 +3469,12 @@ int uw_commit(uw_context ctx) { size_t uw_transactionals_max = SIZE_MAX; -void uw_register_transactional(uw_context ctx, void *data, uw_callback commit, uw_callback rollback, +int uw_register_transactional(uw_context ctx, void *data, uw_callback commit, uw_callback rollback, uw_callback_with_retry free) { if (ctx->used_transactionals >= ctx->n_transactionals) { if (ctx->used_transactionals+1 > uw_transactionals_max) - uw_error(ctx, FATAL, "Exceeded limit on number of transactionals"); + // Exceeded limit on number of transactionals. + return -1; ctx->transactionals = realloc(ctx->transactionals, sizeof(transactional) * (ctx->used_transactionals+1)); ++ctx->n_transactionals; } @@ -3482,6 +3483,8 @@ void uw_register_transactional(uw_context ctx, void *data, uw_callback commit, u ctx->transactionals[ctx->used_transactionals].commit = commit; ctx->transactionals[ctx->used_transactionals].rollback = rollback; ctx->transactionals[ctx->used_transactionals++].free = free; + + return 0; } -- cgit v1.2.3