From 29d2a45abc8bf802bb2be2dd17fc75f1a519ac38 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Wed, 11 Oct 2023 15:44:40 +0000 Subject: [PATCH] Move reading settings from the database off of the main thread. It now happens periodically in a worker, which means I don't think there's anything blocking the main thread anymore. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4504 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- src/ssb.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++- src/ssb.h | 7 ++- src/ssb.rpc.c | 59 ++----------------- src/ssb.tests.c | 6 +- 4 files changed, 165 insertions(+), 60 deletions(-) diff --git a/src/ssb.c b/src/ssb.c index fa432f64..1b9f3620 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -188,6 +188,7 @@ typedef struct _tf_ssb_t uv_timer_t broadcast_cleanup_timer; uv_timer_t broadcast_timer; uv_timer_t trace_timer; + uv_timer_t settings_timer; uv_tcp_t server; uint8_t pub[crypto_sign_PUBLICKEYBYTES]; @@ -236,6 +237,8 @@ typedef struct _tf_ssb_t int ref_count; uv_thread_t thread_self; + bool is_room; + char* room_name; } tf_ssb_t; typedef struct _tf_ssb_connection_message_request_t @@ -333,6 +336,8 @@ static void _tf_ssb_connection_close(tf_ssb_connection_t* connection, const char static void _tf_ssb_nonce_inc(uint8_t* nonce); static void _tf_ssb_write(tf_ssb_connection_t* connection, void* data, size_t size); static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value); +static void _tf_ssb_update_settings(tf_ssb_t* ssb); +static void _tf_ssb_start_update_settings(tf_ssb_t* ssb, int delay_ms); static void _tf_ssb_add_debug_close(tf_ssb_t* ssb, tf_ssb_connection_t* connection, const char* reason) { @@ -2157,7 +2162,9 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path ssb->connections_tracker = tf_ssb_connections_create(ssb); + _tf_ssb_update_settings(ssb); tf_ssb_rpc_register(ssb); + _tf_ssb_start_update_settings(ssb, 5000); return ssb; } @@ -2327,11 +2334,17 @@ void tf_ssb_destroy(tf_ssb_t* ssb) uv_close((uv_handle_t*)&ssb->server, _tf_ssb_on_handle_close); } + if (ssb->settings_timer.data && !uv_is_closing((uv_handle_t*)&ssb->settings_timer)) + { + uv_close((uv_handle_t*)&ssb->settings_timer, _tf_ssb_on_handle_close); + } + while (ssb->broadcast_listener.data || ssb->broadcast_sender.data || ssb->broadcast_timer.data || ssb->broadcast_cleanup_timer.data || ssb->trace_timer.data || + ssb->settings_timer.data || ssb->server.data || ssb->ref_count) { @@ -2430,6 +2443,8 @@ void tf_ssb_destroy(tf_ssb_t* ssb) uv_mutex_destroy(&ssb->db_readers_lock); uv_mutex_destroy(&ssb->db_writer_lock); tf_free((void*)ssb->db_path); + tf_free(ssb->room_name); + ssb->room_name = NULL; tf_free(ssb); } @@ -3646,9 +3661,9 @@ void tf_ssb_unref(tf_ssb_t* ssb) ssb->ref_count--; } -void tf_ssb_set_main_thread(tf_ssb_t* ssb) +void tf_ssb_set_main_thread(tf_ssb_t* ssb, bool main_thread) { - ssb->thread_self = uv_thread_self(); + ssb->thread_self = main_thread ? uv_thread_self() : 0; } typedef struct _connection_work_t @@ -3713,3 +3728,137 @@ void tf_ssb_connection_run_work( _tf_ssb_connection_after_work_callback(&work->work, result); } } + +bool tf_ssb_is_room(tf_ssb_t* ssb) +{ + return ssb->is_room; +} + +void tf_ssb_set_is_room(tf_ssb_t* ssb, bool is_room) +{ + ssb->is_room = is_room; +} + +const char* tf_ssb_get_room_name(tf_ssb_t* ssb) +{ + return ssb->room_name; +} + +void tf_ssb_set_room_name(tf_ssb_t* ssb, const char* room_name) +{ + tf_free(ssb->room_name); + ssb->room_name = tf_strdup(room_name); +} + +typedef struct _update_settings_t +{ + uv_work_t work; + tf_ssb_t* ssb; + bool is_room; + char room_name[1024]; +} update_settings_t; + +static bool _get_global_setting_string(tf_ssb_t* ssb, const char* name, char* out_value, size_t size) +{ + bool result = false; + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + sqlite3_stmt* statement; + if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK) + { + if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK) + { + if (sqlite3_step(statement) == SQLITE_ROW) + { + snprintf(out_value, size, "%s", sqlite3_column_text(statement, 0)); + result = true; + } + } + sqlite3_finalize(statement); + } + else + { + tf_printf("prepare failed: %s\n", sqlite3_errmsg(db)); + } + tf_ssb_release_db_reader(ssb, db); + return result; +} + +static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool default_value) +{ + bool result = default_value; + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + sqlite3_stmt* statement; + if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK) + { + if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK) + { + if (sqlite3_step(statement) == SQLITE_ROW) + { + result = sqlite3_column_int(statement, 0) != 0; + } + } + sqlite3_finalize(statement); + } + else + { + tf_printf("prepare failed: %s\n", sqlite3_errmsg(db)); + } + tf_ssb_release_db_reader(ssb, db); + return result; +} + +static void _tf_ssb_update_settings_work(uv_work_t* work) +{ + update_settings_t* update = work->data; + update->is_room = _get_global_setting_bool(update->ssb, "room", true); + _get_global_setting_string(update->ssb, "room_name", update->room_name, sizeof(update->room_name)); +} + +static void _tf_ssb_update_settings_after_work(uv_work_t* work, int result) +{ + update_settings_t* update = work->data; + tf_ssb_set_is_room(update->ssb, update->is_room); + tf_ssb_set_room_name(update->ssb, update->room_name); + tf_free(update); +} + +static void _tf_ssb_start_update_settings_timer(uv_timer_t* timer) +{ + update_settings_t* update = tf_malloc(sizeof(update_settings_t)); + *update = (update_settings_t) + { + .work = + { + .data = update, + }, + .ssb = timer->data, + }; + int result = uv_queue_work(tf_ssb_get_loop(timer->data), &update->work, _tf_ssb_update_settings_work, _tf_ssb_update_settings_after_work); + if (result) + { + _tf_ssb_update_settings_after_work(&update->work, result); + } +} + +static void _tf_ssb_update_settings(tf_ssb_t* ssb) +{ + update_settings_t* update = tf_malloc(sizeof(update_settings_t)); + *update = (update_settings_t) + { + .work = + { + .data = update, + }, + .ssb = ssb, + }; + _tf_ssb_update_settings_work(&update->work); + _tf_ssb_update_settings_after_work(&update->work, 0); +} + +static void _tf_ssb_start_update_settings(tf_ssb_t* ssb, int delay_ms) +{ + ssb->settings_timer.data = ssb; + uv_timer_init(ssb->loop, &ssb->settings_timer); + uv_timer_start(&ssb->settings_timer, _tf_ssb_start_update_settings_timer, delay_ms, delay_ms); + uv_unref((uv_handle_t*)&ssb->settings_timer); +} diff --git a/src/ssb.h b/src/ssb.h index 72561ee1..9571c9bd 100644 --- a/src/ssb.h +++ b/src/ssb.h @@ -216,4 +216,9 @@ tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb); void tf_ssb_ref(tf_ssb_t* ssb); void tf_ssb_unref(tf_ssb_t* ssb); -void tf_ssb_set_main_thread(tf_ssb_t* ssb); +void tf_ssb_set_main_thread(tf_ssb_t* ssb, bool main_thread); + +bool tf_ssb_is_room(tf_ssb_t* ssb); +void tf_ssb_set_is_room(tf_ssb_t* ssb, bool is_room); +const char* tf_ssb_get_room_name(tf_ssb_t* ssb); +void tf_ssb_set_room_name(tf_ssb_t* ssb, const char* room_name); diff --git a/src/ssb.rpc.c b/src/ssb.rpc.c index c38abdf8..44c29e50 100644 --- a/src/ssb.rpc.c +++ b/src/ssb.rpc.c @@ -45,55 +45,6 @@ static int64_t _get_global_setting_int64(tf_ssb_t* ssb, const char* name, int64_ return result; } -static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool default_value) -{ - bool result = default_value; - sqlite3* db = tf_ssb_acquire_db_reader(ssb); - sqlite3_stmt* statement; - if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK) - { - if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK) - { - if (sqlite3_step(statement) == SQLITE_ROW) - { - result = sqlite3_column_int(statement, 0) != 0; - } - } - sqlite3_finalize(statement); - } - else - { - tf_printf("prepare failed: %s\n", sqlite3_errmsg(db)); - } - tf_ssb_release_db_reader(ssb, db); - return result; -} - -static bool _get_global_setting_string(tf_ssb_t* ssb, const char* name, char* out_value, size_t size) -{ - bool result = false; - sqlite3* db = tf_ssb_acquire_db_reader(ssb); - sqlite3_stmt* statement; - if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK) - { - if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK) - { - if (sqlite3_step(statement) == SQLITE_ROW) - { - snprintf(out_value, size, "%s", sqlite3_column_text(statement, 0)); - result = true; - } - } - sqlite3_finalize(statement); - } - else - { - tf_printf("prepare failed: %s\n", sqlite3_errmsg(db)); - } - tf_ssb_release_db_reader(ssb, db); - return result; -} - static void _tf_ssb_rpc_gossip_ping_callback(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) { char buffer[256]; @@ -328,7 +279,7 @@ void _tf_ssb_rpc_tunnel_cleanup(tf_ssb_t* ssb, void* user_data) static void _tf_ssb_rpc_tunnel_connect(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) { tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); - if (!_get_global_setting_bool(ssb, "room", true)) + if (!tf_ssb_is_room(ssb)) { tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "tunnel.connect"); return; @@ -427,12 +378,10 @@ static void _tf_ssb_rpc_room_meta(tf_ssb_connection_t* connection, uint8_t flags tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); JSContext* context = tf_ssb_get_context(ssb); JSValue response = JS_FALSE; - if (_get_global_setting_bool(ssb, "room", true)) + if (tf_ssb_is_room(ssb)) { - char room_name[1024] = "tilde friends tunnel"; - _get_global_setting_string(ssb, "room_name", room_name, sizeof(room_name)); response = JS_NewObject(context); - JS_SetPropertyStr(context, response, "name", JS_NewString(context, room_name)); + JS_SetPropertyStr(context, response, "name", JS_NewString(context, tf_ssb_get_room_name(ssb))); JS_SetPropertyStr(context, response, "membership", JS_FALSE); JSValue features = JS_NewArray(context); JS_SetPropertyUint32(context, features, 0, JS_NewString(context, "tunnel")); @@ -454,7 +403,7 @@ static void _tf_ssb_rpc_room_meta(tf_ssb_connection_t* connection, uint8_t flags static void _tf_ssb_rpc_room_attendants(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) { tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); - if (!_get_global_setting_bool(ssb, "room", true)) + if (!tf_ssb_is_room(ssb)) { tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "room.attendants"); return; diff --git a/src/ssb.tests.c b/src/ssb.tests.c index d5b3b07e..b82911fb 100644 --- a/src/ssb.tests.c +++ b/src/ssb.tests.c @@ -638,8 +638,8 @@ void tf_ssb_test_bench(const tf_test_options_t* options) uint8_t id0bin[k_id_bin_len]; tf_ssb_id_str_to_bin(id0bin, id0); - tf_ssb_set_main_thread(ssb0); - tf_ssb_set_main_thread(ssb1); + tf_ssb_set_main_thread(ssb0, true); + tf_ssb_set_main_thread(ssb1, true); uv_idle_t idle0 = { .data = ssb0 }; uv_idle_init(&loop, &idle0); @@ -666,6 +666,8 @@ void tf_ssb_test_bench(const tf_test_options_t* options) tf_ssb_remove_message_added_callback(ssb1, _message_added, &count); clock_gettime(CLOCK_REALTIME, &end_time); + tf_ssb_set_main_thread(ssb0, false); + tf_ssb_set_main_thread(ssb1, false); count = _ssb_test_count_messages(ssb1); if (count < k_messages) {