Skip to content

Commit a227958

Browse files
avs_commons 5.3.1
Features - Added AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY and AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY options that allow setting address family configuration of an already created socket - Automatically upgrading IPv4 sockets to IPv6 when connecting is now possible - Added AVS_UNIT_MOCK_DECLARE() and AVS_UNIT_MOCK_DEFINE() to facilitate declaring mocked functions with external linkage Improvements - Slightly changed the semantics of avs_sched_run(), to fix erroneous behavior on platforms with low-resolution system clocks
1 parent a4a25b6 commit a227958

File tree

12 files changed

+162
-39
lines changed

12 files changed

+162
-39
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## avs_commons 5.3.1 (June 12th, 2023)
4+
5+
### Features
6+
7+
* Added ``AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY`` and
8+
``AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY`` options that allow setting address
9+
family configuration of an already created socket
10+
* Automatically upgrading IPv4 sockets to IPv6 when connecting is now possible
11+
* Added ``AVS_UNIT_MOCK_DECLARE()`` and ``AVS_UNIT_MOCK_DEFINE()`` to facilitate
12+
declaring mocked functions with external linkage
13+
14+
### Improvements
15+
16+
* Slightly changed the semantics of ``avs_sched_run()``, to fix erroneous
17+
behavior on platforms with low-resolution system clocks
18+
319
## avs_commons 5.3.0 (March 10th, 2023)
420

521
### Features

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
cmake_minimum_required(VERSION 3.6.0)
1818
project(avs_commons C)
1919

20-
set(AVS_COMMONS_VERSION "5.3.0")
20+
set(AVS_COMMONS_VERSION "5.3.1")
2121

2222
################# DISTRIBUTION #################################################
2323

include_public/avsystem/commons/avs_sched.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,22 @@ static inline int avs_sched_wait_for_next(avs_sched_t *sched,
209209
* Executes jobs scheduled for execution before or at the current point in time.
210210
*
211211
* Specifically, this function will execute any jobs scheduled for before or at
212-
* the time of entry to this function. If any of the executed jobs schedule more
213-
* jobs for "now", they will <em>not</em> be executed.
212+
* the time returned by @ref avs_time_monotonic_now, queried once at the entry
213+
* to this function.
214+
*
215+
* Note: In principle, jobs scheduled for "now" during the run of
216+
* <c>avs_sched_run()</c> are <em>not</em> supposed to be executed within the
217+
* same run. However, this depends on a sufficiently high resolution of the
218+
* underlying monotonic clock - each @ref AVS_SCHED_NOW call performs a new call
219+
* to @ref avs_time_monotonic_now, and such job will only be executed within the
220+
* same run if the numeric value of the clock did not change since entry to
221+
* <c>avs_sched_run()</c>.
222+
*
223+
* In other words, in the worst case scenario of an indirect "infinite loop" of
224+
* a scheduler job scheduling itself for another execution "now",
225+
* <c>avs_sched_run()</c> will block for one "tick" of the monotonic clock
226+
* (i.e., a period between changes of its numeric value), plus execution time of
227+
* the last such job.
214228
*
215229
* @param sched Scheduler object to access.
216230
*/

include_public/avsystem/commons/avs_socket.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,35 @@ typedef enum {
605605
* the behaviour is undefined.
606606
*/
607607
AVS_NET_SOCKET_OPT_CONNECTION_ID_RESUMED,
608+
609+
/**
610+
* Used to set and retrieve the IP protocol version preferred for
611+
* communication. This controls the same value as the
612+
* <c>preferred_family</c> field of the @ref avs_net_socket_configuration_t
613+
* structure.
614+
*
615+
* The value is passed in the <c>addr_family</c> field of the
616+
* @ref avs_net_socket_opt_value_t union. The change will take effect at the
617+
* next call to a method that performs address resolution, such as
618+
* @ref avs_net_socket_connect . If the socket is already bound or
619+
* connected, it will <strong>not</strong> be reconnected automatically.
620+
*/
621+
AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY,
622+
623+
/**
624+
* Used to set and retrieve the only IP protocol version that is allowed to
625+
* be used for communication. This controls the same value as the
626+
* <c>address_family</c> field of the @ref avs_net_socket_configuration_t
627+
* structure. It may be set to <c>AVS_NET_UNSPEC</c> to allow usage of both
628+
* IPv4 and IPv6, if supported by the system.
629+
*
630+
* The value is passed in the <c>addr_family</c> field of the
631+
* @ref avs_net_socket_opt_value_t union. The change will take effect at the
632+
* next call to a method that performs address resolution, such as
633+
* @ref avs_net_socket_connect . If the socket is already bound or
634+
* connected, it will <strong>not</strong> be reconnected automatically.
635+
*/
636+
AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY,
608637
} avs_net_socket_opt_key_t;
609638

610639
typedef enum {

include_public/avsystem/commons/avs_unit_mock_helpers.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,41 @@ unsigned avs_unit_mock_invocations__(avs_unit_mock_func_ptr *invoked_func);
140140
avs_unit_mock_invocations__((avs_unit_mock_func_ptr *) &AVS_UNIT_MOCK(func))
141141

142142
/**
143-
* Declares and defines a mocked function pointer.
143+
* Produces a forward declaration of a mocked function pointer.
144+
*
145+
* The declaration may be preceded with linkage specifiers such as
146+
* <c>static</c>.
147+
*
148+
* @param _function_to_mock Name of the mocked function.
149+
*/
150+
#define AVS_UNIT_MOCK_DECLARE(_function_to_mock) \
151+
__typeof__(_function_to_mock) *AVS_UNIT_MOCK(_function_to_mock)
152+
153+
/**
154+
* Defines a mocked function pointer.
155+
*
156+
* The definition shall exist in a single translation unit only, and may be
157+
* preceded with linkage specifiers such as <c>static</c>.
144158
*
145159
* @param _function_to_mock Name of the mocked function.
146160
*/
147-
#define AVS_UNIT_MOCK_CREATE(_function_to_mock) \
148-
static __typeof__(_function_to_mock) *AVS_UNIT_MOCK(_function_to_mock); \
161+
#define AVS_UNIT_MOCK_DEFINE(_function_to_mock) \
162+
__typeof__(_function_to_mock) *AVS_UNIT_MOCK(_function_to_mock) = NULL; \
149163
static void _avs_unit_mock_constructor_##_function_to_mock(void) \
150164
__attribute__((constructor)); \
151165
static void _avs_unit_mock_constructor_##_function_to_mock(void) { \
152166
avs_unit_mock_add__( \
153167
(avs_unit_mock_func_ptr *) &AVS_UNIT_MOCK(_function_to_mock)); \
154168
}
155169

170+
/**
171+
* Declares and defines a mocked function pointer with internal linkage.
172+
*
173+
* @param _function_to_mock Name of the mocked function.
174+
*/
175+
#define AVS_UNIT_MOCK_CREATE(_function_to_mock) \
176+
static AVS_UNIT_MOCK_DEFINE(_function_to_mock)
177+
156178
#ifdef __cplusplus
157179
}
158180
#endif

