Skip to content

Commit de4af4c

Browse files
committed
feat: Add async event handling (callbacks) code.
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. This also makes toxcore compatible with languages that don't (easily) support callbacks from C into the non-C language. If we remove the callbacks, this allows us to add fields to the events without breaking the API.
1 parent cde796f commit de4af4c

42 files changed

Lines changed: 5068 additions & 24 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ jobs:
7676
testing/misc_tools.c
7777
toxav/*.c
7878
toxcore/*.c
79+
toxcore/*/*.c
7980
toxencryptsave/*.c
8081
-lpthread
8182
$(pkg-config --cflags --libs libsodium opus vpx)

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ jobs:
109109
testing/misc_tools.c
110110
toxav/*.c
111111
toxcore/*.c
112+
toxcore/*/*.c
112113
toxencryptsave/*.c
113114
$(pkg-config --cflags --libs libsodium opus vpx)
114115
- name: Run the test
@@ -146,6 +147,7 @@ jobs:
146147
testing/misc_tools.c
147148
toxav/*.c
148149
toxcore/*.c
150+
toxcore/*/*.c
149151
toxencryptsave/*.c
150152
-D__COMPCERT__ -DDISABLE_VLA
151153
-lpthread $(pkg-config --cflags --libs libsodium opus vpx)

CMakeLists.txt

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ set(toxcore_PKGCONFIG_REQUIRES)
161161
# LAYER 1: Crypto core
162162
# --------------------
163163
set(toxcore_SOURCES ${toxcore_SOURCES}
164+
toxcore/ccompat.c
164165
toxcore/ccompat.h
165166
toxcore/crypto_core.c
166167
toxcore/crypto_core.h)
@@ -240,10 +241,47 @@ set(toxcore_SOURCES ${toxcore_SOURCES}
240241
set(toxcore_SOURCES ${toxcore_SOURCES}
241242
toxcore/tox_api.c
242243
toxcore/tox.c
243-
toxcore/tox_private.h
244-
toxcore/tox.h)
244+
toxcore/tox.h
245+
toxcore/tox_private.h)
245246
set(toxcore_API_HEADERS ${toxcore_API_HEADERS} ${toxcore_SOURCE_DIR}/toxcore/tox.h^tox)
246247

248+
# LAYER 9: New async events API
249+
# -------------------
250+
set(toxcore_SOURCES ${toxcore_SOURCES}
251+
toxcore/events/conference_connected.c
252+
toxcore/events/conference_invite.c
253+
toxcore/events/conference_message.c
254+
toxcore/events/conference_peer_list_changed.c
255+
toxcore/events/conference_peer_name.c
256+
toxcore/events/conference_title.c
257+
toxcore/events/file_chunk_request.c
258+
toxcore/events/file_recv.c
259+
toxcore/events/file_recv_chunk.c
260+
toxcore/events/file_recv_control.c
261+
toxcore/events/friend_connection_status.c
262+
toxcore/events/friend_lossless_packet.c
263+
toxcore/events/friend_lossy_packet.c
264+
toxcore/events/friend_message.c
265+
toxcore/events/friend_name.c
266+
toxcore/events/friend_read_receipt.c
267+
toxcore/events/friend_request.c
268+
toxcore/events/friend_status.c
269+
toxcore/events/friend_status_message.c
270+
toxcore/events/friend_typing.c
271+
toxcore/events/events_alloc.c
272+
toxcore/events/events_alloc.h
273+
toxcore/events/self_connection_status.c
274+
toxcore/tox_events.c
275+
toxcore/tox_events.h)
276+
set(toxcore_API_HEADERS ${toxcore_API_HEADERS} ${toxcore_SOURCE_DIR}/toxcore/tox_events.h^tox)
277+
278+
# LAYER 10: Dispatch recorded events to callbacks.
279+
# -------------------
280+
set(toxcore_SOURCES ${toxcore_SOURCES}
281+
toxcore/tox_dispatch.c
282+
toxcore/tox_dispatch.h)
283+
set(toxcore_API_HEADERS ${toxcore_API_HEADERS} ${toxcore_SOURCE_DIR}/toxcore/tox_dispatch.h^tox)
284+
247285
################################################################################
248286
#
249287
# :: Audio/Video Library
@@ -441,6 +479,8 @@ auto_test(save_load)
441479
auto_test(send_message)
442480
auto_test(set_name)
443481
auto_test(set_status_message)
482+
auto_test(tox_dispatch)
483+
auto_test(tox_events)
444484
auto_test(tox_many)
445485
auto_test(tox_many_tcp)
446486
auto_test(tox_one)
@@ -558,4 +598,3 @@ if (BUILD_FUZZ_TESTS)
558598
add_executable(bootstrap_fuzzer testing/fuzzing/bootstrap_harness.cc)
559599
target_link_libraries(bootstrap_fuzzer toxcore_static fuzz_adapter -fsanitize=fuzzer)
560600
endif()
561-

auto_tests/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ flaky_tests = {
6161
"//c-toxcore/toxcore:onion",
6262
"//c-toxcore/toxcore:onion_announce",
6363
"//c-toxcore/toxcore:onion_client",
64+
"//c-toxcore/toxcore:tox_dispatch",
65+
"//c-toxcore/toxcore:tox_events",
6466
"//c-toxcore/toxencryptsave",
6567
"@libsodium",
6668
"@libvpx",

auto_tests/tox_dispatch_test.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/* Auto Tests: Many clients.
2+
*/
3+
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <time.h>
8+
9+
#include "../testing/misc_tools.h"
10+
#include "../toxcore/tox.h"
11+
#include "../toxcore/tox_dispatch.h"
12+
#include "../toxcore/tox_events.h"
13+
#include "check_compat.h"
14+
15+
static void handle_events_friend_message(Tox *tox, const Tox_Event_Friend_Message *event, void *user_data)
16+
{
17+
bool *success = (bool *)user_data;
18+
19+
ck_assert(tox_event_friend_message_get_message_length(event) == sizeof("hello"));
20+
const uint8_t *msg = tox_event_friend_message_get_message(event);
21+
ck_assert_msg(memcmp(msg, "hello", sizeof("hello")) == 0,
22+
"message was not expected 'hello' but '%s'", (const char *)msg);
23+
24+
*success = true;
25+
}
26+
27+
static bool await_message(Tox **toxes, const Tox_Dispatch *dispatch)
28+
{
29+
for (uint32_t i = 0; i < 100; ++i) {
30+
// Ignore events on tox 1.
31+
tox_events_free(tox_events_iterate(toxes[0], nullptr));
32+
// Check if tox 2 got the message from tox 1.
33+
Tox_Events *events = tox_events_iterate(toxes[1], nullptr);
34+
35+
bool success = false;
36+
tox_dispatch_invoke(dispatch, events, toxes[1], &success);
37+
tox_events_free(events);
38+
39+
if (success) {
40+
return true;
41+
}
42+
43+
c_sleep(tox_iteration_interval(toxes[0]));
44+
}
45+
46+
return false;
47+
}
48+
49+
static void test_tox_events(void)
50+
{
51+
uint8_t message[sizeof("hello")];
52+
memcpy(message, "hello", sizeof(message));
53+
54+
Tox *toxes[2];
55+
uint32_t index[2];
56+
57+
for (uint32_t i = 0; i < 2; ++i) {
58+
index[i] = i + 1;
59+
toxes[i] = tox_new_log(nullptr, nullptr, &index[i]);
60+
tox_events_init(toxes[i]);
61+
ck_assert_msg(toxes[i] != nullptr, "failed to create tox instances %u", i);
62+
}
63+
64+
Tox_Dispatch *dispatch = tox_dispatch_new(nullptr);
65+
ck_assert_msg(dispatch != nullptr, "failed to create event dispatcher");
66+
67+
tox_events_callback_friend_message(dispatch, handle_events_friend_message);
68+
69+
uint8_t pk[TOX_PUBLIC_KEY_SIZE];
70+
tox_self_get_dht_id(toxes[0], pk);
71+
tox_bootstrap(toxes[1], "localhost", tox_self_get_udp_port(toxes[0], nullptr), pk, nullptr);
72+
73+
tox_self_get_public_key(toxes[0], pk);
74+
tox_friend_add_norequest(toxes[1], pk, nullptr);
75+
76+
tox_self_get_public_key(toxes[1], pk);
77+
tox_friend_add_norequest(toxes[0], pk, nullptr);
78+
79+
printf("bootstrapping and connecting 2 toxes\n");
80+
81+
while (tox_self_get_connection_status(toxes[0]) == TOX_CONNECTION_NONE ||
82+
tox_self_get_connection_status(toxes[1]) == TOX_CONNECTION_NONE) {
83+
// Ignore connection events for now.
84+
tox_events_free(tox_events_iterate(toxes[0], nullptr));
85+
tox_events_free(tox_events_iterate(toxes[1], nullptr));
86+
87+
c_sleep(tox_iteration_interval(toxes[0]));
88+
}
89+
90+
printf("toxes online, waiting for friend connection\n");
91+
92+
while (tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_NONE ||
93+
tox_friend_get_connection_status(toxes[1], 0, nullptr) == TOX_CONNECTION_NONE) {
94+
// Ignore connection events for now.
95+
tox_events_free(tox_events_iterate(toxes[0], nullptr));
96+
tox_events_free(tox_events_iterate(toxes[1], nullptr));
97+
98+
c_sleep(tox_iteration_interval(toxes[0]));
99+
}
100+
101+
printf("friends are connected via %s, now sending message\n",
102+
tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_TCP ? "TCP" : "UDP");
103+
104+
Tox_Err_Friend_Send_Message err;
105+
tox_friend_send_message(toxes[0], 0, TOX_MESSAGE_TYPE_NORMAL, message, sizeof(message), &err);
106+
ck_assert(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK);
107+
108+
ck_assert(await_message(toxes, dispatch));
109+
110+
tox_dispatch_free(dispatch);
111+
112+
for (uint32_t i = 0; i < 2; ++i) {
113+
tox_kill(toxes[i]);
114+
}
115+
}
116+
117+
int main(void)
118+
{
119+
setvbuf(stdout, nullptr, _IONBF, 0);
120+
test_tox_events();
121+
return 0;
122+
}

