diff --git a/CMakeLists.txt b/CMakeLists.txt index 85970c0..44e42aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,9 @@ add_library(fuse-wsfs src/wsfs/server.c src/wsfs/message.c src/wsfs/message_queue.c + src/wsfs/time/timepoint.c + src/wsfs/time/timer.c + src/wsfs/time/timeout_manager.c src/wsfs/operation/lookup.c src/wsfs/operation/getattr.c src/wsfs/operation/readdir.c @@ -81,6 +84,8 @@ pkg_check_modules(GTEST gtest_main) add_executable(alltests test-src/test_response_parser.cc test-src/test_server.cc + test-src/test_timepoint.cc + test-src/test_timer.cc ) target_link_libraries(alltests PUBLIC fuse-wsfs ${EXTRA_LIBS} ${GTEST_LIBRARIES}) diff --git a/src/wsfs/jsonrpc/server.c b/src/wsfs/jsonrpc/server.c index b3d0fc2..4ac622c 100644 --- a/src/wsfs/jsonrpc/server.c +++ b/src/wsfs/jsonrpc/server.c @@ -5,6 +5,8 @@ #include "wsfs/jsonrpc/request.h" #include "wsfs/jsonrpc/response.h" +#define WSFS_DEFAULT_TIMEOUT (10 * 1000) + static struct wsfs_jsonrpc_method const * wsfs_jsonrpc_server_getmethod( struct wsfs_jsonrpc_server * server, char const * name) @@ -18,16 +20,41 @@ static struct wsfs_jsonrpc_method const * wsfs_jsonrpc_server_getmethod( return method; } +static void wsfs_jsonrpc_server_timeout( + struct wsfs_timer * timer) +{ + struct wsfs_jsonrpc_server * server = timer->user_data; + + if (server->request.is_pending) + { + wsfs_jsonrpc_method_finished_fn * finished = server->request.finished; + void * user_data = server->request.user_data; + + server->request.is_pending = false; + server->request.id = 0; + server->request.user_data = NULL; + server->request.finished = NULL; + wsfs_timer_cancel(&server->request.timer); + + finished(user_data, WSFS_BAD_TIMEOUT, NULL); + } +} + void wsfs_jsonrpc_server_init( - struct wsfs_jsonrpc_server * server) + struct wsfs_jsonrpc_server * server, + struct wsfs_timeout_manager * timeout_manager) { server->methods = NULL; server->request.is_pending = false; + + wsfs_timer_init(&server->request.timer, timeout_manager); } void wsfs_jsonrpc_server_cleanup( struct wsfs_jsonrpc_server * server) { + wsfs_timer_cleanup(&server->request.timer); + if (server->request.is_pending) { server->request.finished(server->request.user_data, WSFS_BAD, NULL); @@ -74,6 +101,8 @@ void wsfs_jsonrpc_server_invoke( server->request.finished = finished; server->request.user_data = user_data; server->request.id = 42; + wsfs_timer_start(&server->request.timer, wsfs_timepoint_in_msec(WSFS_DEFAULT_TIMEOUT), + &wsfs_jsonrpc_server_timeout, server); va_list args; va_start(args, param_info); @@ -87,6 +116,7 @@ void wsfs_jsonrpc_server_invoke( server->request.finished = NULL; server->request.user_data = NULL; server->request.id = 0; + wsfs_timer_cancel(&server->request.timer); finished(user_data, WSFS_BAD, NULL); } @@ -146,6 +176,7 @@ void wsfs_jsonrpc_server_onresult( server->request.id = 0; server->request.user_data = NULL; server->request.finished = NULL; + wsfs_timer_cancel(&server->request.timer); finished(user_data, response.status, response.result); } diff --git a/src/wsfs/jsonrpc/server.h b/src/wsfs/jsonrpc/server.h index 6d05d2a..029292a 100644 --- a/src/wsfs/jsonrpc/server.h +++ b/src/wsfs/jsonrpc/server.h @@ -13,6 +13,9 @@ using std::size_t; #include #include "wsfs/jsonrpc/method.h" +#include "wsfs/time/timeout_manager.h" +#include "wsfs/time/timer.h" + struct wsfs_jsonrpc_request { @@ -20,6 +23,7 @@ struct wsfs_jsonrpc_request wsfs_jsonrpc_method_finished_fn * finished; void * user_data; int id; + struct wsfs_timer timer; }; struct wsfs_jsonrpc_server @@ -35,7 +39,8 @@ extern "C" #endif extern void wsfs_jsonrpc_server_init( - struct wsfs_jsonrpc_server * server); + struct wsfs_jsonrpc_server * server, + struct wsfs_timeout_manager * manager); extern void wsfs_jsonrpc_server_cleanup( struct wsfs_jsonrpc_server * server); diff --git a/src/wsfs/operation/read.c b/src/wsfs/operation/read.c index 7cc60b1..0fbbd63 100644 --- a/src/wsfs/operation/read.c +++ b/src/wsfs/operation/read.c @@ -39,7 +39,7 @@ static void wsfs_operation_read_finished(void * user_data, wsfs_status status, j fuse_req_t request = user_data; char * buffer = NULL; - size_t length; + size_t length = 0; if (NULL != data) { json_t * data_holder = json_object_get(data, "data"); diff --git a/src/wsfs/server_protocol.c b/src/wsfs/server_protocol.c index 3113200..b1edea6 100644 --- a/src/wsfs/server_protocol.c +++ b/src/wsfs/server_protocol.c @@ -17,6 +17,8 @@ static int wsfs_server_protocol_callback( struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); struct wsfs_server_protocol * protocol = ws_protocol->user; + wsfs_timeout_manager_check(&protocol->timeout_manager); + switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: @@ -146,7 +148,9 @@ bool wsfs_server_protocol_init( protocol->wsi = NULL; wsfs_message_queue_init(&protocol->queue); - wsfs_jsonrpc_server_init(&protocol->rpc); + wsfs_timeout_manager_init(&protocol->timeout_manager); + + wsfs_jsonrpc_server_init(&protocol->rpc, &protocol->timeout_manager); wsfs_jsonrpc_server_add(&protocol->rpc, "lookup", &wsfs_server_protocol_invoke, protocol); wsfs_jsonrpc_server_add(&protocol->rpc, "getattr", &wsfs_server_protocol_invoke, protocol); wsfs_jsonrpc_server_add(&protocol->rpc, "readdir", &wsfs_server_protocol_invoke, protocol); @@ -154,7 +158,17 @@ bool wsfs_server_protocol_init( wsfs_jsonrpc_server_add(&protocol->rpc, "close", &wsfs_server_protocol_invoke, protocol); wsfs_jsonrpc_server_add(&protocol->rpc, "read", &wsfs_server_protocol_invoke, protocol); - return wsfs_filesystem_init(&protocol->filesystem, &protocol->rpc, mount_point); + bool const success = wsfs_filesystem_init(&protocol->filesystem, &protocol->rpc, mount_point); + + // cleanup on error + if (!success) + { + wsfs_jsonrpc_server_cleanup(&protocol->rpc); + wsfs_timeout_manager_cleanup(&protocol->timeout_manager); + wsfs_message_queue_cleanup(&protocol->queue); + } + + return success; } void wsfs_server_protocol_cleanup( @@ -162,6 +176,7 @@ void wsfs_server_protocol_cleanup( { wsfs_filesystem_cleanup(&protocol->filesystem); wsfs_jsonrpc_server_cleanup(&protocol->rpc); + wsfs_timeout_manager_cleanup(&protocol->timeout_manager); wsfs_message_queue_cleanup(&protocol->queue); protocol->wsi = NULL; } diff --git a/src/wsfs/server_protocol_intern.h b/src/wsfs/server_protocol_intern.h index 83ee1f5..3897bde 100644 --- a/src/wsfs/server_protocol_intern.h +++ b/src/wsfs/server_protocol_intern.h @@ -5,9 +5,11 @@ #include "wsfs/filesystem.h" #include "wsfs/message_queue.h" #include "wsfs/jsonrpc/server.h" +#include "wsfs/time/timeout_manager.h" struct wsfs_server_protocol { + struct wsfs_timeout_manager timeout_manager; struct wsfs_filesystem filesystem; struct wsfs_jsonrpc_server rpc; struct wsfs_message_queue queue; diff --git a/src/wsfs/time/timeout_manager.c b/src/wsfs/time/timeout_manager.c new file mode 100644 index 0000000..361ac49 --- /dev/null +++ b/src/wsfs/time/timeout_manager.c @@ -0,0 +1,84 @@ +#include "wsfs/time/timeout_manager_intern.h" + +#include +#include "wsfs/time/timer_intern.h" +#include "wsfs/time/timepoint.h" + +void wsfs_timeout_manager_init( + struct wsfs_timeout_manager * manager) +{ + manager->timers = NULL; +} + +void wsfs_timeout_manager_cleanup( + struct wsfs_timeout_manager * manager) +{ + struct wsfs_timer * timer = manager->timers; + while (NULL != timer) + { + struct wsfs_timer * next = timer->next; + + wsfs_timer_trigger(timer); + + timer = next; + } + + manager->timers = NULL; + +} + +void wsfs_timeout_manager_check( + struct wsfs_timeout_manager * manager) +{ + struct wsfs_timer * timer = manager->timers; + while (NULL != timer) + { + struct wsfs_timer * next = timer->next; + + if (wsfs_timer_is_timeout(timer)) + { + wsfs_timeout_manager_removetimer(manager, timer); + wsfs_timer_trigger(timer); + } + + timer = next; + } +} + +void wsfs_timeout_manager_addtimer( + struct wsfs_timeout_manager * manager, + struct wsfs_timer * timer) +{ + if (NULL != manager->timers) + { + manager->timers->prev = timer; + } + + timer->next = manager->timers; + timer->prev = NULL; + manager->timers = timer; +} + +void wsfs_timeout_manager_removetimer( + struct wsfs_timeout_manager * manager, + struct wsfs_timer * timer) +{ + struct wsfs_timer * prev = timer->prev; + struct wsfs_timer * next = timer->next; + + if (NULL != prev) + { + prev->next = next; + } + + if (NULL != next) + { + next->prev = prev; + } + + if (manager->timers == timer) + { + manager->timers = next; + } +} + diff --git a/src/wsfs/time/timeout_manager.h b/src/wsfs/time/timeout_manager.h new file mode 100644 index 0000000..81381c0 --- /dev/null +++ b/src/wsfs/time/timeout_manager.h @@ -0,0 +1,29 @@ +#ifndef _WSFS_TIME_TIMEOUT_MANAGER_H +#define _WSFS_TIME_TIMEOUT_MANAGER_H + +struct wsfs_timer; +struct wsfs_timeout_manager +{ + struct wsfs_timer * timers; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wsfs_timeout_manager_init( + struct wsfs_timeout_manager * manager); + +extern void wsfs_timeout_manager_cleanup( + struct wsfs_timeout_manager * manager); + +extern void wsfs_timeout_manager_check( + struct wsfs_timeout_manager * manager); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/wsfs/time/timeout_manager_intern.h b/src/wsfs/time/timeout_manager_intern.h new file mode 100644 index 0000000..3bf8357 --- /dev/null +++ b/src/wsfs/time/timeout_manager_intern.h @@ -0,0 +1,24 @@ +#ifndef _WSFS_TIME_TIMEOUT_MANAGER_INTERN_H +#define _WSFS_TIME_TIMEOUT_MANAGER_INTERN_H + +#include "wsfs/time/timeout_manager.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wsfs_timeout_manager_addtimer( + struct wsfs_timeout_manager * manager, + struct wsfs_timer * timer); + +extern void wsfs_timeout_manager_removetimer( + struct wsfs_timeout_manager * manager, + struct wsfs_timer * timer); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/wsfs/time/timepoint.c b/src/wsfs/time/timepoint.c new file mode 100644 index 0000000..9949d79 --- /dev/null +++ b/src/wsfs/time/timepoint.c @@ -0,0 +1,31 @@ +#include "wsfs/time/timepoint.h" + +#include + +#define WSFS_MSEC_PER_SEC ((wsfs_timepoint) 1000) +#define WSFS_NSEC_PER_MSEC ((wsfs_timepoint) 1000 * 1000) + +wsfs_timepoint wsfs_timepoint_now(void) +{ + struct timespec timepoint; + clock_gettime(CLOCK_MONOTONIC, &timepoint); + + wsfs_timepoint const now = (timepoint.tv_sec * WSFS_MSEC_PER_SEC) + (timepoint.tv_nsec / WSFS_NSEC_PER_MSEC); + return now; +} + +wsfs_timepoint wsfs_timepoint_in_msec(wsfs_timediff value) +{ + wsfs_timepoint const now = wsfs_timepoint_now(); + wsfs_timepoint result = now + ((wsfs_timepoint) value); + + return result; +} + +bool wsfs_timepoint_is_elapsed(wsfs_timepoint timepoint) +{ + wsfs_timepoint const now = wsfs_timepoint_now(); + wsfs_timediff const diff = (wsfs_timediff) (timepoint - now); + + return (0 > diff); +} diff --git a/src/wsfs/time/timepoint.h b/src/wsfs/time/timepoint.h new file mode 100644 index 0000000..3723159 --- /dev/null +++ b/src/wsfs/time/timepoint.h @@ -0,0 +1,31 @@ +#ifndef _WSFS_TIME_TIMEPOINT_H +#define _WSFS_TIME_TIMEPOINT_H + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +typedef uint64_t wsfs_timepoint; +typedef int64_t wsfs_timediff; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern wsfs_timepoint wsfs_timepoint_now(void); + +extern wsfs_timepoint wsfs_timepoint_in_msec( + wsfs_timediff value); + +extern bool wsfs_timepoint_is_elapsed( + wsfs_timepoint timepoint); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/wsfs/time/timer.c b/src/wsfs/time/timer.c new file mode 100644 index 0000000..4f1d00b --- /dev/null +++ b/src/wsfs/time/timer.c @@ -0,0 +1,65 @@ +#include "wsfs/time/timer_intern.h" +#include "wsfs/time/timeout_manager_intern.h" + +#include +#include + +void wsfs_timer_init( + struct wsfs_timer * timer, + struct wsfs_timeout_manager * manager) +{ + timer->manager = manager; + timer->timeout = 0; + timer->timeout_handler = NULL; + timer->user_data = NULL; + timer->prev = NULL; + timer->next = NULL; +} + +void wsfs_timer_cleanup( + struct wsfs_timer * timer) +{ + memset(timer, 0, sizeof(struct wsfs_timer)); +} + +void wsfs_timer_start( + struct wsfs_timer * timer, + wsfs_timepoint absolute_timeout, + wsfs_timer_timeout_fn * handler, + void * user_data) +{ + timer->timeout = absolute_timeout; + timer->timeout_handler = handler; + timer->user_data = user_data; + + wsfs_timeout_manager_addtimer(timer->manager, timer); +} + +void wsfs_timer_cancel( + struct wsfs_timer * timer) +{ + wsfs_timeout_manager_removetimer(timer->manager, timer); + + timer->timeout = 0; + timer->timeout_handler = NULL; + timer->user_data = NULL; +} + +bool wsfs_timer_is_timeout( + struct wsfs_timer * timer) +{ + return wsfs_timepoint_is_elapsed(timer->timeout); +} + + +void wsfs_timer_trigger( + struct wsfs_timer * timer) +{ + if (NULL != timer->timeout_handler) + { + timer->prev = NULL; + timer->next = NULL; + + timer->timeout_handler(timer); + } +} diff --git a/src/wsfs/time/timer.h b/src/wsfs/time/timer.h new file mode 100644 index 0000000..4c7d9a3 --- /dev/null +++ b/src/wsfs/time/timer.h @@ -0,0 +1,48 @@ +#ifndef _WSFS_TIMER_H +#define _WSFS_TIMER_H + +#include "wsfs/time/timepoint.h" + +struct wsfs_timer; +struct wsfs_timeout_manager; + +typedef void wsfs_timer_timeout_fn(struct wsfs_timer * timer); + +struct wsfs_timer +{ + struct wsfs_timeout_manager * manager; + wsfs_timepoint timeout; + wsfs_timer_timeout_fn * timeout_handler; + void * user_data; + struct wsfs_timer * next; + struct wsfs_timer * prev; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wsfs_timer_init( + struct wsfs_timer * timer, + struct wsfs_timeout_manager * manager); + +extern void wsfs_timer_cleanup( + struct wsfs_timer * timer); + +extern void wsfs_timer_start( + struct wsfs_timer * timer, + wsfs_timepoint absolute_timeout, + wsfs_timer_timeout_fn * handler, + void * user_data); + +extern void wsfs_timer_cancel( + struct wsfs_timer * timer); + +#ifdef __cplusplus +} +#endif + + + +#endif diff --git a/src/wsfs/time/timer_intern.h b/src/wsfs/time/timer_intern.h new file mode 100644 index 0000000..09c431c --- /dev/null +++ b/src/wsfs/time/timer_intern.h @@ -0,0 +1,25 @@ +#ifndef _WSFS_TIME_TIMER_INTERN_H +#define _WSFS_TIME_TIMER_INTERN_H + +#ifndef __cplusplus +#include +#endif + +#include "wsfs/time/timer.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern bool wsfs_timer_is_timeout( + struct wsfs_timer * timer); + +extern void wsfs_timer_trigger( + struct wsfs_timer * timer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-src/test_timepoint.cc b/test-src/test_timepoint.cc new file mode 100644 index 0000000..c52b12a --- /dev/null +++ b/test-src/test_timepoint.cc @@ -0,0 +1,34 @@ +#include +#include +#include "wsfs/time/timepoint.h" + + +TEST(timepoint, now) +{ + wsfs_timepoint start = wsfs_timepoint_now(); + usleep(42 * 1000); + wsfs_timepoint end = wsfs_timepoint_now(); + + ASSERT_LT(start, end); + ASSERT_LT(end, start + 500); +} + +TEST(timepoint, in_msec) +{ + wsfs_timepoint now = wsfs_timepoint_now(); + wsfs_timepoint later = wsfs_timepoint_in_msec(42); + + ASSERT_LT(now, later); + ASSERT_LT(later, now + 500); +} + +TEST(timepoint, elapsed) +{ + wsfs_timepoint now; + + now = wsfs_timepoint_now(); + ASSERT_TRUE(wsfs_timepoint_is_elapsed(now - 1)); + + now = wsfs_timepoint_now(); + ASSERT_FALSE(wsfs_timepoint_is_elapsed(now + 500)); +} diff --git a/test-src/test_timer.cc b/test-src/test_timer.cc new file mode 100644 index 0000000..9bfbbec --- /dev/null +++ b/test-src/test_timer.cc @@ -0,0 +1,100 @@ +#include + +#include +#include + +#include "wsfs/time/timer.h" +#include "wsfs/time/timeout_manager.h" + +using std::size_t; + +namespace +{ + void on_timeout(struct wsfs_timer * timer) + { + bool * triggered = reinterpret_cast(timer->user_data); + *triggered = true; + } +} + +TEST(timer, init) +{ + struct wsfs_timeout_manager manager; + struct wsfs_timer timer; + + wsfs_timeout_manager_init(&manager); + wsfs_timer_init(&timer, &manager); + + wsfs_timer_cleanup(&timer); + wsfs_timeout_manager_cleanup(&manager); +} + +TEST(timer, trigger) +{ + struct wsfs_timeout_manager manager; + struct wsfs_timer timer; + + wsfs_timeout_manager_init(&manager); + wsfs_timer_init(&timer, &manager); + + bool triggered = false; + wsfs_timer_start(&timer, wsfs_timepoint_in_msec(250), &on_timeout, reinterpret_cast(&triggered)); + usleep(500 * 1000); + wsfs_timeout_manager_check(&manager); + + ASSERT_TRUE(triggered); + + wsfs_timer_cleanup(&timer); + wsfs_timeout_manager_cleanup(&manager); +} + +TEST(timer, cancel) +{ + struct wsfs_timeout_manager manager; + struct wsfs_timer timer; + + wsfs_timeout_manager_init(&manager); + wsfs_timer_init(&timer, &manager); + + bool triggered = false; + wsfs_timer_start(&timer, wsfs_timepoint_in_msec(250), &on_timeout, &triggered); + usleep(500 * 1000); + wsfs_timer_cancel(&timer); + wsfs_timeout_manager_check(&manager); + + ASSERT_FALSE(triggered); + + wsfs_timer_cleanup(&timer); + wsfs_timeout_manager_cleanup(&manager); +} + +TEST(timer, multiple_timers) +{ + static size_t const count = 5; + struct wsfs_timeout_manager manager; + struct wsfs_timer timer[count]; + bool triggered[count]; + + wsfs_timeout_manager_init(&manager); + + for(size_t i = 0; i < count; i++) + { + wsfs_timer_init(&timer[i], &manager); + triggered[i] = false; + wsfs_timer_start(&timer[i], wsfs_timepoint_in_msec(300 - (50 * i)), &on_timeout, &triggered[i]); + } + + for(size_t i = 0; i < count; i++) + { + usleep(100 * 1000); + wsfs_timeout_manager_check(&manager); + } + + for(size_t i = 0; i < count; i++) + { + ASSERT_TRUE(triggered[i]); + wsfs_timer_cleanup(&timer[i]); + } + + wsfs_timeout_manager_cleanup(&manager); +}