The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
channel_test.c
Go to the documentation of this file.
1/*
2 * channel_test.c Stress test for channels and the sleep/wake handshake
3 *
4 * Version: $Id: bd6d6452cbf184ae0d15e904e7d183d31ec2719e $
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 *
20 * @copyright 2026 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
21 * @copyright 2016 Alan DeKok (aland@freeradius.org)
22 */
23
24RCSID("$Id: bd6d6452cbf184ae0d15e904e7d183d31ec2719e $")
25
26#include <freeradius-devel/server/base.h>
27#include <freeradius-devel/io/channel.h>
28#include <freeradius-devel/io/control.h>
29#include <freeradius-devel/io/message.h>
30#include <freeradius-devel/util/debug.h>
31#include <freeradius-devel/util/event.h>
32#include <freeradius-devel/util/rand.h>
33#include <freeradius-devel/util/syserror.h>
34#include <freeradius-devel/util/talloc.h>
35
36#ifdef HAVE_STDATOMIC_H
37# include <stdatomic.h>
38#else
39# include <freeradius-devel/util/stdatomic.h>
40#endif
41
42#ifdef HAVE_GETOPT_H
43# include <getopt.h>
44#endif
45
46#include <pthread.h>
47
48#define MPRINT1 if (debug_lvl) printf
49#define MPRINT2 if (debug_lvl > 1) printf
50
51/** What travels in each request, and is echoed back in its reply
52 */
53typedef struct {
54 uint64_t requestor;
55 uint64_t responder;
56 uint64_t seq;
57} payload_t;
58
59#define PAYLOAD_SIZE (sizeof(payload_t))
60
61typedef struct requestor_s requestor_t;
62typedef struct responder_s responder_t;
63
64/** A requestor's view of one channel
65 */
66typedef struct {
69 size_t responder; //!< index of the responder at the other end.
70 fr_message_set_t *reply_ms; //!< replies are allocated from this by the responder.
71
72 uint64_t sent;
73 uint64_t replied;
74 uint64_t outstanding;
75 uint64_t last_seq; //!< of the last reply, replies must arrive in order.
76
77 bool closing;
78 bool closed;
80
82 size_t id;
85 fr_message_set_t *ms; //!< requests are allocated from this.
86
87 req_channel_t *channel; //!< one per responder.
88 size_t num_closed;
89};
90
91/** A responder's view of one channel
92 */
97
99 size_t id;
102
103 resp_channel_t *channel; //!< one slot per requestor.
107
108 fr_channel_data_t **pending; //!< requests received but not yet replied to.
112
113 uint64_t received;
114 uint64_t sleeps; //!< times we blocked.
115 uint64_t rescued; //!< final drains which found work, i.e. skipped signals we caught.
116};
117
118static int debug_lvl = 0;
119static size_t num_requestors = 2;
120static size_t num_responders = 4;
121static uint64_t max_messages = 100000;
122static uint64_t max_outstanding = 64;
125static unsigned int watchdog_secs = 5;
126static bool broken = false;
127static size_t set_messages = 0; //!< message set size override, 0 for automatic.
128
131
132static _Atomic(uint64_t) progress; //!< total replies received, watched by the watchdog.
133
134static pthread_mutex_t wd_mutex = PTHREAD_MUTEX_INITIALIZER;
135static pthread_cond_t wd_cond = PTHREAD_COND_INITIALIZER;
136static bool finished = false;
137
138static NEVER_RETURNS void usage(void)
139{
140 fprintf(stderr, "usage: channel_test [OPTS]\n");
141 fprintf(stderr, " -r <requestors> Number of requestor threads (default %zu).\n", num_requestors);
142 fprintf(stderr, " -w <responders> Number of responder threads (default %zu).\n", num_responders);
143 fprintf(stderr, " -m <messages> Requests to send per channel (default %" PRIu64 ").\n", max_messages);
144 fprintf(stderr, " -o <outstanding> Requests in flight per channel (default %" PRIu64 ").\n", max_outstanding);
145 fprintf(stderr, " -W <usec> Max busy work per reply (default %u).\n", max_work_usec);
146 fprintf(stderr, " -P <usec> Max pause between requestor bursts (default %u).\n", max_pause_usec);
147 fprintf(stderr, " -t <secs> Watchdog interval, abort if no progress (default %u).\n", watchdog_secs);
148 fprintf(stderr, " -b Block without announcing, to prove the watchdog catches a stall.\n");
149 fprintf(stderr, " -M <messages> Messages per set, power of two (default: twice the window plus 64).\n");
150 fprintf(stderr, " -x Debugging mode.\n");
151
152 fr_exit_now(EXIT_FAILURE);
153}
154
155/** Burn a random amount of time without a syscall
156 */
157static void spin(uint32_t max_usec)
158{
159 fr_time_t until;
160
161 if (!max_usec) return;
162
163 until = fr_time_add(fr_time(), fr_time_delta_from_usec(fr_rand() % (max_usec + 1)));
164 while (fr_time_lt(fr_time(), until)) {
165 /* spin */
166 }
167}
168
169/**********************************************************************
170 * Requestor
171 **********************************************************************/
172
174{
176 payload_t pl;
177
178 memcpy(&pl, cd->m.data, sizeof(pl));
179 fr_assert_msg((pl.requestor == rc->requestor->id) && (pl.responder == rc->responder),
180 "requestor %zu channel %zu: reply belongs to requestor %" PRIu64 " channel %" PRIu64,
181 rc->requestor->id, rc->responder, pl.requestor, pl.responder);
182 fr_assert_msg(pl.seq == rc->last_seq + 1,
183 "requestor %zu channel %zu: reply %" PRIu64 " after %" PRIu64 " (sent %" PRIu64 " replied %" PRIu64 ")",
184 rc->requestor->id, rc->responder, pl.seq, rc->last_seq, rc->sent, rc->replied);
185 rc->last_seq = pl.seq;
186
187 fr_assert(rc->outstanding > 0);
188 rc->outstanding--;
189 rc->replied++;
190
191 fr_message_done(&cd->m);
192
194}
195
196static void requestor_closed(fr_channel_t *ch, UNUSED fr_time_t now, void *uctx)
197{
198 requestor_t *r = uctx;
200
201 fr_assert(rc->closing);
202 rc->closed = true;
203 r->num_closed++;
204 MPRINT1("requestor %zu: responder %zu acked close\n", r->id, rc->responder);
205}
206
210
211static void *requestor_thread(void *arg)
212{
213 requestor_t *r = arg;
214 size_t i;
215 bool wait;
216
217 MPRINT1("requestor %zu: started\n", r->id);
218
219 for (i = 0; i < num_responders; i++) {
220 if (fr_channel_signal_open(r->channel[i].ch, r->channel[i].reply_ms) < 0) {
221 fprintf(stderr, "requestor %zu: failed signalling open: %s\n", r->id, fr_strerror());
222 fr_exit_now(EXIT_FAILURE);
223 }
224 }
225
226 while (r->num_closed < num_responders) {
227 int num_events;
228
229 for (i = 0; i < num_responders; i++) {
230 req_channel_t *rc = &r->channel[i];
231
232 while (!rc->closing && (rc->sent < max_messages) && (rc->outstanding < max_outstanding)) {
233 fr_message_t *m;
235 payload_t pl = {
236 .requestor = r->id,
237 .responder = i,
238 .seq = rc->sent + 1
239 };
240
242 if (!m) {
245 if (!m) break; /* wait for replies to free some */
246 }
247 cd = (fr_channel_data_t *) m;
248
249 memcpy(cd->m.data, &pl, sizeof(pl));
250 cd->m.when = fr_time();
251
252 if (fr_channel_send_request(rc->ch, cd) < 0) {
253 MPRINT2("requestor %zu: channel %zu full\n", r->id, i);
254 fr_message_done(&cd->m);
255 break; /* wait for the responder to drain */
256 }
257
258 rc->sent++;
259 rc->outstanding++;
260 }
261
262 if (!rc->closing && (rc->sent == max_messages) && (rc->outstanding == 0)) {
263 MPRINT1("requestor %zu: closing channel to responder %zu\n", r->id, i);
264 rc->closing = true;
266 fprintf(stderr, "requestor %zu: failed signalling close: %s\n", r->id, fr_strerror());
267 fr_exit_now(EXIT_FAILURE);
268 }
269 }
270 }
271
272 /*
273 * Go quiet for a bit so the responders run dry,
274 * announce, and block. The next burst then lands
275 * on a responder which is sleeping, or is in the
276 * middle of deciding to.
277 */
279
280 /*
281 * Everything we can send is sent. Announce that
282 * we are about to block, drain every channel one
283 * last time, and only block if that found nothing.
284 */
285 wait = !fr_control_sleeping(r->control);
286 for (i = 0; i < num_responders; i++) {
287 while (fr_channel_recv_reply(r->channel[i].ch)) wait = false;
288 }
289 if (!wait) fr_control_awake(r->control);
290
291 num_events = fr_event_corral(r->el, fr_time(), wait);
293 if (num_events < 0) {
294 fprintf(stderr, "requestor %zu: failed waiting for events: %s\n", r->id, fr_syserror(errno));
295 fr_exit_now(EXIT_FAILURE);
296 }
297 if (num_events > 0) fr_event_service(r->el);
298 }
299
300 MPRINT1("requestor %zu: exiting\n", r->id);
301
302 return NULL;
303}
304
305/**********************************************************************
306 * Responder
307 **********************************************************************/
308
310{
311 responder_t *w = uctx;
312 size_t tail;
313
314 cd->channel.ch = ch;
315
316 fr_assert_msg(w->pending_num < w->pending_size, "responder %zu: pending overflow", w->id);
317 tail = (w->pending_head + w->pending_num) % w->pending_size;
318 w->pending[tail] = cd;
319 w->pending_num++;
320 w->received++;
321}
322
323static void responder_open(fr_channel_t *ch, fr_message_set_t *reply_ms, UNUSED fr_time_t now, void *uctx)
324{
325 responder_t *w = uctx;
326 resp_channel_t *slot;
327 size_t i;
328
329 for (i = 0; i < num_requestors; i++) {
330 if (w->channel[i].ch) continue;
331
332 slot = &w->channel[i];
333 slot->ch = ch;
334 slot->reply_ms = reply_ms;
337 w->num_channels++;
338 MPRINT1("responder %zu: channel %zu opened\n", w->id, i);
339 return;
340 }
341 fr_assert_fail("responder has no free channel slot");
342}
343
344static void responder_close(fr_channel_t *ch, UNUSED fr_time_t now, void *uctx)
345{
346 responder_t *w = uctx;
348 unsigned int num;
349
350 /*
351 * The requestor only closes once every reply is
352 * in, so nothing can be queued or pending.
353 */
355 if (num > 0) {
356 fprintf(stderr, "responder %zu: %u requests queued at close\n", w->id, num);
357 fr_exit_now(EXIT_FAILURE);
358 }
359
361 slot->ch = NULL;
362 w->num_channels--;
363 w->num_closed++;
364 MPRINT1("responder %zu: channel closed, %zu remain\n", w->id, w->num_channels);
365 if (w->num_closed == num_requestors) w->running = false;
366}
367
372
373/** The responder half of the handshake, as fr_worker() does it
374 *
375 * @return true if the final drain found work, so the caller must not block.
376 */
378{
379 size_t i;
380 bool received;
381
382 received = fr_control_sleeping(w->control);
383
384 for (i = 0; i < num_requestors; i++) {
385 if (!w->channel[i].ch) continue;
386 if (!fr_channel_active(w->channel[i].ch)) continue;
387
388 while (fr_channel_recv_request(w->channel[i].ch)) received = true;
389 }
390
391 if (received) {
392 w->rescued++;
394 }
395
396 return received;
397}
398
399/** Reply to everything pending at the start of this pass
400 *
401 * Replies made during the pass may queue more requests, which wait for
402 * the next pass, as they would in the worker.
403 */
405{
406 size_t batch = w->pending_num;
407
408 while (batch--) {
410 fr_channel_t *ch = cd->channel.ch;
412 fr_message_t *m;
414 fr_time_t start = fr_time();
415
417
419 if (!m) {
422 if (!m) return; /* requestor has not freed replies yet */
423 }
424 reply = (fr_channel_data_t *) m;
425
426 memcpy(reply->m.data, cd->m.data, PAYLOAD_SIZE);
427 reply->m.when = fr_time();
428 reply->reply.processing_time = fr_time_sub(reply->m.when, start);
429 reply->reply.cpu_time = reply->reply.processing_time;
430 reply->reply.request_time = cd->m.when;
431
432 if (fr_channel_send_reply(ch, reply) < 0) {
433 MPRINT2("responder %zu: reply queue full\n", w->id);
435 return; /* leave the request pending */
436 }
437
438 fr_message_done(&cd->m);
439 w->pending_head = (w->pending_head + 1) % w->pending_size;
440 w->pending_num--;
441 }
442}
443
444static void *responder_thread(void *arg)
445{
446 responder_t *w = arg;
447
448 MPRINT1("responder %zu: started\n", w->id);
449
450 while (w->running || (w->pending_num > 0)) {
451 bool wait, announced = false;
452 int num_events;
453
454 wait = (w->pending_num == 0);
455 if (wait && !broken) {
456 announced = true;
457 if (responder_sleeping(w)) wait = false;
458 }
459 if (wait) w->sleeps++;
460
461 num_events = fr_event_corral(w->el, fr_time(), wait);
462 if (announced) fr_control_awake(w->control);
463
464 if (num_events < 0) {
465 fprintf(stderr, "responder %zu: failed waiting for events: %s\n", w->id, fr_syserror(errno));
466 fr_exit_now(EXIT_FAILURE);
467 }
468 if (num_events > 0) fr_event_service(w->el);
469
470 responder_run(w);
471 }
472
473 MPRINT1("responder %zu: exiting\n", w->id);
474
475 return NULL;
476}
477
478/**********************************************************************
479 * Watchdog
480 **********************************************************************/
481
482static void dump_state(void)
483{
484 size_t i, j;
485
486 for (i = 0; i < num_requestors; i++) {
487 for (j = 0; j < num_responders; j++) {
488 req_channel_t *rc = &requestors[i].channel[j];
489
490 fprintf(stderr, "requestor %zu -> responder %zu: sent %" PRIu64 " replied %" PRIu64
491 " outstanding %" PRIu64 "%s%s\n",
492 i, j, rc->sent, rc->replied, rc->outstanding,
493 rc->closing ? " closing" : "", rc->closed ? " closed" : "");
494 }
495 }
496
497 for (i = 0; i < num_responders; i++) {
498 responder_t *w = &responders[i];
499
500 fprintf(stderr, "responder %zu: received %" PRIu64 " pending %zu"
501 " sleeps %" PRIu64 " %s\n",
502 i, w->received, w->pending_num, w->sleeps,
503 fr_control_is_sleeping(w->control) ? "SLEEPING" : "awake");
504 }
505}
506
507static void *watchdog_thread(UNUSED void *arg)
508{
509 uint64_t last = 0;
510
511 pthread_mutex_lock(&wd_mutex);
512 while (!finished) {
513 struct timespec ts;
514 int ret;
515
516 clock_gettime(CLOCK_REALTIME, &ts);
517 ts.tv_sec += watchdog_secs;
518
519 ret = pthread_cond_timedwait(&wd_cond, &wd_mutex, &ts);
520 if (finished) break;
521 if (ret != ETIMEDOUT) continue;
522
523 {
524 uint64_t now = atomic_load_explicit(&progress, memory_order_relaxed);
525
526 if (now == last) {
527 fprintf(stderr, "STALL: no replies in %u seconds\n", watchdog_secs);
528 dump_state();
529 fr_exit_now(EXIT_FAILURE);
530 }
531 MPRINT1("watchdog: %" PRIu64 " replies\n", now);
532 last = now;
533 }
534 }
535 pthread_mutex_unlock(&wd_mutex);
536
537 return NULL;
538}
539
540/**********************************************************************
541 * Main
542 **********************************************************************/
543
544int main(int argc, char *argv[])
545{
546 int c;
547 size_t i, j;
548 TALLOC_CTX *ctx;
549 pthread_attr_t attr;
550 pthread_t *req_id, *resp_id, wd_id;
551 fr_time_t start;
552 int64_t elapsed_usec;
553 uint64_t sent = 0, replied = 0, signals = 0, spurious_req = 0, spurious_rep = 0, sleeps = 0, rescued = 0;
554 size_t num_messages;
555
556 while ((c = getopt(argc, argv, "bhm:M:o:P:r:t:w:W:x")) != -1) switch (c) {
557 case 'b':
558 broken = true;
559 break;
560
561 case 'M':
562 set_messages = strtoul(optarg, NULL, 10);
563 break;
564
565 case 'm':
566 max_messages = strtoull(optarg, NULL, 10);
567 break;
568
569 case 'o':
570 max_outstanding = strtoull(optarg, NULL, 10);
571 break;
572
573 case 'P':
574 max_pause_usec = (uint32_t) strtoul(optarg, NULL, 10);
575 break;
576
577 case 'r':
578 num_requestors = strtoul(optarg, NULL, 10);
579 break;
580
581 case 't':
582 watchdog_secs = (unsigned int) strtoul(optarg, NULL, 10);
583 break;
584
585 case 'w':
586 num_responders = strtoul(optarg, NULL, 10);
587 break;
588
589 case 'W':
590 max_work_usec = (uint32_t) strtoul(optarg, NULL, 10);
591 break;
592
593 case 'x':
594 debug_lvl++;
595 break;
596
597 case 'h':
598 default:
599 usage();
600 }
601
604
606
607 MEM(ctx = talloc_init_const("channel_test"));
608
609 /*
610 * Each thread owns an event list, a control plane, and
611 * the atomic queue behind it. All of them are built here
612 * before any thread starts, so no thread ever touches
613 * another's event list.
614 */
615 MEM(requestors = talloc_zero_array(ctx, requestor_t, num_requestors));
616 MEM(responders = talloc_zero_array(ctx, responder_t, num_responders));
617
618 /*
619 * Message sets want a power of two. They are also sized
620 * well above the window on purpose: a set which fills and
621 * spills into a second message ring corrupts live messages
622 * when their replies complete out of order, which is a
623 * message set bug and not what this test is for. Pass a
624 * small -M to reproduce it.
625 */
626 num_messages = 1024;
627 while (num_messages < (max_outstanding * 32)) num_messages <<= 1;
628 if (set_messages) num_messages = set_messages;
629
630 for (i = 0; i < num_responders; i++) {
631 responder_t *w = &responders[i];
632
633 w->id = i;
634 w->running = true;
635 MEM(w->el = fr_event_list_alloc(ctx, NULL, NULL));
638 (fr_control_open(w->control) < 0)) {
639 fprintf(stderr, "failed setting up responder %zu control plane: %s\n", i, fr_strerror());
640 fr_exit_now(EXIT_FAILURE);
641 }
642 MEM(w->channel = talloc_zero_array(ctx, resp_channel_t, num_requestors));
644 MEM(w->pending = talloc_zero_array(ctx, fr_channel_data_t *, w->pending_size));
645 }
646
647 for (i = 0; i < num_requestors; i++) {
648 requestor_t *r = &requestors[i];
649
650 r->id = i;
651 MEM(r->el = fr_event_list_alloc(ctx, NULL, NULL));
654 (fr_control_open(r->control) < 0)) {
655 fprintf(stderr, "failed setting up requestor %zu control plane: %s\n", i, fr_strerror());
656 fr_exit_now(EXIT_FAILURE);
657 }
658 {
659 size_t n = num_messages;
660
661 while (n < (num_messages * num_responders)) n <<= 1;
662 MEM(r->ms = fr_message_set_create(ctx, (int) n, sizeof(fr_channel_data_t), n * 256, false));
663 }
664 MEM(r->channel = talloc_zero_array(ctx, req_channel_t, num_responders));
665
666 for (j = 0; j < num_responders; j++) {
667 req_channel_t *rc = &r->channel[j];
668
669 rc->requestor = r;
670 rc->responder = j;
671 MEM(rc->reply_ms = fr_message_set_create(ctx, (int) num_messages, sizeof(fr_channel_data_t),
672 num_messages * 256, false));
673 MEM(rc->ch = fr_channel_create(ctx, r->control, responders[j].control, false));
676 }
677 }
678
679 (void) pthread_attr_init(&attr);
680 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
681
682 MEM(req_id = talloc_array(ctx, pthread_t, num_requestors));
683 MEM(resp_id = talloc_array(ctx, pthread_t, num_responders));
684
685 start = fr_time();
686
687 (void) pthread_create(&wd_id, &attr, watchdog_thread, NULL);
688 for (i = 0; i < num_responders; i++) (void) pthread_create(&resp_id[i], &attr, responder_thread, &responders[i]);
689 for (i = 0; i < num_requestors; i++) (void) pthread_create(&req_id[i], &attr, requestor_thread, &requestors[i]);
690
691 for (i = 0; i < num_requestors; i++) (void) pthread_join(req_id[i], NULL);
692 for (i = 0; i < num_responders; i++) (void) pthread_join(resp_id[i], NULL);
693
694 pthread_mutex_lock(&wd_mutex);
695 finished = true;
696 pthread_cond_signal(&wd_cond);
697 pthread_mutex_unlock(&wd_mutex);
698 (void) pthread_join(wd_id, NULL);
699
700 elapsed_usec = fr_time_delta_to_usec(fr_time_sub(fr_time(), start));
701
702 /*
703 * Every request got exactly one reply, and every message
704 * buffer came back.
705 */
706 for (i = 0; i < num_requestors; i++) {
707 requestor_t *r = &requestors[i];
708
709 for (j = 0; j < num_responders; j++) {
710 req_channel_t *rc = &r->channel[j];
711
714 fr_assert(rc->outstanding == 0);
715 sent += rc->sent;
716 replied += rc->replied;
717
720
721 /*
722 * The channel holds both ends' counters.
723 */
724 signals += fr_channel_requestor_stats(rc->ch)->signals;
725 spurious_req += fr_channel_responder_stats(rc->ch)->spurious;
726 spurious_rep += fr_channel_requestor_stats(rc->ch)->spurious;
727 }
728
731 }
732
733 for (i = 0; i < num_responders; i++) {
734 responder_t *w = &responders[i];
735
736 fr_assert(w->pending_num == 0);
737 sleeps += w->sleeps;
738 rescued += w->rescued;
739 }
740
741 fr_assert(sent == replied);
742
743 printf("requests %" PRIu64 "\n", sent);
744 printf("replies %" PRIu64 "\n", replied);
745 printf("request signals %" PRIu64 " (%.1f%% of requests skipped the signal)\n",
746 signals, 100.0 * (double) (sent - signals) / (double) sent);
747 printf("spurious signals %" PRIu64 " request, %" PRIu64 " reply\n", spurious_req, spurious_rep);
748 printf("responder sleeps %" PRIu64 "\n", sleeps);
749 printf("rescued by drain %" PRIu64 "\n", rescued);
750 printf("elapsed %.3fs, %.0f requests/s\n",
751 (double) elapsed_usec / 1e6, ((double) sent * 1e6) / (double) elapsed_usec);
752
753 fflush(stdout);
754 talloc_free(ctx);
755
756 fr_exit_now(EXIT_SUCCESS);
757}
int n
Definition acutest.h:642
static uint8_t reply[4]
#define RCSID(id)
Definition build.h:603
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:413
#define UNUSED
Definition build.h:415
void * fr_channel_requestor_uctx_get(fr_channel_t *ch)
Get network-specific data from a channel.
Definition channel.c:982
fr_channel_stats_t const * fr_channel_responder_stats(fr_channel_t const *ch)
Statistics for the responder's end of a channel.
Definition channel.c:1063
bool fr_channel_recv_reply(fr_channel_t *ch)
Receive a reply message from the channel.
Definition channel.c:354
unsigned int fr_channel_responder_discard(fr_channel_t *ch)
Discard any requests the requestor queued but we never received.
Definition channel.c:895
int fr_channel_signal_open(fr_channel_t *ch, fr_message_set_t *reply_ms)
Send a channel to a responder.
Definition channel.c:1013
int fr_channel_signal_responder_close(fr_channel_t *ch)
Signal a responder that the channel is closing.
Definition channel.c:862
int fr_channel_send_request(fr_channel_t *ch, fr_channel_data_t *cd)
Send a request message into the channel.
Definition channel.c:271
fr_channel_t * fr_channel_create(TALLOC_CTX *ctx, fr_control_t *requestor, fr_control_t *responder, bool same)
Create a new channel.
Definition channel.c:167
fr_channel_stats_t const * fr_channel_requestor_stats(fr_channel_t const *ch)
Statistics for the requestor's end of a channel.
Definition channel.c:1056
int fr_channel_callbacks_add(fr_control_t *c, fr_channel_callbacks_t const *callbacks, void *uctx)
Register the owner's channel callbacks on its control plane.
Definition channel.c:784
int fr_channel_set_recv_reply(fr_channel_t *ch, fr_channel_recv_callback_t recv_reply, void *uctx)
Definition channel.c:990
void * fr_channel_responder_uctx_get(fr_channel_t *ch)
Get responder-specific data from a channel.
Definition channel.c:957
bool fr_channel_recv_request(fr_channel_t *ch)
Receive a request message from the channel.
Definition channel.c:418
void fr_channel_requestor_uctx_add(fr_channel_t *ch, void *uctx)
Add network-specific data to a channel.
Definition channel.c:970
void fr_channel_responder_uctx_add(fr_channel_t *ch, void *uctx)
Add responder-specific data to a channel.
Definition channel.c:945
int fr_channel_set_recv_request(fr_channel_t *ch, fr_channel_recv_callback_t recv_request, void *uctx)
Definition channel.c:998
int fr_channel_send_reply(fr_channel_t *ch, fr_channel_data_t *cd)
Send a reply message into the channel.
Definition channel.c:457
bool fr_channel_active(fr_channel_t *ch)
Check if a channel is active.
Definition channel.c:850
int fr_channel_responder_ack_close(fr_channel_t *ch)
Acknowledge that the channel is closing.
Definition channel.c:916
A full channel, which consists of two ends.
Definition channel.c:129
fr_message_t m
the message header
Definition channel.h:112
fr_channel_open_t open
Definition channel.h:219
fr_channel_closed_t closed
Definition channel.h:221
uint64_t signals
Number of kevent signals we've sent.
Definition channel.h:89
uint64_t spurious
Number of signals we received which found no data waiting.
Definition channel.h:91
What the owner of a control plane does with channel lifecycle events.
Definition channel.h:218
Channel information which is added to a message.
Definition channel.h:111
size_t pending_head
size_t pending_size
resp_channel_t * channel
one slot per requestor.
int main(int argc, char *argv[])
uint64_t requestor
static void * requestor_thread(void *arg)
static uint32_t max_work_usec
static size_t num_responders
uint64_t sleeps
times we blocked.
static unsigned int watchdog_secs
requestor_t * requestor
fr_channel_data_t ** pending
requests received but not yet replied to.
uint64_t seq
size_t pending_num
static void responder_recv_request(fr_channel_t *ch, fr_channel_data_t *cd, void *uctx)
static uint64_t max_outstanding
#define PAYLOAD_SIZE
uint64_t rescued
final drains which found work, i.e. skipped signals we caught.
static void * watchdog_thread(UNUSED void *arg)
fr_message_set_t * reply_ms
replies are allocated from this by the responder.
static void * responder_thread(void *arg)
static void dump_state(void)
uint64_t sent
static uint32_t max_pause_usec
fr_message_set_t * ms
requests are allocated from this.
static void responder_open(fr_channel_t *ch, fr_message_set_t *reply_ms, UNUSED fr_time_t now, void *uctx)
static uint64_t max_messages
fr_event_list_t * el
static void requestor_closed(fr_channel_t *ch, UNUSED fr_time_t now, void *uctx)
size_t responder
index of the responder at the other end.
uint64_t outstanding
size_t num_closed
static void spin(uint32_t max_usec)
Burn a random amount of time without a syscall.
uint64_t replied
fr_control_t * control
req_channel_t * channel
one per responder.
fr_control_t * control
static size_t num_requestors
static size_t set_messages
message set size override, 0 for automatic.
static responder_t * responders
size_t num_channels
uint64_t received
uint64_t last_seq
of the last reply, replies must arrive in order.
static bool broken
#define MPRINT2
static void responder_close(fr_channel_t *ch, UNUSED fr_time_t now, void *uctx)
static fr_channel_callbacks_t const responder_callbacks
static bool responder_sleeping(responder_t *w)
The responder half of the handshake, as fr_worker() does it.
static void responder_run(responder_t *w)
Reply to everything pending at the start of this pass.
fr_channel_t * ch
#define MPRINT1
size_t num_closed
static fr_channel_callbacks_t const requestor_callbacks
static requestor_t * requestors
fr_channel_t * ch
fr_event_list_t * el
fr_message_set_t * reply_ms
uint64_t responder
static void requestor_recv_reply(fr_channel_t *ch, fr_channel_data_t *cd, UNUSED void *uctx)
static int debug_lvl
What travels in each request, and is echoed back in its reply.
A requestor's view of one channel.
A responder's view of one channel.
#define FR_CONTROL_MAX_SIZE
Definition control.h:76
#define FR_CONTROL_MAX_MESSAGES
Definition control.h:75
static pthread_cond_t wd_cond
static pthread_mutex_t wd_mutex
static bool finished
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:248
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:254
#define MEM(x)
Definition debug.h:38
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:272
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:113
talloc_free(hp)
bool fr_control_sleeping(fr_control_t *c)
Record that the reader of this control plane is about to block.
Definition control.c:843
void fr_control_awake(fr_control_t *c)
Record that the reader of this control plane is running again.
Definition control.c:860
bool fr_control_is_sleeping(fr_control_t *c)
Check whether the reader of this control plane is blocked.
Definition control.c:875
int fr_control_open(fr_control_t *c)
Open the control-plane signalling path.
Definition control.c:446
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, uint16_t slots, uint16_t max_size)
Create a control-plane signaling path.
Definition control.c:427
The control structure.
Definition control.c:211
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2401
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
Definition event.c:2088
#define fr_time()
Definition event.c:58
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.
Definition event.c:2608
Stores all information relating to an event list.
Definition event.c:375
unsigned int uint32_t
fr_message_set_t * fr_message_set_create(TALLOC_CTX *ctx, int num_messages, size_t message_size, size_t ring_buffer_size, bool unlimited_size)
Create a message set.
Definition message.c:127
int fr_message_done(fr_message_t *m)
Mark a message as done.
Definition message.c:195
int fr_message_set_messages_used(fr_message_set_t *ms)
Count the number of used messages.
Definition message.c:1319
void fr_message_set_gc(fr_message_set_t *ms)
Garbage collect the message set.
Definition message.c:1345
fr_message_t * fr_message_and_data_alloc(fr_message_set_t *ms, size_t size)
Reserve and commit a message atomically.
Definition message.c:1132
A Message set, composed of message headers and ring buffer data.
Definition message.c:94
fr_time_t when
when this message was sent
Definition message.h:47
uint8_t * data
pointer to the data in the ring buffer
Definition message.h:49
#define fr_assert(_expr)
Definition rad_assert.h:37
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:128
@ memory_order_relaxed
Definition stdatomic.h:127
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:302
#define _Atomic(T)
Definition stdatomic.h:77
#define atomic_load_explicit(object, order)
Definition stdatomic.h:312
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:127
int fr_time_start(void)
Initialize the local time.
Definition time.c:157
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
Definition time.h:641
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
Definition time.h:577
#define fr_time_lt(_a, _b)
Definition time.h:239
"server local" time.
Definition time.h:69
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558