From 5ff099763b1f56414572e1c12eb2f003117db5a0 Mon Sep 17 00:00:00 2001 From: Ansa89 Date: Fri, 16 Dec 2016 11:56:51 +0100 Subject: [PATCH] Implement tox_loop --- .travis.yml | 2 + CMakeLists.txt | 20 ++- auto_tests/Makefile.inc | 11 +- auto_tests/tox_loop_test.c | 61 ++++--- circle.yml | 1 + cmake/Dependencies.cmake | 32 ++-- cmake/ModulePackage.cmake | 27 +++ configure.ac | 251 +++++++++++++++++++++++++++ libtoxcore.pc.in | 2 +- toxcore/Makefile.inc | 6 + toxcore/Messenger.c | 28 +++ toxcore/Messenger.h | 20 ++- toxcore/TCP_client.c | 11 ++ toxcore/TCP_client.h | 16 +- toxcore/TCP_connection.c | 31 ++++ toxcore/TCP_connection.h | 4 + toxcore/network.c | 10 ++ toxcore/network.h | 13 ++ toxcore/tox.api.h | 34 +++- toxcore/tox.c | 338 +++++++++++++++++++++++++++++++------ toxcore/tox.h | 41 ++++- 21 files changed, 852 insertions(+), 107 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2e6365524a..9592413397 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,8 @@ addons: packages: - check - libcv-dev # For av_test. + - libev-dev # For tox_loop. + - libevent-dev # For tox_loop. - libhighgui-dev # For av_test. - libopencv-contrib-dev # For av_test. - libsndfile1-dev # For av_test. diff --git a/CMakeLists.txt b/CMakeLists.txt index e9743d4909..5bcafaf3d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,6 +183,12 @@ if (BUILD_TOXAV) endif() endif() +if(LIBEV_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_LIBEV") +elseif(LIBEVENT_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_LIBEVENT") +endif() + ################################################################################ # # :: Tox Core Library @@ -234,6 +240,18 @@ if(RT_LIBRARIES) set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} "-lrt") endif() +if(LIBEV_FOUND) + target_link_modules(toxnetwork ${LIBEV_LIBRARIES}) + set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} "-lev") +elseif(LIBEVENT_FOUND) + target_link_modules(toxnetwork ${LIBEVENT_LIBRARIES}) + if(NOT WIN32) + set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} "-levent -levent_pthreads") + else() + set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} "-levent") + endif() +endif() + if(WIN32) target_link_modules(toxnetwork ws2_32 iphlpapi) set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} "-lws2_32 -liphlpapi") @@ -425,7 +443,7 @@ auto_test(resource_leak) auto_test(save_friend) auto_test(skeleton) auto_test(tox) -auto_test(tox_loop_test) +auto_test(tox_loop) auto_test(tox_many) auto_test(tox_many_tcp) auto_test(tox_one) diff --git a/auto_tests/Makefile.inc b/auto_tests/Makefile.inc index 698064c463..d8d8b1ebeb 100644 --- a/auto_tests/Makefile.inc +++ b/auto_tests/Makefile.inc @@ -1,7 +1,7 @@ if BUILD_TESTS -TESTS = encryptsave_test messenger_autotest crypto_test network_test onion_test TCP_test tox_test dht_autotest tox_strncasecmp_test -check_PROGRAMS = encryptsave_test messenger_autotest crypto_test network_test onion_test TCP_test tox_test dht_autotest tox_strncasecmp_test +TESTS = encryptsave_test messenger_autotest crypto_test network_test onion_test TCP_test tox_loop_test tox_test dht_autotest tox_strncasecmp_test +check_PROGRAMS = encryptsave_test messenger_autotest crypto_test network_test onion_test TCP_test tox_loop_test tox_test dht_autotest tox_strncasecmp_test AUTOTEST_CFLAGS = \ $(LIBSODIUM_CFLAGS) \ @@ -61,6 +61,13 @@ TCP_test_CFLAGS = $(AUTOTEST_CFLAGS) TCP_test_LDADD = $(AUTOTEST_LDADD) +tox_loop_test_SOURCES = ../auto_tests/tox_loop_test.c + +tox_loop_test_CFLAGS = $(AUTOTEST_CFLAGS) + +tox_loop_test_LDADD = $(AUTOTEST_LDADD) + + tox_test_SOURCES = ../auto_tests/tox_test.c tox_test_CFLAGS = $(AUTOTEST_CFLAGS) diff --git a/auto_tests/tox_loop_test.c b/auto_tests/tox_loop_test.c index 56e78bdab0..7589cc4bc1 100644 --- a/auto_tests/tox_loop_test.c +++ b/auto_tests/tox_loop_test.c @@ -1,15 +1,13 @@ - - -#include "helpers.h" - -#include "../toxcore/tox.h" - #include #include -#include #include +#include #include +#include "../toxcore/tox.h" + +#include "helpers.h" + #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 */ @@ -19,38 +17,38 @@ #define TOX_LOCALHOST "127.0.0.1" #endif -struct loop_test { +typedef struct { int start_count, stop_count; pthread_mutex_t mutex; Tox *tox; -}; +} loop_test; void tox_loop_cb_start(Tox *tox, void *user_data) { - struct loop_test *userdata = user_data; + loop_test *userdata = (loop_test *) 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; + loop_test *userdata = (loop_test *) 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; + loop_test *userdata = (loop_test *) data; + tox_loop(userdata->tox, data, NULL); + return NULL; } START_TEST(test_tox_loop) { - pthread_t worker1, worker2; - struct Tox_Options opts; - struct loop_test userdata; + pthread_t worker, worker_tcp; + struct Tox_Options *opts = tox_options_new(NULL); + loop_test userdata; uint8_t dpk[TOX_PUBLIC_KEY_SIZE]; int retval; @@ -58,43 +56,42 @@ START_TEST(test_tox_loop) 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_options_set_tcp_port(opts, TCP_RELAY_PORT); + userdata.tox = tox_new(opts, NULL); 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); + pthread_create(&worker, NULL, tox_loop_worker, &userdata); tox_self_get_dht_id(userdata.tox, dpk); - tox_options_default(&opts); - struct loop_test userdata_tcp; + tox_options_default(opts); + 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); + userdata_tcp.tox = tox_new(opts, NULL); 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_create(&worker_tcp, 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", + TOX_ERR_BOOTSTRAP error; + 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"); + ck_assert_msg(tox_bootstrap(userdata_tcp.tox, TOX_LOCALHOST, 33445, dpk, &error), "Bootstrap error, %i", error); pthread_mutex_unlock(&userdata_tcp.mutex); sleep(10); tox_loop_stop(userdata.tox); - pthread_join(worker1, (void **)&retval); + pthread_join(worker, (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); + pthread_join(worker_tcp, (void **)&retval); ck_assert_msg(retval == 0, "tox_loop didn't return 0"); tox_kill(userdata_tcp.tox); @@ -103,9 +100,9 @@ START_TEST(test_tox_loop) END_TEST #ifdef TRAVIS_ENV -uint8_t timeout_mux = 20; +static uint8_t timeout_mux = 20; #else -uint8_t timeout_mux = 10; +static uint8_t timeout_mux = 10; #endif static Suite *tox_suite(void) diff --git a/circle.yml b/circle.yml index 6733024c99..fe53b0057a 100644 --- a/circle.yml +++ b/circle.yml @@ -15,6 +15,7 @@ dependencies: - sudo apt-get install clang - sudo apt-get install build-essential libtool autotools-dev automake checkinstall check git yasm - sudo apt-get install libopus-dev libvpx-dev pkg-config + - sudo apt-get install libev-dev libevent-dev # ------------ network_test requires that "localhost" resolves to ::1 ------------ - sudo bash -c "echo '::1 localhost ipv6-localhost ipv6-loopback' >> /etc/hosts" # ipv6 localhost entry diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 5ad332bddf..4cb96594ab 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -8,30 +8,38 @@ include(ModulePackage) find_package(Threads REQUIRED) -find_library(NCURSES_LIBRARIES ncurses ) -find_library(UTIL_LIBRARIES util ) -find_library(RT_LIBRARIES rt ) +find_library(NCURSES_LIBRARIES ncurses ) +find_library(UTIL_LIBRARIES util ) +find_library(RT_LIBRARIES rt ) # For toxcore. -pkg_use_module(LIBSODIUM libsodium ) +pkg_use_module(LIBSODIUM libsodium ) +pkg_use_module(LIBEV ev ) +if(NOT LIBEV_FOUND) + if(NOT WIN32) + pkg_use_module(LIBEVENT libevent_pthreads ) + else() + pkg_use_module(LIBEVENT libevent ) + endif() +endif() # For toxav. -pkg_use_module(OPUS opus ) -pkg_use_module(VPX vpx ) +pkg_use_module(OPUS opus ) +pkg_use_module(VPX vpx ) # For tox-bootstrapd. -pkg_use_module(LIBCONFIG libconfig ) +pkg_use_module(LIBCONFIG libconfig ) # For auto tests. -pkg_use_module(CHECK check ) +pkg_use_module(CHECK check ) # For tox-spectest. -pkg_use_module(MSGPACK msgpack ) +pkg_use_module(MSGPACK msgpack ) # For av_test. -pkg_use_module(OPENCV opencv ) -pkg_use_module(PORTAUDIO portaudio-2.0) -pkg_use_module(SNDFILE sndfile ) +pkg_use_module(OPENCV opencv ) +pkg_use_module(PORTAUDIO portaudio-2.0 ) +pkg_use_module(SNDFILE sndfile ) ############################################################################### # diff --git a/cmake/ModulePackage.cmake b/cmake/ModulePackage.cmake index 3a4eb9b90c..4d22ee4167 100644 --- a/cmake/ModulePackage.cmake +++ b/cmake/ModulePackage.cmake @@ -2,6 +2,8 @@ option(ENABLE_SHARED "Build shared (dynamic) libraries for all modules" ON) option(ENABLE_STATIC "Build static libraries for all modules" ON) option(COMPILE_AS_CXX "Compile all C code as C++ code" OFF) +include(FindPackageHandleStandardArgs) + if(NOT ENABLE_SHARED AND NOT ENABLE_STATIC) message(WARNING "Both static and shared libraries are disabled; " @@ -48,6 +50,31 @@ function(pkg_use_module mod pkg) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isystem ${dir}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${dir}" PARENT_SCOPE) endforeach() + else() + set(${mod}_DEFINITIONS ${${mod}_CFLAGS_OTHER}) + find_path(${mod}_INCLUDE_DIR NAMES ${ARGV1}.h + HINTS ${${mod}_INCLUDEDIR} ${${mod}_INCLUDE_DIRS} + PATH_SUFFIXES ${ARGV1}) + find_library(${mod}_LIBRARY NAMES ${ARGV1} lib${ARGV1} + HINTS ${${mod}_LIBDIR} ${${mod}_LIBRARY_DIRS}) + find_package_handle_standard_args(${mod} DEFAULT_MSG + ${mod}_LIBRARY ${mod}_INCLUDE_DIR) + + if(${mod}_FOUND) + mark_as_advanced(${mod}_INCLUDE_DIR ${mod}_LIBRARY) + set(${mod}_LIBRARIES ${${mod}_LIBRARY} PARENT_SCOPE) + set(${mod}_INCLUDE_DIRS ${${mod}_INCLUDE_DIR} PARENT_SCOPE) + set(${mod}_FOUND TRUE PARENT_SCOPE) + link_directories(${${mod}_LIBRARY_DIRS}) + include_directories(${${mod}_INCLUDE_DIRS}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${${mod}_CFLAGS_OTHER}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${mod}_CFLAGS_OTHER}" PARENT_SCOPE) + + foreach(dir ${${mod}_INCLUDE_DIRS}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isystem ${dir}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${dir}" PARENT_SCOPE) + endforeach() + endif() endif() endfunction() diff --git a/configure.ac b/configure.ac index 9eaa840fc7..18bc923497 100644 --- a/configure.ac +++ b/configure.ac @@ -31,6 +31,8 @@ LIBCHECK_FOUND="no" WANT_NACL="no" ADD_NACL_OBJECTS_TO_PKGCONFIG="yes" SET_SO_VERSION="yes" +WANT_LIBEV="no" +WANT_LIBEVENT="no" AC_ARG_ENABLE([soname-versions], [AC_HELP_STRING([--enable-soname-versions], [enable soname versions (must be disabled for android) (default: enabled)]) ], @@ -55,6 +57,32 @@ AC_ARG_ENABLE([nacl], ] ) +AC_ARG_ENABLE([libev], + [AC_HELP_STRING([--enable-libev], [use libev to watch for events (default: disabled)]) ], + [ + if test "x$enableval" = "xyes"; then + WANT_LIBEV="yes" + elif test "x$enableval" = "xno"; then + WANT_LIBEV="no" + fi + ] +) + +AC_ARG_ENABLE([libevent], + [AC_HELP_STRING([--enable-libevent], [use libevent to watch for events (default: disabled)]) ], + [ + if test "x$enableval" = "xyes"; then + WANT_LIBEVENT="yes" + elif test "x$enableval" = "xno"; then + WANT_LIBEVENT="no" + fi + ] +) + +if test "x$WANT_LIBEV" = "xyes" && test "x$WANT_LIBEVENT" = "xyes"; then + AC_MSG_ERROR([cannot enable both libev and libevent, please choose one]) +fi + AC_ARG_ENABLE([randombytes-stir], [AC_HELP_STRING([--enable-randombytes-stir], [use randombytes_stir() instead of sodium_init() for faster startup on android (default: disabled)]) ], [ @@ -211,6 +239,10 @@ LIBSODIUM_SEARCH_HEADERS= LIBSODIUM_SEARCH_LIBS= NACL_SEARCH_HEADERS= NACL_SEARCH_LIBS= +LIBEV_SEARCH_HEADERS= +LIBEV_SEARCH_LIBS= +LIBEVENT_SEARCH_HEADERS= +LIBEVENT_SEARCH_LIBS= AC_ARG_WITH(dependency-search, AC_HELP_STRING([--with-dependency-search=DIR], @@ -264,6 +296,42 @@ AC_ARG_WITH(libsodium-libs, ] ) +AC_ARG_WITH(libev-headers, + AC_HELP_STRING([--with-libev-headers=DIR], + [search for libev header files in DIR]), + [ + LIBEV_SEARCH_HEADERS="$withval" + AC_MSG_NOTICE([will search for libev header files in $withval]) + ] +) + +AC_ARG_WITH(libev-libs, + AC_HELP_STRING([--with-libev-libs=DIR], + [search for libev libraries in DIR]), + [ + LIBEV_SEARCH_LIBS="$withval" + AC_MSG_NOTICE([will search for libev libraries in $withval]) + ] +) + +AC_ARG_WITH(libevent-headers, + AC_HELP_STRING([--with-libevent-headers=DIR], + [search for libevent header files in DIR]), + [ + LIBEVENT_SEARCH_HEADERS="$withval" + AC_MSG_NOTICE([will search for libevent header files in $withval]) + ] +) + +AC_ARG_WITH(libevent-libs, + AC_HELP_STRING([--with-libevent-libs=DIR], + [search for libevent libraries in DIR]), + [ + LIBEVENT_SEARCH_LIBS="$withval" + AC_MSG_NOTICE([will search for libevent libraries in $withval]) + ] +) + if test "x$WANT_NACL" = "xyes"; then enable_shared=no enable_static=yes @@ -394,6 +462,123 @@ elif test "x$LIBSODIUM_FOUND" = "xno"; then AC_SUBST(LIBSODIUM_LDFLAGS) fi +if test "x$WANT_LIBEV" = "xyes"; then + if test -n "$PKG_CONFIG"; then + PKG_CHECK_MODULES([LIBEV], [libev], + [ + LIBEV_PKG_CONFIG="yes" + ], + [ + LIBEV_PKG_CONFIG="no" + ] + ) + else + LIBEV_PKG_CONFIG="no" + fi + + if test "x$LIBEV_PKG_CONFIG" = "xno"; then + LIBEV_LIBS= + LIBEV_LDFLAGS= + LDFLAGS_SAVE="$LDFLAGS" + + if test -n "$LIBEV_SEARCH_LIBS"; then + LDFLAGS="-L$LIBEV_SEARCH_LIBS $LDFLAGS" + AC_CHECK_LIB(ev, ev_run, + [ + LIBEV_LDFLAGS="-L$LIBEV_SEARCH_LIBS" + LIBEV_LIBS="-lev" + ], + [ + AC_MSG_ERROR([required library libev was not found in requested location $LIBEV_SEARCH_LIBS]) + ] + ) + else + AC_CHECK_LIB(ev, ev_run, + [ + LIBEV_LIBS="-lev" + ], + [ + AC_MSG_ERROR([required library libev was not found in requested location $LIBEV_SEARCH_LIBS]) + ] + ) + fi + + LDFLAGS="$LDFLAGS_SAVE" + fi + + AC_SUBST(LIBEV_LIBS) + AC_SUBST(LIBEV_LDFLAGS) + AC_DEFINE([HAVE_LIBEV], [1], [Define to 1 if you have the 'libev' library (-lev).]) +fi + +if test "x$WANT_LIBEVENT" = "xyes"; then + if test -n "$PKG_CONFIG"; then + if test "x$WIN32" = "xno"; then + PKG_CHECK_MODULES([LIBEVENT], [libevent_pthreads], + [ + LIBEVENT_PKG_CONFIG="yes" + ], + [ + LIBEVENT_PKG_CONFIG="no" + ], + ) + else + PKG_CHECK_MODULES([LIBEVENT], [libevent], + [ + LIBEVENT_PKG_CONFIG="yes" + ], + [ + LIBEVENT_PKG_CONFIG="no" + ], + ) + fi + else + LIBEVENT_PKG_CONFIG="no" + fi + + if test "x$LIBEVENT_PKG_CONFIG" = "xno"; then + LIBEVENT_LIBS= + LIBEVENT_LDFLAGS= + LDFLAGS_SAVE="$LDFLAGS" + + if test -n "$LIBEVENT_SEARCH_LIBS"; then + LDFLAGS="-L$LIBEVENT_SEARCH_LIBS $LDFLAGS" + AC_CHECK_LIB(event, event_base_loop, + [ + LIBEVENT_LDFLAGS="-L$LIBEVENT_SEARCH_LIBS" + if test "x$WIN32" = "xno"; then + LIBEVENT_LIBS="-levent -levent_pthreads" + else + LIBEVENT_LIBS="-levent" + fi + ], + [ + AC_MSG_ERROR([required library libevent was not found in requested location $LIBEVENT_SEARCH_LIBS]) + ] + ) + else + AC_CHECK_LIB(event, event_base_loop, + [ + if test "x$WIN32" = "xno"; then + LIBEVENT_LIBS="-levent -levent_pthreads" + else + LIBEVENT_LIBS="-levent" + fi + ], + [ + AC_MSG_ERROR([required library libevent was not found in requested location $LIBEVENT_SEARCH_LIBS]) + ] + ) + fi + + LDFLAGS="$LDFLAGS_SAVE" + fi + + AC_SUBST(LIBEVENT_LIBS) + AC_SUBST(LIBEVENT_LDFLAGS) + AC_DEFINE([HAVE_LIBEVENT], [1], [Define to 1 if you have the 'libevent' library (-levent).]) +fi + # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) @@ -453,6 +638,72 @@ elif test "x$LIBSODIUM_FOUND" = "xno"; then AC_SUBST(LIBSODIUM_CFLAGS) fi +if test "x$WANT_LIBEV" = "xyes"; then + if test "x$LIBEV_PKG_CONFIG" = "xno"; then + LIBEV_CFLAGS= + CFLAGS_SAVE="$CFLAGS" + CPPFLAGS_SAVE="$CPPFLAGS" + + if test -n "$LIBEV_SEARCH_HEADERS"; then + CFLAGS="-I$LIBEV_SEARCH_HEADERS $CFLAGS" + CPPFLAGS="-I$LIBEV_SEARCH_HEADERS $CPPFLAGS" + AC_CHECK_HEADER(ev.h, + [ + LIBEV_CFLAGS="-I$LIBEV_SEARCH_HEADERS" + ], + [ + AC_MSG_ERROR([header files for required library libev were not found in requested location $LIBEV_SEARCH_HEADERS]) + ] + ) + else + AC_CHECK_HEADER(ev.h, + [], + [ + AC_MSG_ERROR([header files for required library libev was not found on your system, please check http://libev.schmorp.de/]) + ] + ) + fi + + CFLAGS="$CFLAGS_SAVE" + CPPFLAGS="$CPPFLAGS_SAVE" + fi + + AC_SUBST(LIBEV_CFLAGS) +fi + +if test "x$WANT_LIBEVENT" = "xyes"; then + if test "x$LIBEVENT_PKG_CONFIG" = "xno"; then + LIBEVENT_CFLAGS= + CFLAGS_SAVE="$CFLAGS" + CPPFLAGS_SAVE="$CPPFLAGS" + + if test -n "$LIBEVENT_SEARCH_HEADERS"; then + CFLAGS="-I$LIBEVENT_SEARCH_HEADERS $CFLAGS" + CPPFLAGS="-I$LIBEVENT_SEARCH_HEADERS $CPPFLAGS" + AC_CHECK_HEADERS([event2/event.h event2/thread.h], + [ + LIBEVENT_CFLAGS="-I$LIBEVENT_SEARCH_HEADERS" + ], + [ + AC_MSG_ERROR([header files for required library libevent were not found in requested location $LIBEVENT_SEARCH_HEADERS]) + ] + ) + else + AC_CHECK_HEADERS([event2/event.h event2/thread.h], + [], + [ + AC_MSG_ERROR([header files for required library libevent was not found on your system, please check http://libevent.org/]) + ] + ) + fi + + CFLAGS="$CFLAGS_SAVE" + CPPFLAGS="$CPPFLAGS_SAVE" + fi + + AC_SUBST(LIBEVENT_CFLAGS) +fi + # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_TYPE_INT16_T diff --git a/libtoxcore.pc.in b/libtoxcore.pc.in index 90bb6c28b3..1ecb2272ba 100644 --- a/libtoxcore.pc.in +++ b/libtoxcore.pc.in @@ -7,5 +7,5 @@ Name: libtoxcore Description: Tox protocol library Requires: Version: @PACKAGE_VERSION@ -Libs: @NACL_OBJECTS_PKGCONFIG@ -L${libdir} -ltoxcore @NACL_LDFLAGS@ -ltoxdns -ltoxencryptsave @NACL_LIBS@ @LIBS@ @MATH_LDFLAGS@ @PTHREAD_LDFLAGS@ +Libs: @NACL_OBJECTS_PKGCONFIG@ -L${libdir} -ltoxcore @NACL_LDFLAGS@ -ltoxdns -ltoxencryptsave @NACL_LIBS@ @LIBEV_LIBS@ @LIBEVENT_LIBS@ @LIBS@ @MATH_LDFLAGS@ @PTHREAD_LDFLAGS@ Cflags: -I${includedir} diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index 2ca256501a..7501530cd5 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -55,12 +55,16 @@ libtoxcore_la_CFLAGS = -I$(top_srcdir) \ -I$(top_srcdir)/toxcore \ $(LIBSODIUM_CFLAGS) \ $(NACL_CFLAGS) \ + $(LIBEV_CFLAGS) \ + $(LIBEVENT_CFLAGS) \ $(PTHREAD_CFLAGS) libtoxcore_la_LDFLAGS = $(LT_LDFLAGS) \ $(EXTRA_LT_LDFLAGS) \ $(LIBSODIUM_LDFLAGS) \ $(NACL_LDFLAGS) \ + $(LIBEV_LDFLAGS) \ + $(LIBEVENT_LDFLAGS) \ $(MATH_LDFLAGS) \ $(RT_LIBS) \ $(WINSOCK2_LIBS) @@ -68,6 +72,8 @@ libtoxcore_la_LDFLAGS = $(LT_LDFLAGS) \ libtoxcore_la_LIBADD = $(LIBSODIUM_LIBS) \ $(NACL_OBJECTS) \ $(NAC_LIBS) \ + $(LIBEV_LIBS) \ + $(LIBEVENT_LIBS) \ $(PTHREAD_LIBS) if SET_SO_VERSION diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 5dda5b3039..24aa8a9b82 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -1930,6 +1930,28 @@ Messenger *new_messenger(Messenger_Options *options, unsigned int *error) return NULL; } +#ifdef HAVE_LIBEV + m->dispatcher = ev_loop_new(0); +#elif HAVE_LIBEVENT +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) + evthread_use_windows_threads(); +#else + evthread_use_pthreads(); +#endif /* WIN32 || _WIN32 || __WIN32__ */ + m->dispatcher = event_base_new(); +#else + m->loop_run = false; +#endif /* HAVE_LIBEV */ + +#if defined(HAVE_LIBEV) || defined(HAVE_LIBEVENT) + + if (!m->dispatcher) { + free(m); + return NULL; + } + +#endif /* HAVE_LIBEV || HAVE_LIBEVENT */ + Logger *log = NULL; if (options->log_callback) { @@ -2057,6 +2079,12 @@ void kill_messenger(Messenger *m) clear_receipts(m, i); } +#ifdef HAVE_LIBEV + ev_loop_destroy(m->dispatcher); +#elif HAVE_LIBEVENT + event_base_free(m->dispatcher); +#endif + logger_kill(m->log); free(m->friendlist); free(m); diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index 7275ffbf7c..300e7f2589 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -29,6 +29,13 @@ #include "friend_requests.h" #include "logger.h" +#ifdef HAVE_LIBEV +#include +#elif HAVE_LIBEVENT +#include +#include +#endif + #define MAX_NAME_LENGTH 128 /* TODO(irungentoo): this must depend on other variable. */ #define MAX_STATUSMESSAGE_LENGTH 1007 @@ -273,9 +280,16 @@ 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); +#ifdef HAVE_LIBEV + struct ev_loop *dispatcher; + ev_async stop_loop; +#elif HAVE_LIBEVENT + struct event_base *dispatcher; +#else + bool loop_run; +#endif + void (*loop_begin_cb)(struct Messenger *m, void *user_data); + void (*loop_end_cb)(struct Messenger *m, void *user_data); Messenger_Options options; }; diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 3e6de14a7c..8989f2442d 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -985,6 +985,17 @@ void kill_TCP_connection(TCP_Client_Connection *TCP_connection) wipe_priority_list(TCP_connection); kill_sock(TCP_connection->sock); + +#ifdef HAVE_LIBEV + ev_io_stop(TCP_connection->sock_listener.dispatcher, &TCP_connection->sock_listener.listener); +#elif HAVE_LIBEVENT + + if (TCP_connection->sock_listener) { + event_free(TCP_connection->sock_listener); + } + +#endif + crypto_memzero(TCP_connection, sizeof(TCP_Client_Connection)); free(TCP_connection); } diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h index 212543147c..8a698869b3 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -27,6 +27,12 @@ #include "TCP_server.h" #include "crypto_core.h" +#ifdef HAVE_LIBEV +#include +#elif HAVE_LIBEVENT +#include +#endif + #define TCP_CONNECTION_TIMEOUT 10 typedef enum { @@ -52,7 +58,15 @@ enum { }; typedef struct { uint8_t status; - Socket sock; + Socket sock; +#ifdef HAVE_LIBEV + struct { + ev_io listener; + struct ev_loop *dispatcher; + } sock_listener; +#elif HAVE_LIBEVENT + struct event *sock_listener; +#endif uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; /* our public key */ uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; /* public key of the server */ IP_Port ip_port; /* The ip and port of the server */ diff --git a/toxcore/TCP_connection.c b/toxcore/TCP_connection.c index 18c1e76e2d..9b51443daa 100644 --- a/toxcore/TCP_connection.c +++ b/toxcore/TCP_connection.c @@ -67,6 +67,37 @@ const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c) } +/** + * Return number of elements of TCP connection array. + * + * @param tcp_c struct containing TCP_con array + * + * @return number of elements of TCP connection array + */ +uint32_t tcp_connections_length(const TCP_Connections *tcp_c) +{ + return tcp_c->tcp_connections_length; +} + + +/** + * Return TCP connection stored at "idx" position. + * + * @param tcp_c struct containing TCP_con array + * @param idx index of TCP connection to return (values from 0 to tcp_connections_length() - 1) + * + * @return TCP connection stored at "idx" position, or NULL if errors occurred + */ +const TCP_con *tcp_connections_connection_at(const TCP_Connections *tcp_c, uint32_t idx) +{ + if (idx >= tcp_c->tcp_connections_length) { + return NULL; + } + + return &tcp_c->tcp_connections[idx]; +} + + /* Set the size of the array to num. * * return -1 if realloc fails. diff --git a/toxcore/TCP_connection.h b/toxcore/TCP_connection.h index f023b1ab51..276303e7a2 100644 --- a/toxcore/TCP_connection.h +++ b/toxcore/TCP_connection.h @@ -82,6 +82,10 @@ typedef struct TCP_Connections TCP_Connections; const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c); +uint32_t tcp_connections_length(const TCP_Connections *tcp_c); + +const TCP_con *tcp_connections_connection_at(const TCP_Connections *tcp_c, uint32_t idx); + /* Send a packet to the TCP connection. * * return -1 on failure. diff --git a/toxcore/network.c b/toxcore/network.c index 215ab8e023..62b36ae625 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -797,6 +797,16 @@ void kill_networking(Networking_Core *net) kill_sock(net->sock); } +#ifdef HAVE_LIBEV + ev_io_stop(net->sock_listener.dispatcher, &net->sock_listener.listener); +#elif HAVE_LIBEVENT + + if (net->sock_listener) { + event_free(net->sock_listener); + } + +#endif + free(net); } diff --git a/toxcore/network.h b/toxcore/network.h index 28795602f8..4f27a81d45 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -33,6 +33,11 @@ #include "ccompat.h" #include "logger.h" +#ifdef HAVE_LIBEV +#include +#elif HAVE_LIBEVENT +#include +#endif #include #include #include @@ -316,6 +321,14 @@ typedef struct { uint16_t port; /* Our UDP socket. */ Socket sock; +#ifdef HAVE_LIBEV + struct { + ev_io listener; + struct ev_loop *dispatcher; + } sock_listener; +#elif HAVE_LIBEVENT + struct event *sock_listener; +#endif } Networking_Core; /* Run this before creating sockets. diff --git a/toxcore/tox.api.h b/toxcore/tox.api.h index 0bcf40e13d..cacfa26cb9 100644 --- a/toxcore/tox.api.h +++ b/toxcore/tox.api.h @@ -838,19 +838,45 @@ const uint32_t iteration_interval(); */ void iterate(any user_data); + /** - * Run $iterate() any time a packet arrives, only returns after ${loop.stop}(). + * Error codes for $loop(). */ -uint32_t loop(any user_data); +error for loop { + /** + * Invalid arguments passed. + */ + NULL, + /** + * Failed running events dispatcher. + */ + BREAK, + /** + * Failed running select(). + */ + SELECT, + /** + * Failed getting sockets file descriptors. + */ + GET_FDS, +} + + +/** + * Run $iterate() any time a packet arrives, returns after ${loop.stop}() or ${kill}(). + */ +bool loop(any user_data) with error for loop; + namespace loop { + /** * Tell $loop() to return. */ void stop(); /** - * Callback ran when $loop() calls into $iterate(), the client can lock a mutex here. + * This callback is invoked when $loop() calls into $iterate(), the client can lock a mutex here. */ event begin const { /** @@ -860,7 +886,7 @@ namespace loop { } /** - * Callback ran when $loop() is finished with $iterate(), the client can unlock the mutex here. + * This callback is invoked when $loop() is finished with $iterate(), the client can unlock the mutex here. */ event end const { /** diff --git a/toxcore/tox.c b/toxcore/tox.c index 5e2f14d228..0234fbf3a8 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -34,9 +34,19 @@ typedef struct Messenger Tox; #include "Messenger.h" #include "group.h" #include "logger.h" +#include "net_crypto.h" #include "../toxencryptsave/defines.h" +#include +#if !defined(HAVE_LIBEV) && !defined(HAVE_LIBEVENT) +#if defined (WIN32) || defined(_WIN32) || defined(__WIN32__) +#include +#else +#include +#endif /* WIN32 || _WIN32 || __WIN32__ */ +#endif /* !HAVE_LIBEV && !HAVE_LIBEVENT */ + #define SET_ERROR_PARAMETER(param, x) {if(param) {*param = x;}} #if TOX_HASH_LENGTH != CRYPTO_SHA256_SIZE @@ -71,6 +81,12 @@ typedef struct Messenger Tox; #error TOX_MAX_STATUS_MESSAGE_LENGTH is assumed to be equal to MAX_STATUSMESSAGE_LENGTH #endif +#if defined(HAVE_LIBEV) || defined(HAVE_LIBEVENT) +typedef struct { + Tox *tox; + void *user_data; +} Event_Arg; +#endif bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch) { @@ -203,6 +219,8 @@ void tox_kill(Tox *tox) } Messenger *m = tox; + + tox_loop_stop(tox); kill_groupchats((Group_Chats *)m->conferences_object); kill_messenger(m); } @@ -342,68 +360,261 @@ void tox_iterate(Tox *tox, void *user_data) do_groupchats((Group_Chats *)m->conferences_object, user_data); } -uint32_t tox_fd_count(Tox *tox) +void tox_callback_loop_begin(Tox *tox, tox_loop_begin_cb *callback) { + if (tox == NULL) { + return; + } + Messenger *m = tox; - return 1 + m->net_crypto->tcp_c->tcp_connections_length; + m->loop_begin_cb = callback; } -/** - * 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) +void tox_callback_loop_end(Tox *tox, tox_loop_end_cb *callback) { + if (tox == NULL) { + return; + } + Messenger *m = tox; - int count = 0; + m->loop_end_cb = callback; +} - if (max_sockets >= 1) { - sockets[count] = m->net->sock; - max_sockets--; - count++; +#ifdef HAVE_LIBEV +static void tox_stop_loop_cb(struct ev_loop *dispatcher, ev_async *listener, int events) +{ + if (dispatcher == NULL || listener == NULL) { + return; } - TCP_Connections *conns = m->net_crypto->tcp_c; - int x; + Event_Arg *tmp = (Event_Arg *) listener->data; + Messenger *m = tmp->tox; - for (x = 0; x < conns->tcp_connections_length; x++) { - if (max_sockets == 0) { - break; - } + if (ev_is_active(&m->net->sock_listener.listener) || ev_is_pending(&m->net->sock_listener.listener)) { + ev_io_stop(dispatcher, &m->net->sock_listener.listener); + } - TCP_con *conn = &conns->tcp_connections[x]; - sockets[count] = conn->connection->sock; - count++; - max_sockets--; + uint32_t len = tcp_connections_length(m->net_crypto->tcp_c); + + for (uint32_t i = 0; i < len; i++) { + const TCP_con *conn = tcp_connections_connection_at(m->net_crypto->tcp_c, i); + + if (ev_is_active(&conn->connection->sock_listener.listener) + || ev_is_pending(&conn->connection->sock_listener.listener)) { + ev_io_stop(dispatcher, &conn->connection->sock_listener.listener); + } } - return count; + ev_async_stop(dispatcher, listener); + + ev_break(dispatcher, EVBREAK_ALL); } -void tox_callback_loop_begin(Tox *tox, tox_loop_begin_cb *callback) +static void tox_do_iterate(struct ev_loop *dispatcher, ev_io *sock_listener, int events) { - Messenger *m = tox; - m->loop_begin_cb = callback; + if (dispatcher == NULL || sock_listener == NULL) { + return; + } + + Event_Arg *tmp = (Event_Arg *) sock_listener->data; + Messenger *m = tmp->tox; + + if (m->loop_begin_cb) { + m->loop_begin_cb(m, tmp->user_data); + } + + tox_iterate(tmp->tox, tmp->user_data); + + if (!ev_is_active(&m->net->sock_listener.listener) && !ev_is_pending(&m->net->sock_listener.listener)) { + m->net->sock_listener.dispatcher = dispatcher; + ev_io_init(&m->net->sock_listener.listener, tox_do_iterate, m->net->sock, EV_READ); + m->net->sock_listener.listener.data = sock_listener->data; + ev_io_start(dispatcher, &m->net->sock_listener.listener); + } + + uint32_t len = tcp_connections_length(m->net_crypto->tcp_c); + + for (uint32_t i = 0; i < len; i++) { + const TCP_con *conn = tcp_connections_connection_at(m->net_crypto->tcp_c, i); + + if (!ev_is_active(&conn->connection->sock_listener.listener) + && !ev_is_pending(&conn->connection->sock_listener.listener)) { + conn->connection->sock_listener.dispatcher = dispatcher; + ev_io_init(&conn->connection->sock_listener.listener, tox_do_iterate, conn->connection->sock, EV_READ); + conn->connection->sock_listener.listener.data = sock_listener->data; + ev_io_start(m->dispatcher, &conn->connection->sock_listener.listener); + } + } + + if (m->loop_end_cb) { + m->loop_end_cb(m, tmp->user_data); + } } +#elif HAVE_LIBEVENT +static void tox_do_iterate(evutil_socket_t fd, short events, void *arg) +{ + if (arg == NULL) { + return; + } -void tox_callback_loop_end(Tox *tox, tox_loop_end_cb *callback) + Event_Arg *tmp = (Event_Arg *) arg; + Messenger *m = tmp->tox; + struct timeval timeout; + + if (m->loop_begin_cb) { + m->loop_begin_cb(m, tmp->user_data); + } + + tox_iterate(tmp->tox, tmp->user_data); + + timeout.tv_sec = 0; + + // TODO(cleverca22): use a longer timeout. + timeout.tv_usec = tox_iteration_interval(tmp->tox) * 1000 * 2; + + if (!m->net->sock_listener) { + m->net->sock_listener = event_new(m->dispatcher, m->net->sock, EV_READ | EV_PERSIST, tox_do_iterate, arg); + } + + event_add(m->net->sock_listener, &timeout); + + uint32_t len = tcp_connections_length(m->net_crypto->tcp_c); + + for (uint32_t i = 0; i < len; i++) { + const TCP_con *conn = tcp_connections_connection_at(m->net_crypto->tcp_c, i); + + if (!conn->connection->sock_listener) { + conn->connection->sock_listener = event_new(m->dispatcher, conn->connection->sock, EV_READ | EV_PERSIST, tox_do_iterate, + arg); + } + + event_add(conn->connection->sock_listener, NULL); + } + + if (m->loop_end_cb) { + m->loop_end_cb(m, tmp->user_data); + } +} +#else +/** + * Gathers a list of every network file descriptor, + * where an activity is expected on. + * + * @param sockets a pointer to an array (the pointed array can be NULL). + * @param sockets_num the number of current known sockets (will be updated by the funciton). + * + * @return false if errors occurred, true otherwise. + */ +static bool tox_fds(Messenger *m, Socket **sockets, uint32_t *sockets_num) { - Messenger *m = tox; - m->loop_end_cb = callback; + if (m == NULL || sockets == NULL || sockets_num == NULL) { + return false; + } + + uint32_t len = tcp_connections_length(m->net_crypto->tcp_c); + uint32_t fdcount = 1 + len; + + if (fdcount != *sockets_num || *sockets == NULL) { + Socket *tmp_sockets = (Socket *) realloc(*sockets, fdcount * sizeof(Socket)); + + if (tmp_sockets == NULL) { + return false; + } + + *sockets = tmp_sockets; + *sockets_num = fdcount; + } + + (*sockets)[0] = m->net->sock; + + uint32_t i = 0; + + while (i < fdcount - 1 && i < len) { + const TCP_con *conn = tcp_connections_connection_at(m->net_crypto->tcp_c, i); + i++; + + if (conn != NULL) { + (*sockets)[i] = conn->connection->sock; + } else { + (*sockets)[i] = 0; + } + } + + return true; } +#endif -uint32_t tox_loop(Tox *tox, void *user_data) +bool tox_loop(Tox *tox, void *user_data, TOX_ERR_LOOP *error) { - struct timeval timeout; - int maxfd; - uint32_t i, list_size = 0; - uint32_t *fdlist = NULL; + if (tox == NULL) { + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_NULL); + + return false; + } + Messenger *m = tox; - m->loop_run = true; - fd_set readable; +#ifdef HAVE_LIBEV + bool ret = true; + Event_Arg *tmp = (Event_Arg *) calloc(1, sizeof(Event_Arg)); + + tmp->tox = tox; + tmp->user_data = user_data; + + ev_async_init(&m->stop_loop, tox_stop_loop_cb); + m->stop_loop.data = tmp; + ev_async_start(m->dispatcher, &m->stop_loop); + + ev_io stub_listener; + ev_init(&stub_listener, tox_do_iterate); + stub_listener.data = tmp; + tox_do_iterate(m->dispatcher, &stub_listener, 0); + + // TODO(Ansa89): travis states that "ev_run" returns "void", + // but "man 3 ev" states it returns "bool" +#if 0 + ret = !ev_run(m->dispatcher, 0); + + if (ret) { + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_OK); + } else { + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_BREAK); + } + +#endif + + ev_run(m->dispatcher, 0); + + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_OK); + + free(tmp); +#elif HAVE_LIBEVENT + Event_Arg *tmp = (Event_Arg *) calloc(1, sizeof(Event_Arg)); + + tmp->tox = tox; + tmp->user_data = user_data; + + tox_do_iterate(0, 0, tmp); + bool ret = event_base_dispatch(m->dispatcher) < 0 ? false : true; + + if (ret) { + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_OK); + } else { + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_BREAK); + } + + free(tmp); +#else + bool ret = true; + uint32_t fdcount = 0; + Socket *fdlist = NULL; + + m->loop_run = true; while (m->loop_run) { + Socket maxfd; + fd_set readable; + if (m->loop_begin_cb) { m->loop_begin_cb(tox, user_data); } @@ -413,16 +624,27 @@ uint32_t tox_loop(Tox *tox, void *user_data) maxfd = 0; FD_ZERO(&readable); - uint32_t fdcount = tox_fd_count(tox); + // TODO(cleverca22): is it a good idea to reuse previous fdlist when + // fdcount!=0 && tox_fds()==false? + if (fdcount == 0 && !tox_fds(m, &fdlist, &fdcount)) { + // We must stop because maxfd won't be set. + // TODO(cleverca22): should we call loop_end_cb() on error? + if (m->loop_end_cb) { + m->loop_end_cb(tox, user_data); + } + + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_GET_FDS); + + free(fdlist); - if (fdcount > list_size) { - fdlist = realloc(fdlist, fdcount * sizeof(uint32_t)); - list_size = fdcount; + return false; } - fdcount = tox_fds(tox, fdlist, list_size); + for (uint32_t i = 0; i < fdcount; i++) { + if (fdlist[i] == 0) { + continue; + } - for (i = 0; i < fdcount; i++) { FD_SET(fdlist[i], &readable); if (fdlist[i] > maxfd) { @@ -430,27 +652,49 @@ uint32_t tox_loop(Tox *tox, void *user_data) } } + struct timeval timeout; + timeout.tv_sec = 0; - timeout.tv_usec = tox_iteration_interval(tox) * 1000 * 2; // TODO, use a longer timeout (cleverca22) + + // TODO(cleverca22): use a longer timeout. + timeout.tv_usec = tox_iteration_interval(tox) * 1000 * 2; if (m->loop_end_cb) { m->loop_end_cb(tox, user_data); } - int ret = select(maxfd, &readable, NULL, NULL, &timeout); + if (select(maxfd, &readable, NULL, NULL, &timeout) < 0 && errno != EBADF) { + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_SELECT); + + free(fdlist); - if (ret < 0) { - return ret; + return false; } } - return 0; + SET_ERROR_PARAMETER(error, TOX_ERR_LOOP_OK); + + free(fdlist); +#endif + + return ret; } void tox_loop_stop(Tox *tox) { + if (tox == NULL) { + return; + } + Messenger *m = tox; + +#ifdef HAVE_LIBEV + ev_async_send(m->dispatcher, &m->stop_loop); +#elif HAVE_LIBEVENT + event_base_loopbreak(m->dispatcher); +#else m->loop_run = false; +#endif } void tox_self_get_address(const Tox *tox, uint8_t *address) diff --git a/toxcore/tox.h b/toxcore/tox.h index 0be019cdc7..ab4ae7b100 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -991,9 +991,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(). + * Error codes for tox_loop(). */ -uint32_t tox_loop(Tox *tox, void *user_data); +typedef enum TOX_ERR_LOOP { + + /** + * The function returned successfully. + */ + TOX_ERR_LOOP_OK, + + /** + * Invalid arguments passed. + */ + TOX_ERR_LOOP_NULL, + + /** + * Failed running events dispatcher. + */ + TOX_ERR_LOOP_BREAK, + + /** + * Failed running select(). + */ + TOX_ERR_LOOP_SELECT, + + /** + * Failed getting sockets file descriptors. + */ + TOX_ERR_LOOP_GET_FDS, + +} TOX_ERR_LOOP; + + +/** + * Run tox_iterate() any time a packet arrives, returns after tox_loop_stop() or tox_kill(). + */ +bool tox_loop(Tox *tox, void *user_data, TOX_ERR_LOOP *error); /** * Tell tox_loop() to return. @@ -1009,7 +1042,7 @@ 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. + * This callback is invoked 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); @@ -1022,7 +1055,7 @@ 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. + * This callback is invoked 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);