forked from cory/tildefriends
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
93
src/ssb.js.c
93
src/ssb.js.c
@ -26,6 +26,8 @@
|
|||||||
#define _countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
|
#define _countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const int k_sql_async_timeout_ms = 60 * 1000;
|
||||||
|
|
||||||
static JSClassID _tf_ssb_classId;
|
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);
|
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
|
typedef struct _sql_work_t
|
||||||
{
|
{
|
||||||
uv_work_t request;
|
uv_work_t request;
|
||||||
|
uv_async_t async;
|
||||||
|
uv_timer_t timeout;
|
||||||
tf_ssb_t* ssb;
|
tf_ssb_t* ssb;
|
||||||
|
sqlite3* db;
|
||||||
|
uv_mutex_t lock;
|
||||||
const char* query;
|
const char* query;
|
||||||
uint8_t* binds;
|
uint8_t* binds;
|
||||||
size_t binds_count;
|
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_t* trace = tf_ssb_get_trace(sql_work->ssb);
|
||||||
tf_trace_begin(trace, "sql_async_work");
|
tf_trace_begin(trace, "sql_async_work");
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader_restricted(sql_work->ssb);
|
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;
|
sqlite3_stmt* statement = NULL;
|
||||||
sql_work->result = sqlite3_prepare(db, sql_work->query, -1, &statement, NULL);
|
sql_work->result = sqlite3_prepare(db, sql_work->query, -1, &statement, NULL);
|
||||||
if (sql_work->result == SQLITE_OK)
|
if (sql_work->result == SQLITE_OK)
|
||||||
@ -512,9 +522,16 @@ static void _tf_ssb_sqlAsync_work(uv_work_t* work)
|
|||||||
}
|
}
|
||||||
sql_work->result = r;
|
sql_work->result = r;
|
||||||
if (r != SQLITE_OK && r != SQLITE_DONE)
|
if (r != SQLITE_OK && r != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
if (sqlite3_is_interrupted(db))
|
||||||
|
{
|
||||||
|
sql_work->error = tf_strdup("Timed out");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
sql_work->error = tf_strdup(sqlite3_errmsg(db));
|
sql_work->error = tf_strdup(sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_tf_ssb_sql_append(&sql_work->rows, &sql_work->rows_count, &(uint8_t[]) { 0 }, 1);
|
_tf_ssb_sql_append(&sql_work->rows, &sql_work->rows_count, &(uint8_t[]) { 0 }, 1);
|
||||||
sqlite3_finalize(statement);
|
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));
|
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_release_db_reader(sql_work->ssb, db);
|
||||||
tf_ssb_record_thread_busy(sql_work->ssb, false);
|
tf_ssb_record_thread_busy(sql_work->ssb, false);
|
||||||
tf_trace_end(trace);
|
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)
|
static void _tf_ssb_sqlAsync_after_work(uv_work_t* work, int status)
|
||||||
{
|
{
|
||||||
sql_work_t* sql_work = work->data;
|
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);
|
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, response);
|
||||||
JS_FreeValue(context, row);
|
JS_FreeValue(context, row);
|
||||||
|
if (is_error)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tf_free(sql_work->binds);
|
|
||||||
tf_free(sql_work->rows);
|
|
||||||
|
|
||||||
JSValue result = JS_UNDEFINED;
|
JSValue result = JS_UNDEFINED;
|
||||||
if (sql_work->result == SQLITE_OK || sql_work->result == SQLITE_DONE)
|
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[0]);
|
||||||
JS_FreeValue(context, sql_work->promise[1]);
|
JS_FreeValue(context, sql_work->promise[1]);
|
||||||
JS_FreeValue(context, sql_work->callback);
|
JS_FreeValue(context, sql_work->callback);
|
||||||
JS_FreeCString(context, sql_work->query);
|
_tf_ssb_sqlAsync_destroy(sql_work);
|
||||||
|
|
||||||
tf_free(sql_work->error);
|
|
||||||
tf_free(sql_work);
|
|
||||||
tf_trace_end(trace);
|
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)
|
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);
|
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,
|
.data = work,
|
||||||
},
|
},
|
||||||
|
.async =
|
||||||
|
{
|
||||||
|
.data = work,
|
||||||
|
},
|
||||||
|
.timeout =
|
||||||
|
{
|
||||||
|
.data = work,
|
||||||
|
},
|
||||||
.ssb = ssb,
|
.ssb = ssb,
|
||||||
.callback = JS_DupValue(context, argv[2]),
|
.callback = JS_DupValue(context, argv[2]),
|
||||||
.query = query,
|
.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 result = JS_NewPromiseCapability(context, work->promise);
|
||||||
JSValue error_value = JS_UNDEFINED;
|
JSValue error_value = JS_UNDEFINED;
|
||||||
if (ssb)
|
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);
|
JSValue call_result = JS_Call(context, work->promise[1], JS_UNDEFINED, 1, &error_value);
|
||||||
tf_util_report_error(context, call_result);
|
tf_util_report_error(context, call_result);
|
||||||
JS_FreeValue(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[0]);
|
||||||
JS_FreeValue(context, work->promise[1]);
|
JS_FreeValue(context, work->promise[1]);
|
||||||
JS_FreeValue(context, work->callback);
|
JS_FreeValue(context, work->callback);
|
||||||
JS_FreeValue(context, error_value);
|
_tf_ssb_sqlAsync_destroy(work);
|
||||||
JS_FreeCString(context, query);
|
|
||||||
tf_free(work->binds);
|
|
||||||
tf_free(work->error);
|
|
||||||
tf_free(work);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user