The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
schedule.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: 989f2d3c4dba7e6218ea9a53c57e91948d043f59 $
19 *
20 * @brief Network / worker thread scheduling
21 * @file io/schedule.c
22 *
23 * @copyright 2016 Alan DeKok (aland@freeradius.org)
24 */
25RCSID("$Id: 989f2d3c4dba7e6218ea9a53c57e91948d043f59 $")
26
27#define LOG_DST sc->log
28
29#include <freeradius-devel/autoconf.h>
30
31#include <freeradius-devel/io/schedule.h>
32#include <freeradius-devel/io/thread.h>
33#include <freeradius-devel/util/dlist.h>
34#include <freeradius-devel/util/rb.h>
35#include <freeradius-devel/util/syserror.h>
36#include <freeradius-devel/server/module_rlm.h>
37#include <freeradius-devel/server/trigger.h>
38#include <freeradius-devel/util/semaphore.h>
39
40#include <pthread.h>
41
42/** Scheduler specific information for worker threads
43 *
44 * Wraps a fr_worker_t, tracking additional information that
45 * the scheduler uses.
46 */
47typedef struct {
48 fr_thread_t thread; //!< common thread structure - must be first!
49
50 int uses; //!< how many network threads are using it
51 fr_time_t cpu_time; //!< how much CPU time this worker has used
52
53 fr_schedule_t *sc; //!< the scheduler we are running under
54
55 fr_worker_t *worker; //!< the worker data structure
57
58/** Scheduler specific information for network threads
59 *
60 * Wraps a fr_network_t, tracking additional information that
61 * the scheduler uses.
62 */
63typedef struct {
64 fr_thread_t thread; //!< common thread structure - must be first!
65
66 fr_schedule_t *sc; //!< the scheduler we are running under
67
68 fr_network_t *nr; //!< the receive data structure
69
70 fr_timer_t *ev; //!< timer for stats_interval
72
73
74/**
75 * The scheduler
76 */
78 bool running; //!< is the scheduler running?
79
80 CONF_SECTION *cs; //!< thread pool configuration section
81 fr_event_list_t *el; //!< event list for single-threaded mode.
82 bool single_threaded; //!< true if running in single-threaded mode.
83
84 fr_log_t *log; //!< log destination
85 fr_log_lvl_t lvl; //!< log level
86
87 fr_schedule_config_t *config; //!< configuration
88
89 unsigned int num_workers_exited; //!< number of exited workers
90
91 fr_sem_t *worker_sem; //!< for inter-thread signaling
92 fr_sem_t *network_sem; //!< for inter-thread signaling
93 fr_sem_t *coord_sem; //!< for inter-thread signaling
94
97
98 fr_dlist_head_t workers; //!< list of workers
99 fr_dlist_head_t networks; //!< list of networks
100
101 fr_network_t *single_network; //!< for single-threaded mode
102 fr_worker_t *single_worker; //!< for single-threaded mode
103};
104
105static _Thread_local int worker_id = -1; //!< Internal ID of the current worker thread.
106
107/** Return the worker id for the current thread
108 *
109 * @return worker ID
110 */
112{
113 return worker_id;
114}
115
116/** Explicitly set the worker id for the current thread
117 *
118 * **Only to be used in test programs like unit_test_module**
119 */
121{
122 worker_id = id;
123}
124
125/** Unify the "clean up worker" path.
126 *
127 */
128static void fr_schedule_worker_exit(fr_worker_t **p_worker, fr_schedule_thread_detach_t worker_thread_detach)
129{
130 fr_worker_t *worker = *p_worker;
131
132 *p_worker = NULL;
133
134 /*
135 * Mark the interpreter as shutting down before the thread data is detached, so that
136 * requests are cancelled, and are not run.
137 */
138 if (worker) fr_worker_shutting_down(worker);
139
140 /*
141 * Detach the thread-local data before the worker is freed. This call frees the
142 * thread-local trunks, connections and their timers and file descriptors. Requests
143 * which hold those connections are cancelled.
144 */
145 if (worker_thread_detach) worker_thread_detach(NULL);
146
147 if (worker) fr_worker_exit(worker);
148}
149
150/** Entry point for worker threads
151 *
152 * @param[in] arg the fr_schedule_worker_t
153 * @return NULL
154 */
155static void *fr_schedule_worker_thread(void *arg)
156{
157 fr_schedule_worker_t *sw = talloc_get_type_abort(arg, fr_schedule_worker_t);
158 fr_schedule_t *sc = sw->sc;
160 char worker_name[32];
161
162 worker_id = sw->thread.id; /* Store the current worker ID */
163
164 snprintf(worker_name, sizeof(worker_name), "Worker %u", sw->thread.id);
165
166#ifdef HAVE_PTHREAD_SETNAME_NP
167# ifdef __APPLE__
168 pthread_setname_np(worker_name);
169# else
170 pthread_setname_np(pthread_self(), worker_name);
171# endif
172#endif
173
174 if (fr_thread_setup(&sw->thread, worker_name) < 0) goto fail;
175
176 sw->worker = fr_worker_alloc(sw->thread.ctx, sw->thread.el, worker_name, sc->log, sc->lvl, &sc->config->worker);
177 if (!sw->worker) {
178 PERROR("%s - Failed creating worker", worker_name);
179 goto fail;
180 }
181
182 /*
183 * @todo make this a registry
184 */
185 if (sc->worker_thread_instantiate) {
186 CONF_SECTION *cs;
187 char section_name[32];
188
189 snprintf(section_name, sizeof(section_name), "%u", sw->thread.id);
190
191 cs = cf_section_find(sc->cs, "worker", section_name);
192 if (!cs) cs = cf_section_find(sc->cs, "worker", NULL);
193
194 if (sc->worker_thread_instantiate(sw->thread.ctx, sw->thread.el, cs) < 0) {
195 PERROR("%s - Worker thread instantiation failed", worker_name);
196 goto fail;
197 }
198 }
199
200 /*
201 * Add this worker to all network threads.
202 */
203 fr_dlist_foreach(&sc->networks, fr_schedule_network_t, sn) {
204 if (unlikely(fr_network_worker_add(sn->nr, sw->worker) < 0)) {
205 PERROR("%s - Failed adding worker to network %u", worker_name, sn->thread.id);
206 goto fail; /* FIXME - Should maybe try to undo partial adds? */
207 }
208 }
209
210 /*
211 * Tell the originator that the thread has started.
212 */
213 fr_thread_start(&sw->thread, sc->worker_sem);
214
215 /*
216 * Do all of the work.
217 */
218 fr_worker(sw->worker);
219
220 status = FR_THREAD_EXITED;
221
222fail:
223 fr_schedule_worker_exit(&sw->worker, sc->worker_thread_detach);
224
225 /*
226 * If we're exiting, there should be no events left for the worker to process.
227 */
229
230 fr_thread_exit(&sw->thread, status, sc->worker_sem);
231
232 return NULL;
233}
234
235
236static void stats_timer(fr_timer_list_t *tl, fr_time_t now, void *uctx)
237{
238 fr_schedule_network_t *sn = talloc_get_type_abort(uctx, fr_schedule_network_t);
239
240 fr_network_stats_log(sn->nr, sn->sc->log);
241
242 (void) fr_timer_at(sn, tl, &sn->ev, fr_time_add(now, sn->sc->config->stats_interval), false, stats_timer, sn);
243}
244
245/** Initialize and run the network thread.
246 *
247 * @param[in] arg the fr_schedule_network_t
248 * @return NULL
249 */
250static void *fr_schedule_network_thread(void *arg)
251{
252 fr_schedule_network_t *sn = talloc_get_type_abort(arg, fr_schedule_network_t);
253 fr_schedule_t *sc = sn->sc;
255 char network_name[32];
256
257 snprintf(network_name, sizeof(network_name), "Network %u", sn->thread.id);
258
259#ifdef HAVE_PTHREAD_SETNAME_NP
260# ifdef __APPLE__
261 pthread_setname_np(network_name);
262# else
263 pthread_setname_np(pthread_self(), network_name);
264# endif
265#endif
266
267 if (fr_thread_setup(&sn->thread, network_name) < 0) goto fail;
268
269 sn->nr = fr_network_create(sn->thread.ctx, sn->thread.el, network_name, sc->log, sc->lvl, &sc->config->network);
270 if (!sn->nr) {
271 PERROR("%s - Failed creating network", network_name);
272 goto fail;
273 }
274
275 /*
276 * Tell the originator that the thread has started.
277 */
278 fr_thread_start(&sn->thread, sc->network_sem);
279
280 /*
281 * Print out statistics for this network IO handler.
282 */
283 if (fr_time_delta_ispos(sc->config->stats_interval)) {
284 (void) fr_timer_in(sn, sn->thread.el->tl, &sn->ev, sn->sc->config->stats_interval, false, stats_timer, sn);
285 }
286
287 /*
288 * Call the main event processing loop of the network
289 * thread Will not return until the worker is about
290 * to exit.
291 */
292 fr_network(sn->nr);
293
294 status = FR_THREAD_EXITED;
295
296fail:
297 fr_thread_exit(&sn->thread, status, sc->network_sem);
298
299 return NULL;
300}
301
302/** Create a scheduler and spawn the child threads.
303 *
304 * @param[in] ctx talloc context.
305 * @param[in] single_threaded no workers are spawned, everything runs in a common event loop.
306 * @param[in] el event list, only for single-threaded mode.
307 * @param[in] logger destination for all logging messages.
308 * @param[in] lvl log level.
309 * @param[in] worker_thread_instantiate callback for new worker threads.
310 * @param[in] worker_thread_detach callback to destroy resources
311 * allocated by worker_thread_instantiate.
312 * @param[in] config configuration for the scheduler
313 * @return
314 * - NULL on error
315 * - fr_schedule_t new scheduler
316 */
318 bool single_threaded,
320 fr_log_t *logger, fr_log_lvl_t lvl,
321 fr_schedule_thread_instantiate_t worker_thread_instantiate,
322 fr_schedule_thread_detach_t worker_thread_detach,
324{
325 unsigned int i;
326 fr_schedule_worker_t *sw, *next_sw;
327 fr_schedule_network_t *sn, *next_sn;
329
330 sc = talloc_zero(ctx, fr_schedule_t);
331 if (!sc) {
332 fr_strerror_const("Failed allocating memory");
333 return NULL;
334 }
335
336 /*
337 * Parse any scheduler-specific configuration.
338 */
339 if (!config) {
340 MEM(sc->config = talloc_zero(sc, fr_schedule_config_t));
341 sc->config->max_networks = 1;
342 sc->config->max_workers = 4;
343 } else {
344 sc->config = config;
345
346 if (sc->config->max_networks < 1) sc->config->max_networks = 1;
347 if (sc->config->max_networks > 64) sc->config->max_networks = 64;
348 if (sc->config->max_workers < 1) sc->config->max_workers = 1;
349 if (sc->config->max_workers > 64) sc->config->max_workers = 64;
350 }
351
352 sc->el = el;
353 sc->single_threaded = single_threaded;
354 sc->log = logger;
355 sc->lvl = lvl;
356 sc->cs = sc->config->cs;
357
358 sc->worker_thread_instantiate = worker_thread_instantiate;
359 sc->worker_thread_detach = worker_thread_detach;
360 sc->running = true;
361
362 /*
363 * If we're single-threaded, create network / worker, and insert them into the event loop.
364 */
365 if (single_threaded) {
366 sc->single_network = fr_network_create(sc, el, "Network", sc->log, sc->lvl, &sc->config->network);
367 if (!sc->single_network) {
368 PERROR("Failed creating network");
369 pre_instantiate_st_fail:
371 return NULL;
372 }
373
374 if (fr_coords_create(sc, el) < 0) {
375 PERROR("Failed creating coordinators");
376 if (unlikely(fr_network_destroy(sc->single_network) < 0)) {
377 PERROR("Failed destroying network");
378 }
379 goto pre_instantiate_st_fail;
380 }
381
382 worker_id = 0;
383 sc->single_worker = fr_worker_alloc(sc, el, "Worker", sc->log, sc->lvl, &sc->config->worker);
384 if (!sc->single_worker) {
385 PERROR("Failed creating worker");
386 if (unlikely(fr_network_destroy(sc->single_network) < 0)) {
387 PERROR("Failed destroying network");
388 }
389 goto pre_instantiate_st_fail;
390 }
391
392 /*
393 * Parent thread-specific data from the single_worker
394 */
395 if (sc->worker_thread_instantiate) {
396 CONF_SECTION *subcs;
397
398 subcs = cf_section_find(sc->cs, "worker", "0");
399 if (!subcs) subcs = cf_section_find(sc->cs, "worker", NULL);
400
401 if (sc->worker_thread_instantiate(sc->single_worker, el, subcs) < 0) {
402 PERROR("Worker thread instantiation failed");
403 destroy_both:
404 if (unlikely(fr_network_destroy(sc->single_network) < 0)) {
405 PERROR("Failed destroying network");
406 }
407 fr_worker_exit(sc->single_worker);
408 goto pre_instantiate_st_fail;
409 }
410 }
411
412 if (fr_command_register_hook(NULL, "0", sc->single_worker, cmd_worker_table) < 0) {
413 PERROR("Failed adding worker commands");
414 st_fail:
415 if (sc->worker_thread_detach) sc->worker_thread_detach(NULL);
416 goto destroy_both;
417 }
418
419 if (fr_command_register_hook(NULL, "0", sc->single_network, cmd_network_table) < 0) {
420 PERROR("Failed adding network commands");
421 goto st_fail;
422 }
423
424 /*
425 * Register the worker with the network, so
426 * things like fr_network_send_request() work.
427 */
428 fr_network_worker_add_self(sc->single_network, sc->single_worker);
429 DEBUG("Scheduler created in single-threaded mode");
430
431 if (fr_event_pre_insert(el, fr_worker_pre_event, sc->single_worker) < 0) {
432 fr_strerror_const("Failed adding pre-check to event list");
433 goto st_fail;
434 }
435
436 if (fr_coord_pre_event_insert(el) < 0) {
437 fr_strerror_const("Failed adding coordinator pre-check to event list");
438 goto st_fail;
439 }
440
441 /*
442 * Add the event which processes request_t packets.
443 */
444 if (fr_event_post_insert(el, fr_worker_post_event, sc->single_worker) < 0) {
445 fr_strerror_const("Failed inserting post-processing event");
446 goto st_fail;
447 }
448
450 fr_strerror_const("Failed adding coordinator post-processing to event list");
451 goto st_fail;
452 }
453
454 return sc;
455 }
456
457 /*
458 * Create the lists which hold the workers and networks.
459 */
460 fr_dlist_init(&sc->workers, fr_schedule_worker_t, thread.entry);
461 fr_dlist_init(&sc->networks, fr_schedule_network_t, thread.entry);
462
463 sc->network_sem = fr_sem_alloc();
464 if (!sc->network_sem) {
465 sem_fail:
466 ERROR("Failed creating semaphore: %s", fr_syserror(errno));
467 fr_sem_free(sc->network_sem);
468 fr_sem_free(sc->worker_sem);
470 return NULL;
471 }
472
473 sc->worker_sem = fr_sem_alloc();
474 if (!sc->worker_sem) goto sem_fail;
475
476 sc->coord_sem = fr_sem_alloc();
477 if (!sc->coord_sem) goto sem_fail;
478
479 /*
480 * Create the network threads first.
481 */
482 for (i = 0; i < sc->config->max_networks; i++) {
483 DEBUG3("Creating %u/%u networks", i + 1, sc->config->max_networks);
484
485 /*
486 * Create a worker "glue" structure
487 */
488 sn = talloc_zero(sc, fr_schedule_network_t);
489 if (!sn) {
490 ERROR("Network %u - Failed allocating memory", i);
491 break;
492 }
493
494 sn->thread.id = i;
495 sn->sc = sc;
497
499 talloc_free(sn);
500 PERROR("Failed creating network %u", i);
501 break;
502 }
503
504 fr_dlist_insert_head(&sc->networks, sn);
505 }
506
507 /*
508 * Wait for all of the networks to signal us that either
509 * they've started, OR there's been a problem and they
510 * can't start.
511 */
512 if (fr_thread_wait_list(sc->network_sem, &sc->networks) < 0) {
514 return NULL;
515 }
516
517 /*
518 * Create the coordination threads
519 */
520 if (fr_coord_start(sc->config->max_workers, sc->coord_sem) < 0) {
522 return NULL;
523 }
524
525 /*
526 * Create all of the workers.
527 */
528 for (i = 0; i < sc->config->max_workers; i++) {
529 DEBUG3("Creating %u/%u workers", i + 1, sc->config->max_workers);
530
531 /*
532 * Create a worker "glue" structure
533 */
534 sw = talloc_zero(sc, fr_schedule_worker_t);
535 if (!sw) {
536 ERROR("Worker %u - Failed allocating memory", i);
537 break;
538 }
539
540 sw->thread.id = i;
541 sw->sc = sc;
543
545 talloc_free(sw);
546 PERROR("Failed creating worker %u", i);
547 break;
548 }
549
550 fr_dlist_insert_head(&sc->workers, sw);
551 }
552
553 /*
554 * Wait for all of the workers to signal us that either
555 * they've started, OR there's been a problem and they
556 * can't start.
557 */
558 if (fr_thread_wait_list(sc->worker_sem, &sc->workers) < 0) {
560 return NULL;
561 }
562
563 for (sw = fr_dlist_head(&sc->workers), i = 0;
564 sw != NULL;
565 sw = next_sw, i++) {
566 char buffer[32];
567
568 next_sw = fr_dlist_next(&sc->workers, sw);
569
570 snprintf(buffer, sizeof(buffer), "%u", i);
572 PERROR("Failed adding worker commands");
573 mt_fail:
575 return NULL;
576 }
577 }
578
579 for (sn = fr_dlist_head(&sc->networks), i = 0;
580 sn != NULL;
581 sn = next_sn, i++) {
582 char buffer[32];
583
584 next_sn = fr_dlist_next(&sc->networks, sn);
585
586 snprintf(buffer, sizeof(buffer), "%u", i);
588 PERROR("Failed adding network commands");
589 goto mt_fail;
590 }
591 }
592
593 if (sc) INFO("Scheduler created successfully with %u networks and %u workers",
594 sc->config->max_networks, (unsigned int)fr_dlist_num_elements(&sc->workers));
595
596 /*
597 * Instantiate thread-local data for the main thread too.
598 * In single-threaded mode this is done above. In
599 * multi-worker mode the main thread also needs module
600 * thread data so that triggers can use module xlats.
601 */
602 if (sc->worker_thread_instantiate &&
603 unlikely((sc->worker_thread_instantiate(sc, el, NULL) < 0))) {
604 PERROR("Main thread instantiation failed");
605 goto mt_fail;
606 }
607
608 return sc;
609}
610
611/** Destroy a scheduler, and tell its child threads to exit.
612 *
613 * @note This may be called with no worker or network threads in the case of a
614 * instantiation error. This function _should_ deal with that condition
615 * gracefully.
616 *
617 * @param[in] sc_to_free the scheduler
618 * @return
619 * - <0 on error
620 * - 0 on success
621 */
623{
624 fr_schedule_t *sc = *sc_to_free;
625 unsigned int i;
628 int ret;
629
630 if (!sc) return 0;
631
632 sc->running = false;
633
634 /*
635 * Single threaded mode: kill the only network / worker we have.
636 */
637 if (sc->single_threaded) {
638 /*
639 * Destroy the network side first. It tells the
640 * workers to close.
641 */
642 if (unlikely(fr_network_destroy(sc->single_network) < 0)) {
643 ERROR("Failed destroying network");
644 }
645
646 /*
647 * Add events to handle close down gracefully.
648 */
649 if (unlikely((fr_network_close_event_insert(sc->single_network) < 0) ||
650 (fr_worker_close_event_insert(sc->single_worker) < 0))) {
651 ERROR("Failed setting up close events");
652 }
653
654 /*
655 * Run the event loop so the worker gets the signal from
656 * the network and shuts down gracefully.
657 */
659
660 /*
661 * Detach worker from coordinators. This needs to be done
662 * before the worker is freed.
663 */
664 if (modules_rlm_coord_detach() > 0) {
665 /*
666 * Run the event loop again to handle coordinator detach
667 * messages, with different callbacks to determine when
668 * to exit the loop.
669 */
670 fr_network_close_event_delete(sc->single_network);
672 ERROR("Failed setting up coordinator close events");
673 }
675 }
676
677 fr_schedule_worker_exit(&sc->single_worker, sc->worker_thread_detach);
679
680 goto done;
681 } else {
682 /*
683 * Detach thread-local data for the main thread.
684 * Worker threads handle their own detach, but
685 * the main thread was instantiated explicitly
686 * by fr_schedule_create.
687 */
688 if (sc->worker_thread_detach) sc->worker_thread_detach(NULL);
689 }
690
691 /*
692 * Signal each network thread to exit.
693 */
694 fr_dlist_foreach(&sc->networks, fr_schedule_network_t, sne) {
695 if (fr_network_exit(sne->nr) < 0) {
696 PERROR("Failed signaling network %u to exit", sne->thread.id);
697 }
698 }
699
700 /*
701 * If the network threads are running, tell them to exit,
702 * and wait for them to do so. Each network thread tells
703 * all of its worker threads that it's exiting. It then
704 * closes the channels. When the workers see that there
705 * are no input channels, they exit, too.
706 */
707 for (i = 0; i < (unsigned int)fr_dlist_num_elements(&sc->networks); i++) {
708 DEBUG2("Scheduler - Waiting for semaphore indicating network exit %u/%u", i + 1,
709 (unsigned int)fr_dlist_num_elements(&sc->networks));
710 SEM_WAIT_INTR(sc->network_sem);
711 }
712 DEBUG2("Scheduler - All networks indicated exit complete");
713
714 while ((sn = fr_dlist_pop_head(&sc->networks)) != NULL) {
715 /*
716 * Ensure that the thread has exited before
717 * cleaning up the context.
718 *
719 * This also ensures that the child threads have
720 * exited before the main thread cleans up the
721 * module instances.
722 */
723 if ((ret = pthread_join(sn->thread.pthread_id, NULL)) != 0) {
724 ERROR("Failed joining network %u: %s", sn->thread.id, fr_syserror(ret));
725 } else {
726 DEBUG2("Network %u joined (cleaned up)", sn->thread.id);
727 }
728 }
729
730 /*
731 * Wait for all worker threads to finish. THEN clean up
732 * modules. Otherwise, the modules will be removed from
733 * underneath the workers!
734 */
735 for (i = 0; i < (unsigned int)fr_dlist_num_elements(&sc->workers); i++) {
736 DEBUG2("Scheduler - Waiting for semaphore indicating worker exit %u/%u", i + 1,
737 (unsigned int)fr_dlist_num_elements(&sc->workers));
738 SEM_WAIT_INTR(sc->worker_sem);
739 }
740 DEBUG2("Scheduler - All workers indicated exit complete");
741
742 /*
743 * Clean up the exited workers.
744 */
745 while ((sw = fr_dlist_pop_head(&sc->workers)) != NULL) {
746 /*
747 * Ensure that the thread has exited before
748 * cleaning up the context.
749 *
750 * This also ensures that the child threads have
751 * exited before the main thread cleans up the
752 * module instances.
753 */
754 if ((ret = pthread_join(sw->thread.pthread_id, NULL)) != 0) {
755 ERROR("Failed joining worker %u: %s", sw->thread.id, fr_syserror(ret));
756 } else {
757 DEBUG2("Worker %u joined (cleaned up)", sw->thread.id);
758 }
759 }
760
762
763 fr_sem_free(sc->coord_sem);
764 fr_sem_free(sc->network_sem);
765 fr_sem_free(sc->worker_sem);
766
767done:
768 /*
769 * Now that all of the workers are done, we can return to
770 * the caller, and have it dlclose() the modules.
771 */
773 *sc_to_free = NULL;
774
775 return 0;
776}
777
778/** Add a fr_listen_instance_t to a scheduler.
779 *
780 * @param[in] sc the scheduler
781 * @param[in] li the ctx and callbacks for the transport.
782 * @return
783 * - NULL on error
784 * - the fr_network_t that the socket was added to.
785 */
787{
788 fr_network_t *nr;
789
790 (void) talloc_get_type_abort(sc, fr_schedule_t);
791
792 if (sc->single_threaded) {
793 nr = sc->single_network;
794 } else {
796
797 /*
798 * @todo - round robin it among the listeners?
799 * or maybe add it to the same parent thread?
800 */
801 sn = fr_dlist_head(&sc->networks);
802 nr = sn->nr;
803 }
804
805 if (fr_network_listen_add(nr, li) < 0) return NULL;
806
807 return nr;
808}
809
810/** Add a directory NOTE_EXTEND to a scheduler.
811 *
812 * @param[in] sc the scheduler
813 * @param[in] li the ctx and callbacks for the transport.
814 * @return
815 * - NULL on error
816 * - the fr_network_t that the socket was added to.
817 */
819{
820 fr_network_t *nr;
821
822 (void) talloc_get_type_abort(sc, fr_schedule_t);
823
824 if (sc->single_threaded) {
825 nr = sc->single_network;
826 } else {
828
829 /*
830 * @todo - round robin it among the listeners?
831 * or maybe add it to the same parent thread?
832 */
833 sn = fr_dlist_head(&sc->networks);
834 nr = sn->nr;
835 }
836
837 if (fr_network_directory_add(nr, li) < 0) return NULL;
838
839 return nr;
840}
static int const char char buffer[256]
Definition acutest.h:641
#define _Thread_local
Definition atexit.h:213
#define RCSID(id)
Definition build.h:603
#define unlikely(_x)
Definition build.h:486
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1247
fr_command_register_hook_t fr_command_register_hook
Definition command.c:41
int fr_coords_create(TALLOC_CTX *ctx, fr_event_list_t *el)
Start coordinators in single threaded mode.
Definition coord.c:619
void fr_coords_destroy(void)
Clean up coordinators in single threaded mode.
Definition coord.c:601
int fr_coord_close_event_insert(fr_event_list_t *el)
Definition coord.c:900
void fr_coord_thread_join(void)
Wait for all the coordinator threads to exit.
Definition coord.c:193
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
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
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
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.
Definition dlist.h:468
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:98
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 void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition dlist.h:320
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
Head of a doubly linked list.
Definition dlist.h:51
void fr_event_list_empty(fr_event_list_t *el)
talloc_free(hp)
fr_cmd_table_t cmd_network_table[]
Definition network.c:2266
void fr_network_close_event_delete(fr_network_t *nr)
Remove "close" events from the event loop.
Definition network.c:1772
int fr_network_worker_add(fr_network_t *nr, fr_worker_t *worker)
Add a worker to a network in a different thread.
Definition network.c:260
int fr_network_destroy(fr_network_t *nr)
Stop a network thread in an orderly way.
Definition network.c:1782
void fr_network(fr_network_t *nr)
Definition network.c:1936
void fr_network_worker_add_self(fr_network_t *nr, fr_worker_t *worker)
Add a worker to a network in the same thread.
Definition network.c:276
int fr_network_directory_add(fr_network_t *nr, fr_listen_instance_t *li)
Add a "watch directory" call to a network.
Definition network.c:249
int fr_network_exit(fr_network_t *nr)
Signal a network thread to exit.
Definition network.c:2034
int fr_network_listen_add(fr_network_t *nr, fr_listen_instance_t *li)
Add a fr_listen_instance_t to a network.
Definition network.c:199
void fr_network_stats_log(fr_network_t const *nr, fr_log_t const *log)
Definition network.c:2199
int fr_network_close_event_insert(fr_network_t *nr)
Add events to the loop which will exit when the number of workers has reached zero.
Definition network.c:1757
fr_network_t * fr_network_create(TALLOC_CTX *ctx, fr_event_list_t *el, char const *name, fr_log_t const *logger, fr_log_lvl_t lvl, fr_network_config_t const *config)
Create a network.
Definition network.c:2067
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG3(_fmt,...)
Definition log.h:271
int fr_event_loop_finalize(fr_event_list_t *el)
Run an event loop during shutdown.
Definition event.c:2483
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
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
fr_log_lvl_t
Definition log.h:64
int modules_rlm_coord_detach(void)
Runs the coord_detach method of all registered backend modules.
Definition module_rlm.c:999
static const conf_parser_t config[]
Definition base.c:162
#define DEBUG2(fmt,...)
static bool done
Definition radclient.c:80
#define INFO(fmt,...)
Definition radict.c:67
CONF_SECTION * cs
thread pool configuration section
Definition schedule.c:80
fr_thread_t thread
common thread structure - must be first!
Definition schedule.c:64
fr_timer_t * ev
timer for stats_interval
Definition schedule.c:70
static _Thread_local int worker_id
Internal ID of the current worker thread.
Definition schedule.c:105
fr_sem_t * coord_sem
for inter-thread signaling
Definition schedule.c:93
fr_schedule_t * fr_schedule_create(TALLOC_CTX *ctx, bool single_threaded, fr_event_list_t *el, fr_log_t *logger, fr_log_lvl_t lvl, fr_schedule_thread_instantiate_t worker_thread_instantiate, fr_schedule_thread_detach_t worker_thread_detach, fr_schedule_config_t *config)
Create a scheduler and spawn the child threads.
Definition schedule.c:317
fr_network_t * fr_schedule_listen_add(fr_schedule_t *sc, fr_listen_instance_t *li)
Add a fr_listen_instance_t to a scheduler.
Definition schedule.c:786
fr_event_list_t * el
event list for single-threaded mode.
Definition schedule.c:81
fr_schedule_t * sc
the scheduler we are running under
Definition schedule.c:66
fr_sem_t * worker_sem
for inter-thread signaling
Definition schedule.c:91
fr_worker_t * single_worker
for single-threaded mode
Definition schedule.c:102
fr_log_lvl_t lvl
log level
Definition schedule.c:85
static void stats_timer(fr_timer_list_t *tl, fr_time_t now, void *uctx)
Definition schedule.c:236
fr_schedule_config_t * config
configuration
Definition schedule.c:87
fr_network_t * single_network
for single-threaded mode
Definition schedule.c:101
fr_schedule_thread_instantiate_t worker_thread_instantiate
thread instantiation callback
Definition schedule.c:95
fr_network_t * nr
the receive data structure
Definition schedule.c:68
fr_schedule_thread_detach_t worker_thread_detach
Definition schedule.c:96
bool running
is the scheduler running?
Definition schedule.c:78
void fr_schedule_worker_id_set(int id)
Explicitly set the worker id for the current thread.
Definition schedule.c:120
int fr_schedule_worker_id(void)
Return the worker id for the current thread.
Definition schedule.c:111
static void * fr_schedule_worker_thread(void *arg)
Entry point for worker threads.
Definition schedule.c:155
fr_network_t * fr_schedule_directory_add(fr_schedule_t *sc, fr_listen_instance_t *li)
Add a directory NOTE_EXTEND to a scheduler.
Definition schedule.c:818
fr_sem_t * network_sem
for inter-thread signaling
Definition schedule.c:92
unsigned int num_workers_exited
number of exited workers
Definition schedule.c:89
static void fr_schedule_worker_exit(fr_worker_t **p_worker, fr_schedule_thread_detach_t worker_thread_detach)
Unify the "clean up worker" path.
Definition schedule.c:128
int fr_schedule_destroy(fr_schedule_t **sc_to_free)
Destroy a scheduler, and tell its child threads to exit.
Definition schedule.c:622
bool single_threaded
true if running in single-threaded mode.
Definition schedule.c:82
fr_dlist_head_t networks
list of networks
Definition schedule.c:99
fr_log_t * log
log destination
Definition schedule.c:84
fr_dlist_head_t workers
list of workers
Definition schedule.c:98
static void * fr_schedule_network_thread(void *arg)
Initialize and run the network thread.
Definition schedule.c:250
Scheduler specific information for network threads.
Definition schedule.c:63
The scheduler.
Definition schedule.c:77
int(* fr_schedule_thread_instantiate_t)(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
Setup a new thread.
Definition schedule.h:56
void(* fr_schedule_thread_detach_t)(void *uctx)
Explicitly free resources allocated by fr_schedule_thread_instantiate_t.
Definition schedule.h:62
fr_time_delta_t stats_interval
print channel statistics
Definition schedule.h:71
void fr_sem_free(fr_sem_t *sem)
Definition semaphore.c:50
fr_sem_t * fr_sem_alloc(void)
Definition semaphore.c:30
#define SEM_WAIT_INTR(_x)
Definition semaphore.h:56
sem_t fr_sem_t
Definition semaphore.h:53
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
Definition log.h:93
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
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 status
running, etc.
Definition thread.h:49
fr_thread_status_t
Track the child thread status.
Definition thread.h:38
@ FR_THREAD_INITIALIZING
initialized, but not running
Definition thread.h:40
@ 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
fr_event_list_t * el
our event list
Definition thread.h:53
unsigned int id
unique ID for this thread
Definition thread.h:48
TALLOC_CTX * ctx
our allocation ctx
Definition thread.h:52
pthread_t pthread_id
of this thread
Definition thread.h:50
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define fr_timer_in(...)
Definition timer.h:87
#define fr_timer_at(...)
Definition timer.h:81
static fr_event_list_t * el
#define fr_strerror_const(_msg)
Definition strerror.h:223
int fr_worker_pre_event(UNUSED fr_time_t now, UNUSED fr_time_delta_t wake, void *uctx)
Pre-event handler.
Definition worker.c:1537
fr_worker_t * fr_worker_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, char const *name, fr_log_t const *logger, fr_log_lvl_t lvl, fr_worker_config_t *config)
Create a worker.
Definition worker.c:1260
fr_cmd_table_t cmd_worker_table[]
Definition worker.c:1784
void fr_worker_shutting_down(fr_worker_t *worker)
Tell the interpreter of the worker to cancel requests, instead of running them.
Definition worker.c:1602
void fr_worker_post_event(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Post-event handler.
Definition worker.c:1558
int fr_worker_close_event_insert(fr_worker_t *worker)
Add events to the loop which will send close ack to the network.
Definition worker.c:1581
void fr_worker(fr_worker_t *worker)
The main loop and entry point of the stand-alone worker thread.
Definition worker.c:1463
void fr_worker_exit(fr_worker_t *worker)
Tidy up a worker on normal exit.
Definition worker.c:970
A worker which takes packets from a master, and processes them.
Definition worker.c:89
int uses
how many network threads are using it
Definition schedule.c:50
fr_schedule_t * sc
the scheduler we are running under
Definition schedule.c:53
fr_thread_t thread
common thread structure - must be first!
Definition schedule.c:48
fr_time_t cpu_time
how much CPU time this worker has used
Definition schedule.c:51
fr_worker_t * worker
the worker data structure
Definition schedule.c:55
Scheduler specific information for worker threads.
Definition schedule.c:47