1#include <freeradius-devel/util/test/acutest.h>
2#include <freeradius-devel/util/test/acutest_helpers.h>
3#include <freeradius-devel/util/syserror.h>
45#define DEBUG_LVL_SET if (acutest_verbose_level_ >= 3) fr_debug_lvl = L_DBG_LVL_4 + 1
60 slen = write(tc->
fd[0], &wake,
sizeof(wake));
61 if (slen < 0)
return -1;
62 if (slen == 0)
return 0;
130 if (ret == 0)
return;
144 while (read(tc->
fd[0], &wake,
sizeof(wake)) > 0) {
153 if (preq->
freed)
continue;
178 UNUSED int fd_errno,
void *uctx)
261 if (stats) stats->
failed++;
272 our_preq->
freed =
true;
273 if (stats) stats->
freed++;
283 static size_t to_write;
289 slen = read(fd,
buff,
sizeof(
buff));
290 if (slen <= 0)
return;
295 slen = write(tc->
fd[1],
buff, (
size_t)to_write);
296 if (slen < 0)
return;
298 if (slen < (
ssize_t)to_write) {
301 printf(
"%s - Partial write %zu bytes left\n", __FUNCTION__, to_write);
314 talloc_free_children(tc);
347 socketpair(AF_UNIX, SOCK_STREAM, 0, tc->
fd);
360 char const *log_prefix,
UNUSED void *uctx)
365 memset(&cstat, 0,
sizeof(cstat));
515 socketpair(AF_UNIX, SOCK_STREAM, 0, tc->
fd);
524 char const *log_prefix,
void *uctx)
576 if (tconn == NULL)
return;
602 char const *log_prefix,
void *uctx)
657 if (tconn == NULL)
return;
814 TEST_CASE(
"cancellation via trunk free - TRUNK_REQUEST_STATE_BACKLOG");
822 TEST_CASE(
"cancellation via signal - TRUNK_REQUEST_STATE_BACKLOG");
838 TEST_CASE(
"cancellation via trunk free - TRUNK_REQUEST_STATE_PARTIAL");
862 TEST_CASE(
"cancellation via signal - TRUNK_REQUEST_STATE_PARTIAL");
887 TEST_CASE(
"cancellation via trunk free - TRUNK_REQUEST_STATE_SENT");
909 TEST_CASE(
"cancellation via signal - TRUNK_REQUEST_STATE_SENT");
933 TEST_CASE(
"cancellation via trunk free - TRUNK_REQUEST_STATE_CANCEL_PARTIAL");
964 TEST_CASE(
"cancellation via trunk free - TRUNK_REQUEST_STATE_CANCEL_SENT");
994 TEST_CASE(
"trunk free after TRUNK_REQUEST_STATE_CANCEL_COMPLETE");
1062 TEST_CASE(
"TRUNK_REQUEST_STATE_PARTIAL -> TRUNK_REQUEST_STATE_SENT");
1083 TEST_CASE(
"TRUNK_REQUEST_STATE_CANCEL_PARTIAL -> TRUNK_REQUEST_STATE_CANCEL_SENT");
1126 .backlog_on_failed_conn =
true
1146 TEST_CASE(
"dequeue on reconnect - TRUNK_REQUEST_STATE_PENDING");
1171 TEST_CASE(
"cancel on reconnect - TRUNK_REQUEST_STATE_PARTIAL");
1214 TEST_CASE(
"cancel on reconnect - TRUNK_REQUEST_STATE_SENT");
1252 TEST_CASE(
"free on reconnect - TRUNK_REQUEST_STATE_CANCEL");
1293 TEST_CASE(
"free on reconnect - TRUNK_REQUEST_STATE_CANCEL_PARTIAL");
1350 TEST_CASE(
"free on reconnect - TRUNK_REQUEST_STATE_CANCEL_SENT");
1439 TEST_CASE(
"C1 connecting, !max_req_per_conn - Enqueue MUST NOT spawn");
1452 TEST_CASE(
"C1 active, !max_req_per_conn - Enqueue MUST NOT spawn");
1483 printf(
"Rebalance %p\n", preq);
1497 TEST_CASE(
"C2 connected, R0 - Signal inactive");
1505 TEST_CASE(
"C1 connected, C2 inactive, R3 - Enqueued");
1514 TEST_CASE(
"C2 active, R3 - Signal active, should balance");
1524#define ALLOC_REQ(_id) \
1526 treq_##_id = trunk_request_alloc(trunk, NULL); \
1527 preq_##_id = talloc_zero(ctx, test_proto_request_t); \
1528 preq_##_id->treq = treq_##_id; \
1529 preq_##_id->priority = next_prio++; \
1541 .max_req_per_conn = 2,
1542 .target_req_per_conn = 2,
1546 trunk_request_t *treq_a = NULL, *treq_b = NULL, *treq_c = NULL, *treq_d = NULL, *treq_e = NULL;
1563 TEST_CASE(
"C0, R1 - Enqueue should spawn");
1582 TEST_CASE(
"C1 connecting, R2 - MUST NOT spawn");
1588 TEST_CASE(
"C1 connecting, R3 - MUST NOT spawn");
1594 TEST_CASE(
"C1 connecting, R4 - MUST NOT spawn");
1600 TEST_CASE(
"C1 connecting, R5 - MUST NOT spawn, NO CAPACITY");
1612 TEST_CASE(
"C1 active, R4 - Check pending 2");
1623 TEST_CASE(
"C1 active, R4 - Check sent 2");
1655 TEST_CASE(
"C1 active, R0 - Check complete 2, pending 0");
1701 .max_req_per_conn = 0,
1702 .target_req_per_conn = 2,
1719 memset(&stats, 0,
sizeof(stats));
1725 TEST_CASE(
"C0, R1 - Enqueue should spawn");
1738 TEST_CASE(
"C1 connecting, R2 - MUST NOT spawn");
1752 TEST_CASE(
"C1 connected, R3 - should spawn");
1774 TEST_CASE(
"C1 connected, C2 connecting, R2 - MUST NOT spawn");
1809 TEST_CASE(
"C0, R1 - Enqueue should spawn");
1821 TEST_CASE(
"C1 connecting, R2 - MUST NOT spawn");
1835 TEST_CASE(
"C1 connected, R3 - should spawn");
1889 TEST_CASE(
"PENDING - connections are connecting");
1900 TEST_CASE(
"ACTIVE - connections are active");
1908 TEST_CASE(
"FAILED - all connections closed / in reconnect backoff");
1935 .max_req_per_conn = 1,
1936 .target_req_per_conn = 1,
1972 TEST_CASE(
"PENDING - connection connecting");
1985 TEST_CASE(
"FULL - the maximum number of connections are all full");
2005 .max_req_per_conn = 0,
2006 .target_req_per_conn = 0,
2007 .req_pool_headers = 1,
2011 size_t i = 0, requests = 100000;
2012 fr_time_t enqueue_start, enqueue_stop, io_start, io_stop;
2026 memset(&stats, 0,
sizeof(stats));
2053 for (i = 0; i < requests; i++) {
2063 enqueue_time =
fr_time_sub(enqueue_stop, enqueue_start);
2065 INFO(
"Enqueue time %pV (%u rps) (%"PRIu64
"/%"PRIu64
")",
2083 INFO(
"I/O time %pV (%u rps)",
2090 INFO(
"Total time %pV (%u rps)",
static int acutest_verbose_level_
#define TEST_CHECK_LEN(_got, _exp)
static uint8_t request[4]
#define CMP_PREFER_SMALLER(_a, _b)
Evaluates to +1 for a > b, and -1 for a < b.
#define CC_NO_UBSAN(_sanitize)
fr_cmp_ret_t
Result of an ordering comparison.
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Head of a doubly linked list.
Entry in a doubly linked list.
#define fr_event_fd_insert(...)
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
uint64_t fr_event_list_num_timers(fr_event_list_t *el)
Return the number of timer events currently scheduled.
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Stores all information relating to an event list.
void * fr_minmax_heap_min_peek(fr_minmax_heap_t *hp)
fr_cmp_ret_t fr_pointer_cmp(void const *a, void const *b)
Compares two pointers.
int fr_nonblock(UNUSED int fd)
static const trunk_io_funcs_t io_funcs
static fr_event_list_t * events
uint64_t connection_get_num_timed_out(connection_t const *conn)
Return the number of times this connection has timed out whilst connecting.
uint64_t connection_get_num_reconnected(connection_t const *conn)
Return the number of times we've attempted to establish or re-establish this connection.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
int connection_signal_on_fd(connection_t *conn, int fd)
Setup the connection to change states to connected or failed based on I/O events.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Holds a complete set of functions for a connection.
static char buff[sizeof("18446744073709551615")+3]
static fr_time_t test_time(void)
static fr_time_t test_time_base
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
static fr_time_t fr_time_add_time_delta(fr_time_t a, fr_time_delta_t b)
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
static fr_time_delta_t fr_time_delta_from_nsec(int64_t nsec)
#define fr_time_sub(_a, _b)
Subtract one time from another.
A time delta, a difference in time measured in nanoseconds.
void fr_timer_list_set_time_func(fr_timer_list_t *tl, fr_event_time_source_t func)
Override event list time source.
A management API for bonding multiple connections together.
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
void trunk_reconnect(trunk_t *trunk, int states, connection_reason_t reason)
Force the trunk to re-establish its connections.
void trunk_request_signal_partial(trunk_request_t *treq)
Signal a partial write.
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
void trunk_connection_signal_readable(trunk_connection_t *tconn)
Signal that a trunk connection is readable.
trunk_request_t * trunk_request_alloc(trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
uint32_t trunk_request_count_by_connection(trunk_connection_t const *tconn, int req_state)
Return the count number of requests associated with a trunk connection.
struct trunk_request_pub_s pub
Public fields in the trunk request.
struct trunk_pub_s pub
Public fields in the trunk connection.
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
fr_dlist_head_t connecting
Connections which are not yet in the open state.
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
struct trunk_connection_pub_s pub
Public fields in the trunk connection.
uint16_t trunk_connection_count_by_state(trunk_t *trunk, int conn_state)
Return the count number of connections in the specified states.
trunk_t * trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_io_funcs_t const *funcs, trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start, fr_pair_list_t *trigger_args)
Allocate a new collection of connections.
fr_minmax_heap_t * active
Connections which can service requests.
void trunk_request_free(trunk_request_t **treq_to_free)
If the trunk request is freed then update the target requests.
void trunk_connection_signal_active(trunk_connection_t *tconn)
Signal a trunk connection is no longer full.
void trunk_connection_signal_inactive(trunk_connection_t *tconn)
Signal a trunk connection cannot accept more requests.
uint64_t trunk_request_count_by_state(trunk_t *trunk, int conn_state, int req_state)
Return a count of requests on a connection in a specific state.
void trunk_request_signal_cancel_partial(trunk_request_t *treq)
Signal a partial cancel write.
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
void trunk_connection_signal_reconnect(trunk_connection_t *tconn, connection_reason_t reason)
Signal a trunk connection is no longer viable.
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Associates request queues with a connection.
Main trunk management handle.
#define TRUNK_VERIFY(_trunk)
trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
#define TRUNK_REQUEST_STATE_ALL
All request states.
@ TRUNK_CONN_FULL
Connection is full and can't accept any more requests.
@ TRUNK_CONN_CONNECTING
Connection is connecting.
@ TRUNK_CONN_CLOSED
Connection was closed, either explicitly or due to failure.
@ TRUNK_CONN_ACTIVE
Connection is connected and ready to service requests.
@ TRUNK_STATE_PENDING
Trunk has connections, but none are usable yet; connections are being opened (INIT / CONNECTING).
@ TRUNK_STATE_FAILED
Trunk has connections, but they have all failed and are closed / in reconnect backoff.
@ TRUNK_STATE_ACTIVE
Trunk has at least one active connection which can service requests.
@ TRUNK_STATE_FULL
Trunk has no active connections, but has one or more connected connections which are all full (at cap...
@ TRUNK_STATE_IDLE
Trunk has no connections.
uint64_t _CONST req_alloc_reused
How many requests were reused.
trunk_request_state_t _CONST state
Which list the request is now located in.
trunk_connection_t *_CONST tconn
Connection this request belongs to.
trunk_connection_alloc_t connection_alloc
Allocate a new connection_t.
@ TRUNK_ENQUEUE_OK
Operation was successful.
@ TRUNK_ENQUEUE_NO_CAPACITY
At maximum number of connections, and no connection has capacity.
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
#define TRUNK_CONN_ALL
All connection states.
uint64_t _CONST req_alloc_new
How many requests we've allocated.
connection_t *_CONST conn
The underlying connection.
void *_CONST preq
Data for the muxer to write to the connection.
trunk_request_state_t
Used for sanity checks and to simplify freeing.
@ TRUNK_REQUEST_STATE_PARTIAL
Some of the request was written to the socket, more of it should be written later.
@ TRUNK_REQUEST_STATE_CANCEL_SENT
We've informed the remote server that the request has been cancelled.
@ TRUNK_REQUEST_STATE_CANCEL
A request on a particular socket was cancel.
@ TRUNK_REQUEST_STATE_CANCEL_PARTIAL
We partially wrote a cancellation request.
@ TRUNK_REQUEST_STATE_BACKLOG
In the backlog.
@ TRUNK_REQUEST_STATE_PENDING
In the queue of a connection and is pending writing.
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
trunk_request_cancel_mux_t request_cancel_mux
!< Read one or more requests from a connection.
trunk_cancel_reason_t
Reasons for a request being cancelled.
trunk_state_t _CONST state
Current state of the trunk.
Common configuration parameters for a trunk.
I/O functions to pass to trunk_alloc.
trunk_request_t * treq
Trunk request.
fr_dlist_head_t sent
Requests written, in the order they were written.
static void test_demux(UNUSED fr_event_list_t *el, UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
uint64_t cancelled
Count of tests in this run that were cancelled.
static void test_trunk_state_full(void)
uint64_t freed
Count of tests in this run that were freed.
int fd[2]
Socket pair. fd[1] loops data back to fd[0].
static connection_t * test_setup_socket_pair_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el, connection_conf_t const *conn_conf, char const *log_prefix, UNUSED void *uctx)
static void test_enqueue_and_io_speed(void)
bool cancelled
Seen by the cancelled callback.
static void _conn_io_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED int fd_errno, void *uctx)
static void _conn_io_loopback(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Whenever the second socket in a socket pair is readable, read all pending data, and write it back.
static void test_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
static void test_trunk_state_pending_active_failed(void)
static void test_socket_pair_alloc_then_connect_timeout(void)
static void test_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
static void _conn_io_write(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
static void test_partial_to_complete_states(void)
fr_dlist_t entry
Entry in test_conn_t sent.
static connection_t * test_setup_socket_pair_1s_timeout_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el, UNUSED connection_conf_t const *conf, char const *log_prefix, void *uctx)
static void test_request_free(UNUSED request_t *request, void *preq, void *uctx)
static void test_socket_pair_alloc_then_free(void)
bool failed
Seen by the failed callback.
static void test_request_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, void *uctx)
static void test_connection_rebalance_requests(void)
static connection_state_t _conn_init_no_signal(void **h_out, connection_t *conn, UNUSED void *uctx)
static connection_t * test_setup_socket_pair_1s_reconnection_delay_alloc(trunk_connection_t *tconn, fr_event_list_t *el, UNUSED connection_conf_t const *conn_conf, char const *log_prefix, void *uctx)
static void test_connection_levels_max(void)
static void test_socket_pair_alloc_then_reconnect_check_delay(void)
bool freed
Seen by the free callback.
uint64_t failed
Count of tests in this run that failed.
test_proto_request_t * preq
Request that was written.
static void _conn_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
bool completed
Seen by the complete callback.
static void _conn_notify(trunk_connection_t *tconn, connection_t *conn, fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
int priority
Priority of request.
static void test_socket_pair_alloc_then_reconnect_then_free(void)
static trunk_t * test_setup_trunk(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_conf_t *conf, bool with_cancel_mux, void *uctx)
static connection_state_t _conn_open(fr_event_list_t *el, void *h, UNUSED void *uctx)
Insert I/O handlers that loop any data back round.
static void test_enqueue_cancellation_points(void)
static fr_cmp_ret_t test_preq_cmp(void const *a, void const *b)
static void test_requeue_on_reconnect(void)
static void test_connection_start_on_enqueue(void)
bool signal_partial
Muxer should signal that this request is partially written.
static void test_request_complete(UNUSED request_t *request, void *preq, UNUSED void *rctx, void *uctx)
static int test_conn_write(test_conn_t *tc, test_proto_request_t *preq)
Write a request to the connection.
static void test_request_cancel(UNUSED connection_t *conn, void *preq, UNUSED trunk_cancel_reason_t reason, void *uctx)
static void test_enqueue_basic(void)
uint64_t completed
Count of tests in this run that completed.
static void test_connection_levels_alternating_edges(void)
static void _conn_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static connection_state_t _conn_init(void **h_out, connection_t *conn, UNUSED void *uctx)
Allocate a basic socket pair.
bool signal_cancel_partial
Muxer should signal that this request is partially cancelled.
A request the muxer has written, waiting for the demuxer.
static fr_event_list_t * el
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
#define fr_box_time_delta(_val)