1 #include <freeradius-devel/util/acutest.h>
2 #include <freeradius-devel/util/acutest_helpers.h>
3 #include <freeradius-devel/util/syserror.h>
5 #include <sys/socket.h>
29 #define DEBUG_LVL_SET if (acutest_verbose_level_ >= 3) fr_debug_lvl = L_DBG_LVL_4 + 1
35 int fd = *(talloc_get_type_abort(conn->h,
int));
53 slen = write(fd, &preq,
sizeof(preq));
55 if (slen == 0)
return;
56 if (slen < (
ssize_t)
sizeof(preq)) abort();
67 int fd = *(talloc_get_type_abort(conn->h,
int));
87 slen = write(fd, &preq,
sizeof(preq));
92 if (slen == 0)
return;
93 if (slen < (
ssize_t)
sizeof(preq)) abort();
102 int fd = *(talloc_get_type_abort(conn->h,
int));
107 slen = read(fd, &preq,
sizeof(preq));
108 if (slen <= 0)
break;
125 if (preq->
freed)
continue;
152 UNUSED int fd_errno,
void *uctx)
176 int fd = *(talloc_get_type_abort(conn->h,
int));
245 our_preq->
freed =
true;
254 int *our_h = talloc_get_type_abort(uctx,
int);
256 static size_t to_write;
262 slen = read(fd,
buff,
sizeof(
buff));
263 if (slen <= 0)
return;
268 slen = write(our_h[1],
buff, (
size_t)to_write);
269 if (slen < 0)
return;
271 if (slen < (
ssize_t)to_write) {
274 printf(
"%s - Partial write %zu bytes left\n", __FUNCTION__, to_write);
285 int *our_h = talloc_get_type_abort(h,
int);
287 talloc_free_children(our_h);
300 int *our_h = talloc_get_type_abort(h,
int);
317 h = talloc_array(conn,
int, 2);
318 socketpair(AF_UNIX, SOCK_STREAM, 0, h);
331 char const *log_prefix,
UNUSED void *uctx)
336 memset(&cstat, 0,
sizeof(cstat));
482 h = talloc_array(conn,
int, 2);
483 socketpair(AF_UNIX, SOCK_STREAM, 0, h);
492 char const *log_prefix,
void *uctx)
544 if (tconn == NULL)
return;
570 char const *log_prefix,
void *uctx)
625 if (tconn == NULL)
return;
782 TEST_CASE(
"cancellation via trunk free - FR_TRUNK_REQUEST_STATE_BACKLOG");
790 TEST_CASE(
"cancellation via signal - FR_TRUNK_REQUEST_STATE_BACKLOG");
806 TEST_CASE(
"cancellation via trunk free - FR_TRUNK_REQUEST_STATE_PARTIAL");
830 TEST_CASE(
"cancellation via signal - FR_TRUNK_REQUEST_STATE_PARTIAL");
855 TEST_CASE(
"cancellation via trunk free - FR_TRUNK_REQUEST_STATE_SENT");
877 TEST_CASE(
"cancellation via signal - FR_TRUNK_REQUEST_STATE_SENT");
901 TEST_CASE(
"cancellation via trunk free - FR_TRUNK_REQUEST_STATE_CANCEL_PARTIAL");
932 TEST_CASE(
"cancellation via trunk free - FR_TRUNK_REQUEST_STATE_CANCEL_SENT");
962 TEST_CASE(
"trunk free after FR_TRUNK_REQUEST_STATE_CANCEL_COMPLETE");
1030 TEST_CASE(
"FR_TRUNK_REQUEST_STATE_PARTIAL -> FR_TRUNK_REQUEST_STATE_SENT");
1051 TEST_CASE(
"FR_TRUNK_REQUEST_STATE_CANCEL_PARTIAL -> FR_TRUNK_REQUEST_STATE_CANCEL_SENT");
1094 .backlog_on_failed_conn =
true
1114 TEST_CASE(
"dequeue on reconnect - FR_TRUNK_REQUEST_STATE_PENDING");
1139 TEST_CASE(
"cancel on reconnect - FR_TRUNK_REQUEST_STATE_PARTIAL");
1182 TEST_CASE(
"cancel on reconnect - FR_TRUNK_REQUEST_STATE_SENT");
1220 TEST_CASE(
"free on reconnect - FR_TRUNK_REQUEST_STATE_CANCEL");
1261 TEST_CASE(
"free on reconnect - FR_TRUNK_REQUEST_STATE_CANCEL_PARTIAL");
1318 TEST_CASE(
"free on reconnect - FR_TRUNK_REQUEST_STATE_CANCEL_SENT");
1407 TEST_CASE(
"C1 connecting, !max_req_per_conn - Enqueue MUST NOT spawn");
1420 TEST_CASE(
"C1 active, !max_req_per_conn - Enqueue MUST NOT spawn");
1451 printf(
"Rebalance %p\n", preq);
1465 TEST_CASE(
"C2 connected, R0 - Signal inactive");
1473 TEST_CASE(
"C1 connected, C2 inactive, R3 - Enqueued");
1482 TEST_CASE(
"C2 active, R3 - Signal active, should balance");
1492 #define ALLOC_REQ(_id) \
1494 treq_##_id = fr_trunk_request_alloc(trunk, NULL); \
1495 preq_##_id = talloc_zero(ctx, test_proto_request_t); \
1496 preq_##_id->treq = treq_##_id; \
1497 preq_##_id->priority = next_prio++; \
1509 .max_req_per_conn = 2,
1510 .target_req_per_conn = 2,
1514 fr_trunk_request_t *treq_a = NULL, *treq_b = NULL, *treq_c = NULL, *treq_d = NULL, *treq_e = NULL;
1531 TEST_CASE(
"C0, R1 - Enqueue should spawn");
1550 TEST_CASE(
"C1 connecting, R2 - MUST NOT spawn");
1556 TEST_CASE(
"C1 connecting, R3 - MUST NOT spawn");
1562 TEST_CASE(
"C1 connecting, R4 - MUST NOT spawn");
1568 TEST_CASE(
"C1 connecting, R5 - MUST NOT spawn, NO CAPACITY");
1580 TEST_CASE(
"C1 active, R4 - Check pending 2");
1591 TEST_CASE(
"C1 active, R4 - Check sent 2");
1623 TEST_CASE(
"C1 active, R0 - Check complete 2, pending 0");
1669 .max_req_per_conn = 0,
1670 .target_req_per_conn = 2,
1693 TEST_CASE(
"C0, R1 - Enqueue should spawn");
1706 TEST_CASE(
"C1 connecting, R2 - MUST NOT spawn");
1720 TEST_CASE(
"C1 connected, R3 - should spawn");
1742 TEST_CASE(
"C1 connected, C2 connecting, R2 - MUST NOT spawn");
1777 TEST_CASE(
"C0, R1 - Enqueue should spawn");
1789 TEST_CASE(
"C1 connecting, R2 - MUST NOT spawn");
1803 TEST_CASE(
"C1 connected, R3 - should spawn");
1830 .max_req_per_conn = 0,
1831 .target_req_per_conn = 0,
1832 .req_pool_headers = 1,
1836 size_t i = 0, requests = 100000;
1837 fr_time_t enqueue_start, enqueue_stop, io_start, io_stop;
1878 for (i = 0; i < requests; i++) {
1888 enqueue_time =
fr_time_sub(enqueue_stop, enqueue_start);
1890 INFO(
"Enqueue time %pV (%u rps) (%"PRIu64
"/%"PRIu64
")",
1908 INFO(
"I/O time %pV (%u rps)",
1915 INFO(
"Total time %pV (%u rps)",
static int acutest_verbose_level_
#define TEST_CHECK_LEN(_got, _exp)
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
@ FR_CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
@ FR_CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
@ FR_CONNECTION_FAILED
Connection is being reconnected because it failed.
Holds a complete set of functions for a connection.
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
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.
#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.
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.
void fr_event_list_set_time_func(fr_event_list_t *el, fr_event_time_source_t func)
Override event list time source.
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.
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)
int fr_nonblock(UNUSED int fd)
int8_t fr_pointer_cmp(void const *a, void const *b)
Compares two pointers.
uint64_t failed
Requests which failed a filter.
static fr_event_list_t * events
uint64_t fr_connection_get_num_timed_out(fr_connection_t const *conn)
Return the number of times this connection has timed out whilst connecting.
uint64_t fr_connection_get_num_reconnected(fr_connection_t const *conn)
Return the number of times we've attempted to establish or re-establish this connection.
int fr_connection_signal_on_fd(fr_connection_t *conn, int fd)
Setup the connection to change states to connected or failed based on I/O events.
fr_connection_t * fr_connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_connection_funcs_t const *funcs, fr_connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
void fr_connection_signal_reconnect(fr_connection_t *conn, fr_connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
static char buff[sizeof("18446744073709551615")+3]
static fr_time_t test_time_base
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
#define fr_time()
Allow us to arbitrarily manipulate time.
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.
A management API for bonding multiple connections together.
void fr_trunk_request_free(fr_trunk_request_t **treq_to_free)
If the trunk request is freed then update the target requests.
void fr_trunk_request_signal_cancel_sent(fr_trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
fr_dlist_head_t connecting
Connections which are not yet in the open state.
struct fr_trunk_pub_s pub
Public fields in the trunk connection.
void fr_trunk_reconnect(fr_trunk_t *trunk, int states, fr_connection_reason_t reason)
Force the trunk to re-establish its connections.
void fr_trunk_connection_signal_active(fr_trunk_connection_t *tconn)
Signal a trunk connection is no longer full.
void fr_trunk_request_signal_cancel(fr_trunk_request_t *treq)
Cancel a trunk request.
struct fr_trunk_connection_pub_s pub
Public fields in the trunk connection.
fr_trunk_request_t * fr_trunk_request_alloc(fr_trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
fr_minmax_heap_t * active
Connections which can service requests.
int fr_trunk_connection_pop_cancellation(fr_trunk_request_t **treq_out, fr_trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
void fr_trunk_request_signal_sent(fr_trunk_request_t *treq)
Signal that the request was written to a connection successfully.
fr_trunk_enqueue_t fr_trunk_request_enqueue(fr_trunk_request_t **treq_out, fr_trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
uint32_t fr_trunk_request_count_by_connection(fr_trunk_connection_t const *tconn, int req_state)
Return the count number of requests associated with a trunk connection.
uint16_t fr_trunk_connection_count_by_state(fr_trunk_t *trunk, int conn_state)
Return the count number of connections in the specified states.
void fr_trunk_request_signal_complete(fr_trunk_request_t *treq)
Signal that a trunk request is complete.
uint64_t fr_trunk_request_count_by_state(fr_trunk_t *trunk, int conn_state, int req_state)
Return a count of requests on a connection in a specific state.
void fr_trunk_request_signal_partial(fr_trunk_request_t *treq)
Signal a partial write.
fr_trunk_t * fr_trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_trunk_io_funcs_t const *funcs, fr_trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start)
Allocate a new collection of connections.
int fr_trunk_connection_pop_request(fr_trunk_request_t **treq_out, fr_trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
void fr_trunk_connection_signal_inactive(fr_trunk_connection_t *tconn)
Signal a trunk connection cannot accept more requests.
void fr_trunk_connection_signal_readable(fr_trunk_connection_t *tconn)
Signal that a trunk connection is readable.
struct fr_trunk_request_pub_s pub
Public fields in the trunk request.
void fr_trunk_connection_signal_reconnect(fr_trunk_connection_t *tconn, fr_connection_reason_t reason)
Signal a trunk connection is no longer viable.
void fr_trunk_connection_signal_writable(fr_trunk_connection_t *tconn)
Signal that a trunk connection is writable.
void fr_trunk_request_signal_cancel_complete(fr_trunk_request_t *treq)
Signal that a remote server acked our cancellation.
void fr_trunk_request_signal_cancel_partial(fr_trunk_request_t *treq)
Signal a partial cancel write.
Associates request queues with a connection.
Main trunk management handle.
uint64_t _CONST req_alloc_reused
How many requests were reused.
fr_trunk_cancel_reason_t
Reasons for a request being cancelled.
fr_trunk_connection_t *_CONST tconn
Connection this request belongs to.
@ FR_TRUNK_CONN_CONNECTING
Connection is connecting.
@ FR_TRUNK_CONN_ACTIVE
Connection is connected and ready to service requests.
fr_trunk_request_state_t
Used for sanity checks and to simplify freeing.
@ FR_TRUNK_REQUEST_STATE_BACKLOG
In the backlog.
@ FR_TRUNK_REQUEST_STATE_CANCEL
A request on a particular socket was cancel.
@ FR_TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
@ FR_TRUNK_REQUEST_STATE_PENDING
In the queue of a connection and is pending writing.
@ FR_TRUNK_REQUEST_STATE_CANCEL_SENT
We've informed the remote server that the request has been cancelled.
@ FR_TRUNK_REQUEST_STATE_CANCEL_PARTIAL
We partially wrote a cancellation request.
@ FR_TRUNK_REQUEST_STATE_PARTIAL
Some of the request was written to the socket, more of it should be written later.
#define FR_TRUNK_CONN_ALL
All connection states.
fr_trunk_request_state_t _CONST state
Which list the request is now located in.
uint64_t _CONST req_alloc_new
How many requests we've allocated.
fr_trunk_connection_alloc_t connection_alloc
Allocate a new fr_connection_t.
#define FR_TRUNK_VERIFY(_trunk)
#define FR_TRUNK_REQUEST_STATE_ALL
All request states.
fr_trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
@ FR_TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
@ FR_TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
@ FR_TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
@ FR_TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
fr_connection_t *_CONST conn
The underlying connection.
void *_CONST preq
Data for the muxer to write to the connection.
fr_trunk_request_cancel_mux_t request_cancel_mux
!< Read one or more requests from a connection.
@ FR_TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
@ FR_TRUNK_ENQUEUE_NO_CAPACITY
At maximum number of connections, and no connection has capacity.
@ FR_TRUNK_ENQUEUE_OK
Operation was successful.
Common configuration parameters for a trunk.
I/O functions to pass to fr_trunk_alloc.
static void test_cancel_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
fr_trunk_request_t * treq
Trunk request.
static void _conn_notify(fr_trunk_connection_t *tconn, fr_connection_t *conn, fr_event_list_t *el, fr_trunk_connection_event_t notify_on, UNUSED void *uctx)
uint64_t cancelled
Count of tests in this run that were cancelled.
uint64_t freed
Count of tests in this run that were freed.
static fr_connection_t * test_setup_socket_pair_connection_alloc(fr_trunk_connection_t *tconn, fr_event_list_t *el, fr_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 test_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED 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_socket_pair_alloc_then_connect_timeout(void)
static fr_connection_t * test_setup_socket_pair_1s_reconnection_delay_alloc(fr_trunk_connection_t *tconn, fr_event_list_t *el, UNUSED fr_connection_conf_t const *conn_conf, char const *log_prefix, 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)
static void test_demux(UNUSED fr_event_list_t *el, UNUSED fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
static fr_trunk_t * test_setup_trunk(TALLOC_CTX *ctx, fr_event_list_t *el, fr_trunk_conf_t *conf, bool with_cancel_mux, 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_connection_rebalance_requests(void)
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.
static fr_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.
uint64_t failed
Count of tests in this run that failed.
static fr_connection_state_t _conn_init(void **h_out, fr_connection_t *conn, UNUSED void *uctx)
Allocate a basic socket pair.
static void _conn_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
static fr_connection_t * test_setup_socket_pair_1s_timeout_connection_alloc(fr_trunk_connection_t *tconn, fr_event_list_t *el, UNUSED fr_connection_conf_t const *conf, char const *log_prefix, void *uctx)
bool completed
Seen by the complete callback.
int priority
Priority of request.
static void test_socket_pair_alloc_then_reconnect_then_free(void)
static void test_enqueue_cancellation_points(void)
static void test_requeue_on_reconnect(void)
static void test_connection_start_on_enqueue(void)
static void test_request_cancel(UNUSED fr_connection_t *conn, void *preq, UNUSED fr_trunk_cancel_reason_t reason, void *uctx)
static int8_t test_preq_cmp(void const *a, void const *b)
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 void test_enqueue_basic(void)
uint64_t completed
Count of tests in this run that completed.
static fr_connection_state_t _conn_init_no_signal(void **h_out, fr_connection_t *conn, UNUSED void *uctx)
static void test_connection_levels_alternating_edges(void)
static void _conn_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static void test_request_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED fr_trunk_request_state_t state, void *uctx)
bool signal_cancel_partial
Muxer should signal that this request is partially cancelled.
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)