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: 930920257369b074664855f16dcb848ce29bebfd $
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: 930920257369b074664855f16dcb848ce29bebfd $")
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
105 MEM(ctx = talloc_init_const("control_master"));
106
107 master_ctx = talloc_zero(ctx, master_ctx_t);
108
109 MPRINT1("Master started.\n");
110
111 for (i = 0; i < num_aq; i++) {
113 }
114
115 while (master_ctx->num_messages < (max_messages * num_workers)) {
116 int num_events;
117
118 MPRINT3("Master waiting for events (seen %ld).\n", master_ctx->num_messages);
119
120 num_events = fr_event_corral(el, fr_time(), true);
121 if (num_events < 0) {
122 fprintf(stderr, "Failed reading kevent: %s\n", fr_syserror(errno));
123 fr_exit_now(EXIT_FAILURE);
124 }
125 if (num_events > 0) {
127 }
128 }
129
130 MPRINT1("Master exiting. Seen %ld messages.\n", master_ctx->num_messages);
131
132 talloc_free(ctx);
133
134 return NULL;
135}
136
137static void *control_worker(void *arg)
138{
139 size_t i, aq_num;
140 TALLOC_CTX *ctx;
141 worker_args_t *wa = (worker_args_t *) arg;
142
143 MEM(ctx = talloc_init_const("control_worker"));
144
145 aq_num = single_aq ? 0 : wa->id;
146
147 MPRINT1("\tWorker %ld started using queue %ld.\n", wa->id, aq_num);
148
149 for (i = 0; i < max_messages; i++) {
150 my_message_t m;
151 int delay = 0;
152
154 m.counter = i;
155 m.worker = wa->id;
156
157retry:
158 if (fr_control_message_send(control[aq_num], wa->rb, FR_CONTROL_ID_CHANNEL, &m, sizeof(m)) < 0) {
159 MPRINT1("\tWorker %ld retrying message %zu\n", wa->id, i);
160 delay += 10;
161 usleep(delay);
162 goto retry;
163 }
164
165 MPRINT2("\tWorker %ld sent message %zu\n", wa->id, i);
166 }
167
168 MPRINT1("\tWorker %ld exiting.\n", wa->id);
169
170 talloc_free(ctx);
171
172 return NULL;
173}
174
175
176
177int main(int argc, char *argv[])
178{
179 int c;
180 TALLOC_CTX *autofree = talloc_autofree_context();
181 pthread_attr_t attr;
182 pthread_t master_id, *worker_id;
183 size_t i;
184 worker_args_t *worker_args;
185
187
189
190 while ((c = getopt(argc, argv, "hm:qw:x")) != -1) switch (c) {
191 case 'x':
192 debug_lvl++;
193 break;
194
195 case 'm':
196 max_messages = atoi(optarg);
197 break;
198
199 case 'q':
200 single_aq = false;
201 break;
202
203 case 'w':
204 num_workers = atoi(optarg);
205 break;
206
207 case 'h':
208 default:
209 usage();
210 }
211
212#if 0
213 argc -= (optind - 1);
214 argv += (optind - 1);
215#endif
216
219
222 aq = talloc_array(autofree, fr_atomic_queue_t *, num_aq);
223 for (i = 0; i < num_aq; i++) {
225 fr_assert(aq[i] != NULL);
226 }
227
228 control = talloc_array(autofree, fr_control_t *, num_aq);
229 for (i = 0; i < num_aq; i++) {
231 if (!control[i]) {
232 fprintf(stderr, "control_test: Failed to create control plane\n");
233 fr_exit_now(EXIT_FAILURE);
234 }
235 }
236
237 /*
238 * Start the threads, with the channel.
239 */
240 (void) pthread_attr_init(&attr);
241 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
242
243 (void) pthread_create(&master_id, &attr, control_master, NULL);
244 worker_id = talloc_array(autofree, pthread_t, num_workers);
245 worker_args = talloc_array(autofree, worker_args_t, num_workers);
246 for (i = 0; i < num_workers; i++) {
247 worker_args[i].id = i;
249 (void) pthread_create(&worker_id[i], &attr, control_worker, &worker_args[i]);
250 }
251
252 (void) pthread_join(master_id, NULL);
253 for (i = 0; i < num_workers; i++) {
254 (void) pthread_join(worker_id[i], NULL);
255 }
256
258
260
262
263 return 0;
264}
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
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
"server local" time.
Definition time.h:69
static fr_slen_t data
Definition value.h:1291