src/net/compat/posix/avs_net_impl.c

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,26 +1003,23 @@ resolve_addrinfo_for_socket(net_socket_impl_t *net_socket,
10031003
avs_net_af_t socket_family =
10041004
get_avs_af(get_socket_family(net_socket->socket));
10051005
if (socket_family != AVS_NET_AF_UNSPEC && socket_family != family) {
1006-
# if defined(AVS_COMMONS_NET_WITH_IPV4) && defined(AVS_COMMONS_NET_WITH_IPV6)
1006+
# ifdef WITH_AVS_V4MAPPED
10071007
if (socket_family == AVS_NET_AF_INET6) {
1008-
# ifdef WITH_AVS_V4MAPPED
10091008
// If we have an already created socket that is bound to IPv6,
10101009
// but the requested family is something else, use v4-mapping
10111010
resolve_flags |= AVS_NET_ADDRINFO_RESOLVE_F_V4MAPPED;
1012-
# else // WITH_AVS_V4MAPPED
1011+
} else
1012+
# endif // WITH_AVS_V4MAPPED
1013+
{
1014+
// The case when we have an already created socket, we cannot
1015+
// use IPv6-to-IPv4 mapping, and the requested family is
1016+
// different from the socket's bound one will be handled in
1017+
// try_connect() and ensure_socket_bound_to_family()
10131018
if (!for_connect) {
1014-
// We shouldn't recreate the socket for sendto, just give up
1019+
// ...but we shouldn't recreate the socket for sendto, so
1020+
// just give up in that case
10151021
return NULL;
10161022
}
1017-
# endif // WITH_AVS_V4MAPPED
1018-
} else
1019-
# endif // defined(AVS_COMMONS_NET_WITH_IPV4) &&
1020-
// defined(AVS_COMMONS_NET_WITH_IPV6)
1021-
{
1022-
// If we have an already created socket, we cannot use
1023-
// IPv6-to-IPv4 mapping, and the requested family is different
1024-
// than the socket's bound one - we're screwed, just give up
1025-
return NULL;
10261023
}
10271024
}
10281025
}
@@ -1135,9 +1132,7 @@ static avs_error_t create_listening_socket(net_socket_impl_t *net_socket,
11351132
return err;
11361133
}
11371134

