From 248b25841383e0244da31e6435cee0a7439cafb4 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Wed, 12 Jun 2024 20:29:39 -0400 Subject: [PATCH] Make database.getAll() not block the main thread on database access. --- src/database.js.c | 95 ++++++++++++++++++++++++++++++++++++++--------- src/tests.c | 2 +- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/src/database.js.c b/src/database.js.c index fe6323ca..a3405123 100644 --- a/src/database.js.c +++ b/src/database.js.c @@ -320,31 +320,90 @@ static JSValue _database_remove(JSContext* context, JSValueConst this_val, int a return JS_UNDEFINED; } +typedef struct _database_get_all_t +{ + const char* id; + const char* key; + size_t key_length; + char** out_values; + size_t* out_lengths; + int out_values_length; + JSValue promise[2]; +} database_get_all_t; + +static void _database_get_all_work(tf_ssb_t* ssb, void* user_data) +{ + database_get_all_t* work = user_data; + sqlite3_stmt* statement; + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + if (sqlite3_prepare(db, "SELECT key FROM properties WHERE id = ?", -1, &statement, NULL) == SQLITE_OK) + { + if (sqlite3_bind_text(statement, 1, work->id, -1, NULL) == SQLITE_OK) + { + while (sqlite3_step(statement) == SQLITE_ROW) + { + work->out_values = tf_resize_vec(work->out_values, sizeof(char*) * (work->out_values_length + 1)); + work->out_lengths = tf_resize_vec(work->out_lengths, sizeof(size_t) * (work->out_values_length + 1)); + size_t length = sqlite3_column_bytes(statement, 0); + char* data = tf_malloc(length + 1); + memcpy(data, sqlite3_column_text(statement, 0), length); + data[length] = '\0'; + work->out_values[work->out_values_length] = data; + work->out_lengths[work->out_values_length] = length; + work->out_values_length++; + } + } + sqlite3_finalize(statement); + } + tf_ssb_release_db_reader(ssb, db); +} + +static void _database_get_all_after_work(tf_ssb_t* ssb, int status, void* user_data) +{ + database_get_all_t* work = user_data; + JSContext* context = tf_ssb_get_context(ssb); + JSValue result = JS_NewArray(context); + ; + for (int i = 0; i < work->out_values_length; i++) + { + JS_SetPropertyUint32(context, result, i, JS_NewStringLen(context, work->out_values[i], work->out_lengths[i])); + tf_free((void*)work->out_values[i]); + } + JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result); + tf_util_report_error(context, error); + JS_FreeValue(context, error); + JS_FreeValue(context, result); + JS_FreeValue(context, work->promise[0]); + JS_FreeValue(context, work->promise[1]); + tf_free(work->out_values); + tf_free(work->out_lengths); + tf_free(work); +} + static JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue array = JS_UNDEFINED; + JSValue result = JS_UNDEFINED; database_t* database = JS_GetOpaque(this_val, _database_class_id); if (database) { - sqlite3_stmt* statement; tf_ssb_t* ssb = tf_task_get_ssb(database->task); - sqlite3* db = tf_ssb_acquire_db_reader(ssb); - if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = ?1", -1, &statement, NULL) == SQLITE_OK) - { - if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK) - { - array = JS_NewArray(context); - uint32_t index = 0; - while (sqlite3_step(statement) == SQLITE_ROW) - { - JS_SetPropertyUint32(context, array, index++, JS_NewStringLen(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0))); - } - } - sqlite3_finalize(statement); - } - tf_ssb_release_db_reader(ssb, db); + + size_t length; + const char* key = JS_ToCStringLen(context, &length, argv[0]); + database_get_all_t* work = tf_malloc(sizeof(database_get_all_t) + strlen(database->id) + 1 + length + 1); + *work = (database_get_all_t) { + .id = (const char*)(work + 1), + .key = (const char*)(work + 1) + strlen(database->id) + 1, + .key_length = length, + }; + memcpy((char*)work->id, database->id, strlen(database->id) + 1); + memcpy((char*)work->key, key, length + 1); + JS_FreeCString(context, key); + + tf_ssb_run_work(ssb, _database_get_all_work, _database_get_all_after_work, work); + result = JS_NewPromiseCapability(context, work->promise); } - return array; + return result; } static JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) diff --git a/src/tests.c b/src/tests.c index 89386dfd..61238652 100644 --- a/src/tests.c +++ b/src/tests.c @@ -279,7 +279,7 @@ static void _test_database(const tf_test_options_t* options) " await db.set('c', 3);\n" "\n" " var expected = ['a', 'b', 'c'];\n" - " var have = db.getAll();\n" + " var have = await db.getAll();\n" " for (var i = 0; i < have.length; i++) {\n" " var item = have[i];\n" " if (expected.indexOf(item) == -1) {\n"