Skip to content

Commit

Permalink
avs_commons 5.3.1
Browse files Browse the repository at this point in the history
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
  • Loading branch information
PiotrRoszkowski committed Jun 12, 2023
1 parent a4a25b6 commit a227958
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 39 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## avs_commons 5.3.1 (June 12th, 2023)

### 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

## avs_commons 5.3.0 (March 10th, 2023)

### Features
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
cmake_minimum_required(VERSION 3.6.0)
project(avs_commons C)

set(AVS_COMMONS_VERSION "5.3.0")
set(AVS_COMMONS_VERSION "5.3.1")

################# DISTRIBUTION #################################################

Expand Down
18 changes: 16 additions & 2 deletions include_public/avsystem/commons/avs_sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,22 @@ static inline int avs_sched_wait_for_next(avs_sched_t *sched,
* Executes jobs scheduled for execution before or at the current point in time.
*
* Specifically, this function will execute any jobs scheduled for before or at
* the time of entry to this function. If any of the executed jobs schedule more
* jobs for "now", they will <em>not</em> be executed.
* the time returned by @ref avs_time_monotonic_now, queried once at the entry
* to this function.
*
* Note: In principle, jobs scheduled for "now" during the run of
* <c>avs_sched_run()</c> are <em>not</em> supposed to be executed within the
* same run. However, this depends on a sufficiently high resolution of the
* underlying monotonic clock - each @ref AVS_SCHED_NOW call performs a new call
* to @ref avs_time_monotonic_now, and such job will only be executed within the
* same run if the numeric value of the clock did not change since entry to
* <c>avs_sched_run()</c>.
*
* In other words, in the worst case scenario of an indirect "infinite loop" of
* a scheduler job scheduling itself for another execution "now",
* <c>avs_sched_run()</c> will block for one "tick" of the monotonic clock
* (i.e., a period between changes of its numeric value), plus execution time of
* the last such job.
*
* @param sched Scheduler object to access.
*/
Expand Down
29 changes: 29 additions & 0 deletions include_public/avsystem/commons/avs_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,35 @@ typedef enum {
* the behaviour is undefined.
*/
AVS_NET_SOCKET_OPT_CONNECTION_ID_RESUMED,

/**
* Used to set and retrieve the IP protocol version preferred for
* communication. This controls the same value as the
* <c>preferred_family</c> field of the @ref avs_net_socket_configuration_t
* structure.
*
* The value is passed in the <c>addr_family</c> field of the
* @ref avs_net_socket_opt_value_t union. The change will take effect at the
* next call to a method that performs address resolution, such as
* @ref avs_net_socket_connect . If the socket is already bound or
* connected, it will <strong>not</strong> be reconnected automatically.
*/
AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY,

/**
* Used to set and retrieve the only IP protocol version that is allowed to
* be used for communication. This controls the same value as the
* <c>address_family</c> field of the @ref avs_net_socket_configuration_t
* structure. It may be set to <c>AVS_NET_UNSPEC</c> to allow usage of both
* IPv4 and IPv6, if supported by the system.
*
* The value is passed in the <c>addr_family</c> field of the
* @ref avs_net_socket_opt_value_t union. The change will take effect at the
* next call to a method that performs address resolution, such as
* @ref avs_net_socket_connect . If the socket is already bound or
* connected, it will <strong>not</strong> be reconnected automatically.
*/
AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY,
} avs_net_socket_opt_key_t;

typedef enum {
Expand Down
28 changes: 25 additions & 3 deletions include_public/avsystem/commons/avs_unit_mock_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,19 +140,41 @@ unsigned avs_unit_mock_invocations__(avs_unit_mock_func_ptr *invoked_func);
avs_unit_mock_invocations__((avs_unit_mock_func_ptr *) &AVS_UNIT_MOCK(func))

/**
* Declares and defines a mocked function pointer.
* Produces a forward declaration of a mocked function pointer.
*
* The declaration may be preceded with linkage specifiers such as
* <c>static</c>.
*
* @param _function_to_mock Name of the mocked function.
*/
#define AVS_UNIT_MOCK_DECLARE(_function_to_mock) \
__typeof__(_function_to_mock) *AVS_UNIT_MOCK(_function_to_mock)

/**
* Defines a mocked function pointer.
*
* The definition shall exist in a single translation unit only, and may be
* preceded with linkage specifiers such as <c>static</c>.
*
* @param _function_to_mock Name of the mocked function.
*/
#define AVS_UNIT_MOCK_CREATE(_function_to_mock) \
static __typeof__(_function_to_mock) *AVS_UNIT_MOCK(_function_to_mock); \
#define AVS_UNIT_MOCK_DEFINE(_function_to_mock) \
__typeof__(_function_to_mock) *AVS_UNIT_MOCK(_function_to_mock) = NULL; \
static void _avs_unit_mock_constructor_##_function_to_mock(void) \
__attribute__((constructor)); \
static void _avs_unit_mock_constructor_##_function_to_mock(void) { \
avs_unit_mock_add__( \
(avs_unit_mock_func_ptr *) &AVS_UNIT_MOCK(_function_to_mock)); \
}

/**
* Declares and defines a mocked function pointer with internal linkage.
*
* @param _function_to_mock Name of the mocked function.
*/
#define AVS_UNIT_MOCK_CREATE(_function_to_mock) \
static AVS_UNIT_MOCK_DEFINE(_function_to_mock)

#ifdef __cplusplus
}
#endif
Expand Down
71 changes: 50 additions & 21 deletions src/net/compat/posix/avs_net_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1003,26 +1003,23 @@ resolve_addrinfo_for_socket(net_socket_impl_t *net_socket,
avs_net_af_t socket_family =
get_avs_af(get_socket_family(net_socket->socket));
if (socket_family != AVS_NET_AF_UNSPEC && socket_family != family) {
# if defined(AVS_COMMONS_NET_WITH_IPV4) && defined(AVS_COMMONS_NET_WITH_IPV6)
# ifdef WITH_AVS_V4MAPPED
if (socket_family == AVS_NET_AF_INET6) {
# ifdef WITH_AVS_V4MAPPED
// If we have an already created socket that is bound to IPv6,
// but the requested family is something else, use v4-mapping
resolve_flags |= AVS_NET_ADDRINFO_RESOLVE_F_V4MAPPED;
# else // WITH_AVS_V4MAPPED
} else
# endif // WITH_AVS_V4MAPPED
{
// The case when we have an already created socket, we cannot
// use IPv6-to-IPv4 mapping, and the requested family is
// different from the socket's bound one will be handled in
// try_connect() and ensure_socket_bound_to_family()
if (!for_connect) {
// We shouldn't recreate the socket for sendto, just give up
// ...but we shouldn't recreate the socket for sendto, so
// just give up in that case
return NULL;
}
# endif // WITH_AVS_V4MAPPED
} else
# endif // defined(AVS_COMMONS_NET_WITH_IPV4) &&
// defined(AVS_COMMONS_NET_WITH_IPV6)
{
// If we have an already created socket, we cannot use
// IPv6-to-IPv4 mapping, and the requested family is different
// than the socket's bound one - we're screwed, just give up
return NULL;
}
}
}
Expand Down Expand Up @@ -1135,9 +1132,7 @@ static avs_error_t create_listening_socket(net_socket_impl_t *net_socket,
return err;
}

