Skip to content

Commit

Permalink
feat: Add async event handling (callbacks) code.
Browse files Browse the repository at this point in the history
Instead of synchronously handling events as they happen in
`tox_iterate`, this first collects all events in a structure and then
lets the client process them. This allows clients to process events in
parallel, since the data structure returned is mostly immutable. Message
buffers are returned non-const so clients can steal them (but only until
they call `tox_events_free`, which frees all internal pointers,
including byte buffers).

This also makes toxcore compatible with languages that don't (easily)
support callbacks from C into the non-C language.
  • Loading branch information
iphydf committed Feb 2, 2022
1 parent 71965a0 commit 212d12c
Show file tree
Hide file tree
Showing 17 changed files with 518 additions and 13 deletions.
18 changes: 16 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ set(toxcore_PKGCONFIG_REQUIRES)
# LAYER 1: Crypto core
# --------------------
set(toxcore_SOURCES ${toxcore_SOURCES}
toxcore/ccompat.c
toxcore/ccompat.h
toxcore/crypto_core.c
toxcore/crypto_core.h)
Expand Down Expand Up @@ -240,10 +241,22 @@ set(toxcore_SOURCES ${toxcore_SOURCES}
set(toxcore_SOURCES ${toxcore_SOURCES}
toxcore/tox_api.c
toxcore/tox.c
toxcore/tox_private.h
toxcore/tox.h)
toxcore/tox.h
toxcore/tox_events.c
toxcore/tox_events.h
toxcore/tox_private.h)
set(toxcore_API_HEADERS ${toxcore_API_HEADERS} ${toxcore_SOURCE_DIR}/toxcore/tox.h^tox)

# LAYER 9: New async events API
# -------------------
set(toxcore_SOURCES ${toxcore_SOURCES}
toxcore/events/friend_message.c
toxcore/events/internal.c
toxcore/events/internal.h
toxcore/tox_events.c
toxcore/tox_events.h)
set(toxcore_API_HEADERS ${toxcore_API_HEADERS} ${toxcore_SOURCE_DIR}/toxcore/tox_events.h^tox)

