From 200ee1cace2f17537e6982ac447ea65d7c7a00b3 Mon Sep 17 00:00:00 2001 From: michael bishop Date: Mon, 12 Sep 2016 11:36:49 -0300 Subject: initial version of tox_loop --- CMakeLists.txt | 1 + auto_tests/tox_loop_test.c | 135 +++++++++++++++++++++++++++++++++++++++++++++ toxcore/Messenger.h | 4 ++ toxcore/tox.api.h | 32 +++++++++++ toxcore/tox.c | 111 +++++++++++++++++++++++++++++++++++++ toxcore/tox.h | 36 ++++++++++++ 6 files changed, 319 insertions(+) create mode 100644 auto_tests/tox_loop_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a3b12cef..e9743d49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -425,6 +425,7 @@ auto_test(resource_leak) auto_test(save_friend) auto_test(skeleton) auto_test(tox) +auto_test(tox_loop_test) auto_test(tox_many) auto_test(tox_many_tcp) auto_test(tox_one) diff --git a/auto_tests/tox_loop_test.c b/auto_tests/tox_loop_test.c new file mode 100644 index 00000000..56e78bda --- /dev/null +++ b/auto_tests/tox_loop_test.c @@ -0,0 +1,135 @@ + + +#include "helpers.h" + +#include "../toxcore/tox.h" + +#include +#include +#include +#include +#include + +#define TCP_RELAY_PORT 33448 +/* The Travis-CI container responds poorly to ::1 as a localhost address + * You're encouraged to -D FORCE_TESTS_IPV6 on a local test */ +#ifdef FORCE_TESTS_IPV6 +#define TOX_LOCALHOST "::1" +#else +#define TOX_LOCALHOST "127.0.0.1" +#endif + +struct loop_test { + int start_count, stop_count; + pthread_mutex_t mutex; + Tox *tox; +}; + +void tox_loop_cb_start(Tox *tox, void *user_data) +{ + struct loop_test *userdata = user_data; + pthread_mutex_lock(&userdata->mutex); + userdata->start_count++; +} + +void tox_loop_cb_stop(Tox *tox, void *user_data) +{ + struct loop_test *userdata = user_data; + userdata->stop_count++; + pthread_mutex_unlock(&userdata->mutex); +} + +void *tox_loop_worker(void *data) +{ + struct loop_test *userdata = data; + int retval = tox_loop(userdata->tox, data); + return (void *)retval; +} + +START_TEST(test_tox_loop) +{ + pthread_t worker1, worker2; + struct Tox_Options opts; + struct loop_test userdata; + uint8_t dpk[TOX_PUBLIC_KEY_SIZE]; + int retval; + + userdata.start_count = 0; + userdata.stop_count = 0; + pthread_mutex_init(&userdata.mutex, NULL); + + tox_options_default(&opts); + opts.tcp_port = TCP_RELAY_PORT; + userdata.tox = tox_new(&opts, 0); + tox_callback_loop_begin(userdata.tox, tox_loop_cb_start); + tox_callback_loop_end(userdata.tox, tox_loop_cb_stop); + pthread_create(&worker1, NULL, tox_loop_worker, &userdata); + + tox_self_get_dht_id(userdata.tox, dpk); + + tox_options_default(&opts); + struct loop_test userdata_tcp; + userdata_tcp.start_count = 0; + userdata_tcp.stop_count = 0; + pthread_mutex_init(&userdata_tcp.mutex, NULL); + userdata_tcp.tox = tox_new(&opts, 0); + tox_callback_loop_begin(userdata_tcp.tox, tox_loop_cb_start); + tox_callback_loop_end(userdata_tcp.tox, tox_loop_cb_stop); + pthread_create(&worker2, NULL, tox_loop_worker, &userdata_tcp); + + pthread_mutex_lock(&userdata_tcp.mutex); + TOX_ERR_BOOTSTRAP error = 0; + ck_assert_msg(tox_add_tcp_relay(userdata_tcp.tox, TOX_LOCALHOST, TCP_RELAY_PORT, dpk, &error), "add relay error, %i", + error); + ck_assert_msg(tox_bootstrap(userdata_tcp.tox, TOX_LOCALHOST, 33445, dpk, 0), "Bootstrap error"); + pthread_mutex_unlock(&userdata_tcp.mutex); + + sleep(10); + + tox_loop_stop(userdata.tox); + pthread_join(worker1, (void **)&retval); + ck_assert_msg(retval == 0, "tox_loop didn't return 0"); + + tox_kill(userdata.tox); + ck_assert_msg(userdata.start_count == userdata.stop_count, "start and stop must match"); + + tox_loop_stop(userdata_tcp.tox); + pthread_join(worker2, (void **)&retval); + ck_assert_msg(retval == 0, "tox_loop didn't return 0"); + + tox_kill(userdata_tcp.tox); + ck_assert_msg(userdata_tcp.start_count == userdata_tcp.stop_count, "start and stop must match"); +} +END_TEST + +#ifdef TRAVIS_ENV +uint8_t timeout_mux = 20; +#else +uint8_t timeout_mux = 10; +#endif + +static Suite *tox_suite(void) +{ + Suite *s = suite_create("Tox loop"); + + /* test the new tox_loop function */ + DEFTESTCASE_SLOW(tox_loop, 4 * timeout_mux); + + return s; +} + +int main(int argc, char *argv[]) +{ + srand((unsigned int) time(NULL)); + + Suite *tox = tox_suite(); + SRunner *test_runner = srunner_create(tox); + + int number_failed = 0; + srunner_run_all(test_runner, CK_NORMAL); + number_failed = srunner_ntests_failed(test_runner); + + srunner_free(test_runner); + + return number_failed; +} diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index e1dba698..7275ffbf 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -273,6 +273,10 @@ struct Messenger { void (*core_connection_change)(struct Messenger *m, unsigned int, void *); unsigned int last_connection_status; + uint8_t loop_run; + void (*loop_begin_cb)(struct Messenger *tox, void *user_data); + void (*loop_end_cb)(struct Messenger *tox, void *user_data); + Messenger_Options options; }; diff --git a/toxcore/tox.api.h b/toxcore/tox.api.h index 281cb998..0bcf40e1 100644 --- a/toxcore/tox.api.h +++ b/toxcore/tox.api.h @@ -838,6 +838,38 @@ const uint32_t iteration_interval(); */ void iterate(any user_data); +/** + * Run $iterate() any time a packet arrives, only returns after ${loop.stop}(). + */ +uint32_t loop(any user_data); + +namespace loop { + /** + * Tell $loop() to return. + */ + void stop(); + + /** + * Callback ran when $loop() calls into $iterate(), the client can lock a mutex here. + */ + event begin const { + /** + * No extra parameters. + */ + typedef void(); + } + + /** + * Callback ran when $loop() is finished with $iterate(), the client can unlock the mutex here. + */ + event end const { + /** + * No extra parameters. + */ + typedef void(); + } +} + /******************************************************************************* * diff --git a/toxcore/tox.c b/toxcore/tox.c index fe34c284..5e2f14d2 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -342,6 +342,117 @@ void tox_iterate(Tox *tox, void *user_data) do_groupchats((Group_Chats *)m->conferences_object, user_data); } +uint32_t tox_fd_count(Tox *tox) +{ + Messenger *m = tox; + return 1 + m->net_crypto->tcp_c->tcp_connections_length; +} + +/** + * Gathers a list of every network FD activity is expected on + * @param sockets an array of size tox_fd_count() + */ +uint32_t tox_fds(Tox *tox, uint32_t *sockets, uint32_t max_sockets) +{ + Messenger *m = tox; + int count = 0; + + if (max_sockets >= 1) { + sockets[count] = m->net->sock; + max_sockets--; + count++; + } + + TCP_Connections *conns = m->net_crypto->tcp_c; + int x; + + for (x = 0; x < conns->tcp_connections_length; x++) { + if (max_sockets == 0) { + break; + } + + TCP_con *conn = &conns->tcp_connections[x]; + sockets[count] = conn->connection->sock; + count++; + max_sockets--; + } + + return count; +} + +void tox_callback_loop_begin(Tox *tox, tox_loop_begin_cb *callback) +{ + Messenger *m = tox; + m->loop_begin_cb = callback; +} + +void tox_callback_loop_end(Tox *tox, tox_loop_end_cb *callback) +{ + Messenger *m = tox; + m->loop_end_cb = callback; +} + +uint32_t tox_loop(Tox *tox, void *user_data) +{ + struct timeval timeout; + int maxfd; + uint32_t i, list_size = 0; + uint32_t *fdlist = NULL; + Messenger *m = tox; + m->loop_run = true; + fd_set readable; + + + while (m->loop_run) { + if (m->loop_begin_cb) { + m->loop_begin_cb(tox, user_data); + } + + tox_iterate(tox, user_data); + + maxfd = 0; + FD_ZERO(&readable); + + uint32_t fdcount = tox_fd_count(tox); + + if (fdcount > list_size) { + fdlist = realloc(fdlist, fdcount * sizeof(uint32_t)); + list_size = fdcount; + } + + fdcount = tox_fds(tox, fdlist, list_size); + + for (i = 0; i < fdcount; i++) { + FD_SET(fdlist[i], &readable); + + if (fdlist[i] > maxfd) { + maxfd = fdlist[i]; + } + } + + timeout.tv_sec = 0; + timeout.tv_usec = tox_iteration_interval(tox) * 1000 * 2; // TODO, use a longer timeout (cleverca22) + + if (m->loop_end_cb) { + m->loop_end_cb(tox, user_data); + } + + int ret = select(maxfd, &readable, NULL, NULL, &timeout); + + if (ret < 0) { + return ret; + } + } + + return 0; +} + +void tox_loop_stop(Tox *tox) +{ + Messenger *m = tox; + m->loop_run = false; +} + void tox_self_get_address(const Tox *tox, uint8_t *address) { if (address) { diff --git a/toxcore/tox.h b/toxcore/tox.h index 75757b6f..0be019cd 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -990,6 +990,42 @@ uint32_t tox_iteration_interval(const Tox *tox); */ void tox_iterate(Tox *tox, void *user_data); +/** + * Run tox_iterate() any time a packet arrives, only returns after tox_loop_stop(). + */ +uint32_t tox_loop(Tox *tox, void *user_data); + +/** + * Tell tox_loop() to return. + */ +void tox_loop_stop(Tox *tox); + +/** + * No extra parameters. + */ +typedef void tox_loop_begin_cb(Tox *tox, void *user_data); + + +/** + * Set the callback for the `loop_begin` event. Pass NULL to unset. + * + * Callback ran when tox_loop() calls into tox_iterate(), the client can lock a mutex here. + */ +void tox_callback_loop_begin(Tox *tox, tox_loop_begin_cb *callback); + +/** + * No extra parameters. + */ +typedef void tox_loop_end_cb(Tox *tox, void *user_data); + + +/** + * Set the callback for the `loop_end` event. Pass NULL to unset. + * + * Callback ran when tox_loop() is finished with tox_iterate(), the client can unlock the mutex here. + */ +void tox_callback_loop_end(Tox *tox, tox_loop_end_cb *callback); + /******************************************************************************* * -- cgit v1.2.3