# if defined(AVS_COMMONS_NET_WITH_IPV4) \
&& defined(AVS_COMMONS_NET_WITH_IPV6) \
&& !defined(WITH_AVS_V4MAPPED)
# if defined(AVS_COMMONS_NET_WITH_IPV4) && defined(AVS_COMMONS_NET_WITH_IPV6)
static avs_error_t
ensure_socket_bound_to_family(net_socket_impl_t *net_socket,
const sockaddr_endpoint_union_t *target_address) {
Expand Down Expand Up @@ -1182,23 +1177,21 @@ ensure_socket_bound_to_family(net_socket_impl_t *net_socket,
return create_listening_socket(net_socket, &new_addr.addr, new_addr_len);
}
# endif // defined(AVS_COMMONS_NET_WITH_IPV4) &&
// defined(AVS_COMMONS_NET_WITH_IPV6) && !defined(WITH_AVS_V4MAPPED)
// defined(AVS_COMMONS_NET_WITH_IPV6)

static avs_error_t try_connect(net_socket_impl_t *net_socket,
const sockaddr_endpoint_union_t *address) {
char socket_was_already_open = (net_socket->socket != INVALID_SOCKET);
avs_error_t err = AVS_OK;
# if defined(AVS_COMMONS_NET_WITH_IPV4) \
&& defined(AVS_COMMONS_NET_WITH_IPV6) \
&& !defined(WITH_AVS_V4MAPPED)
# if defined(AVS_COMMONS_NET_WITH_IPV4) && defined(AVS_COMMONS_NET_WITH_IPV6)
if (socket_was_already_open && net_socket->type == AVS_NET_UDP_SOCKET) {
err = ensure_socket_bound_to_family(net_socket, address);
if (avs_is_err(err)) {
return err;
}
}
# endif // defined(AVS_COMMONS_NET_WITH_IPV4) &&
// defined(AVS_COMMONS_NET_WITH_IPV6) && !defined(WITH_AVS_V4MAPPED)
// defined(AVS_COMMONS_NET_WITH_IPV6)
if (!socket_was_already_open) {
if ((net_socket->socket =
socket(address->sockaddr_ep.addr.sa_family,
Expand Down Expand Up @@ -2056,6 +2049,14 @@ static avs_error_t get_opt_net(avs_net_socket_t *net_socket_,
case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
out_option_value->flag = false;
return AVS_OK;
case AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY:
out_option_value->addr_family =
net_socket->configuration.preferred_family;
return AVS_OK;
case AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY:
out_option_value->addr_family =
net_socket->configuration.address_family;
return AVS_OK;
default:
LOG(DEBUG,
_("get_opt_net: unknown or unsupported option key: ")
Expand All @@ -2065,6 +2066,17 @@ static avs_error_t get_opt_net(avs_net_socket_t *net_socket_,
}
}

static bool is_valid_addr_family(avs_net_af_t family) {
return family == AVS_NET_AF_UNSPEC
# ifdef AVS_COMMONS_NET_WITH_IPV4
|| family == AVS_NET_AF_INET4
# endif /* AVS_COMMONS_NET_WITH_IPV4 */
# ifdef AVS_COMMONS_NET_WITH_IPV6
|| family == AVS_NET_AF_INET6
# endif /* AVS_COMMONS_NET_WITH_IPV6 */
;
}

static avs_error_t set_opt_net(avs_net_socket_t *net_socket_,
avs_net_socket_opt_key_t option_key,
avs_net_socket_opt_value_t option_value) {
Expand All @@ -2073,6 +2085,23 @@ static avs_error_t set_opt_net(avs_net_socket_t *net_socket_,
case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
net_socket->recv_timeout = option_value.recv_timeout;
return AVS_OK;
case AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY:
if (!is_valid_addr_family(option_value.addr_family)) {
LOG(DEBUG, _("set_opt_net: unsupported preferred address family"));
return avs_errno(AVS_EINVAL);
} else {
net_socket->configuration.preferred_family =
option_value.addr_family;
return AVS_OK;
}
case AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY:
if (!is_valid_addr_family(option_value.addr_family)) {
LOG(DEBUG, _("set_opt_net: unsupported forced address family"));
return avs_errno(AVS_EINVAL);
} else {
net_socket->configuration.address_family = option_value.addr_family;
return AVS_OK;
}
default:
LOG(DEBUG,
_("set_opt_net: unknown or unsupported option key: ")
Expand Down
2 changes: 1 addition & 1 deletion src/sched/avs_sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ static AVS_LIST(avs_sched_job_t) fetch_job(avs_sched_t *sched,
AVS_LIST(avs_sched_job_t) result = NULL;
nonfailing_mutex_lock(sched->mutex);
if (sched->jobs
&& avs_time_monotonic_before(sched->jobs->instant, deadline)) {
&& !avs_time_monotonic_before(deadline, sched->jobs->instant)) {
if (sched->jobs->handle_ptr) {
nonfailing_mutex_lock(g_handle_access_mutex);
assert(*sched->jobs->handle_ptr == sched->jobs);
Expand Down
19 changes: 13 additions & 6 deletions src/utils/avs_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -437,13 +437,20 @@ const char *avs_time_duration_as_string_impl__(
int result;

if (avs_time_duration_valid(time)) {
if (time.seconds < 0 && time.nanoseconds > 0) {
++time.seconds;
time.nanoseconds = 1000000000 - time.nanoseconds;
bool negative = time.seconds < 0;
if (negative) {
if (time.nanoseconds > 0) {
++time.seconds;
time.nanoseconds = 1000000000 - time.nanoseconds;
}
time.seconds = -time.seconds;
}
result = avs_simple_snprintf(
*buf, AVS_TIME_DURATION_AS_STRING_MAX_LENGTH, "%s.%09" PRId32,
AVS_INT64_AS_STRING(time.seconds), time.nanoseconds);
assert(time.seconds >= 0);
result = avs_simple_snprintf(*buf,
AVS_TIME_DURATION_AS_STRING_MAX_LENGTH,
"%s%s.%09" PRId32, negative ? "-" : "",
AVS_INT64_AS_STRING(time.seconds),
time.nanoseconds);
} else {
result = avs_simple_snprintf(
*buf, AVS_TIME_DURATION_AS_STRING_MAX_LENGTH, "TIME_INVALID");
Expand Down
2 changes: 2 additions & 0 deletions tests/net/socket_common_testcases.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ run_socket_set_opt_test_cases(avs_net_socket_t *socket,
opt_val.state = AVS_NET_SOCKET_STATE_CONNECTED;
break;
case AVS_NET_SOCKET_OPT_ADDR_FAMILY:
case AVS_NET_SOCKET_OPT_PREFERRED_ADDR_FAMILY:
case AVS_NET_SOCKET_OPT_FORCED_ADDR_FAMILY:
opt_val.addr_family = AVS_NET_AF_INET4;
break;
case AVS_NET_SOCKET_OPT_MTU:
Expand Down
6 changes: 3 additions & 3 deletions tests/net/socket_nosec.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,13 @@ AVS_UNIT_TEST(socket, udp_connect_ipv4v6) {
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_bind(socket, "0.0.0.0", "0"));
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_get_local_port(socket, bound_port,
sizeof(bound_port)));
// Upgrading from IPv4 to IPv6 will not work
AVS_UNIT_ASSERT_FAILED(avs_net_socket_connect(socket, "::1", listen_port));
// Upgrading from IPv4 to IPv6
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_connect(socket, "::1", listen_port));
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_cleanup(&socket));

AVS_UNIT_ASSERT_SUCCESS(avs_net_udp_socket_create(&socket, NULL));
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_bind(socket, "::", bound_port));
// ...but downgrading from IPv6 to IPv4 should
// Downgrading from IPv6 to IPv4
AVS_UNIT_ASSERT_SUCCESS(
avs_net_socket_connect(socket, "127.0.0.1", listen_port));
AVS_UNIT_ASSERT_SUCCESS(avs_net_socket_get_local_port(
Expand Down
2 changes: 0 additions & 2 deletions tests/sched/test_sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ int clock_gettime(clockid_t clock, struct timespec *t) {
// all clocks are equivalent for our purposes, so ignore clock
t->tv_sec = (time_t) MOCK_CLOCK.since_monotonic_epoch.seconds;
t->tv_nsec = MOCK_CLOCK.since_monotonic_epoch.nanoseconds;
MOCK_CLOCK = avs_time_monotonic_add(
MOCK_CLOCK, avs_time_duration_from_scalar(1, AVS_TIME_NS));
return 0;
} else {
return orig_clock_gettime(clock, t);
Expand Down
6 changes: 6 additions & 0 deletions tests/utils/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,9 @@ AVS_UNIT_TEST(time, negative_duration_as_string) {
AVS_UNIT_ASSERT_EQUAL_STRING(AVS_TIME_DURATION_AS_STRING(value),
"-122.999999544");
}

AVS_UNIT_TEST(time, small_negative_duration_as_string) {
avs_time_duration_t value = { -1, 234 };
AVS_UNIT_ASSERT_EQUAL_STRING(AVS_TIME_DURATION_AS_STRING(value),
"-0.999999766");
}

0 comments on commit a227958

Please sign in to comment.