################################################################################
#
# :: Audio/Video Library
Expand Down Expand Up @@ -441,6 +454,7 @@ auto_test(save_load)
auto_test(send_message)
auto_test(set_name)
auto_test(set_status_message)
auto_test(tox_events)
auto_test(tox_many)
auto_test(tox_many_tcp)
auto_test(tox_one)
Expand Down
1 change: 1 addition & 0 deletions auto_tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ flaky_tests = {
"//c-toxcore/toxcore:onion",
"//c-toxcore/toxcore:onion_announce",
"//c-toxcore/toxcore:onion_client",
"//c-toxcore/toxcore:tox_events",
"//c-toxcore/toxencryptsave",
"@libsodium",
"@libvpx",
Expand Down
105 changes: 105 additions & 0 deletions auto_tests/tox_events_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* Auto Tests: Many clients.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "../testing/misc_tools.h"
#include "../toxcore/tox.h"
#include "../toxcore/tox_events.h"
#include "check_compat.h"

static bool await_message(Tox **toxes)
{
for (uint32_t i = 0; i < 100; ++i) {
// Ignore events on tox 1.
tox_events_free(tox_events_iterate(toxes[0]));
// Check if tox 2 got the message from tox 1.
Tox_Events *events = tox_events_iterate(toxes[1]);

if (events != nullptr) {
ck_assert(tox_events_get_friend_messages_size(events) == 1);
const Tox_Event_Friend_Message *msg_event = tox_events_get_friend_message(events, 0);
ck_assert(tox_event_friend_message_get_message_length(msg_event) == sizeof("hello"));
const uint8_t *msg = tox_event_friend_message_get_message(msg_event);
ck_assert_msg(memcmp(msg, "hello", sizeof("hello")) == 0,
"message was not expected 'hello' but '%s'", (const char *)msg);
tox_events_free(events);
return true;
}

c_sleep(tox_iteration_interval(toxes[0]));
}

return false;
}

static void test_tox_events(void)
{
uint8_t message[sizeof("hello")];
memcpy(message, "hello", sizeof(message));

Tox *toxes[2];
uint32_t index[2];

for (uint32_t i = 0; i < 2; ++i) {
index[i] = i + 1;
toxes[i] = tox_new_log(nullptr, nullptr, &index[i]);
tox_events_init(toxes[i]);
ck_assert_msg(toxes[i] != nullptr, "failed to create tox instances %u", i);
}

uint8_t pk[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(toxes[0], pk);
tox_bootstrap(toxes[1], "localhost", tox_self_get_udp_port(toxes[0], nullptr), pk, nullptr);

tox_self_get_public_key(toxes[0], pk);
tox_friend_add_norequest(toxes[1], pk, nullptr);

tox_self_get_public_key(toxes[1], pk);
tox_friend_add_norequest(toxes[0], pk, nullptr);

printf("bootstrapping and connecting 2 toxes\n");

while (tox_self_get_connection_status(toxes[0]) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(toxes[1]) == TOX_CONNECTION_NONE) {
// Ignore connection events for now.
tox_events_free(tox_events_iterate(toxes[0]));
tox_events_free(tox_events_iterate(toxes[1]));

c_sleep(tox_iteration_interval(toxes[0]));
}

printf("toxes online, waiting for friend connection\n");

while (tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_NONE ||
tox_friend_get_connection_status(toxes[1], 0, nullptr) == TOX_CONNECTION_NONE) {
// Ignore connection events for now.
tox_events_free(tox_events_iterate(toxes[0]));
tox_events_free(tox_events_iterate(toxes[1]));

c_sleep(tox_iteration_interval(toxes[0]));
}

printf("friends are connected via %s, now sending message\n",
tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_TCP ? "TCP" : "UDP");

Tox_Err_Friend_Send_Message err;
tox_friend_send_message(toxes[0], 0, TOX_MESSAGE_TYPE_NORMAL, message, sizeof(message), &err);
ck_assert(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK);

ck_assert(await_message(toxes));

for (uint32_t i = 0; i < 2; ++i) {
tox_kill(toxes[i]);
}
}

int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_tox_events();
return 0;
}
3 changes: 3 additions & 0 deletions other/docker/autotools/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

docker build -t toxchat/toxcore-autotools -f other/docker/autotools/Dockerfile .
1 change: 1 addition & 0 deletions other/docker/circleci/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ RUN apt-get update && \
libopus-dev \
libsodium-dev \
libvpx-dev \
llvm-dev \
ninja-build \
pkg-config \
&& apt-get clean \
Expand Down
6 changes: 6 additions & 0 deletions other/docker/circleci/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh

SANITIZER="${1:-asan}"

docker build -t toxchat/toxcore-circleci other/docker/circleci
docker run --rm -it -v "$PWD:/c-toxcore" toxchat/toxcore-circleci "$SANITIZER"
15 changes: 15 additions & 0 deletions toxcore/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exports_files(

cc_library(
name = "ccompat",
srcs = ["ccompat.c"],
hdrs = ["ccompat.h"],
visibility = ["//c-toxcore:__subpackages__"],
)
Expand Down Expand Up @@ -459,6 +460,20 @@ cc_library(
],
)

cc_library(
name = "tox_events",
srcs = ["tox_events.c"] + glob([
"events/*.c",
"events/*.h",
]),
hdrs = ["tox_events.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":ccompat",
":toxcore",
],
)

sh_library(
name = "cimple_files",
srcs = glob([
Expand Down
7 changes: 6 additions & 1 deletion toxcore/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ libtoxcore_la_include_HEADERS = \
libtoxcore_la_includedir = $(includedir)/tox

libtoxcore_la_SOURCES = ../toxcore/ccompat.h \
../toxcore/events/internal.h \
../toxcore/events/internal.c \
../toxcore/events/friend_message.c \
../toxcore/DHT.h \
../toxcore/DHT.c \
../toxcore/mono_time.h \
Expand All @@ -31,8 +34,10 @@ libtoxcore_la_SOURCES = ../toxcore/ccompat.h \
../toxcore/state.h \
../toxcore/state.c \
../toxcore/tox.h \
../toxcore/tox_private.h \
../toxcore/tox.c \
../toxcore/tox_events.h \
../toxcore/tox_events.c \
../toxcore/tox_private.h \
../toxcore/tox_api.c \
../toxcore/util.h \
../toxcore/util.c \
Expand Down
15 changes: 15 additions & 0 deletions toxcore/ccompat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "ccompat.h"

#include <stdlib.h>
#include <string.h>

void *salloc(uint32_t size, const void *default_value)
{
void *ptr = malloc(size);

if (ptr != nullptr) {
memcpy(ptr, default_value, size);
}

return ptr;
}
5 changes: 5 additions & 0 deletions toxcore/ccompat.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>

bool unused_for_tokstyle(void);

Expand Down Expand Up @@ -59,8 +60,12 @@ bool unused_for_tokstyle(void);
#ifndef static_assert
#define static_assert(cond, msg) extern const int unused_for_static_assert
#endif
#define new(T) (T *)salloc(sizeof(T), &(T){0})
#define delete(obj) free(obj)
#endif

void *salloc(uint32_t size, const void *default_value);

#ifdef __GNUC__
#define GNU_PRINTF(f, a) __attribute__((__format__(__printf__, f, a)))
#else
Expand Down
Loading

0 comments on commit 212d12c

Please sign in to comment.