Enforce a timeout on user SQL queries.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4430 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
b252b921f8
commit
dcea08f73b
95
src/ssb.js.c
95
src/ssb.js.c
@ -26,6 +26,8 @@
|
||||
#define _countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
|
||||
#endif
|
||||
|
||||
static const int k_sql_async_timeout_ms = 60 * 1000;
|
||||
|
||||
static JSClassID _tf_ssb_classId;
|
||||
|
||||
void _tf_ssb_on_rpc(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data);
|
||||
@ -403,7 +405,11 @@ static JSValue _tf_ssb_closeConnection(JSContext* context, JSValueConst this_val
|
||||
typedef struct _sql_work_t
|
||||
{
|
||||
uv_work_t request;
|
||||
uv_async_t async;
|
||||
uv_timer_t timeout;
|
||||
tf_ssb_t* ssb;
|
||||
sqlite3* db;
|
||||
uv_mutex_t lock;
|
||||
const char* query;
|
||||
uint8_t* binds;
|
||||
size_t binds_count;
|
||||
@ -429,6 +435,10 @@ static void _tf_ssb_sqlAsync_work(uv_work_t* work)
|
||||
tf_trace_t* trace = tf_ssb_get_trace(sql_work->ssb);
|
||||
tf_trace_begin(trace, "sql_async_work");
|
||||
sqlite3* db = tf_ssb_acquire_db_reader_restricted(sql_work->ssb);
|
||||
uv_mutex_lock(&sql_work->lock);
|
||||
sql_work->db = db;
|
||||
uv_mutex_unlock(&sql_work->lock);
|
||||
uv_async_send(&sql_work->async);
|
||||
sqlite3_stmt* statement = NULL;
|
||||
sql_work->result = sqlite3_prepare(db, sql_work->query, -1, &statement, NULL);
|
||||
if (sql_work->result == SQLITE_OK)
|
||||
@ -513,7 +523,14 @@ static void _tf_ssb_sqlAsync_work(uv_work_t* work)
|
||||
sql_work->result = r;
|
||||
if (r != SQLITE_OK && r != SQLITE_DONE)
|
||||
{
|
||||
sql_work->error = tf_strdup(sqlite3_errmsg(db));
|
||||
if (sqlite3_is_interrupted(db))
|
||||
{
|
||||
sql_work->error = tf_strdup("Timed out");
|
||||
}
|
||||
else
|
||||
{
|
||||
sql_work->error = tf_strdup(sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
_tf_ssb_sql_append(&sql_work->rows, &sql_work->rows_count, &(uint8_t[]) { 0 }, 1);
|
||||
sqlite3_finalize(statement);
|
||||
@ -522,11 +539,38 @@ static void _tf_ssb_sqlAsync_work(uv_work_t* work)
|
||||
{
|
||||
sql_work->error = tf_strdup(sqlite3_errmsg(db));
|
||||
}
|
||||
uv_mutex_lock(&sql_work->lock);
|
||||
sql_work->db = NULL;
|
||||
uv_mutex_unlock(&sql_work->lock);
|
||||
tf_ssb_release_db_reader(sql_work->ssb, db);
|
||||
tf_ssb_record_thread_busy(sql_work->ssb, false);
|
||||
tf_trace_end(trace);
|
||||
}
|
||||
|
||||
static void _tf_ssb_sqlAsync_handle_close(uv_handle_t* handle)
|
||||
{
|
||||
sql_work_t* work = handle->data;
|
||||
handle->data = NULL;
|
||||
if (!work->async.data &&
|
||||
!work->timeout.data)
|
||||
{
|
||||
tf_free(work);
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_sqlAsync_destroy(sql_work_t* work)
|
||||
{
|
||||
tf_free(work->binds);
|
||||
tf_free(work->error);
|
||||
if (work->rows)
|
||||
{
|
||||
tf_free(work->rows);
|
||||
}
|
||||
uv_mutex_destroy(&work->lock);
|
||||
uv_close((uv_handle_t*)&work->timeout, _tf_ssb_sqlAsync_handle_close);
|
||||
uv_close((uv_handle_t*)&work->async, _tf_ssb_sqlAsync_handle_close);
|
||||
}
|
||||
|
||||
static void _tf_ssb_sqlAsync_after_work(uv_work_t* work, int status)
|
||||
{
|
||||
sql_work_t* sql_work = work->data;
|
||||
@ -582,17 +626,19 @@ static void _tf_ssb_sqlAsync_after_work(uv_work_t* work, int status)
|
||||
}
|
||||
|
||||
JSValue response = JS_Call(context, sql_work->callback, JS_UNDEFINED, 1, &row);
|
||||
tf_util_report_error(context, response);
|
||||
bool is_error = tf_util_report_error(context, response);
|
||||
JS_FreeValue(context, response);
|
||||
JS_FreeValue(context, row);
|
||||
if (is_error)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
tf_free(sql_work->binds);
|
||||
tf_free(sql_work->rows);
|
||||
|
||||
JSValue result = JS_UNDEFINED;
|
||||
if (sql_work->result == SQLITE_OK || sql_work->result == SQLITE_DONE)
|
||||
@ -613,13 +659,27 @@ static void _tf_ssb_sqlAsync_after_work(uv_work_t* work, int status)
|
||||
JS_FreeValue(context, sql_work->promise[0]);
|
||||
JS_FreeValue(context, sql_work->promise[1]);
|
||||
JS_FreeValue(context, sql_work->callback);
|
||||
JS_FreeCString(context, sql_work->query);
|
||||
|
||||
tf_free(sql_work->error);
|
||||
tf_free(sql_work);
|
||||
_tf_ssb_sqlAsync_destroy(sql_work);
|
||||
tf_trace_end(trace);
|
||||
}
|
||||
|
||||
static void _tf_ssb_sqlAsync_timeout(uv_timer_t* timer)
|
||||
{
|
||||
sql_work_t* work = timer->data;
|
||||
uv_mutex_lock(&work->lock);
|
||||
if (work->db)
|
||||
{
|
||||
sqlite3_interrupt(work->db);
|
||||
}
|
||||
uv_mutex_unlock(&work->lock);
|
||||
}
|
||||
|
||||
static void _tf_ssb_sqlAsync_start_timer(uv_async_t* async)
|
||||
{
|
||||
sql_work_t* work = async->data;
|
||||
uv_timer_start(&work->timeout, _tf_ssb_sqlAsync_timeout, k_sql_async_timeout_ms, 0);
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_sqlAsync(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
@ -631,10 +691,21 @@ static JSValue _tf_ssb_sqlAsync(JSContext* context, JSValueConst this_val, int a
|
||||
{
|
||||
.data = work,
|
||||
},
|
||||
.async =
|
||||
{
|
||||
.data = work,
|
||||
},
|
||||
.timeout =
|
||||
{
|
||||
.data = work,
|
||||
},
|
||||
.ssb = ssb,
|
||||
.callback = JS_DupValue(context, argv[2]),
|
||||
.query = query,
|
||||
};
|
||||
uv_mutex_init(&work->lock);
|
||||
uv_async_init(tf_ssb_get_loop(ssb), &work->async, _tf_ssb_sqlAsync_start_timer);
|
||||
uv_timer_init(tf_ssb_get_loop(ssb), &work->timeout);
|
||||
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||
JSValue error_value = JS_UNDEFINED;
|
||||
if (ssb)
|
||||
@ -690,14 +761,12 @@ static JSValue _tf_ssb_sqlAsync(JSContext* context, JSValueConst this_val, int a
|
||||
JSValue call_result = JS_Call(context, work->promise[1], JS_UNDEFINED, 1, &error_value);
|
||||
tf_util_report_error(context, call_result);
|
||||
JS_FreeValue(context, call_result);
|
||||
JS_FreeValue(context, error_value);
|
||||
JS_FreeCString(context, query);
|
||||
JS_FreeValue(context, work->promise[0]);
|
||||
JS_FreeValue(context, work->promise[1]);
|
||||
JS_FreeValue(context, work->callback);
|
||||
JS_FreeValue(context, error_value);
|
||||
JS_FreeCString(context, query);
|
||||
tf_free(work->binds);
|
||||
tf_free(work->error);
|
||||
tf_free(work);
|
||||
_tf_ssb_sqlAsync_destroy(work);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user