From c6ed3035896b5cca544ef9bf245cd76019783b1f Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Fri, 24 Oct 2008 17:30:07 -0400 Subject: Properly freeing libpq results on errors --- demo/sql.urp | 1 + include/urweb.h | 25 +++++++++++++------------ src/c/urweb.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/cjr_print.sml | 4 +++- tests/aborter2.ur | 7 +++++++ tests/aborter2.urp | 5 +++++ 6 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 tests/aborter2.ur create mode 100644 tests/aborter2.urp diff --git a/demo/sql.urp b/demo/sql.urp index 7894da95..06fbbd24 100644 --- a/demo/sql.urp +++ b/demo/sql.urp @@ -1,3 +1,4 @@ +debug database dbname=test sql sql.sql diff --git a/include/urweb.h b/include/urweb.h index 5a6c7178..6ac7df15 100644 --- a/include/urweb.h +++ b/include/urweb.h @@ -19,6 +19,8 @@ failure_kind uw_begin(uw_context, char *path); __attribute__((noreturn)) void uw_error(uw_context, failure_kind, const char *fmt, ...); char *uw_error_message(uw_context); +void uw_push_cleanup(uw_context, void (*func)(void *), void *arg); +void uw_pop_cleanup(uw_context); void *uw_malloc(uw_context, size_t); void uw_begin_region(uw_context); @@ -38,29 +40,28 @@ char *uw_Basis_htmlifyFloat(uw_context, uw_Basis_float); char *uw_Basis_htmlifyString(uw_context, uw_Basis_string); char *uw_Basis_htmlifyBool(uw_context, uw_Basis_bool); -void uw_Basis_htmlifyInt_w(uw_context, uw_Basis_int); -void uw_Basis_htmlifyFloat_w(uw_context, uw_Basis_float); -void uw_Basis_htmlifyString_w(uw_context, uw_Basis_string); -void uw_Basis_htmlifyBool_w(uw_context, uw_Basis_bool); +uw_unit uw_Basis_htmlifyInt_w(uw_context, uw_Basis_int); +uw_unit uw_Basis_htmlifyFloat_w(uw_context, uw_Basis_float); +uw_unit uw_Basis_htmlifyString_w(uw_context, uw_Basis_string); +uw_unit uw_Basis_htmlifyBool_w(uw_context, uw_Basis_bool); char *uw_Basis_attrifyInt(uw_context, uw_Basis_int); char *uw_Basis_attrifyFloat(uw_context, uw_Basis_float); char *uw_Basis_attrifyString(uw_context, uw_Basis_string); -void uw_Basis_attrifyInt_w(uw_context, uw_Basis_int); -void uw_Basis_attrifyFloat_w(uw_context, uw_Basis_float); -void uw_Basis_attrifyString_w(uw_context, uw_Basis_string); - +uw_unit uw_Basis_attrifyInt_w(uw_context, uw_Basis_int); +uw_unit uw_Basis_attrifyFloat_w(uw_context, uw_Basis_float); +uw_unit uw_Basis_attrifyString_w(uw_context, uw_Basis_string); char *uw_Basis_urlifyInt(uw_context, uw_Basis_int); char *uw_Basis_urlifyFloat(uw_context, uw_Basis_float); char *uw_Basis_urlifyString(uw_context, uw_Basis_string); char *uw_Basis_urlifyBool(uw_context, uw_Basis_bool); -void uw_Basis_urlifyInt_w(uw_context, uw_Basis_int); -void uw_Basis_urlifyFloat_w(uw_context, uw_Basis_float); -void uw_Basis_urlifyString_w(uw_context, uw_Basis_string); -void uw_Basis_urlifyBool_w(uw_context, uw_Basis_bool); +uw_unit uw_Basis_urlifyInt_w(uw_context, uw_Basis_int); +uw_unit uw_Basis_urlifyFloat_w(uw_context, uw_Basis_float); +uw_unit uw_Basis_urlifyString_w(uw_context, uw_Basis_string); +uw_unit uw_Basis_urlifyBool_w(uw_context, uw_Basis_bool); uw_Basis_int uw_Basis_unurlifyInt(uw_context, char **); uw_Basis_float uw_Basis_unurlifyFloat(uw_context, char **); diff --git a/src/c/urweb.c b/src/c/urweb.c index d4fd1844..039ba119 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -15,6 +15,11 @@ typedef struct regions { struct regions *next; } regions; +typedef struct { + void (*func)(void*); + void *arg; +} cleanup; + struct uw_context { char *page, *page_front, *page_back; char *heap, *heap_front, *heap_back; @@ -26,6 +31,8 @@ struct uw_context { regions *regions; + cleanup *cleanup, *cleanup_front, *cleanup_back; + char error_message[ERROR_BUF_LEN]; }; @@ -46,6 +53,8 @@ uw_context uw_init(size_t page_len, size_t heap_len) { ctx->regions = NULL; + ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0); + ctx->error_message[0] = 0; return ctx; @@ -63,6 +72,7 @@ void uw_free(uw_context ctx) { free(ctx->page); free(ctx->heap); free(ctx->inputs); + free(ctx->cleanup); free(ctx); } @@ -70,6 +80,7 @@ void uw_reset_keep_request(uw_context ctx) { ctx->page_front = ctx->page; ctx->heap_front = ctx->heap; ctx->regions = NULL; + ctx->cleanup_front = ctx->cleanup; ctx->error_message[0] = 0; } @@ -78,6 +89,7 @@ void uw_reset_keep_error_message(uw_context ctx) { ctx->page_front = ctx->page; ctx->heap_front = ctx->heap; ctx->regions = NULL; + ctx->cleanup_front = ctx->cleanup; } void uw_reset(uw_context ctx) { @@ -107,14 +119,46 @@ failure_kind uw_begin(uw_context ctx, char *path) { } __attribute__((noreturn)) void uw_error(uw_context ctx, failure_kind fk, const char *fmt, ...) { + cleanup *cl; + va_list ap; va_start(ap, fmt); vsnprintf(ctx->error_message, ERROR_BUF_LEN, fmt, ap); + for (cl = ctx->cleanup; cl < ctx->cleanup_front; ++cl) + cl->func(cl->arg); + + ctx->cleanup_front = ctx->cleanup; + longjmp(ctx->jmp_buf, fk); } +void uw_push_cleanup(uw_context ctx, void (*func)(void *), void *arg) { + if (ctx->cleanup_front >= ctx->cleanup_back) { + int len = ctx->cleanup_back - ctx->cleanup, newLen; + if (len == 0) + newLen = 1; + else + newLen *= 2; + ctx->cleanup = realloc(ctx->cleanup, newLen); + ctx->cleanup_front = ctx->cleanup + len; + ctx->cleanup_back = ctx->cleanup + newLen; + } + + ctx->cleanup_front->func = func; + ctx->cleanup_front->arg = arg; + ++ctx->cleanup_front; +} + +void uw_pop_cleanup(uw_context ctx) { + if (ctx->cleanup_front == ctx->cleanup) + uw_error(ctx, FATAL, "Attempt to pop from empty cleanup action stack"); + + --ctx->cleanup_front; + ctx->cleanup_front->func(ctx->cleanup_front->arg); +} + char *uw_error_message(uw_context ctx) { return ctx->error_message; } diff --git a/src/cjr_print.sml b/src/cjr_print.sml index 7d74376e..26f6149e 100644 --- a/src/cjr_print.sml +++ b/src/cjr_print.sml @@ -850,6 +850,8 @@ fun p_exp' par env (e, loc) = string "uw_end_region(ctx);", newline, + string "uw_push_cleanup(ctx, (void (*)(void *))PQclear, res);", + newline, string "n = PQntuples(res);", newline, string "for (i = 0; i < n; ++i) {", @@ -906,7 +908,7 @@ fun p_exp' par env (e, loc) = string "}", newline, newline, - string "PQclear(res);", + string "uw_pop_cleanup(ctx);", newline, if wontLeakAnything then box [string "uw_end_region(ctx);", diff --git a/tests/aborter2.ur b/tests/aborter2.ur new file mode 100644 index 00000000..a7270ba1 --- /dev/null +++ b/tests/aborter2.ur @@ -0,0 +1,7 @@ +table t : { X : int } + +fun main () : transaction page = + v <- query (SELECT * FROM t) + (fn r (_ : int) => return (error Shot down!)) + 0; + return Result: {[v]} diff --git a/tests/aborter2.urp b/tests/aborter2.urp new file mode 100644 index 00000000..edc6c7da --- /dev/null +++ b/tests/aborter2.urp @@ -0,0 +1,5 @@ +debug +database dbname=aborter +sql aborter2.sql + +aborter2 -- cgit v1.2.3