From 001407495e8ce63fcff702515db38b882d425da8 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Wed, 11 Dec 2013 14:57:54 -0500 Subject: Fix regression in http.c for long-polling connections; add lazy initialization of database connections, to avoid the overhead in handlers that don't use SQL --- src/c/urweb.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'src/c/urweb.c') diff --git a/src/c/urweb.c b/src/c/urweb.c index 8bd5ada9..cd724cbf 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -431,6 +431,7 @@ struct uw_context { unsigned long long source_count; void *db; + int transaction_started; jmp_buf jmp_buf; @@ -507,6 +508,7 @@ uw_context uw_init(int id, void *logger_data, uw_logger log_debug) { ctx->sz_inputs = ctx->n_subinputs = ctx->used_subinputs = 0; ctx->db = NULL; + ctx->transaction_started = 0; ctx->regions = NULL; @@ -631,6 +633,7 @@ void uw_reset_keep_error_message(uw_context ctx) { ctx->amInitializing = 0; ctx->usedSig = 0; ctx->needsResig = 0; + ctx->transaction_started = 0; } void uw_reset_keep_request(uw_context ctx) { @@ -766,16 +769,20 @@ void uw_login(uw_context ctx) { failure_kind uw_begin(uw_context ctx, char *path) { int r = setjmp(ctx->jmp_buf); - if (r == 0) { - if (ctx->app->db_begin(ctx)) - uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); - + if (r == 0) ctx->app->handle(ctx, path); - } return r; } +void uw_ensure_transaction(uw_context ctx) { + if (!ctx->transaction_started) { + if (ctx->app->db_begin(ctx)) + uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); + ctx->transaction_started = 1; + } +} + uw_Basis_client uw_Basis_self(uw_context ctx) { if (ctx->client == NULL) uw_error(ctx, FATAL, "Call to Basis.self() from page that has only server-side code"); @@ -3205,7 +3212,7 @@ int uw_rollback(uw_context ctx, int will_retry) { if (ctx->transactionals[i].free) ctx->transactionals[i].free(ctx->transactionals[i].data, will_retry); - return ctx->app ? ctx->app->db_rollback(ctx) : 0; + return (ctx->app && ctx->transaction_started) ? ctx->app->db_rollback(ctx) : 0; } static const char begin_xhtml[] = "\n\n"; @@ -3262,7 +3269,7 @@ void uw_commit(uw_context ctx) { } } - if (ctx->app->db_commit(ctx)) { + if (ctx->transaction_started && ctx->app->db_commit(ctx)) { uw_set_error_message(ctx, "Error running SQL COMMIT"); return; } @@ -3453,6 +3460,7 @@ failure_kind uw_initialize(uw_context ctx) { if (r == 0) { if (ctx->app->db_begin(ctx)) uw_error(ctx, FATAL, "Error running SQL BEGIN"); + ctx->transaction_started = 1; ctx->app->initializer(ctx); if (ctx->app->db_commit(ctx)) uw_error(ctx, FATAL, "Error running SQL COMMIT"); @@ -4037,6 +4045,7 @@ failure_kind uw_runCallback(uw_context ctx, void (*callback)(uw_context)) { if (r == 0) { if (ctx->app->db_begin(ctx)) uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); + ctx->transaction_started = 1; callback(ctx); } @@ -4085,6 +4094,7 @@ failure_kind uw_begin_onError(uw_context ctx, char *msg) { if (r == 0) { if (ctx->app->db_begin(ctx)) uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); + ctx->transaction_started = 1; uw_buffer_reset(&ctx->outHeaders); if (on_success[0]) -- cgit v1.2.3 From 4c76d279aed3f26bebe9432afe7a0da8892ffc48 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Wed, 11 Dec 2013 18:22:10 -0500 Subject: Change handling of returned text blobs, to activate the normal EWrite optimizations --- include/urweb/urweb_cpp.h | 2 ++ src/c/urweb.c | 34 ++++++++++++++++++++++++++++++++++ src/checknest.sml | 6 ++++-- src/cjr.sml | 2 +- src/cjr_print.sml | 26 ++++++++++++++++++++++++-- src/cjrize.sml | 11 +++++++++-- src/iflow.sml | 9 ++++++--- src/jscomp.sml | 10 ++++++++-- src/mono.sml | 2 +- src/mono_print.sml | 36 ++++++++++++++++++++++++------------ src/mono_reduce.sml | 6 ++++-- src/mono_util.sml | 13 ++++++++++--- src/monoize.sml | 20 +++++++++++++++++++- src/prepare.sml | 9 ++++++++- 14 files changed, 154 insertions(+), 32 deletions(-) (limited to 'src/c/urweb.c') diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h index fb3c83a2..d1fb4d37 100644 --- a/include/urweb/urweb_cpp.h +++ b/include/urweb/urweb_cpp.h @@ -209,6 +209,7 @@ uw_Basis_string uw_Basis_requestHeader(struct uw_context *, uw_Basis_string); void uw_write_header(struct uw_context *, uw_Basis_string); void uw_clear_headers(struct uw_context *); +void uw_Basis_clear_page(struct uw_context *); uw_Basis_string uw_Basis_get_cookie(struct uw_context *, uw_Basis_string c); uw_unit uw_Basis_set_cookie(struct uw_context *, uw_Basis_string prefix, uw_Basis_string c, uw_Basis_string v, uw_Basis_time *expires, uw_Basis_bool secure); @@ -255,6 +256,7 @@ uw_Basis_postBody uw_getPostBody(struct uw_context *); void uw_mayReturnIndirectly(struct uw_context *); __attribute__((noreturn)) void uw_return_blob(struct uw_context *, uw_Basis_blob, uw_Basis_string mimeType); +__attribute__((noreturn)) void uw_return_blob_from_page(struct uw_context *, uw_Basis_string mimeType); __attribute__((noreturn)) void uw_redirect(struct uw_context *, uw_Basis_string url); uw_Basis_time uw_Basis_now(struct uw_context *); diff --git a/src/c/urweb.c b/src/c/urweb.c index cd724cbf..1201b09b 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -1351,6 +1351,10 @@ void uw_clear_headers(uw_context ctx) { uw_buffer_reset(&ctx->outHeaders); } +void uw_Basis_clear_page(uw_context ctx) { + uw_buffer_reset(&ctx->page); +} + static void uw_check_script(uw_context ctx, size_t extra) { ctx_uw_buffer_check(ctx, "script", &ctx->script, extra); } @@ -3736,6 +3740,36 @@ __attribute__((noreturn)) void uw_return_blob(uw_context ctx, uw_Basis_blob b, u longjmp(ctx->jmp_buf, RETURN_INDIRECTLY); } +__attribute__((noreturn)) void uw_return_blob_from_page(uw_context ctx, uw_Basis_string mimeType) { + cleanup *cl; + int len; + char *oldh; + + if (!ctx->allowed_to_return_indirectly) + uw_error(ctx, FATAL, "Tried to return a blob from an RPC"); + + ctx->returning_indirectly = 1; + oldh = old_headers(ctx); + uw_buffer_reset(&ctx->outHeaders); + + uw_write_header(ctx, on_success); + uw_write_header(ctx, "Content-Type: "); + uw_write_header(ctx, mimeType); + uw_write_header(ctx, "\r\nContent-Length: "); + ctx_uw_buffer_check(ctx, "headers", &ctx->outHeaders, INTS_MAX); + sprintf(ctx->outHeaders.front, "%lu%n", (unsigned long)uw_buffer_used(&ctx->page), &len); + ctx->outHeaders.front += len; + uw_write_header(ctx, "\r\n"); + if (oldh) uw_write_header(ctx, oldh); + + for (cl = ctx->cleanup; cl < ctx->cleanup_front; ++cl) + cl->func(cl->arg); + + ctx->cleanup_front = ctx->cleanup; + + longjmp(ctx->jmp_buf, RETURN_INDIRECTLY); +} + __attribute__((noreturn)) void uw_redirect(uw_context ctx, uw_Basis_string url) { cleanup *cl; char *s; diff --git a/src/checknest.sml b/src/checknest.sml index 05ad8e9a..fa418d89 100644 --- a/src/checknest.sml +++ b/src/checknest.sml @@ -56,7 +56,8 @@ fun expUses globals = | ECase (e, pes, _) => foldl (fn ((_, e), s) => IS.union (eu e, s)) (eu e) pes | EError (e, _) => eu e - | EReturnBlob {blob, mimeType, ...} => IS.union (eu blob, eu mimeType) + | EReturnBlob {blob = NONE, mimeType, ...} => eu mimeType + | EReturnBlob {blob = SOME blob, mimeType, ...} => IS.union (eu blob, eu mimeType) | ERedirect (e, _) => eu e | EWrite e => eu e @@ -118,7 +119,8 @@ fun annotateExp globals = | ECase (e, pes, ts) => (ECase (ae e, map (fn (p, e) => (p, ae e)) pes, ts), loc) | EError (e, t) => (EError (ae e, t), loc) - | EReturnBlob {blob, mimeType, t} => (EReturnBlob {blob = ae blob, mimeType = ae mimeType, t = t}, loc) + | EReturnBlob {blob = NONE, mimeType, t} => (EReturnBlob {blob = NONE, mimeType = ae mimeType, t = t}, loc) + | EReturnBlob {blob = SOME blob, mimeType, t} => (EReturnBlob {blob = SOME (ae blob), mimeType = ae mimeType, t = t}, loc) | ERedirect (e, t) => (ERedirect (ae e, t), loc) | EWrite e => (EWrite (ae e), loc) diff --git a/src/cjr.sml b/src/cjr.sml index 3a37b26f..8cbabdcc 100644 --- a/src/cjr.sml +++ b/src/cjr.sml @@ -78,7 +78,7 @@ datatype exp' = | ECase of exp * (pat * exp) list * { disc : typ, result : typ } | EError of exp * typ - | EReturnBlob of {blob : exp, mimeType : exp, t : typ} + | EReturnBlob of {blob : exp option, mimeType : exp, t : typ} | ERedirect of exp * typ | EWrite of exp diff --git a/src/cjr_print.sml b/src/cjr_print.sml index e98918e6..dec21eb3 100644 --- a/src/cjr_print.sml +++ b/src/cjr_print.sml @@ -1628,7 +1628,7 @@ and p_exp' par tail env (e, loc) = string "tmp;", newline, string "})"] - | EReturnBlob {blob, mimeType, t} => + | EReturnBlob {blob = SOME blob, mimeType, t} => box [string "({", newline, string "uw_Basis_blob", @@ -1658,6 +1658,27 @@ and p_exp' par tail env (e, loc) = string "tmp;", newline, string "})"] + | EReturnBlob {blob = NONE, mimeType, t} => + box [string "({", + newline, + string "uw_Basis_string", + space, + string "mimeType", + space, + string "=", + space, + p_exp' false false env mimeType, + string ";", + newline, + p_typ env t, + space, + string "tmp;", + newline, + string "uw_return_blob_from_page(ctx, mimeType);", + newline, + string "tmp;", + newline, + string "})"] | ERedirect (e, t) => box [string "({", newline, @@ -3180,7 +3201,8 @@ fun p_file env (ds, ps) = | EField (e, _) => expDb e | ECase (e, pes, _) => expDb e orelse List.exists (expDb o #2) pes | EError (e, _) => expDb e - | EReturnBlob {blob = e1, mimeType = e2, ...} => expDb e1 orelse expDb e2 + | EReturnBlob {blob = NONE, mimeType = e2, ...} => expDb e2 + | EReturnBlob {blob = SOME e1, mimeType = e2, ...} => expDb e1 orelse expDb e2 | ERedirect (e, _) => expDb e | EWrite e => expDb e | ESeq (e1, e2) => expDb e1 orelse expDb e2 diff --git a/src/cjrize.sml b/src/cjrize.sml index 0f4bdb42..d153feff 100644 --- a/src/cjrize.sml +++ b/src/cjrize.sml @@ -372,13 +372,20 @@ fun cifyExp (eAll as (e, loc), sm) = in ((L'.EError (e, t), loc), sm) end - | L.EReturnBlob {blob, mimeType, t} => + | L.EReturnBlob {blob = NONE, mimeType, t} => + let + val (mimeType, sm) = cifyExp (mimeType, sm) + val (t, sm) = cifyTyp (t, sm) + in + ((L'.EReturnBlob {blob = NONE, mimeType = mimeType, t = t}, loc), sm) + end + | L.EReturnBlob {blob = SOME blob, mimeType, t} => let val (blob, sm) = cifyExp (blob, sm) val (mimeType, sm) = cifyExp (mimeType, sm) val (t, sm) = cifyTyp (t, sm) in - ((L'.EReturnBlob {blob = blob, mimeType = mimeType, t = t}, loc), sm) + ((L'.EReturnBlob {blob = SOME blob, mimeType = mimeType, t = t}, loc), sm) end | L.ERedirect (e, t) => let diff --git a/src/iflow.sml b/src/iflow.sml index 0c94cd47..461dc956 100644 --- a/src/iflow.sml +++ b/src/iflow.sml @@ -1587,7 +1587,8 @@ fun evalExp env (e as (_, loc)) k = evalExp env e2 (fn e2 => k (Func (Other "cat", [e1, e2])))) | EError (e, _) => evalExp env e (fn e => St.send (e, loc)) - | EReturnBlob {blob = b, mimeType = m, ...} => + | EReturnBlob {blob = NONE, ...} => raise Fail "Iflow doesn't support blob optimization" + | EReturnBlob {blob = SOME b, mimeType = m, ...} => evalExp env b (fn b => (St.send (b, loc); evalExp env m @@ -2060,8 +2061,10 @@ fun check (file : file) = end | EStrcat (e1, e2) => (EStrcat (doExp env e1, doExp env e2), loc) | EError (e1, t) => (EError (doExp env e1, t), loc) - | EReturnBlob {blob = b, mimeType = m, t} => - (EReturnBlob {blob = doExp env b, mimeType = doExp env m, t = t}, loc) + | EReturnBlob {blob = NONE, mimeType = m, t} => + (EReturnBlob {blob = NONE, mimeType = doExp env m, t = t}, loc) + | EReturnBlob {blob = SOME b, mimeType = m, t} => + (EReturnBlob {blob = SOME (doExp env b), mimeType = doExp env m, t = t}, loc) | ERedirect (e1, t) => (ERedirect (doExp env e1, t), loc) | EWrite e1 => (EWrite (doExp env e1), loc) | ESeq (e1, e2) => (ESeq (doExp env e1, doExp env e2), loc) diff --git a/src/jscomp.sml b/src/jscomp.sml index e0d87a8e..4a2c0365 100644 --- a/src/jscomp.sml +++ b/src/jscomp.sml @@ -1118,12 +1118,18 @@ fun process (file : file) = in ((EError (e, t), loc), st) end - | EReturnBlob {blob, mimeType, t} => + | EReturnBlob {blob = NONE, mimeType, t} => + let + val (mimeType, st) = exp outer (mimeType, st) + in + ((EReturnBlob {blob = NONE, mimeType = mimeType, t = t}, loc), st) + end + | EReturnBlob {blob = SOME blob, mimeType, t} => let val (blob, st) = exp outer (blob, st) val (mimeType, st) = exp outer (mimeType, st) in - ((EReturnBlob {blob = blob, mimeType = mimeType, t = t}, loc), st) + ((EReturnBlob {blob = SOME blob, mimeType = mimeType, t = t}, loc), st) end | ERedirect (e, t) => let diff --git a/src/mono.sml b/src/mono.sml index f5260419..78740d70 100644 --- a/src/mono.sml +++ b/src/mono.sml @@ -93,7 +93,7 @@ datatype exp' = | EStrcat of exp * exp | EError of exp * typ - | EReturnBlob of {blob : exp, mimeType : exp, t : typ} + | EReturnBlob of {blob : exp option, mimeType : exp, t : typ} | ERedirect of exp * typ | EWrite of exp diff --git a/src/mono_print.sml b/src/mono_print.sml index a5156aca..c81b362a 100644 --- a/src/mono_print.sml +++ b/src/mono_print.sml @@ -235,18 +235,30 @@ fun p_exp' par env (e, _) = space, p_typ env t, string ")"] - | EReturnBlob {blob, mimeType, t} => box [string "(blob", - space, - p_exp env blob, - space, - string "in", - space, - p_exp env mimeType, - space, - string ":", - space, - p_typ env t, - string ")"] + | EReturnBlob {blob = SOME blob, mimeType, t} => box [string "(blob", + space, + p_exp env blob, + space, + string "in", + space, + p_exp env mimeType, + space, + string ":", + space, + p_typ env t, + string ")"] + | EReturnBlob {blob = NONE, mimeType, t} => box [string "(blob", + space, + string "", + space, + string "in", + space, + p_exp env mimeType, + space, + string ":", + space, + p_typ env t, + string ")"] | ERedirect (e, t) => box [string "(redirect", space, p_exp env e, diff --git a/src/mono_reduce.sml b/src/mono_reduce.sml index 0dfb7558..e96a0e8f 100644 --- a/src/mono_reduce.sml +++ b/src/mono_reduce.sml @@ -101,7 +101,8 @@ fun impure (e, _) = | ECase (e, pes, _) => impure e orelse List.exists (fn (_, e) => impure e) pes | EError _ => true - | EReturnBlob {blob = e1, mimeType = e2, ...} => impure e1 orelse impure e2 + | EReturnBlob {blob = NONE, mimeType = e2, ...} => impure e2 + | EReturnBlob {blob = SOME e1, mimeType = e2, ...} => impure e1 orelse impure e2 | ERedirect (e, _) => impure e | EStrcat (e1, e2) => impure e1 orelse impure e2 @@ -492,7 +493,8 @@ fun reduce (file : file) = | EStrcat (e1, e2) => summarize d e1 @ summarize d e2 | EError (e, _) => summarize d e @ [Abort] - | EReturnBlob {blob = e1, mimeType = e2, ...} => summarize d e1 @ summarize d e2 @ [Abort] + | EReturnBlob {blob = NONE, mimeType = e2, ...} => summarize d e2 @ [Abort] + | EReturnBlob {blob = SOME e1, mimeType = e2, ...} => summarize d e1 @ summarize d e2 @ [Abort] | ERedirect (e, _) => summarize d e @ [Abort] | EWrite e => summarize d e @ [WritePage] diff --git a/src/mono_util.sml b/src/mono_util.sml index cb871891..cc531625 100644 --- a/src/mono_util.sml +++ b/src/mono_util.sml @@ -261,14 +261,20 @@ fun mapfoldB {typ = fc, exp = fe, bind} = S.map2 (mft t, fn t' => (EError (e', t'), loc))) - | EReturnBlob {blob, mimeType, t} => + | EReturnBlob {blob = NONE, mimeType, t} => + S.bind2 (mfe ctx mimeType, + fn mimeType' => + S.map2 (mft t, + fn t' => + (EReturnBlob {blob = NONE, mimeType = mimeType', t = t'}, loc))) + | EReturnBlob {blob = SOME blob, mimeType, t} => S.bind2 (mfe ctx blob, fn blob' => S.bind2 (mfe ctx mimeType, fn mimeType' => S.map2 (mft t, fn t' => - (EReturnBlob {blob = blob', mimeType = mimeType', t = t'}, loc)))) + (EReturnBlob {blob = SOME blob', mimeType = mimeType', t = t'}, loc)))) | ERedirect (e, t) => S.bind2 (mfe ctx e, fn e' => @@ -495,7 +501,8 @@ fun appLoc f = | ECase (e1, pes, _) => (appl e1; app (appl o #2) pes) | EStrcat (e1, e2) => (appl e1; appl e2) | EError (e1, _) => appl e1 - | EReturnBlob {blob = e1, mimeType = e2, ...} => (appl e1; appl e2) + | EReturnBlob {blob = NONE, mimeType = e2, ...} => appl e2 + | EReturnBlob {blob = SOME e1, mimeType = e2, ...} => (appl e1; appl e2) | ERedirect (e1, _) => appl e1 | EWrite e1 => appl e1 | ESeq (e1, e2) => (appl e1; appl e2) diff --git a/src/monoize.sml b/src/monoize.sml index 2b604325..b1166734 100644 --- a/src/monoize.sml +++ b/src/monoize.sml @@ -4053,6 +4053,24 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.EError ((L'.ERel 0, loc), t), loc)), loc), fm) end + | L.EApp ( + (L.ECApp ((L.EFfi ("Basis", "returnBlob"), _), t), _), + (L.EFfiApp ("Basis", "textBlob", [(e, _)]), _)) => + let + val t = monoType env t + val un = (L'.TRecord [], loc) + val (e, fm) = monoExp (env, st, fm) e + in + ((L'.EAbs ("mt", (L'.TFfi ("Basis", "string"), loc), (L'.TFun (un, t), loc), + (L'.EAbs ("_", un, t, + (L'.ESeq ((L'.EFfiApp ("Basis", "clear_page", []), loc), + (L'.ESeq ((L'.EWrite (liftExpInExp 0 (liftExpInExp 0 e)), loc), + (L'.EReturnBlob {blob = NONE, + mimeType = (L'.ERel 1, loc), + t = t}, loc)), loc)), loc)), loc)), + loc), + fm) + end | L.ECApp ((L.EFfi ("Basis", "returnBlob"), _), t) => let val t = monoType env t @@ -4062,7 +4080,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "string"), loc), (L'.TFun (un, t), loc)), loc), (L'.EAbs ("mt", (L'.TFfi ("Basis", "string"), loc), (L'.TFun (un, t), loc), (L'.EAbs ("_", un, t, - (L'.EReturnBlob {blob = (L'.ERel 2, loc), + (L'.EReturnBlob {blob = SOME (L'.ERel 2, loc), mimeType = (L'.ERel 1, loc), t = t}, loc)), loc)), loc)), loc), fm) diff --git a/src/prepare.sml b/src/prepare.sml index 7f55959c..89cd1b43 100644 --- a/src/prepare.sml +++ b/src/prepare.sml @@ -201,7 +201,14 @@ fun prepExp (e as (_, loc), st) = | EReturnBlob {blob, mimeType, t} => let - val (blob, st) = prepExp (blob, st) + val (blob, st) = case blob of + NONE => (blob, st) + | SOME blob => + let + val (b, st) = prepExp (blob, st) + in + (SOME b, st) + end val (mimeType, st) = prepExp (mimeType, st) in ((EReturnBlob {blob = blob, mimeType = mimeType, t = t}, loc), st) -- cgit v1.2.3 From 57fc55fafbf4d4f4b3aeb07d12eb9591da801b6e Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Wed, 11 Dec 2013 18:54:42 -0500 Subject: RAND_bytes isn't thread-safe, so wrap it with a lock --- src/c/urweb.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/c/urweb.c') diff --git a/src/c/urweb.c b/src/c/urweb.c index 1201b09b..1c66e9e8 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -4023,9 +4023,13 @@ uw_Basis_unit uw_Basis_debug(uw_context ctx, uw_Basis_string s) { return uw_unit_v; } +static pthread_mutex_t rand_mutex = PTHREAD_MUTEX_INITIALIZER; + uw_Basis_int uw_Basis_rand(uw_context ctx) { uw_Basis_int ret; + pthread_mutex_lock(&rand_mutex); int r = RAND_bytes((unsigned char *)&ret, sizeof ret); + pthread_mutex_unlock(&rand_mutex); if (r) return abs(ret); -- cgit v1.2.3 From ee894ab162bd237456a7a11d59bba4f05daa93e7 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Thu, 12 Dec 2013 10:24:38 -0500 Subject: HTTP: avoid duplicate Content-length --- include/urweb/urweb_cpp.h | 1 + src/c/http.c | 11 +++++++---- src/c/urweb.c | 8 ++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src/c/urweb.c') diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h index d1fb4d37..cf046e83 100644 --- a/include/urweb/urweb_cpp.h +++ b/include/urweb/urweb_cpp.h @@ -209,6 +209,7 @@ uw_Basis_string uw_Basis_requestHeader(struct uw_context *, uw_Basis_string); void uw_write_header(struct uw_context *, uw_Basis_string); void uw_clear_headers(struct uw_context *); +int uw_has_contentLength(struct uw_context *); void uw_Basis_clear_page(struct uw_context *); uw_Basis_string uw_Basis_get_cookie(struct uw_context *, uw_Basis_string c); diff --git a/src/c/http.c b/src/c/http.c index d19ce350..ebe50bea 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -233,8 +233,6 @@ static void *worker(void *data) { sock, uw_really_send, close); if (rr != KEEP_OPEN) { - char clen[100]; - if (keepalive) { char *connection = uw_Basis_requestHeader(ctx, "Connection"); @@ -244,8 +242,13 @@ static void *worker(void *data) { if (!should_keepalive) uw_write_header(ctx, "Connection: close\r\n"); - sprintf(clen, "Content-length: %d\r\n", uw_pagelen(ctx)); - uw_write_header(ctx, clen); + if (!uw_has_contentLength(ctx)) { + char clen[100]; + + sprintf(clen, "Content-length: %d\r\n", uw_pagelen(ctx)); + uw_write_header(ctx, clen); + } + uw_send(ctx, sock); } diff --git a/src/c/urweb.c b/src/c/urweb.c index 1c66e9e8..9641333c 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -1347,6 +1347,10 @@ void uw_write_header(uw_context ctx, uw_Basis_string s) { ctx->outHeaders.front += len; } +int uw_has_contentLength(uw_context ctx) { + return strstr(ctx->outHeaders.start, "Content-length: ") != NULL; +} + void uw_clear_headers(uw_context ctx) { uw_buffer_reset(&ctx->outHeaders); } @@ -3723,7 +3727,7 @@ __attribute__((noreturn)) void uw_return_blob(uw_context ctx, uw_Basis_blob b, u uw_write_header(ctx, on_success); uw_write_header(ctx, "Content-Type: "); uw_write_header(ctx, mimeType); - uw_write_header(ctx, "\r\nContent-Length: "); + uw_write_header(ctx, "\r\nContent-length: "); ctx_uw_buffer_check(ctx, "headers", &ctx->outHeaders, INTS_MAX); sprintf(ctx->outHeaders.front, "%lu%n", (unsigned long)b.size, &len); ctx->outHeaders.front += len; @@ -3755,7 +3759,7 @@ __attribute__((noreturn)) void uw_return_blob_from_page(uw_context ctx, uw_Basis uw_write_header(ctx, on_success); uw_write_header(ctx, "Content-Type: "); uw_write_header(ctx, mimeType); - uw_write_header(ctx, "\r\nContent-Length: "); + uw_write_header(ctx, "\r\nContent-length: "); ctx_uw_buffer_check(ctx, "headers", &ctx->outHeaders, INTS_MAX); sprintf(ctx->outHeaders.front, "%lu%n", (unsigned long)uw_buffer_used(&ctx->page), &len); ctx->outHeaders.front += len; -- cgit v1.2.3 From 3fbffffb0b03b980de3ae2e579fc41c3723a5b46 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Thu, 12 Dec 2013 17:42:48 -0500 Subject: Start SQL transactions as read-only when possible, based on conservative program analysis --- include/urweb/types_cpp.h | 2 +- include/urweb/urweb_cpp.h | 1 + src/c/cgi.c | 3 +-- src/c/fastcgi.c | 3 +-- src/c/http.c | 3 +-- src/c/urweb.c | 21 ++++++++++----------- src/cjr_print.sml | 13 ++++++++++++- src/corify.sml | 2 +- src/effectize.sml | 10 ++++++++-- src/export.sig | 2 +- src/export.sml | 4 ++-- src/mysql.sml | 2 +- src/postgres.sml | 4 ++-- src/sqlite.sml | 2 +- src/tag.sml | 6 +++--- 15 files changed, 46 insertions(+), 32 deletions(-) (limited to 'src/c/urweb.c') diff --git a/include/urweb/types_cpp.h b/include/urweb/types_cpp.h index 330f7755..789aecb1 100644 --- a/include/urweb/types_cpp.h +++ b/include/urweb/types_cpp.h @@ -82,7 +82,7 @@ typedef struct { void (*expunger)(struct uw_context *, uw_Basis_client); void (*db_init)(struct uw_context *); - int (*db_begin)(struct uw_context *); + int (*db_begin)(struct uw_context *, int could_write); int (*db_commit)(struct uw_context *); int (*db_rollback)(struct uw_context *); void (*db_close)(struct uw_context *); diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h index cf046e83..8dfffdf9 100644 --- a/include/urweb/urweb_cpp.h +++ b/include/urweb/urweb_cpp.h @@ -86,6 +86,7 @@ uw_Basis_string uw_Basis_maybe_onunload(struct uw_context *, uw_Basis_string); void uw_set_needs_push(struct uw_context *, int); void uw_set_needs_sig(struct uw_context *, int); +void uw_set_could_write_db(struct uw_context *, int); char *uw_Basis_htmlifyInt(struct uw_context *, uw_Basis_int); char *uw_Basis_htmlifyFloat(struct uw_context *, uw_Basis_float); diff --git a/src/c/cgi.c b/src/c/cgi.c index 52c0ca2e..c9ec744a 100644 --- a/src/c/cgi.c +++ b/src/c/cgi.c @@ -134,8 +134,7 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - if (uw_get_app(ctx)->db_begin(ctx)) - uw_error(ctx, FATAL, "Error running SQL BEGIN"); + uw_ensure_transaction(ctx); uw_get_app(ctx)->expunger(ctx, cli); uw_commit(ctx); } diff --git a/src/c/fastcgi.c b/src/c/fastcgi.c index 9e3c8d7e..d6d2391d 100644 --- a/src/c/fastcgi.c +++ b/src/c/fastcgi.c @@ -632,8 +632,7 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - if (uw_get_app(ctx)->db_begin(ctx)) - uw_error(ctx, FATAL, "Error running SQL BEGIN"); + uw_ensure_transaction(ctx); uw_get_app(ctx)->expunger(ctx, cli); uw_commit(ctx); } diff --git a/src/c/http.c b/src/c/http.c index ebe50bea..230d07f0 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -438,8 +438,7 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - if (uw_get_app(ctx)->db_begin(ctx)) - uw_error(ctx, FATAL, "Error running SQL BEGIN"); + uw_ensure_transaction(ctx); uw_get_app(ctx)->expunger(ctx, cli); uw_commit(ctx); } diff --git a/src/c/urweb.c b/src/c/urweb.c index 9641333c..3082f110 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -441,7 +441,7 @@ struct uw_context { const char *script_header; - int needs_push, needs_sig; + int needs_push, needs_sig, could_write_db; size_t n_deltas, used_deltas; delta *deltas; @@ -517,6 +517,7 @@ uw_context uw_init(int id, void *logger_data, uw_logger log_debug) { ctx->script_header = ""; ctx->needs_push = 0; ctx->needs_sig = 0; + ctx->could_write_db = 1; ctx->source_count = 0; @@ -777,7 +778,7 @@ failure_kind uw_begin(uw_context ctx, char *path) { void uw_ensure_transaction(uw_context ctx) { if (!ctx->transaction_started) { - if (ctx->app->db_begin(ctx)) + if (ctx->app->db_begin(ctx, ctx->could_write_db)) uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); ctx->transaction_started = 1; } @@ -1191,6 +1192,10 @@ void uw_set_needs_sig(uw_context ctx, int n) { ctx->needs_sig = n; } +void uw_set_could_write_db(uw_context ctx, int n) { + ctx->could_write_db = n; +} + static void uw_buffer_check_ctx(uw_context ctx, const char *kind, uw_buffer *b, size_t extra, const char *desc) { if (b->back - b->front < extra) { @@ -3466,9 +3471,7 @@ failure_kind uw_initialize(uw_context ctx) { int r = setjmp(ctx->jmp_buf); if (r == 0) { - if (ctx->app->db_begin(ctx)) - uw_error(ctx, FATAL, "Error running SQL BEGIN"); - ctx->transaction_started = 1; + uw_ensure_transaction(ctx); ctx->app->initializer(ctx); if (ctx->app->db_commit(ctx)) uw_error(ctx, FATAL, "Error running SQL COMMIT"); @@ -4085,9 +4088,7 @@ failure_kind uw_runCallback(uw_context ctx, void (*callback)(uw_context)) { int r = setjmp(ctx->jmp_buf); if (r == 0) { - if (ctx->app->db_begin(ctx)) - uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); - ctx->transaction_started = 1; + uw_ensure_transaction(ctx); callback(ctx); } @@ -4134,9 +4135,7 @@ failure_kind uw_begin_onError(uw_context ctx, char *msg) { if (ctx->app->on_error) { if (r == 0) { - if (ctx->app->db_begin(ctx)) - uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); - ctx->transaction_started = 1; + uw_ensure_transaction(ctx); uw_buffer_reset(&ctx->outHeaders); if (on_success[0]) diff --git a/src/cjr_print.sml b/src/cjr_print.sml index 798492d6..5d697eac 100644 --- a/src/cjr_print.sml +++ b/src/cjr_print.sml @@ -3001,11 +3001,18 @@ fun p_file env (ds, ps) = fun couldWrite ek = case ek of - Link => false + Link _ => false | Action ef => ef = ReadCookieWrite | Rpc ef => ef = ReadCookieWrite | Extern _ => false + fun couldWriteDb ek = + case ek of + Link ef => ef <> ReadOnly + | Action ef => ef <> ReadOnly + | Rpc ef => ef <> ReadOnly + | Extern ef => ef <> ReadOnly + val s = case Settings.getUrlPrefix () of "" => s @@ -3091,6 +3098,10 @@ fun p_file env (ds, ps) = end, string "\");", newline]), + string "uw_set_could_write_db(ctx, ", + string (if couldWriteDb ek then "1" else "0"), + string ");", + newline, string "uw_set_needs_push(ctx, ", string (case side of ServerAndPullAndPush => "1" diff --git a/src/corify.sml b/src/corify.sml index c06d62ca..c1c60045 100644 --- a/src/corify.sml +++ b/src/corify.sml @@ -1046,7 +1046,7 @@ fun corifyDecl mods (all as (d, loc : EM.span), st) = | _ => false) args then L'.Extern L'.ReadCookieWrite else - L'.Link + L'.Link L'.ReadCookieWrite in ((L.DVal ("wrap_" ^ s, 0, tf, e), loc) :: wds, (fn st => diff --git a/src/effectize.sml b/src/effectize.sml index 6ced952b..d711e620 100644 --- a/src/effectize.sml +++ b/src/effectize.sml @@ -153,7 +153,7 @@ fun effectize file = in (d, loop (writers, readers, pushers)) end - | DExport (Link, n, t) => + | DExport (Link _, n, t) => (case IM.find (writers, n) of NONE => () | SOME (loc, s) => @@ -162,7 +162,13 @@ fun effectize file = else ErrorMsg.errorAt loc ("A handler (URI prefix \"" ^ s ^ "\") accessible via GET could cause side effects; try accessing it only via forms, removing it from the signature of the main program module, or whitelisting it with the 'safeGet' .urp directive"); - ((DExport (Link, n, IM.inDomain (pushers, n)), #2 d), evs)) + ((DExport (Link (if IM.inDomain (writers, n) then + if IM.inDomain (readers, n) then + ReadCookieWrite + else + ReadWrite + else + ReadOnly), n, IM.inDomain (pushers, n)), #2 d), evs)) | DExport (Action _, n, _) => ((DExport (Action (if IM.inDomain (writers, n) then if IM.inDomain (readers, n) then diff --git a/src/export.sig b/src/export.sig index 9bcfa0d4..881459c5 100644 --- a/src/export.sig +++ b/src/export.sig @@ -33,7 +33,7 @@ datatype effect = | ReadWrite datatype export_kind = - Link + Link of effect | Action of effect | Rpc of effect | Extern of effect diff --git a/src/export.sml b/src/export.sml index 5d200894..a99d0b70 100644 --- a/src/export.sml +++ b/src/export.sml @@ -36,7 +36,7 @@ datatype effect = | ReadWrite datatype export_kind = - Link + Link of effect | Action of effect | Rpc of effect | Extern of effect @@ -49,7 +49,7 @@ fun p_effect ef = fun p_export_kind ck = case ck of - Link => string "link" + Link ef => box [string "link(", p_effect ef, string ")"] | Action ef => box [string "action(", p_effect ef, string ")"] | Rpc ef => box [string "rpc(", p_effect ef, string ")"] | Extern ef => box [string "extern(", p_effect ef, string ")"] diff --git a/src/mysql.sml b/src/mysql.sml index c70a1cdd..884cde36 100644 --- a/src/mysql.sml +++ b/src/mysql.sml @@ -577,7 +577,7 @@ fun init {dbstring, prepared = ss, tables, views, sequences} = newline, newline, - string "static int uw_db_begin(uw_context ctx) {", + string "static int uw_db_begin(uw_context ctx, int could_write) {", newline, string "uw_conn *conn = uw_get_db(ctx);", newline, diff --git a/src/postgres.sml b/src/postgres.sml index 41529173..272097e7 100644 --- a/src/postgres.sml +++ b/src/postgres.sml @@ -402,11 +402,11 @@ fun init {dbstring, prepared = ss, tables, views, sequences} = newline, newline, - string "static int uw_db_begin(uw_context ctx) {", + string "static int uw_db_begin(uw_context ctx, int could_write) {", newline, string "PGconn *conn = uw_get_db(ctx);", newline, - string "PGresult *res = PQexec(conn, \"BEGIN ISOLATION LEVEL SERIALIZABLE\");", + string "PGresult *res = PQexec(conn, could_write ? \"BEGIN ISOLATION LEVEL SERIALIZABLE\" : \"BEGIN ISOLATION LEVEL SERIALIZABLE, READ ONLY\");", newline, newline, string "if (res == NULL) return 1;", diff --git a/src/sqlite.sml b/src/sqlite.sml index 09c4c683..c138415b 100644 --- a/src/sqlite.sml +++ b/src/sqlite.sml @@ -344,7 +344,7 @@ fun init {dbstring, prepared = ss, tables, views, sequences} = newline, newline, - string "static int uw_db_begin(uw_context ctx) {", + string "static int uw_db_begin(uw_context ctx, int could_write) {", newline, string "uw_conn *conn = uw_get_db(ctx);", newline, diff --git a/src/tag.sml b/src/tag.sml index 9c4807c6..865e7f53 100644 --- a/src/tag.sml +++ b/src/tag.sml @@ -145,7 +145,7 @@ fun exp env (e, s) = end in case x of - (CName "Link", _) => tagIt' (Link, "Link") + (CName "Link", _) => tagIt' (Link ReadWrite, "Link") | (CName "Action", _) => tagIt' (Action ReadWrite, "Action") | _ => ((x, e, t), s) end) @@ -180,7 +180,7 @@ fun exp env (e, s) = | EFfiApp ("Basis", "url", [(e, t)]) => let - val (e, s) = tagIt (e, Link, "Url", s) + val (e, s) = tagIt (e, Link ReadWrite, "Url", s) in (EFfiApp ("Basis", "url", [(e, t)]), s) end @@ -201,7 +201,7 @@ fun exp env (e, s) = case eo of SOME (EAbs (_, _, _, (EFfiApp ("Basis", "url", [((ERel 0, _), t)]), _)), _) => let - val (e, s) = tagIt (e', Link, "Url", s) + val (e, s) = tagIt (e', Link ReadWrite, "Url", s) in (EFfiApp ("Basis", "url", [(e, t)]), s) end -- cgit v1.2.3 From 76b26fec5a3b521a084491d77d6c9e1e28ab603c Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Mon, 23 Dec 2013 15:59:17 +0000 Subject: Proper handling of serialization failures during SQL COMMIT --- include/urweb/urweb_cpp.h | 3 ++- src/c/cgi.c | 7 ++++--- src/c/fastcgi.c | 7 ++++--- src/c/http.c | 7 ++++--- src/c/request.c | 6 ++++-- src/c/urweb.c | 29 ++++++++++++++++++++++------- src/postgres.sml | 18 +++++++++++++++++- 7 files changed, 57 insertions(+), 20 deletions(-) (limited to 'src/c/urweb.c') diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h index 8dfffdf9..248e54e4 100644 --- a/include/urweb/urweb_cpp.h +++ b/include/urweb/urweb_cpp.h @@ -40,7 +40,8 @@ failure_kind uw_begin(struct uw_context *, char *path); void uw_ensure_transaction(struct uw_context *); failure_kind uw_begin_onError(struct uw_context *, char *msg); void uw_login(struct uw_context *); -void uw_commit(struct uw_context *); +int uw_commit(struct uw_context *); +// ^-- returns nonzero if the transaction should be restarted int uw_rollback(struct uw_context *, int will_retry); __attribute__((noreturn)) void uw_error(struct uw_context *, failure_kind, const char *fmt, ...); diff --git a/src/c/cgi.c b/src/c/cgi.c index c9ec744a..f1482589 100644 --- a/src/c/cgi.c +++ b/src/c/cgi.c @@ -134,9 +134,10 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - uw_ensure_transaction(ctx); - uw_get_app(ctx)->expunger(ctx, cli); - uw_commit(ctx); + do { + uw_ensure_transaction(ctx); + uw_get_app(ctx)->expunger(ctx, cli); + } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1)); } void uw_post_expunge(uw_context ctx, void *data) { diff --git a/src/c/fastcgi.c b/src/c/fastcgi.c index d6d2391d..bbda0f57 100644 --- a/src/c/fastcgi.c +++ b/src/c/fastcgi.c @@ -632,9 +632,10 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - uw_ensure_transaction(ctx); - uw_get_app(ctx)->expunger(ctx, cli); - uw_commit(ctx); + do { + uw_ensure_transaction(ctx); + uw_get_app(ctx)->expunger(ctx, cli); + } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1)); } void uw_post_expunge(uw_context ctx, void *data) { diff --git a/src/c/http.c b/src/c/http.c index c57740e9..9050aaf4 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -447,9 +447,10 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - uw_ensure_transaction(ctx); - uw_get_app(ctx)->expunger(ctx, cli); - uw_commit(ctx); + do { + uw_ensure_transaction(ctx); + uw_get_app(ctx)->expunger(ctx, cli); + } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1)); } void uw_post_expunge(uw_context ctx, void *data) { diff --git a/src/c/request.c b/src/c/request.c index 5973d979..b925cc3c 100644 --- a/src/c/request.c +++ b/src/c/request.c @@ -116,8 +116,10 @@ static void *periodic_loop(void *data) { return NULL; } while (r == UNLIMITED_RETRY || (r == BOUNDED_RETRY && retries_left > 0)); - if (r != FATAL && r != BOUNDED_RETRY) - uw_commit(ctx); + if (r != FATAL && r != BOUNDED_RETRY) { + if (uw_commit(ctx)) + r = UNLIMITED_RETRY; + } sleep(p->pdic.period); }; diff --git a/src/c/urweb.c b/src/c/urweb.c index 3082f110..57f57694 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -3253,13 +3253,13 @@ static char *find_sig(char *haystack) { return s; } -void uw_commit(uw_context ctx) { +int uw_commit(uw_context ctx) { int i; char *sig; if (uw_has_error(ctx)) { uw_rollback(ctx, 0); - return; + return 0; } for (i = ctx->used_transactionals-1; i >= 0; --i) @@ -3268,7 +3268,7 @@ void uw_commit(uw_context ctx) { ctx->transactionals[i].commit(ctx->transactionals[i].data); if (uw_has_error(ctx)) { uw_rollback(ctx, 0); - return; + return 0; } } @@ -3278,13 +3278,26 @@ void uw_commit(uw_context ctx) { ctx->transactionals[i].commit(ctx->transactionals[i].data); if (uw_has_error(ctx)) { uw_rollback(ctx, 0); - return; + return 0; } } - if (ctx->transaction_started && ctx->app->db_commit(ctx)) { - uw_set_error_message(ctx, "Error running SQL COMMIT"); - return; + if (ctx->transaction_started) { + int code =ctx->app->db_commit(ctx); + + if (code) { + if (code == -1) { + uw_rollback(ctx, 1); + return 1; + } + + for (i = ctx->used_transactionals-1; i >= 0; --i) + if (ctx->transactionals[i].free) + ctx->transactionals[i].free(ctx->transactionals[i].data, 0); + + uw_set_error_message(ctx, "Error running SQL COMMIT"); + return 0; + } } for (i = 0; i < ctx->used_deltas; ++i) { @@ -3390,6 +3403,8 @@ void uw_commit(uw_context ctx) { } while (sig); } } + + return 0; } diff --git a/src/postgres.sml b/src/postgres.sml index 272097e7..8cfa5f48 100644 --- a/src/postgres.sml +++ b/src/postgres.sml @@ -438,7 +438,23 @@ fun init {dbstring, prepared = ss, tables, views, sequences} = newline, newline, string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {", - box [string "PQclear(res);", + box [string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {", + box [newline, + string "PQclear(res);", + newline, + string "return -1;", + newline], + string "}", + newline, + string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {", + box [newline, + string "PQclear(res);", + newline, + string "return -1;", + newline], + string "}", + newline, + string "PQclear(res);", newline, string "return 1;", newline], -- cgit v1.2.3 From f42ed389d1b67749ff441c6389f7fcf14237c211 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Wed, 25 Dec 2013 11:43:41 -0500 Subject: No need to ROLLBACK after a failed COMMIT --- src/c/urweb.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/c/urweb.c') diff --git a/src/c/urweb.c b/src/c/urweb.c index 57f57694..ab6e5b1f 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -3283,13 +3283,11 @@ int uw_commit(uw_context ctx) { } if (ctx->transaction_started) { - int code =ctx->app->db_commit(ctx); + int code = ctx->app->db_commit(ctx); if (code) { - if (code == -1) { - uw_rollback(ctx, 1); + if (code == -1) return 1; - } for (i = ctx->used_transactionals-1; i >= 0; --i) if (ctx->transactionals[i].free) -- cgit v1.2.3 From 8ebc8340ebc13704014d5b2a3bbcd453786162c8 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Wed, 25 Dec 2013 13:11:43 -0500 Subject: Tweaking handling of database transactions --- src/c/cgi.c | 9 +++++---- src/c/fastcgi.c | 9 +++++---- src/c/http.c | 9 +++++---- src/c/urweb.c | 8 ++++++-- 4 files changed, 21 insertions(+), 14 deletions(-) (limited to 'src/c/urweb.c') diff --git a/src/c/cgi.c b/src/c/cgi.c index f1482589..539b83c2 100644 --- a/src/c/cgi.c +++ b/src/c/cgi.c @@ -134,10 +134,11 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - do { - uw_ensure_transaction(ctx); - uw_get_app(ctx)->expunger(ctx, cli); - } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1)); + uw_ensure_transaction(ctx); + uw_get_app(ctx)->expunger(ctx, cli); + + if (uw_commit(ctx)) + uw_error(ctx, UNLIMITED_RETRY, "Rerunning expunge transaction"); } void uw_post_expunge(uw_context ctx, void *data) { diff --git a/src/c/fastcgi.c b/src/c/fastcgi.c index bbda0f57..5c80d3ae 100644 --- a/src/c/fastcgi.c +++ b/src/c/fastcgi.c @@ -632,10 +632,11 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - do { - uw_ensure_transaction(ctx); - uw_get_app(ctx)->expunger(ctx, cli); - } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1)); + uw_ensure_transaction(ctx); + uw_get_app(ctx)->expunger(ctx, cli); + + if (uw_commit(ctx)) + uw_error(ctx, UNLIMITED_RETRY, "Rerunning expunge transaction"); } void uw_post_expunge(uw_context ctx, void *data) { diff --git a/src/c/http.c b/src/c/http.c index 9050aaf4..17833ce1 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -447,10 +447,11 @@ void uw_copy_client_data(void *dst, void *src) { } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { - do { - uw_ensure_transaction(ctx); - uw_get_app(ctx)->expunger(ctx, cli); - } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1)); + uw_ensure_transaction(ctx); + uw_get_app(ctx)->expunger(ctx, cli); + + if (uw_commit(ctx)) + uw_error(ctx, UNLIMITED_RETRY, "Rerunning expunge transaction"); } void uw_post_expunge(uw_context ctx, void *data) { diff --git a/src/c/urweb.c b/src/c/urweb.c index ab6e5b1f..af5cc461 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -3225,7 +3225,11 @@ int uw_rollback(uw_context ctx, int will_retry) { if (ctx->transactionals[i].free) ctx->transactionals[i].free(ctx->transactionals[i].data, will_retry); - return (ctx->app && ctx->transaction_started) ? ctx->app->db_rollback(ctx) : 0; + if (ctx->app && ctx->transaction_started) { + ctx->transaction_started = 0; + return ctx->app->db_rollback(ctx); + } else + return 0; } static const char begin_xhtml[] = "\n\n"; @@ -3461,8 +3465,8 @@ void uw_prune_clients(uw_context ctx) { prev->next = next; else clients_used = next; - uw_reset(ctx); while (fk == UNLIMITED_RETRY) { + uw_reset(ctx); fk = uw_expunge(ctx, c->id, c->data); if (fk == UNLIMITED_RETRY) printf("Unlimited retry during expunge: %s\n", uw_error_message(ctx)); -- cgit v1.2.3 From ee16149ddef734c9945ff0339300fd926ca91b1a Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Fri, 27 Dec 2013 12:10:03 -0500 Subject: Raise exception when recv()ing from someone else's channel; improve setting of client ID in RPCs --- lib/js/urweb.js | 2 +- src/c/urweb.c | 48 +++++++++++++++++++++++------------------------- tests/channelThief.ur | 32 ++++++++++++++++++++++++++++++++ tests/channelThief.urp | 5 +++++ tests/channelThief.urs | 1 + 5 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 tests/channelThief.ur create mode 100644 tests/channelThief.urp create mode 100644 tests/channelThief.urs (limited to 'src/c/urweb.c') diff --git a/lib/js/urweb.js b/lib/js/urweb.js index e211d318..6830945a 100644 --- a/lib/js/urweb.js +++ b/lib/js/urweb.js @@ -1575,7 +1575,7 @@ function rv(chn, parse, k) { er("May not 'recv' in main thread of 'code' for "); if (chn == null) - return; + er("Client-side code tried to recv() from a channel belonging to a different page view."); if (chn < 0) whine("Out-of-bounds channel receive"); diff --git a/src/c/urweb.c b/src/c/urweb.c index af5cc461..0265c67f 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -734,36 +734,34 @@ void uw_push_cleanup(uw_context ctx, void (*func)(void *), void *arg) { char *uw_Basis_htmlifyString(uw_context, const char *); void uw_login(uw_context ctx) { - if (ctx->needs_push) { - char *id_s, *pass_s; + char *id_s, *pass_s; - if ((id_s = uw_Basis_requestHeader(ctx, "UrWeb-Client")) - && (pass_s = uw_Basis_requestHeader(ctx, "UrWeb-Pass"))) { - unsigned id = atoi(id_s); - int pass = atoi(pass_s); - client *c = find_client(id); - - if (c == NULL) - uw_error(ctx, FATAL, "Unknown client ID in HTTP headers (%s, %s)", uw_Basis_htmlifyString(ctx, id_s), uw_Basis_htmlifyString(ctx, pass_s)); - else { - use_client(c); - ctx->client = c; - - if (c->mode != USED) - uw_error(ctx, FATAL, "Stale client ID (%u) in subscription request", id); - if (c->pass != pass) - uw_error(ctx, FATAL, "Wrong client password (%u, %d) in subscription request", id, pass); - } - } else { - client *c = new_client(); - - if (c == NULL) - uw_error(ctx, FATAL, "Limit exceeded on number of message-passing clients"); + if ((id_s = uw_Basis_requestHeader(ctx, "UrWeb-Client")) + && (pass_s = uw_Basis_requestHeader(ctx, "UrWeb-Pass"))) { + unsigned id = atoi(id_s); + int pass = atoi(pass_s); + client *c = find_client(id); + if (c == NULL) + uw_error(ctx, FATAL, "Unknown client ID in HTTP headers (%s, %s)", uw_Basis_htmlifyString(ctx, id_s), uw_Basis_htmlifyString(ctx, pass_s)); + else { use_client(c); - uw_copy_client_data(c->data, ctx->client_data); ctx->client = c; + + if (c->mode != USED) + uw_error(ctx, FATAL, "Stale client ID (%u) in subscription request", id); + if (c->pass != pass) + uw_error(ctx, FATAL, "Wrong client password (%u, %d) in subscription request", id, pass); } + } else if (ctx->needs_push) { + client *c = new_client(); + + if (c == NULL) + uw_error(ctx, FATAL, "Limit exceeded on number of message-passing clients"); + + use_client(c); + uw_copy_client_data(c->data, ctx->client_data); + ctx->client = c; } } diff --git a/tests/channelThief.ur b/tests/channelThief.ur new file mode 100644 index 00000000..1893979a --- /dev/null +++ b/tests/channelThief.ur @@ -0,0 +1,32 @@ +table t : { Ch : channel string } + +fun go () = + let + fun overwrite () = + dml (DELETE FROM t WHERE TRUE); + ch <- channel; + dml (INSERT INTO t (Ch) VALUES ({[ch]})); + return ch + + fun retrieve () = + oneRowE1 (SELECT (t.Ch) FROM t) + + fun transmit () = + ch <- retrieve (); + send ch "Test" + + fun listenOn ch = + s <- recv ch; + alert s + in + ch <- overwrite (); + return +