webmcp
changeset 408:fd5880964d99
Hack to avoid cumulating memory leaks in case of (caught) out-of-memory errors
author | jbe |
---|---|
date | Thu Jan 07 01:23:08 2016 +0100 (2016-01-07) |
parents | 0e641ac76647 |
children | ebe9416db4e0 |
files | libraries/mondelefant/mondelefant_native.c |
line diff
1.1 --- a/libraries/mondelefant/mondelefant_native.c Wed Jan 06 22:59:39 2016 +0100 1.2 +++ b/libraries/mondelefant/mondelefant_native.c Thu Jan 07 01:23:08 2016 +0100 1.3 @@ -28,10 +28,24 @@ 1.4 typedef struct { 1.5 PGconn *pgconn; 1.6 int server_encoding; 1.7 + void *todo_PQfreemem; 1.8 + PGresult *todo_PQclear; 1.9 } mondelefant_conn_t; 1.10 #define MONDELEFANT_SERVER_ENCODING_ASCII 0 1.11 #define MONDELEFANT_SERVER_ENCODING_UTF8 1 1.12 1.13 +// hack to avoid cumulating memory leaks in case of out-of-memory errors 1.14 +static void mondelefant_cleanup(mondelefant_conn_t *conn) { 1.15 + if (conn->todo_PQfreemem) { 1.16 + PQfreemem(conn->todo_PQfreemem); 1.17 + conn->todo_PQfreemem = NULL; 1.18 + } 1.19 + if (conn->todo_PQclear) { 1.20 + PQclear(conn->todo_PQclear); 1.21 + conn->todo_PQclear = NULL; 1.22 + } 1.23 +} 1.24 + 1.25 // transform codepoint-position to byte-position for a given UTF-8 string: 1.26 static size_t utf8_position_to_byte(const char *str, size_t utf8pos) { 1.27 size_t bytepos; 1.28 @@ -598,14 +612,23 @@ 1.29 conn = mondelefant_get_conn(L, 1); 1.30 // get second argument, which must be a string: 1.31 input = luaL_checklstring(L, 2, &input_len); 1.32 + // avoid cumulating memory leaks in case of previous out-of-memory errors: 1.33 + mondelefant_cleanup(conn); 1.34 // call PQescapeByteaConn, which allocates memory itself: 1.35 output = (char *)PQescapeByteaConn( 1.36 conn->pgconn, (const unsigned char *)input, input_len, &output_len 1.37 ); 1.38 - // if PQescapeByteaConn returned NULL, then throw error: 1.39 if (!output) { 1.40 - return luaL_error(L, "Could not allocate memory for binary quoting."); 1.41 + lua_gc(L, LUA_GCCOLLECT, 0); 1.42 + output = (char *)PQescapeByteaConn( 1.43 + conn->pgconn, (const unsigned char *)input, input_len, &output_len 1.44 + ); 1.45 + if (!output) { 1.46 + return luaL_error(L, "Could not allocate memory for binary quoting."); 1.47 + } 1.48 } 1.49 + // ensure call of PQfreemem in case of unexpected out-of-memory error: 1.50 + conn->todo_PQfreemem = output; 1.51 // create Lua string enclosed by single quotes: 1.52 luaL_buffinit(L, &buf); 1.53 luaL_addchar(&buf, '\''); 1.54 @@ -614,6 +637,8 @@ 1.55 luaL_pushresult(&buf); 1.56 // free memory allocated by PQescapeByteaConn: 1.57 PQfreemem(output); 1.58 + // avoid double call of PQfreemem later: 1.59 + conn->todo_PQfreemem = NULL; 1.60 // return Lua string: 1.61 return 1; 1.62 } 1.63 @@ -783,6 +808,7 @@ 1.64 lua_remove(L, -2); 1.65 // Lua stack contains: ..., <buffer>, raw-value 1.66 // branch according to type of value: 1.67 + // NOTE: Lua automatically converts numbers to strings 1.68 if (lua_isnil(L, -1)) { // value is nil 1.69 // push string "NULL" to stack: 1.70 lua_pushliteral(L, "NULL"); 1.71 @@ -790,9 +816,8 @@ 1.72 // push strings "TRUE" or "FALSE" to stack: 1.73 lua_pushstring(L, lua_toboolean(L, -1) ? "TRUE" : "FALSE"); 1.74 } else if (lua_isstring(L, -1)) { // value is string or number 1.75 - // NOTE: In this version of lua a number will be converted 1.76 - // push output of "quote_string" method of database 1.77 - // connection to stack: 1.78 + // push output of "quote_string" method of database connection 1.79 + // to stack: 1.80 lua_tostring(L, -1); 1.81 lua_pushcfunction(L, mondelefant_conn_quote_string); 1.82 lua_pushvalue(L, 1); 1.83 @@ -908,6 +933,8 @@ 1.84 mode = modes[command_idx]; 1.85 // if PQsendQuery call was successful, then fetch result data: 1.86 if (sent_success) { 1.87 + // avoid cumulating memory leaks in case of previous out-of-memory errors: 1.88 + mondelefant_cleanup(conn); 1.89 // NOTE: PQgetResult called one extra time. Break only, if all 1.90 // queries have been processed and PQgetResult returned NULL. 1.91 res = PQgetResult(conn->pgconn); 1.92 @@ -916,6 +943,8 @@ 1.93 pgstatus = PQresultStatus(res); 1.94 rows = PQntuples(res); 1.95 cols = PQnfields(res); 1.96 + // ensure eventual call of PQclear in case of unexpected Lua errors: 1.97 + conn->todo_PQclear = res; 1.98 } 1.99 } 1.100 // handle errors: 1.101 @@ -1041,6 +1070,8 @@ 1.102 if (res) { 1.103 PQclear(res); 1.104 while ((res = PQgetResult(conn->pgconn))) PQclear(res); 1.105 + // avoid double call of PQclear later: 1.106 + conn->todo_PQclear = NULL; 1.107 } 1.108 } 1.109 if (lua_toboolean(L, 3)) { 1.110 @@ -1185,6 +1216,8 @@ 1.111 if (lua_gettop(L) != 4) abort(); // should not happen 1.112 // free memory acquired by libpq: 1.113 PQclear(res); 1.114 + // avoid double call of PQclear later: 1.115 + conn->todo_PQclear = NULL; 1.116 } 1.117 // trace callback at stack position 3 1.118 // result at stack position 4 (top of stack)