The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
coord.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 2892e3b717ae3f9f1ea2cf36dcf2c3a7a4f50fd6 $
19 *
20 * @brief Coordination thread management
21 * @file io/coord.c
22 *
23 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: 2892e3b717ae3f9f1ea2cf36dcf2c3a7a4f50fd6 $")
26
27#include <freeradius-devel/io/listen.h>
28#include <freeradius-devel/io/schedule.h>
29#include <freeradius-devel/io/thread.h>
30#include <freeradius-devel/io/coord_priv.h>
31#include <freeradius-devel/unlang/base.h>
32#include <freeradius-devel/util/syserror.h>
33
34#include <stdalign.h>
35
36/** Events on a coordinator's control plane, sent by workers, in the FR_CONTROL_EVENTSET_COORD event set
37 */
39 FR_COORD_CONTROL_WORKER_ATTACH = 0, //!< Message sent from worker to attach to a coordinator
40 FR_COORD_CONTROL_WORKER_DETACH, //!< Message sent from worker to detach from a coordinator
41 FR_COORD_CONTROL_DATA //!< Worker -> coordinator message to pass data to a callback
43
44/** Events on a coordinator worker's control plane, sent by the coordinator, in the FR_CONTROL_EVENTSET_COORD event set
45 */
47 FR_COORD_WORKER_CONTROL_ACK = 0, //!< Message sent to worker to acknowledge attach / detach
48 FR_COORD_WORKER_CONTROL_DATA //!< Coordinator -> worker message to pass data to a callback
50
51#define MIN_WORKER_ID -1 //!< The minimum value we expect as worker id. -1 is the main thread.
52
56
57/** A coordinator which receives messages from workers
58 */
59struct fr_coord_s {
60 fr_coord_reg_t *coord_reg; //!< Coordinator registration details.
61 fr_event_list_t *el; //!< Coordinator event list.
62 fr_rb_node_t node; //!< Entry in the tree of coordinators.
63 fr_coord_cb_reg_t *callbacks; //!< Array of callbacks for worker -> coordinator messages.
64 uint32_t num_callbacks; //!< Number of callbacks defined.
65 fr_coord_cb_inst_t **cb_inst; //!< Array of callback instance specific data.
66
67 uint32_t max_workers; //!< Maximum number of workers we expect.
68 uint32_t num_workers; //!< How many workers are attached.
69
70 fr_control_t *coord_recv_control; //!< Control plane for worker -> coordinator messages.
71 fr_atomic_queue_t *coord_recv_aq; //!< Atomic queue for worker -> coordinator
72 fr_control_t **coord_send_control; //!< Control planes for coordinator -> worker messages.
73 fr_message_set_t **coord_send_ms; //!< Message sets for coordinator -> worker data.
74 fr_atomic_queue_t **coord_send_aq; //!< Atomic queues for coordinator -> worker data.
75
76 bool exiting; //!< Is this coordinator shutting down.
77 bool single_thread; //!< Are we in single thread mode.
78};
79
80/** The worker end of worker <-> coordinator communication.
81 */
83 fr_coord_t *coord; //!< Coordinator this worker is related to
84 fr_message_set_t *worker_send_ms; //!< Message set for worker -> coordinator messages
85 fr_control_t *worker_recv_control; //!< Coordinator -> worker control plane
86 fr_atomic_queue_t *worker_recv_aq; //!< Atomic queue for coordinator -> worker messages
87 fr_coord_worker_cb_reg_t *callbacks; //!< Callbacks for coordinator -> worker messages
88 uint32_t num_callbacks; //!< Number of callbacks registered.
89};
90
91/** A coordinator registration
92 */
94 char const *name; //!< Name for debugging.
95 fr_dlist_t entry; //!< Entry in list of registrations.
96 fr_coord_cb_reg_t *coord_cb; //!< Callbacks for worker -> coordinator messages.
97 fr_coord_worker_cb_reg_t *worker_cb; //!< Callbacks for coordinator -> worker messages.
98 size_t worker_send_size; //!< Initial size for worker -> coordinator ring buffer.
99 size_t coord_send_size; //!< Initial size for coordinator -> worker ring buffer.
100 module_instance_t const *mi; //!< Module instance which registered this coordinator.
101};
102
103/** Scheduler specific information for coordinator threads
104 */
105typedef struct {
106 fr_thread_t thread; //!< common thread information - must be first!
107
108 uint32_t max_workers; //!< Maximum number of workers which will connect to this coordinator.
109 fr_coord_reg_t *coord_reg; //!< Coordinator registration details.
110 fr_coord_t *coord; //!< The coordinator data structure.
111 fr_sem_t *sem; //!< For inter-thread signaling.
113
114/** Control plane message used for workers attaching / detaching to coordinators
115 */
116typedef struct {
117 int32_t worker; //!< Worker ID
118 fr_control_t *worker_recv_control; //!< Control plane to send messages to this worker
119 fr_atomic_queue_t *worker_recv_aq; //!< Atomic queue to send data to this worker
121
122typedef struct {
123 int32_t worker; //!< Worker ID
124 bool exiting; //!< Is the server exiting
126
127/** Compare coordinators by registration
128 */
129static fr_cmp_ret_t coord_cmp(void const *one, void const *two)
130{
131 fr_coord_t const *a = one, *b = two;
132
133 return CMP(a->coord_reg, b->coord_reg);
134}
135
136/** Register a coordinator
137 *
138 * To be called from mod_instantiate of a module which uses a coordinator
139 *
140 * @param reg_ctx Registration data
141 * @return
142 * - coordination registration on success
143 * - NULL on failure
144 */
146{
147 fr_coord_reg_t *coord_reg;
148
149 fr_assert(reg_ctx->coord_cb);
150 fr_assert(reg_ctx->worker_cb);
151 fr_assert(reg_ctx->mi);
152
153 /* Allocate the list of registered coordinators if not already done */
154 if (!coord_regs) {
155 MEM(coord_regs = talloc_zero(NULL, fr_dlist_head_t));
157 }
158
159 MEM(coord_reg = talloc(coord_regs, fr_coord_reg_t));
160 *coord_reg = (fr_coord_reg_t) {
161 .name = reg_ctx->name,
162 .coord_cb = reg_ctx->coord_cb,
163 .worker_cb = reg_ctx->worker_cb,
164 .worker_send_size = reg_ctx->worker_send_size ? reg_ctx->worker_send_size : 4096,
165 .coord_send_size = reg_ctx->coord_send_size ? reg_ctx->coord_send_size : 4096,
166 .mi = reg_ctx->mi,
167 };
168
170
171 return coord_reg;
172}
173
174/** De-register a coordinator
175 *
176 * To be called from mod_detach of a module which uses a coordinator
177 *
178 * @param coord_reg to de-register
179 */
181{
182 fr_dlist_remove(coord_regs, coord_reg);
183
184 talloc_free(coord_reg);
185
186 if (fr_dlist_num_elements(coord_regs) == 0) TALLOC_FREE(coord_regs);
187}
188
189/** Wait for all the coordinator threads to exit
190 *
191 * To be called during the scheduler shutdown in multi-threaded mode.
192 */
194{
195 int ret;
196
197 if (!coord_threads) return;
198
200 if ((ret = pthread_join(sc->thread.pthread_id, NULL)) != 0) {
201 ERROR("Failed joining coordinator %s: %s", sc->coord_reg->name, fr_syserror(ret));
202 } else {
203 DEBUG2("Coordinator %s joined (cleaned up)", sc->coord_reg->name);
204 }
205
208 }
209}
210
211/** Callback for a coordinator receiving data from a worker
212 */
213static void coord_data_recv(void const *data, size_t data_size, fr_time_t now, void *uctx)
214{
215 fr_coord_t *coord = talloc_get_type_abort(uctx, fr_coord_t);
217 fr_coord_data_t *cd;
218 fr_dbuff_t dbuff;
219
220 fr_assert(data_size == sizeof(cm));
221 memcpy(&cm, data, data_size);
222 fr_assert((cm.worker >= MIN_WORKER_ID) && (cm.worker < (int32_t)coord->max_workers));
223
224 if (unlikely(!fr_atomic_queue_spsc_pop(coord->coord_recv_aq, &cd))) return;
225
226 DEBUG3("Coordinator %s got data from worker %d for callback %" PRIu32,
227 coord->coord_reg->name, cm.worker, cd->coord_cb_id);
228
229 if (cd->coord_cb_id >= coord->num_callbacks) {
230 ERROR("Received data for callback %" PRIu32 " which is not defined", cd->coord_cb_id);
231 fr_message_done(&cd->m);
232 return;
233 }
234
235 fr_dbuff_init(&dbuff, (uint8_t const *)cd->m.data, cd->m.data_size);
236 coord->callbacks[cd->coord_cb_id].callback(coord, cm.worker, &dbuff, now,
237 MODULE_CTX(coord->coord_reg->mi,
238 module_thread(coord->coord_reg->mi)->data, NULL, NULL),
239 coord->cb_inst[cd->coord_cb_id] ?
240 coord->cb_inst[cd->coord_cb_id]->inst_data : NULL,
241 coord->callbacks[cd->coord_cb_id].uctx);
242 fr_message_done(&cd->m);
243}
244
245/** Callback for a worker receiving data from a coordinator
246 */
247static void coord_worker_data_recv(void const *data, size_t data_size, fr_time_t now, void *uctx)
248{
249 fr_coord_worker_t *cw = talloc_get_type_abort(uctx, fr_coord_worker_t);
251 fr_coord_data_t *cd;
252 fr_dbuff_t dbuff;
253
254 fr_assert(data_size == sizeof(cm));
255 memcpy(&cm, data, data_size);
256
257 if (unlikely(!fr_atomic_queue_spsc_pop(cw->worker_recv_aq, &cd))) return;
258
259 DEBUG3("Coordinator %s sent message for callback %" PRIu32, cw->coord->coord_reg->name, cd->coord_cb_id);
260
261 if (cd->coord_cb_id >= cw->num_callbacks) {
262 ERROR("Received message for callback %" PRIu32 " which is not defined", cd->coord_cb_id);
263 fr_message_done(&cd->m);
264 return;
265 }
266
267 fr_dbuff_init(&dbuff, (uint8_t const *)cd->m.data, cd->m.data_size);
268 cw->callbacks[cd->coord_cb_id].callback(cw, &dbuff, now,
270 module_thread(cw->coord->coord_reg->mi)->data, NULL, NULL),
271 cw->callbacks[cd->coord_cb_id].uctx);
272 fr_message_done(&cd->m);
273}
274
275/** Callback run by a coordinator when a worker attaches
276 */
277static void coord_worker_attach(void const *data, NDEBUG_UNUSED size_t data_size, UNUSED fr_time_t now, void *uctx)
278{
279 fr_coord_t *coord = talloc_get_type_abort(uctx, fr_coord_t);
281 fr_coord_msg_t ack;
282 uint32_t thread_id;
283
284 fr_assert(data_size == sizeof(fr_coord_worker_attach_msg_t));
285 fr_assert((msg->worker >= MIN_WORKER_ID) && (msg->worker < (int32_t)coord->max_workers));
286
287 DEBUG2("Worker %d attached to %s", msg->worker, coord->coord_reg->name);
288 coord->num_workers++;
289 thread_id = msg->worker - MIN_WORKER_ID;
290 coord->coord_send_control[thread_id] = msg->worker_recv_control;
291 coord->coord_send_aq[thread_id] = msg->worker_recv_aq;
292
293 ack.worker = msg->worker;
296}
297
298/** Callback run by a coordinator when a worker detaches
299 */
300static void coord_worker_detach(void const *data, NDEBUG_UNUSED size_t data_size, UNUSED fr_time_t now, void *uctx)
301{
302 fr_coord_t *coord = talloc_get_type_abort(uctx, fr_coord_t);
304 fr_coord_msg_t ack;
305 uint32_t thread_id, i;
306
307 fr_assert(data_size == sizeof(fr_coord_worker_detach_msg_t));
308 fr_assert((msg->worker >= MIN_WORKER_ID) && (msg->worker < (int32_t)coord->max_workers));
309 thread_id = msg->worker - MIN_WORKER_ID;
310
311 DEBUG2("Worker %d detached from %s", msg->worker, coord->coord_reg->name);
312 coord->num_workers--;
313 if (msg->exiting) coord->exiting = true;
314
315 /*
316 * If all workers have detached, and we're exiting, run any exit callbacks.
317 */
318 if (coord->exiting && (coord->num_workers == 0)) {
319 fr_coord_cb_inst_t *cb_inst;
320 for (i = 0; i < coord->num_callbacks; i++) {
321 cb_inst = coord->cb_inst[i];
322 if (!cb_inst || !cb_inst->exit_cb) continue;
323 cb_inst->exit_cb(coord, coord->el, cb_inst->inst_data);
324 }
325 }
326
327 ack.worker = msg->worker;
330
331 coord->coord_send_control[thread_id] = NULL;
332 coord->coord_send_aq[thread_id] = NULL;
333}
334
335/** Create a coordinator from its registration
336 *
337 * @param ctx to allocate the coordinator in
338 * @param el Event list to run this coordinator
339 * @param coord_reg Registration to configure this coordinator
340 * @param single_thread Is the server in single thread mode
341 * @param max_workers The maximum number of workers which will attach
342 * @return
343 * - the coordinator on success
344 * - NULL on failure
345 */
346static fr_coord_t *fr_coord_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_coord_reg_t *coord_reg,
347 bool single_thread, uint32_t max_workers)
348{
349 fr_coord_t *coord;
350 uint32_t i, num_threads = max_workers - MIN_WORKER_ID;
351 fr_coord_cb_reg_t *cb = coord_reg->coord_cb;
352
353 MEM(coord = talloc(ctx, fr_coord_t));
354 *coord = (fr_coord_t) {
355 .el = el,
356 .coord_reg = coord_reg,
357 .single_thread = single_thread,
358 .max_workers = max_workers
359 };
360
361 /* Allocate the control plane for receiving messages from workers */
363 if (!coord->coord_recv_control) {
364 fr_strerror_const("Failed creating worker -> coordinator control plane");
365 fail:
366 talloc_free(coord);
367 return NULL;
368 }
369
370 /* Allocate atomic queue for workers sending data to coordinators */
371 coord->coord_recv_aq = fr_atomic_queue_talloc(coord, FR_CONTROL_MAX_MESSAGES, sizeof(uintptr_t));
372 if (!coord->coord_recv_aq) {
373 fr_strerror_const("Failed creating worker -> coordinator data atomic queue");
374 goto fail;
375 }
376
382 coord_data_recv, coord) < 0) goto fail;
383
384 /* Count the number of callbacks defined, for sanity checking messages */
385 while (cb->callback) {
386 coord->num_callbacks++;
387 cb++;
388 }
389 coord->callbacks = coord_reg->coord_cb;
390
391 if (fr_control_open(coord->coord_recv_control) < 0) {
392 fr_strerror_const("Failed opening control plane");
393 goto fail;
394 }
395
396 /*
397 * Coordinator side arrays for holding pointers to worker
398 * specific communication structures. The array sizes are the
399 * number of threads expected to attach which is the number of
400 * workers plus any additional threads, currently just the main
401 * thread (worker id -1)
402 */
403 MEM(coord->coord_send_ms = talloc_array(coord, fr_message_set_t *, num_threads));
404 for (i = 0; i < num_threads; i++) {
406 coord_reg->coord_send_size, true);
407 if (!coord->coord_send_ms[i]) goto fail;
408 }
409 MEM(coord->coord_send_control = talloc_zero_array(coord, fr_control_t *, num_threads));
410 MEM(coord->coord_send_aq = talloc_zero_array(coord, fr_atomic_queue_t *, num_threads));
411
412 MEM(coord->cb_inst = talloc_zero_array(coord, fr_coord_cb_inst_t *, coord->num_callbacks));
413
414 for (i = 0; i < coord->num_callbacks; i++) {
415 if (!coord->callbacks[i].inst_create) continue;
416 coord->cb_inst[i] = coord->callbacks[i].inst_create(coord, coord, coord->el, coord->single_thread,
417 coord->callbacks[i].uctx);
418 if (!coord->cb_inst[i]) goto fail;
419 }
420
421 return coord;
422}
423
424static void fr_coord_destroy(fr_coord_t *coord){
425 uint32_t i;
426
427 for (i = 0; i < coord->num_callbacks; i++) {
428 if (!coord->callbacks[i].inst_destroy) continue;
429 coord->callbacks[i].inst_destroy(coord, coord->cb_inst[i], coord->single_thread,
430 coord->callbacks[i].uctx);
431 }
432}
433
434/** Run the event loop for a coordinator thread when in multi-threaded mode
435 */
436static void fr_coordinate(fr_coord_t *coord)
437{
438 uint32_t i;
439 fr_coord_cb_inst_t *cb_inst;
440
441 /*
442 * Run until we're told to exit AND the number of
443 * workers has dropped to zero.
444 *
445 * Whenever a worker detaches, coord->num_workers
446 * is decremented, so when coord->num_workers == 0,
447 * all workers have detached and are no longer using
448 * the channel.
449 */
450 while (true) {
451 bool wait_for_events = true;
452 int num_events;
453 fr_time_t now = fr_time();
454
455 /*
456 * Check if any coordinator instances report that they have
457 * events to process.
458 */
459 for (i = 0; i < coord->num_callbacks; i++) {
460 cb_inst = coord->cb_inst[i];
461 if (!cb_inst || !cb_inst->event_pre_cb) continue;
462 wait_for_events = (cb_inst->event_pre_cb(now, fr_time_delta_wrap(0), cb_inst->inst_data) == 0);
463 if (!wait_for_events) break;
464 }
465
466 if (wait_for_events) {
467 if (unlikely(coord->exiting) && (coord->num_workers == 0)) break;
468 }
469
470 /*
471 * Check the event list. If there's an error
472 * (e.g. exit), we stop looping and clean up.
473 */
474 DEBUG4("Gathering events");
475 num_events = fr_event_corral(coord->el, fr_time(), true);
476 DEBUG4("%d event(s) pending%s",
477 num_events == -1 ? 0 : num_events, num_events == -1 ? " - event loop exiting" : "");
478 if (num_events < 0) break;
479
480 /*
481 * Service outstanding events.
482 */
483 if (num_events > 0) {
484 DEBUG4("Servicing event(s)");
485 fr_event_service(coord->el);
486 }
487
488 /*
489 * Run any registered instance specific event callbacks
490 */
491 for (i = 0; i < coord->num_callbacks; i++) {
492 cb_inst = coord->cb_inst[i];
493 if (cb_inst && cb_inst->event_post_cb) cb_inst->event_post_cb(coord->el, now, cb_inst->inst_data);
494 }
495 }
496
497 fr_coord_destroy(coord);
498
499 return;
500}
501
502/** Entry point for a coordinator thread
503 */
504static void *fr_coordinate_thread(void *arg)
505{
506 fr_schedule_coord_t *sc = talloc_get_type_abort(arg, fr_schedule_coord_t);
507 fr_coord_reg_t *coord_reg = sc->coord_reg;
509 char coordinate_name[64];
510
511 snprintf(coordinate_name, sizeof(coordinate_name), "Coordinate %s", coord_reg->name);
512
513 if (fr_thread_setup(&sc->thread, coordinate_name) < 0) goto fail;
514
515 sc->coord = fr_coord_create(sc->thread.ctx, sc->thread.el, coord_reg, false, sc->max_workers);
516 if (!sc->coord) {
517 PERROR("%s - Failed creating coordinator thread", coordinate_name);
518 goto fail;
519 }
520
521 /*
522 * Create all the thread specific data for the coordinator thread
523 */
524 if (fr_thread_instantiate(sc->thread.ctx, sc->thread.el) < 0) goto fail;
525
526 /*
527 * Tell the originator that the thread has started.
528 */
529 fr_thread_start(&sc->thread, sc->sem);
530
531 fr_coordinate(sc->coord);
532
533 status = FR_THREAD_EXITED;
534
535fail:
537
538 fr_thread_exit(&sc->thread, status, sc->sem);
539
540 return NULL;
541}
542
543/** Start all registered coordinator threads in multi-threaded mode
544 *
545 * @param num_workers The number of workers which will be attaching
546 * @param sem Semaphore to use signalling the threads are ready
547 * @return
548 * - 0 on success
549 * - -1 on failure
550 */
552{
553 unsigned int num = 0;
554
555 if (!coord_regs) return 0;
556
557 MEM(coord_threads = talloc(NULL, fr_dlist_head_t));
560
563
564 MEM(sc = talloc_zero(coord_threads, fr_schedule_coord_t));
565
566 sc->thread.id = num++;
567 sc->coord_reg = coord_reg;
568 sc->max_workers = num_workers;
569 sc->sem = sem;
570
571 if (fr_thread_create(&sc->thread.pthread_id, fr_coordinate_thread, sc) < 0) {
573 PERROR("Failed creating coordinator %s", coord_reg->name);
574 return -1;
575 }
576
578 }
579
580 /*
581 * Wait for all the coordinators to start.
582 */
583 if (fr_thread_wait_list(sem, coord_threads) < 0) {
584 ERROR("Failed creating coordinator threads");
585 return -1;
586 }
587
588 /*
589 * Insert the coordinators in the tree
590 */
592 fr_assert(sc->coord);
593 fr_rb_insert(&coords, sc->coord);
594 }
595
596 return 0;
597}
598
599/** Clean up coordinators in single threaded mode
600 */
602{
603 fr_coord_t *coord;
605
606 if (fr_rb_num_elements(&coords) == 0) return;
607
608 for (coord = fr_rb_iter_init_inorder(&coords, &iter);
609 coord;
610 coord = fr_rb_iter_next_inorder(&coords, &iter)) {
612 fr_coord_destroy(coord);
613 talloc_free(coord);
614 }
615}
616
617/** Start coordinators in single threaded mode
618 */
619int fr_coords_create(TALLOC_CTX *ctx, fr_event_list_t *el)
620{
621 if (!coord_regs) return 0;
622
624
626 char coordinate_name[64];
627 fr_coord_t *coord;
628
629 snprintf(coordinate_name, sizeof(coordinate_name), "Coordinator %s", coord_reg->name);
630
631 INFO("%s - Starting", coordinate_name);
632
633 coord = fr_coord_create(ctx, el, coord_reg, true, 1);
634 if (!coord) {
635 PERROR("%s - Failed creating coordinator thread", coordinate_name);
636 return -1;
637 }
638
639 fr_rb_insert(&coords, coord);
640 }
641
642 return 0;
643}
644
645/** Signal a coordinator that a worker wants to detach
646 *
647 * @param cw Worker which is detaching.
648 * @param exiting Is the server exiting.
649 */
650int fr_coord_detach(fr_coord_worker_t *cw, bool exiting)
651{
653
654 msg = talloc(cw, fr_coord_worker_detach_msg_t);
655 msg->worker = fr_schedule_worker_id();
656 msg->exiting = exiting;
657
660 msg, sizeof(fr_coord_worker_detach_msg_t)) < 0) return -1;
661
663
664 return 0;
665}
666
667/** A worker got an ack from a coordinator in response to attach / detach
668 */
669static void coordinate_worker_ack(NDEBUG_UNUSED void const *data, NDEBUG_UNUSED size_t data_size,
670 UNUSED fr_time_t now, UNUSED void *uctx)
671{
672#ifndef NDEBUG
673 fr_coord_msg_t const *cm = data;
674
675 fr_assert(data_size == sizeof(fr_coord_msg_t));
677#endif
678}
679
680/** Attach a worker to a coordinator
681 *
682 * @param ctx To allocate worker structure in
683 * @param el Event list for control messages
684 * @param coord_reg Coordinator registration to attach to.
685 * @return
686 * - Worker structure for coordinator use on success
687 * - NULL on failure
688 */
690{
692 fr_coord_worker_cb_reg_t *cb_reg = coord_reg->worker_cb;
694 fr_coord_t find;
695
696 cw = talloc_zero(ctx, fr_coord_worker_t);
697
698 find = (fr_coord_t) {
699 .coord_reg = coord_reg
700 };
701 fr_rb_find((void **)&cw->coord, &coords, &find);
702 if (!cw->coord) {
703 fail:
704 talloc_free(cw);
705 return NULL;
706 }
707
711 coord_reg->worker_send_size, true);
712
713 while (cb_reg->callback) {
714 cw->num_callbacks++;
715 cb_reg++;
716 }
717 cw->callbacks = coord_reg->worker_cb;
718
720 coordinate_worker_ack, cw) < 0) goto fail;
722 coord_worker_data_recv, cw) < 0) goto fail;
723
724 if (fr_control_open(cw->worker_recv_control) < 0) goto fail;
725
726 msg.worker_recv_control = cw->worker_recv_control;
727 msg.worker_recv_aq = cw->worker_recv_aq;
728 msg.worker = fr_schedule_worker_id();
729
732 &msg, sizeof(fr_coord_worker_attach_msg_t)) < 0) goto fail;
733
735
736 return cw;
737}
738
739/** Send generic data from a coordinator to a worker
740 *
741 * @param coord Coordinator which is sending the data.
742 * @param worker_id Worker to send data to.
743 * @param cb_id Callback ID for the worker to run.
744 * @param dbuff Buffer containing data to send.
745 * @return
746 * - 0 on success
747 * - <0 on failure
748 */
750{
752 fr_coord_data_t *cd = NULL;
753 uint32_t thread_id = worker_id - MIN_WORKER_ID;
754
755 fr_assert((worker_id >= MIN_WORKER_ID) && (worker_id < (int32_t)coord->max_workers));
756
757 cm = (fr_coord_msg_t) {
759 };
760
761 cd = (fr_coord_data_t *)fr_message_and_data_alloc(coord->coord_send_ms[thread_id], fr_dbuff_used(dbuff));
762 if (!cd) return -1;
763
764 memcpy(cd->m.data, fr_dbuff_buff(dbuff), fr_dbuff_used(dbuff));
765 cd->coord_cb_id = cb_id;
766 if (!fr_atomic_queue_spsc_push(coord->coord_send_aq[thread_id], &cd)) {
768 return -1;
769 }
770 return fr_control_message_send(coord->coord_send_control[thread_id],
772 &cm, sizeof(fr_coord_msg_t));
773}
774
775/** Broadcast data from a coordinator to all workers
776 *
777 * @param coord Coordinator which is sending the data.
778 * @param cb_id Callback ID for the workers to run.
779 * @param dbuff Buffer containing data to send.
780 * @return
781 * - 0 on success
782 * - <0 on failure - indicating the number of sends which failed.
783 */
785{
786 int32_t i;
787 int failed = 0;
788
789 for (i = 0; i < (int32_t)coord->max_workers; i++) {
790 if (!coord->coord_send_control[i - MIN_WORKER_ID]) continue;
791 if (fr_coord_to_worker_send(coord, i, cb_id, dbuff) < 0) failed++;
792 }
793
794 return 0 - failed;
795}
796
797/** Send data from a worker to a coordinator
798 *
799 * @param cw Worker side of coordinator sending the data.
800 * @param cb_id Callback ID for the coordinator to run.
801 * @param dbuff Buffer containing data to send.
802 * @return
803 * - 0 on success
804 * - < 0 on failure
805 */
807{
809 fr_coord_data_t *cd = NULL;
810
811 cm = (fr_coord_msg_t) {
813 };
814
816 if (!cd) return -1;
817
818 memcpy(cd->m.data, fr_dbuff_buff(dbuff), fr_dbuff_used(dbuff));
819 cd->coord_cb_id = cb_id;
822 return -1;
823 }
824
827}
828
829/** Insert instance specific pre-event callbacks
830 */
832{
833 fr_coord_t *coord;
835 fr_coord_cb_inst_t *cb_inst;
836 uint32_t i;
837
838 if (!coord_regs) return 0;
839
840 for (coord = fr_rb_iter_init_inorder(&coords, &iter);
841 coord != NULL;
842 coord = fr_rb_iter_next_inorder(&coords, &iter)) {
843 for (i = 0; i < coord->num_callbacks; i++) {
844 cb_inst = coord->cb_inst[i];
845 if (cb_inst && cb_inst->event_pre_cb &&
846 fr_event_pre_insert(el, cb_inst->event_pre_cb, cb_inst->inst_data) < 0) {
847 return -1;
848 }
849 }
850 }
851 return 0;
852}
853
854/** Insert instance specific post-event callbacks
855 */
857{
858 fr_coord_t *coord;
860 fr_coord_cb_inst_t *cb_inst;
861 uint32_t i;
862
863 if (!coord_regs) return 0;
864
865 for (coord = fr_rb_iter_init_inorder(&coords, &iter);
866 coord != NULL;
867 coord = fr_rb_iter_next_inorder(&coords, &iter)) {
868 for (i = 0; i < coord->num_callbacks; i++) {
869 cb_inst = coord->cb_inst[i];
870 if (cb_inst && cb_inst->event_post_cb &&
871 fr_event_post_insert(el, cb_inst->event_post_cb, cb_inst->inst_data) < 0) {
872 return -1;
873 }
874 }
875 }
876 return 0;
877}
878
879/** Event loop callback to exit the loop when all workers have detached from all coordinators
880 *
881 * Used during single threaded shut down to allow the event loop to run any
882 * tidy up needed by coordinators.
883 */
885{
886 fr_coord_t *coord;
888
889 if (fr_rb_num_elements(&coords) == 0) return;
890
891 for (coord = fr_rb_iter_init_inorder(&coords, &iter);
892 coord;
893 coord = fr_rb_iter_next_inorder(&coords, &iter)) {
894 if (coord->num_workers > 0) return;
895 }
896
897 fr_event_loop_exit(el, SIGINT);
898}
899
904
905/** Return the coordinator name
906 */
907char const *fr_coord_name(fr_coord_t const *coord)
908{
909 return coord->coord_reg->name;
910}
log_entry msg
Definition acutest.h:947
bool fr_atomic_queue_mpmc_push(fr_atomic_queue_t *aq, void const *data)
Copy an element into the atomic queue, any number of producers.
bool fr_atomic_queue_spsc_push(fr_atomic_queue_t *aq, void const *data)
Copy an element into the atomic queue, one producer only.
fr_atomic_queue_t * fr_atomic_queue_talloc(TALLOC_CTX *ctx, size_t num, size_t elem_len)
Create a fixed-size atomic queue.
bool fr_atomic_queue_spsc_pop(fr_atomic_queue_t *aq, void *data)
Copy an element out of the atomic queue, one consumer only.
Structure to hold the atomic queue.
#define RCSID(id)
Definition build.h:603
#define NDEBUG_UNUSED
Definition build.h:426
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:130
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
fr_cmp_ret_t
Result of an ordering comparison.
Definition build.h:113
@ FR_CONTROL_EVENTSET_COORD
Events the coordinator defines.
Definition control.h:66
#define FR_CONTROL_MAX_SIZE
Definition control.h:76
#define FR_CONTROL_MAX_MESSAGES
Definition control.h:75
static size_t num_workers
fr_dlist_t entry
Entry in list of registrations.
Definition coord.c:95
fr_atomic_queue_t * worker_recv_aq
Atomic queue for coordinator -> worker messages.
Definition coord.c:86
uint32_t max_workers
Maximum number of workers which will connect to this coordinator.
Definition coord.c:108
fr_control_t ** coord_send_control
Control planes for coordinator -> worker messages.
Definition coord.c:72
fr_coord_reg_t * coord_reg
Coordinator registration details.
Definition coord.c:109
fr_coord_worker_cb_reg_t * worker_cb
Callbacks for coordinator -> worker messages.
Definition coord.c:97
fr_coord_reg_t * fr_coord_register(fr_coord_reg_ctx_t *reg_ctx)
Register a coordinator.
Definition coord.c:145
int fr_coords_create(TALLOC_CTX *ctx, fr_event_list_t *el)
Start coordinators in single threaded mode.
Definition coord.c:619
fr_control_t * worker_recv_control
Control plane to send messages to this worker.
Definition coord.c:118
static fr_dlist_head_t * coord_threads
Definition coord.c:54
static fr_coord_t * fr_coord_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_coord_reg_t *coord_reg, bool single_thread, uint32_t max_workers)
Create a coordinator from its registration.
Definition coord.c:346
fr_coord_worker_t * fr_coord_attach(TALLOC_CTX *ctx, fr_event_list_t *el, fr_coord_reg_t *coord_reg)
Attach a worker to a coordinator.
Definition coord.c:689
fr_coord_reg_t * coord_reg
Coordinator registration details.
Definition coord.c:60
size_t worker_send_size
Initial size for worker -> coordinator ring buffer.
Definition coord.c:98
size_t coord_send_size
Initial size for coordinator -> worker ring buffer.
Definition coord.c:99
int32_t worker
Worker ID.
Definition coord.c:117
#define MIN_WORKER_ID
The minimum value we expect as worker id. -1 is the main thread.
Definition coord.c:51
int fr_coord_to_worker_send(fr_coord_t *coord, int32_t worker_id, uint32_t cb_id, fr_dbuff_t *dbuff)
Send generic data from a coordinator to a worker.
Definition coord.c:749
char const * fr_coord_name(fr_coord_t const *coord)
Return the coordinator name.
Definition coord.c:907
uint32_t max_workers
Maximum number of workers we expect.
Definition coord.c:67
bool exiting
Is the server exiting.
Definition coord.c:124
fr_atomic_queue_t * worker_recv_aq
Atomic queue to send data to this worker.
Definition coord.c:119
fr_coord_t * coord
Coordinator this worker is related to.
Definition coord.c:83
fr_coord_control_event_t
Events on a coordinator's control plane, sent by workers, in the FR_CONTROL_EVENTSET_COORD event set.
Definition coord.c:38
@ FR_COORD_CONTROL_DATA
Worker -> coordinator message to pass data to a callback.
Definition coord.c:41
@ FR_COORD_CONTROL_WORKER_ATTACH
Message sent from worker to attach to a coordinator.
Definition coord.c:39
@ FR_COORD_CONTROL_WORKER_DETACH
Message sent from worker to detach from a coordinator.
Definition coord.c:40
fr_coord_cb_inst_t ** cb_inst
Array of callback instance specific data.
Definition coord.c:65
void fr_coord_deregister(fr_coord_reg_t *coord_reg)
De-register a coordinator.
Definition coord.c:180
void fr_coords_destroy(void)
Clean up coordinators in single threaded mode.
Definition coord.c:601
fr_control_t * coord_recv_control
Control plane for worker -> coordinator messages.
Definition coord.c:70
int fr_coord_close_event_insert(fr_event_list_t *el)
Definition coord.c:900
static void coordinate_worker_ack(NDEBUG_UNUSED void const *data, NDEBUG_UNUSED size_t data_size, UNUSED fr_time_t now, UNUSED void *uctx)
A worker got an ack from a coordinator in response to attach / detach.
Definition coord.c:669
int fr_worker_to_coord_send(fr_coord_worker_t *cw, uint32_t cb_id, fr_dbuff_t *dbuff)
Send data from a worker to a coordinator.
Definition coord.c:806
void fr_coord_thread_join(void)
Wait for all the coordinator threads to exit.
Definition coord.c:193
static fr_dlist_head_t * coord_regs
Definition coord.c:53
module_instance_t const * mi
Module instance which registered this coordinator.
Definition coord.c:100
fr_coord_cb_reg_t * callbacks
Array of callbacks for worker -> coordinator messages.
Definition coord.c:63
int fr_coord_post_event_insert(fr_event_list_t *el)
Insert instance specific post-event callbacks.
Definition coord.c:856
int fr_coord_pre_event_insert(fr_event_list_t *el)
Insert instance specific pre-event callbacks.
Definition coord.c:831
uint32_t num_callbacks
Number of callbacks registered.
Definition coord.c:88
fr_coord_cb_reg_t * coord_cb
Callbacks for worker -> coordinator messages.
Definition coord.c:96
fr_atomic_queue_t * coord_recv_aq
Atomic queue for worker -> coordinator.
Definition coord.c:71
static void * fr_coordinate_thread(void *arg)
Entry point for a coordinator thread.
Definition coord.c:504
fr_atomic_queue_t ** coord_send_aq
Atomic queues for coordinator -> worker data.
Definition coord.c:74
uint32_t num_callbacks
Number of callbacks defined.
Definition coord.c:64
static void coord_worker_attach(void const *data, NDEBUG_UNUSED size_t data_size, UNUSED fr_time_t now, void *uctx)
Callback run by a coordinator when a worker attaches.
Definition coord.c:277
bool exiting
Is this coordinator shutting down.
Definition coord.c:76
static void coord_data_recv(void const *data, size_t data_size, fr_time_t now, void *uctx)
Callback for a coordinator receiving data from a worker.
Definition coord.c:213
fr_rb_node_t node
Entry in the tree of coordinators.
Definition coord.c:62
fr_coord_worker_cb_reg_t * callbacks
Callbacks for coordinator -> worker messages.
Definition coord.c:87
fr_event_list_t * el
Coordinator event list.
Definition coord.c:61
int fr_coord_start(uint32_t num_workers, fr_sem_t *sem)
Start all registered coordinator threads in multi-threaded mode.
Definition coord.c:551
fr_message_set_t ** coord_send_ms
Message sets for coordinator -> worker data.
Definition coord.c:73
static void coord_worker_detach(void const *data, NDEBUG_UNUSED size_t data_size, UNUSED fr_time_t now, void *uctx)
Callback run by a coordinator when a worker detaches.
Definition coord.c:300
fr_thread_t thread
common thread information - must be first!
Definition coord.c:106
static void fr_coord_close_post_event(fr_event_list_t *el, UNUSED fr_time_t now, UNUSED void *uctx)
Event loop callback to exit the loop when all workers have detached from all coordinators.
Definition coord.c:884
static void fr_coordinate(fr_coord_t *coord)
Run the event loop for a coordinator thread when in multi-threaded mode.
Definition coord.c:436
fr_control_t * worker_recv_control
Coordinator -> worker control plane.
Definition coord.c:85
bool single_thread
Are we in single thread mode.
Definition coord.c:77
char const * name
Name for debugging.
Definition coord.c:94
static void coord_worker_data_recv(void const *data, size_t data_size, fr_time_t now, void *uctx)
Callback for a worker receiving data from a coordinator.
Definition coord.c:247
int fr_coord_to_worker_broadcast(fr_coord_t *coord, uint32_t cb_id, fr_dbuff_t *dbuff)
Broadcast data from a coordinator to all workers.
Definition coord.c:784
int fr_coord_detach(fr_coord_worker_t *cw, bool exiting)
Signal a coordinator that a worker wants to detach.
Definition coord.c:650
int32_t worker
Worker ID.
Definition coord.c:123
fr_coord_worker_control_event_t
Events on a coordinator worker's control plane, sent by the coordinator, in the FR_CONTROL_EVENTSET_C...
Definition coord.c:46
@ FR_COORD_WORKER_CONTROL_ACK
Message sent to worker to acknowledge attach / detach.
Definition coord.c:47
@ FR_COORD_WORKER_CONTROL_DATA
Coordinator -> worker message to pass data to a callback.
Definition coord.c:48
static fr_cmp_ret_t coord_cmp(void const *one, void const *two)
Compare coordinators by registration.
Definition coord.c:129
fr_coord_t * coord
The coordinator data structure.
Definition coord.c:110
static fr_rb_tree_t coords
Definition coord.c:55
uint32_t num_workers
How many workers are attached.
Definition coord.c:68
fr_message_set_t * worker_send_ms
Message set for worker -> coordinator messages.
Definition coord.c:84
static void fr_coord_destroy(fr_coord_t *coord)
Definition coord.c:424
fr_sem_t * sem
For inter-thread signaling.
Definition coord.c:111
A coordinator registration.
Definition coord.c:93
A coordinator which receives messages from workers.
Definition coord.c:59
Control plane message used for workers attaching / detaching to coordinators.
Definition coord.c:116
The worker end of worker <-> coordinator communication.
Definition coord.c:82
Scheduler specific information for coordinator threads.
Definition coord.c:105
fr_coord_cb_t callback
Callback to run when message is received.
Definition coord.h:49
fr_coord_cb_reg_t * coord_cb
Callbacks for worker -> coordinator messages.
Definition coord.h:65
struct fr_coord_s fr_coord_t
Definition coord.h:38
void * uctx
To pass to all callbacks.
Definition coord.h:52
char const * name
Name for this coordinator.
Definition coord.h:64
module_instance_t const * mi
Module instance registering this coordinator.
Definition coord.h:71
fr_coord_worker_cb_t callback
Definition coord.h:57
size_t coord_send_size
Initial ring buffer size for coordinator -> worker data.
Definition coord.h:69
struct fr_coord_reg_s fr_coord_reg_t
Definition coord.h:36
fr_coord_worker_cb_reg_t * worker_cb
Callbacks for coordinator -> worker messages.
Definition coord.h:66
size_t worker_send_size
Initial ring buffer size for worker -> coordinator data.
Definition coord.h:67
fr_coord_cb_inst_create_t inst_create
Callback to create coordinator instance.
Definition coord.h:50
fr_coord_cb_inst_destroy_t inst_destroy
Callback to destroyed coordinator instance.
Definition coord.h:51
fr_coord_inst_exit_cb_t exit_cb
Callback run when coordinator is asked to exit.
Definition coord_priv.h:49
void * inst_data
Instance data.
Definition coord_priv.h:46
int32_t worker
Worker ID.
Definition coord_priv.h:33
uint32_t coord_cb_id
Callback ID for this message.
Definition coord_priv.h:40
fr_event_status_cb_t event_pre_cb
Pre-event callback.
Definition coord_priv.h:47
fr_event_post_cb_t event_post_cb
Post-event callback.
Definition coord_priv.h:48
fr_message_t m
Message containing data being sent.
Definition coord_priv.h:39
List / data message used between workers and coordinators.
Definition coord_priv.h:38
Generic control message used between workers and coordinators.
Definition coord_priv.h:32
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:819
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:387
#define fr_dbuff_buff(_dbuff_or_marker)
Return the underlying buffer in a dbuff or one of marker.
Definition dbuff.h:934
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:98
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(hp)
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
int fr_control_callback_add(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, fr_control_callback_t callback, void *uctx)
Register a callback for a message.
Definition control.c:659
int fr_control_message_send(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, void const *data, size_t data_size)
Send a control-plane message.
Definition control.c:544
void fr_control_wait(fr_control_t *c)
Wait for a plane control to become readable.
Definition control.c:903
The control structure.
Definition control.c:211
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG3(_fmt,...)
Definition log.h:271
#define DEBUG4(_fmt,...)
Definition log.h:272
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
int fr_event_pre_insert(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Add a pre-event callback to the event list.
Definition event.c:1996
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2423
int fr_event_post_insert(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Add a post-event callback to the event list.
Definition event.c:2043
Stores all information relating to an event list.
Definition event.c:375
unsigned short uint16_t
unsigned int uint32_t
unsigned char uint8_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
fr_message_t * fr_message_and_data_commit(fr_message_set_t *ms, fr_message_t *m, size_t total_size)
Commit a previously reserved message, allocating exactly total_size bytes of packet data.
Definition message.c:1075
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
uint8_t * data
pointer to the data in the ring buffer
Definition message.h:49
size_t data_size
size of the data in the ring buffer
Definition message.h:50
#define MODULE_CTX(_mi, _thread, _env_data, _rctx)
Wrapper to create a module_ctx_t as a compound literal.
Definition module_ctx.h:128
#define fr_assert(_expr)
Definition rad_assert.h:37
#define DEBUG2(fmt,...)
#define INFO(fmt,...)
Definition radict.c:67
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:807
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:850
void fr_rb_iter_delete_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Remove the current node from the tree.
Definition rb.c:925
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:876
struct fr_rb_tree_s fr_rb_tree_t
Definition rb.h:51
#define fr_rb_inline_talloc_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black that verifies elements are of a specific talloc type.
Definition rb.h:155
uint32_t num_elements
How many elements are inside the tree.
Definition rb.h:95
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
The main red black tree structure.
Definition rb.h:71
static _Thread_local int worker_id
Internal ID of the current worker thread.
Definition schedule.c:105
int fr_schedule_worker_id(void)
Return the worker id for the current thread.
Definition schedule.c:111
sem_t fr_sem_t
Definition semaphore.h:53
void * data
Thread specific instance data.
Definition module.h:378
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:517
Module instance data.
Definition module.h:291
static const uchar sc[16]
Definition smbdes.c:115
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
void fr_thread_start(fr_thread_t *thread, fr_sem_t *sem)
Signal the parent that we're done.
Definition thread.c:216
int fr_thread_wait_list(fr_sem_t *sem, fr_dlist_head_t *head)
Wait for multiple threads to signal readiness via a semaphore.
Definition thread.c:79
int fr_thread_create(pthread_t *thread, fr_thread_entry_t func, void *arg)
Create a joinable thread.
Definition thread.c:46
int fr_thread_setup(fr_thread_t *out, char const *name)
Common setup for child threads: block signals, allocate a talloc context, and create an event list.
Definition thread.c:112
int fr_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
Instantiate thread-specific data for modules, virtual servers, xlats, unlang, and TLS.
Definition thread.c:177
void fr_thread_detach(void)
Detach thread-specific data for modules, virtual servers, xlats.
Definition thread.c:202
void fr_thread_exit(fr_thread_t *thread, fr_thread_status_t status, fr_sem_t *sem)
Signal the parent that we're done.
Definition thread.c:231
fr_thread_status_t
Track the child thread status.
Definition thread.h:38
@ FR_THREAD_EXITED
exited, and in the exited queue
Definition thread.h:42
@ FR_THREAD_FAIL
failed, and in the exited queue
Definition thread.h:43
#define fr_time_delta_wrap(_time)
Definition time.h:152
"server local" time.
Definition time.h:69
static fr_event_list_t * el
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1366