auto_tests/tox_events_test.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* Auto Tests: Many clients.
2+
*/
3+
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <time.h>
8+
9+
#include "../testing/misc_tools.h"
10+
#include "../toxcore/tox.h"
11+
#include "../toxcore/tox_events.h"
12+
#include "check_compat.h"
13+
14+
static bool await_message(Tox **toxes)
15+
{
16+
for (uint32_t i = 0; i < 100; ++i) {
17+
// Ignore events on tox 1.
18+
tox_events_free(tox_events_iterate(toxes[0], nullptr));
19+
// Check if tox 2 got the message from tox 1.
20+
Tox_Events *events = tox_events_iterate(toxes[1], nullptr);
21+
22+
if (events != nullptr) {
23+
ck_assert(tox_events_get_friend_message_size(events) == 1);
24+
const Tox_Event_Friend_Message *msg_event = tox_events_get_friend_message(events, 0);
25+
ck_assert(tox_event_friend_message_get_message_length(msg_event) == sizeof("hello"));
26+
const uint8_t *msg = tox_event_friend_message_get_message(msg_event);
27+
ck_assert_msg(memcmp(msg, "hello", sizeof("hello")) == 0,
28+
"message was not expected 'hello' but '%s'", (const char *)msg);
29+
tox_events_free(events);
30+
return true;
31+
}
32+
33+
c_sleep(tox_iteration_interval(toxes[0]));
34+
}
35+
36+
return false;
37+
}
38+
39+
static void test_tox_events(void)
40+
{
41+
uint8_t message[sizeof("hello")];
42+
memcpy(message, "hello", sizeof(message));
43+
44+
Tox *toxes[2];
45+
uint32_t index[2];
46+
47+
for (uint32_t i = 0; i < 2; ++i) {
48+
index[i] = i + 1;
49+
toxes[i] = tox_new_log(nullptr, nullptr, &index[i]);
50+
tox_events_init(toxes[i]);
51+
ck_assert_msg(toxes[i] != nullptr, "failed to create tox instances %u", i);
52+
}
53+
54+
uint8_t pk[TOX_PUBLIC_KEY_SIZE];
55+
tox_self_get_dht_id(toxes[0], pk);
56+
tox_bootstrap(toxes[1], "localhost", tox_self_get_udp_port(toxes[0], nullptr), pk, nullptr);
57+
58+
tox_self_get_public_key(toxes[0], pk);
59+
tox_friend_add_norequest(toxes[1], pk, nullptr);
60+
61+
tox_self_get_public_key(toxes[1], pk);
62+
tox_friend_add_norequest(toxes[0], pk, nullptr);
63+
64+
printf("bootstrapping and connecting 2 toxes\n");
65+
66+
while (tox_self_get_connection_status(toxes[0]) == TOX_CONNECTION_NONE ||
67+
tox_self_get_connection_status(toxes[1]) == TOX_CONNECTION_NONE) {
68+
// Ignore connection events for now.
69+
tox_events_free(tox_events_iterate(toxes[0], nullptr));
70+
tox_events_free(tox_events_iterate(toxes[1], nullptr));
71+
72+
c_sleep(tox_iteration_interval(toxes[0]));
73+
}
74+
75+
printf("toxes online, waiting for friend connection\n");
76+
77+
while (tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_NONE ||
78+
tox_friend_get_connection_status(toxes[1], 0, nullptr) == TOX_CONNECTION_NONE) {
79+
// Ignore connection events for now.
80+
tox_events_free(tox_events_iterate(toxes[0], nullptr));
81+
tox_events_free(tox_events_iterate(toxes[1], nullptr));
82+
83+
c_sleep(tox_iteration_interval(toxes[0]));
84+
}
85+
86+
printf("friends are connected via %s, now sending message\n",
87+
tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_TCP ? "TCP" : "UDP");
88+
89+
Tox_Err_Friend_Send_Message err;
90+
tox_friend_send_message(toxes[0], 0, TOX_MESSAGE_TYPE_NORMAL, message, sizeof(message), &err);
91+
ck_assert(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK);
92+
93+
ck_assert(await_message(toxes));
94+
95+
for (uint32_t i = 0; i < 2; ++i) {
96+
tox_kill(toxes[i]);
97+
}
98+
}
99+
100+
int main(void)
101+
{
102+
setvbuf(stdout, nullptr, _IONBF, 0);
103+
test_tox_events();
104+
return 0;
105+
}

