The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
control_test.c
Go to the documentation of this file.
1/*
2 * control_test.c Tests for control planes
3 *
4 * Version: $Id: adababdd9867943f989f82d05f78dd86ded83685 $
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 2016 Alan DeKok (aland@freeradius.org)
21 */
22
23RCSID("$Id: adababdd9867943f989f82d05f78dd86ded83685 $")
24
25#include <freeradius-devel/server/base.h>
26#include <freeradius-devel/io/control.h>
27#include <freeradius-devel/util/debug.h>
28#include <freeradius-devel/util/syserror.h>
29#include <freeradius-devel/util/talloc.h>
30#include <freeradius-devel/util/time.h>
31
32#include <sys/event.h>
33#include <stdio.h>
34#include <string.h>
35#include <pthread.h>
36
37#ifdef HAVE_GETOPT_H
38# include <getopt.h>
39#endif
40
41#undef MEM
42#define MEM(x) if (!(x)) { fprintf(stderr, "%s[%u] OUT OF MEMORY\n", __FILE__, __LINE__); _exit(EXIT_FAILURE); }
43#define MPRINT1 if (debug_lvl) printf
44#define MPRINT2 if (debug_lvl >= 2) printf
45#define MPRINT3 if (debug_lvl >= 3) printf
46#define CONTROL_MAGIC 0xabcd6809
47
48typedef struct {
49 size_t id;
52
53static int debug_lvl = 0;
55static size_t max_messages = 10;
56static size_t num_workers = 1;
57static int aq_size = 16;
58static fr_control_t **control = NULL;
59static fr_event_list_t *el = NULL;
60static bool single_aq = true;
61static size_t num_aq = 1;
62
63/**********************************************************************/
64
65static NEVER_RETURNS void usage(void)
66{
67 fprintf(stderr, "usage: control_test [OPTS]\n");
68 fprintf(stderr, " -m <messages> Send number of messages.\n");
69 fprintf(stderr, " -w <workers> Number of workers.\n");
70 fprintf(stderr, " -q Use per-worker atomic queues.\n");
71 fprintf(stderr, " -x Debugging mode.\n");
72
73 fr_exit_now(EXIT_SUCCESS);
74}
75
76typedef struct {
78 size_t counter;
79 size_t worker;
81
82typedef struct {
85
86static void recv_control_callback(void *ctx, void const *data, size_t data_size, UNUSED fr_time_t now)
87{
88 my_message_t const *m = data;
89 master_ctx_t *master_ctx = ctx;
90
92 fr_assert(data_size == sizeof(*m));
93
94 MPRINT2("Master got worker %ld message %zu, size %ld.\n", m->worker, m->counter, data_size);
95 if (m->counter == (max_messages - 1)) MPRINT1("Master seen all messages from worker %ld\n", m->worker);
96 master_ctx->num_messages++;
97}
98
99static void *control_master(UNUSED void *arg)
100{
101 TALLOC_CTX *ctx;
102 master_ctx_t *master_ctx;
103 size_t i;
104 fr_time_t start;
105
106 MEM(ctx = talloc_init_const("control_master"));
107
108 master_ctx = talloc_zero(ctx, master_ctx_t);
109
110 MPRINT1("Master started.\n");
111
112 for (i = 0; i < num_aq; i++) {
114 }
115
116 start = fr_time();
117 while (master_ctx->num_messages < (max_messages * num_workers)) {
118 int num_events;
119
120 MPRINT3("Master waiting for events (seen %ld).\n", master_ctx->num_messages);
121
122 num_events = fr_event_corral(el, fr_time(), true);
123 if (num_events < 0) {
124 fprintf(stderr, "Failed reading kevent: %s\n", fr_syserror(errno));
125 fr_exit_now(EXIT_FAILURE);
126 }
127 if (num_events > 0) {
129 }
130 }
131 MPRINT1("Master exiting. Seen %zu messages. %.2f per second\n",
132 master_ctx->num_messages,
133 ((double)master_ctx->num_messages * 1e6) /
135
136 talloc_free(ctx);
137
138 return NULL;
139}
140
141static void *control_worker(void *arg)
142{
143 size_t i, aq_num;
144 TALLOC_CTX *ctx;
145 worker_args_t *wa = (worker_args_t *) arg;
146
147 MEM(ctx = talloc_init_const("control_worker"));
148
149 aq_num = single_aq ? 0 : wa->id;
150
151 MPRINT1("\tWorker %ld started using queue %ld.\n", wa->id, aq_num);
152
153 for (i = 0; i < max_messages; i++) {
154 my_message_t m;
155 int delay = 0;
156
158 m.counter = i;
159 m.worker = wa->id;
160
161retry:
162 if (fr_control_message_send(control[aq_num], wa->rb, FR_CONTROL_ID_CHANNEL, &m, sizeof(m)) < 0) {
163 char const *err;
164 MPRINT1("\tWorker %ld retrying message %zu\n", wa->id, i);
165 while ((err = fr_strerror_pop())) {
166 MPRINT1("\t%s\n", err);
167 }
168 delay += 10;
169 usleep(delay);
170 goto retry;
171 }
172
173 MPRINT2("\tWorker %ld sent message %zu\n", wa->id, i);
174 }
175
176 MPRINT1("\tWorker %ld exiting.\n", wa->id);
177
178 talloc_free(ctx);
179
180 return NULL;
181}
182
183
184
185int main(int argc, char *argv[])
186{
187 int c;
188 TALLOC_CTX *autofree = talloc_autofree_context();
189 pthread_attr_t attr;
190 pthread_t master_id, *worker_id;
191 size_t i;
192 worker_args_t *worker_args;
193
195
197
198 while ((c = getopt(argc, argv, "hm:qw:x")) != -1) switch (c) {
199 case 'x':
200 debug_lvl++;
201 break;
202
203 case 'm':
204 max_messages = atoi(optarg);
205 break;
206
207 case 'q':
208 single_aq = false;
209 break;
210
211 case 'w':
212 num_workers = atoi(optarg);
213 break;
214
215 case 'h':
216 default:
217 usage();
218 }
219
220#if 0
221 argc -= (optind - 1);
222 argv += (optind - 1);
223#endif
224
227
230 aq = talloc_array(autofree, fr_atomic_queue_t *, num_aq);
231 for (i = 0; i < num_aq; i++) {
233 fr_assert(aq[i] != NULL);
234 }
235
236 control = talloc_array(autofree, fr_control_t *, num_aq);
237 for (i = 0; i < num_aq; i++) {
239 if (!control[i]) {
240 fprintf(stderr, "control_test: Failed to create control plane\n");
241 fr_exit_now(EXIT_FAILURE);
242 }
243 }
244
245 /*
246 * Start the threads, with the channel.
247 */
248 (void) pthread_attr_init(&attr);
249 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
250
251 (void) pthread_create(&master_id, &attr, control_master, NULL);
252 worker_id = talloc_array(autofree, pthread_t, num_workers);
253 worker_args = talloc_array(autofree, worker_args_t, num_workers);
254 for (i = 0; i < num_workers; i++) {
255 worker_args[i].id = i;
257 (void) pthread_create(&worker_id[i], &attr, control_worker, &worker_args[i]);
258 }
259
260 (void) pthread_join(master_id, NULL);
261 for (i = 0; i < num_workers; i++) {
262 (void) pthread_join(worker_id[i], NULL);
263 }
264
266
268
270
271 return 0;
272}
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:160
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:286
fr_atomic_queue_t * fr_atomic_queue_alloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue.
Structure to hold the atomic queue.
static TALLOC_CTX * autofree
Definition fuzzer.c:45
#define RCSID(id)
Definition build.h:485
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:315
#define UNUSED
Definition build.h:317
static fr_control_t * control_worker
static fr_control_t * control_master
#define FR_CONTROL_ID_CHANNEL
Definition control.h:56
#define FR_CONTROL_MAX_SIZE
Definition control.h:51
#define FR_CONTROL_MAX_MESSAGES
Definition control.h:50
int main(int argc, char *argv[])
static size_t num_workers
#define CONTROL_MAGIC
static bool single_aq
static int aq_size
static size_t num_aq
static void recv_control_callback(void *ctx, void const *data, size_t data_size, UNUSED fr_time_t now)
#define MPRINT3
size_t num_messages
#define MPRINT2
static fr_event_list_t * el
uint32_t header
static fr_control_t ** control
#define MPRINT1
static size_t max_messages
#define MEM(x)
fr_ring_buffer_t * rb
static NEVER_RETURNS void usage(void)
static fr_atomic_queue_t ** aq
static int debug_lvl
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
static fr_slen_t err
Definition dict.h:861
int fr_control_callback_add(fr_control_t *c, uint32_t id, void *ctx, fr_control_callback_t callback)
Register a callback for an ID.
Definition control.c:417
int fr_control_message_send(fr_control_t *c, fr_ring_buffer_t *rb, uint32_t id, void *data, size_t data_size)
Send a control-plane message.
Definition control.c:332
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_atomic_queue_t *aq)
Create a control-plane signaling path.
Definition control.c:143
The control structure.
Definition control.c:79
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2193
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:2058
talloc_free(reap)
Stores all information relating to an event list.
Definition event.c:377
fr_event_list_t * main_loop_event_list(void)
Return the main loop event list.
Definition main_loop.c:164
int main_loop_init(void)
Initialise the main event loop, setting up signal handlers.
Definition main_loop.c:253
void main_loop_free(void)
Definition main_loop.c:189
unsigned int uint32_t
#define fr_assert(_expr)
Definition rad_assert.h:38
fr_ring_buffer_t * fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
Create a ring buffer.
Definition ring_buffer.c:64
static _Thread_local int worker_id
Internal ID of the current worker thread.
Definition schedule.c:151
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
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:112
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:51
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
static fr_time_delta_t fr_time_sub_time_time(fr_time_t a, fr_time_t b)
Definition time.h:207
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
Definition time.h:632
"server local" time.
Definition time.h:69
char const * fr_strerror_pop(void)
Pop the last library error.
Definition strerror.c:680
static fr_slen_t data
Definition value.h:1293