1138-
# if defined(AVS_COMMONS_NET_WITH_IPV4) \
1139-
&& defined(AVS_COMMONS_NET_WITH_IPV6) \
1140-
&& !defined(WITH_AVS_V4MAPPED)
1135+
# if defined(AVS_COMMONS_NET_WITH_IPV4) && defined(AVS_COMMONS_NET_WITH_IPV6)
11411136
static avs_error_t
11421137
ensure_socket_bound_to_family(net_socket_impl_t *net_socket,
11431138
const sockaddr_endpoint_union_t *target_address) {
@@ -1182,23 +1177,21 @@ ensure_socket_bound_to_family(net_socket_impl_t *net_socket,
11821177
return create_listening_socket(net_socket, &new_addr.addr, new_addr_len);
11831178
}
11841179
# endif // defined(AVS_COMMONS_NET_WITH_IPV4) &&
1185-
// defined(AVS_COMMONS_NET_WITH_IPV6) && !defined(WITH_AVS_V4MAPPED)
1180+
// defined(AVS_COMMONS_NET_WITH_IPV6)
11861181

11871182
static avs_error_t try_connect(net_socket_impl_t *net_socket,
11881183
const sockaddr_endpoint_union_t *address) {
11891184
char socket_was_already_open = (net_socket->socket != INVALID_SOCKET);
11901185
avs_error_t err = AVS_OK;
1191-
# if defined(AVS_COMMONS_NET_WITH_IPV4) \
1192-
&& defined(AVS_COMMONS_NET_WITH_IPV6) \
1193-
&& !defined(WITH_AVS_V4MAPPED)
1186+
# if defined(AVS_COMMONS_NET_WITH_IPV4) && defined(AVS_COMMONS_NET_WITH_IPV6)
11941187
if (socket_was_already_open && net_socket->type == AVS_NET_UDP_SOCKET) {
11951188
err = ensure_socket_bound_to_family(net_socket, address);
11961189
if (avs_is_err(err)) {
11971190
return err;
11981191
}
11991192
}
12001193
# endif // defined(AVS_COMMONS_NET_WITH_IPV4) &&
1201-
// defined(AVS_COMMONS_NET_WITH_IPV6) && !defined(WITH_AVS_V4MAPPED)
1194+
// defined(AVS_COMMONS_NET_WITH_IPV6)
12021195
if (!socket_was_already_open) {
12031196
if ((net_socket->socket =
12041197
socket(address->sockaddr_ep.addr.sa_family,
@@ -2056,6 +2049,14 @@ static avs_error_t get_opt_net(avs_net_socket_t *net_socket_,
20562049
case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
20572050
out_option_value->flag = false;
20582051
return AVS_OK;
2052+
case AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY:
2053+
out_option_value->addr_family =
2054+
net_socket->configuration.preferred_family;
2055+
return AVS_OK;
2056+
case AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY:
2057+
out_option_value->addr_family =
2058+
net_socket->configuration.address_family;
2059+
return AVS_OK;
20592060
default:
20602061
LOG(DEBUG,
20612062
_("get_opt_net: unknown or unsupported option key: ")
@@ -2065,6 +2066,17 @@ static avs_error_t get_opt_net(avs_net_socket_t *net_socket_,
20652066
}
20662067
}
20672068

2069+
static bool is_valid_addr_family(avs_net_af_t family) {
2070+
return family == AVS_NET_AF_UNSPEC
2071+
# ifdef AVS_COMMONS_NET_WITH_IPV4
2072+
|| family == AVS_NET_AF_INET4
2073+
# endif /* AVS_COMMONS_NET_WITH_IPV4 */
2074+
# ifdef AVS_COMMONS_NET_WITH_IPV6
2075+
|| family == AVS_NET_AF_INET6
2076+
# endif /* AVS_COMMONS_NET_WITH_IPV6 */
2077+
;
2078+
}
2079+
20682080
static avs_error_t set_opt_net(avs_net_socket_t *net_socket_,
20692081
avs_net_socket_opt_key_t option_key,
20702082
avs_net_socket_opt_value_t option_value) {
@@ -2073,6 +2085,23 @@ static avs_error_t set_opt_net(avs_net_socket_t *net_socket_,
20732085
case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
20742086
net_socket->recv_timeout = option_value.recv_timeout;
20752087
return AVS_OK;
2088+
case AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY:
2089+
if (!is_valid_addr_family(option_value.addr_family)) {
2090+
LOG(DEBUG, _("set_opt_net: unsupported preferred address family"));
2091+
return avs_errno(AVS_EINVAL);
2092+
} else {
2093+
net_socket->configuration.preferred_family =
2094+
option_value.addr_family;
2095+
return AVS_OK;
2096+
}
2097+
case AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY:
2098+
if (!is_valid_addr_family(option_value.addr_family)) {
2099+
LOG(DEBUG, _("set_opt_net: unsupported forced address family"));
2100+
return avs_errno(AVS_EINVAL);
2101+
} else {
2102+
net_socket->configuration.address_family = option_value.addr_family;
2103+
return AVS_OK;
2104+
}
20762105
default:
20772106
LOG(DEBUG,
20782107
_("set_opt_net: unknown or unsupported option key: ")

src/sched/avs_sched.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ static AVS_LIST(avs_sched_job_t) fetch_job(avs_sched_t *sched,
312312
AVS_LIST(avs_sched_job_t) result = NULL;
313313
nonfailing_mutex_lock(sched->mutex);
314314
if (sched->jobs
315-
&& avs_time_monotonic_before(sched->jobs->instant, deadline)) {
315+
&& !avs_time_monotonic_before(deadline, sched->jobs->instant)) {
316316
if (sched->jobs->handle_ptr) {
317317
nonfailing_mutex_lock(g_handle_access_mutex);
318318
assert(*sched->jobs->handle_ptr == sched->jobs);

src/utils/avs_time.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,13 +437,20 @@ const char *avs_time_duration_as_string_impl__(
437437
int result;
438438

439439
if (avs_time_duration_valid(time)) {
440-
if (time.seconds < 0 && time.nanoseconds > 0) {
441-
++time.seconds;
442-
time.nanoseconds = 1000000000 - time.nanoseconds;
440+
bool negative = time.seconds < 0;
441+
if (negative) {
442+
if (time.nanoseconds > 0) {
443+
++time.seconds;
444+
time.nanoseconds = 1000000000 - time.nanoseconds;
445+
}
446+
time.seconds = -time.seconds;
443447
}
444-
result = avs_simple_snprintf(
445-
*buf, AVS_TIME_DURATION_AS_STRING_MAX_LENGTH, "%s.%09" PRId32,
446-
AVS_INT64_AS_STRING(time.seconds), time.nanoseconds);
448+
assert(time.seconds >= 0);
449+
result = avs_simple_snprintf(*buf,
450+
AVS_TIME_DURATION_AS_STRING_MAX_LENGTH,
451+
"%s%s.%09" PRId32, negative ? "-" : "",
452+
AVS_INT64_AS_STRING(time.seconds),
453+
time.nanoseconds);
447454
} else {
448455
result = avs_simple_snprintf(
449456
*buf, AVS_TIME_DURATION_AS_STRING_MAX_LENGTH, "TIME_INVALID");

tests/net/socket_common_testcases.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ run_socket_set_opt_test_cases(avs_net_socket_t *socket,
6161
opt_val.state = AVS_NET_SOCKET_STATE_CONNECTED;
6262
break;
6363
case AVS_NET_SOCKET_OPT_ADDR_FAMILY:
64+
case AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY:
65+
case AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY:
6466
opt_val.addr_family = AVS_NET_AF_INET4;
6567
break;
6668
case AVS_NET_SOCKET_OPT_MTU:

tests/net/socket_nosec.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,13 @@ AVS_UNIT_TEST(socket, udp_connect_ipv4v6) {
257257
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_bind(socket, "0.0.0.0", "0"));
258258
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_get_local_port(socket, bound_port,
259259
sizeof(bound_port)));
260-
// Upgrading from IPv4 to IPv6 will not work
261-
AVS_UNIT_ASSERT_FAILED(avs_net_socket_connect(socket, "::1", listen_port));
260+
// Upgrading from IPv4 to IPv6
261+
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_connect(socket, "::1", listen_port));
262262
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_cleanup(&socket));
263263

264264
AVS_UNIT_ASSERT_SUCCESS(avs_net_udp_socket_create(&socket, NULL));
265265
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_bind(socket, "::", bound_port));
266-
// ...but downgrading from IPv6 to IPv4 should
266+
// Downgrading from IPv6 to IPv4
267267
AVS_UNIT_ASSERT_SUCCESS(
268268
avs_net_socket_connect(socket, "127.0.0.1", listen_port));
269269
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_get_local_port(

0 commit comments

Comments
 (0)