other/analysis/gen-file.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CPPFLAGS+=("-Itesting")
1010
CPPFLAGS+=("-Itesting/fuzzing")
1111
CPPFLAGS+=("-Itesting/groupchats")
1212
CPPFLAGS+=("-Itoxcore")
13+
CPPFLAGS+=("-Itoxcore/events")
1314
CPPFLAGS+=("-Itoxav")
1415
CPPFLAGS+=("-Itoxencryptsave")
1516

other/make_single_file

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ if (@ARGV and $ARGV[0] eq "-core") {
4848
emit(abs_path $fn);
4949
}
5050
} else {
51-
for my $fn (<toxav/*.c>, <toxcore/*.c>, <toxencryptsave/*.c>) {
51+
for my $fn (<toxav/*.c>, <toxcore/*.c>, <toxcore/*/*.c>, <toxencryptsave/*.c>) {
5252
emit(abs_path $fn);
5353
}
5454
}

toxcore/BUILD.bazel

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ exports_files(
1010

1111
cc_library(
1212
name = "ccompat",
13+
srcs = ["ccompat.c"],
1314
hdrs = ["ccompat.h"],
1415
visibility = ["//c-toxcore:__subpackages__"],
1516
)
@@ -459,9 +460,36 @@ cc_library(
459460
],
460461
)
461462

463+
cc_library(
464+
name = "tox_events",
465+
srcs = ["tox_events.c"] + glob([
466+
"events/*.c",
467+
"events/*.h",
468+
]),
469+
hdrs = ["tox_events.h"],
470+
visibility = ["//c-toxcore:__subpackages__"],
471+
deps = [
472+
":ccompat",
473+
":toxcore",
474+
],
475+
)
476+
477+
cc_library(
478+
name = "tox_dispatch",
479+
srcs = ["tox_dispatch.c"],
480+
hdrs = ["tox_dispatch.h"],
481+
visibility = ["//c-toxcore:__subpackages__"],
482+
deps = [
483+
":ccompat",
484+
":tox_events",
485+
],
486+
)
487+
462488
sh_library(
463489
name = "cimple_files",
464490
srcs = glob([
491+
"events/*.c",
492+
"events/*.h",
465493
"*.c",
466494
"*.h",
467495
]),

0 commit comments

Comments
 (0)