hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
/*
 * Copyright (C) 2011 Philippe Gerum <rpm@xenomai.org>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <copperplate/threadobj.h>
#include <copperplate/heapobj.h>
#include <copperplate/registry-obstack.h>
#include "reference.h"
#include "internal.h"
#include "event.h"
#include "timer.h"
 
/**
 * @ingroup alchemy
 * @defgroup alchemy_event Event flag group services
 *
 * Inter-task notification mechanism based on discrete flags
 *
 * An event flag group is a synchronization object represented by a
 * long-word structure; every available bit in this word represents a
 * user-defined event flag.
 *
 * When a bit is set, the associated event is said to have
 * occurred. Xenomai tasks can use this mechanism to signal the
 * occurrence of particular events to other tasks.
 *
 * Tasks can either wait for events to occur in a conjunctive manner
 * (all awaited events must have occurred to satisfy the wait
 * request), or in a disjunctive way (at least one of the awaited
 * events must have occurred to satisfy the wait request).
 *
 * @{
 */
 
struct syncluster alchemy_event_table;
 
static DEFINE_NAME_GENERATOR(event_namegen, "event",
                struct alchemy_event, name);
 
DEFINE_LOOKUP_PRIVATE(event, RT_EVENT);
 
#ifdef CONFIG_XENO_REGISTRY
 
static int event_registry_open(struct fsobj *fsobj, void *priv)
{
   struct eventobj_waitentry *waitlist, *p;
   struct fsobstack *o = priv;
   struct alchemy_event *evcb;
   unsigned int val;
   size_t waitsz;
   int ret;
 
   evcb = container_of(fsobj, struct alchemy_event, fsobj);
 
   waitsz = sizeof(*p) * 256;
   waitlist = __STD(malloc(waitsz));
   if (waitlist == NULL)
       return -ENOMEM;
 
   ret = eventobj_inquire(&evcb->evobj, waitsz, waitlist, &val);
   if (ret < 0)
       goto out;
 
   fsobstack_init(o);
 
   fsobstack_grow_format(o, "=%lx\n", val);
 
   if (ret) {
       fsobstack_grow_format(o, "--\n[WAITER]\n");
       p = waitlist;
       do {
           fsobstack_grow_format(o, "%s\n", p->name);
           p++;
       } while (--ret > 0);
   }
 
   fsobstack_finish(o);
out:
   __STD(free(waitlist));
 
   return ret;
}
 
static struct registry_operations registry_ops = {
   .open        = event_registry_open,
   .release    = fsobj_obstack_release,
   .read        = fsobj_obstack_read
};
 
#else /* !CONFIG_XENO_REGISTRY */
 
static struct registry_operations registry_ops;
 
#endif /* CONFIG_XENO_REGISTRY */
 
static void event_finalize(struct eventobj *evobj)
{
   struct alchemy_event *evcb = container_of(evobj, struct alchemy_event, evobj);
 
   registry_destroy_file(&evcb->fsobj);
   /* We should never fail here, so we backtrace. */
   __bt(syncluster_delobj(&alchemy_event_table, &evcb->cobj));
   evcb->magic = ~event_magic;
   xnfree(evcb);
}
fnref_register(libalchemy, event_finalize);
 
/**
 * @fn int rt_event_create(RT_EVENT *event, const char *name, unsigned int ivalue, int mode)
 * @brief Create an event flag group.
 *
 * Event groups provide for task synchronization by allowing a set of
 * flags (or "events") to be waited for and posted atomically. An
 * event group contains a mask of received events; an arbitrary set of
 * event flags can be pended or posted in a single operation.
 *
 * @param event The address of an event descriptor which can be later
 * used to identify uniquely the created object, upon success of this
 * call.
 *
 * @param name An ASCII string standing for the symbolic name of the
 * event. When non-NULL and non-empty, a copy of this string is used
 * for indexing the created event into the object registry.
 *
 * @param ivalue The initial value of the group's event mask.
 *
 * @param mode The event group creation mode. The following flags can
 * be OR'ed into this bitmask:
 *
 * - EV_FIFO makes tasks pend in FIFO order on the event flag group.
 *
 * - EV_PRIO makes tasks pend in priority order on the event flag group.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a mode is invalid.
 *
 * - -ENOMEM is returned if the system fails to get memory from the
 * main heap in order to create the event flag group.
 *
 * - -EEXIST is returned if the @a name is conflicting with an already
 * registered event flag group.
 *
 * - -EPERM is returned if this service was called from an invalid
 * context, e.g. interrupt or non-Xenomai thread.
 *
 * @apitags{xthread-only, mode-unrestricted, switch-secondary}
 *
 * @note Event flag groups can be shared by multiple processes which
 * belong to the same Xenomai session.
 */
#ifndef DOXYGEN_CPP
CURRENT_IMPL(int, rt_event_create, (RT_EVENT *event, const char *name,
                   unsigned int ivalue, int mode))
#else
int rt_event_create(RT_EVENT *event, const char *name,
           unsigned int ivalue, int mode)
#endif
{
   int evobj_flags = 0, ret = 0;
   struct alchemy_event *evcb;
   struct service svc;
 
   if (threadobj_irq_p())
       return -EPERM;
 
   if (mode & ~EV_PRIO)
       return -EINVAL;
 
   CANCEL_DEFER(svc);
 
   evcb = xnmalloc(sizeof(*evcb));
   if (evcb == NULL) {
       ret = -ENOMEM;
       goto out;
   }
 
   generate_name(evcb->name, name, &event_namegen);
   if (mode & EV_PRIO)
       evobj_flags = EVOBJ_PRIO;
 
   ret = eventobj_init(&evcb->evobj, ivalue, evobj_flags,
               fnref_put(libalchemy, event_finalize));
   if (ret) {
       xnfree(evcb);
       goto out;
   }
 
   evcb->magic = event_magic;
 
   registry_init_file_obstack(&evcb->fsobj, &registry_ops);
   ret = __bt(registry_add_file(&evcb->fsobj, O_RDONLY,
                    "/alchemy/events/%s", evcb->name));
   if (ret) {
       warning("failed to export event %s to registry, %s",
           evcb->name, symerror(ret));
       ret = 0;
   }
 
   ret = syncluster_addobj(&alchemy_event_table, evcb->name, &evcb->cobj);
   if (ret) {
       registry_destroy_file(&evcb->fsobj);
       eventobj_uninit(&evcb->evobj);
       xnfree(evcb);
   } else
       event->handle = mainheap_ref(evcb, uintptr_t);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_event_delete(RT_EVENT *event)
 * @brief Delete an event flag group.
 *
 * This routine deletes a event flag group previously created by a
 * call to rt_event_create().
 *
 * @param event The event descriptor.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a event is not a valid event flag group
 * descriptor.
 *
 * - -EPERM is returned if this service was called from an
 * asynchronous context.
 *
 * @apitags{mode-unrestricted, switch-secondary}
 */
int rt_event_delete(RT_EVENT *event)
{
   struct alchemy_event *evcb;
   struct service svc;
   int ret = 0;
 
   if (threadobj_irq_p())
       return -EPERM;
 
   CANCEL_DEFER(svc);
 
   evcb = find_alchemy_event(event, &ret);
   if (evcb == NULL)
       goto out;
 
   /*
    * XXX: we rely on copperplate's eventobj to check for event
    * existence, so we refrain from altering the object memory
    * until we know it was valid. So the only safe place to
    * negate the magic tag, deregister from the cluster and
    * release the memory is in the finalizer routine, which is
    * only called for valid objects.
    */
   ret = eventobj_destroy(&evcb->evobj);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_event_wait(RT_EVENT *event, unsigned int mask, unsigned int *mask_r, int mode, RTIME timeout)
 * @brief Wait for an arbitrary set of events (with relative scalar timeout).
 *
 * This routine is a variant of rt_event_wait_timed() accepting a
 * relative timeout specification expressed as a scalar value.
 *
 * @param event The event descriptor.
 *
 * @param mask The set of bits to wait for.
 *
 * @param mask_r The value of the event mask at the time the task was
 * readied.
 *
 * @param mode The pend mode.
 *
 * @param timeout A delay expressed in clock ticks.
 * Passing TM_INFINITE causes the caller to block indefinitely until
 * the request is satisfied. Passing TM_NONBLOCK causes the service
 * to return without blocking in case the request cannot be satisfied
 * immediately.
 *
 * @apitags{xthread-nowait, switch-primary}
 */
 
/**
 * @fn int rt_event_wait_until(RT_EVENT *event, unsigned int mask, unsigned int *mask_r, int mode, RTIME abs_timeout)
 * @brief Wait for an arbitrary set of events (with absolute scalar timeout).
 *
 * This routine is a variant of rt_event_wait_timed() accepting an
 * absolute timeout specification expressed as a scalar value.
 *
 * @param event The event descriptor.
 *
 * @param mask The set of bits to wait for.
 *
 * @param mask_r The value of the event mask at the time the task was
 * readied.
 *
 * @param mode The pend mode.
 *
 * @param abs_timeout An absolute date expressed in clock ticks.
 * Passing TM_INFINITE causes the caller to block indefinitely until
 * the request is satisfied. Passing TM_NONBLOCK causes the service
 * to return without blocking in case the request cannot be satisfied
 * immediately.
 *
 * @apitags{xthread-nowait, switch-primary}
 */
 
/**
 * @fn int rt_event_wait_timed(RT_EVENT *event, unsigned int mask, unsigned int *mask_r, int mode, const struct timespec *abs_timeout)
 * @brief Wait for an arbitrary set of events.
 *
 * Waits for one or more events to be signaled in @a event, or until a
 * timeout elapses.
 *
 * @param event The event descriptor.
 *
 * @param mask The set of bits to wait for. Passing zero causes this
 * service to return immediately with a success value; the current
 * value of the event mask is also copied to @a mask_r.
 *
 * @param mask_r The value of the event mask at the time the task was
 * readied.
 *
 * @param mode The pend mode. The following flags can be OR'ed into
 * this bitmask, each of them affecting the operation:
 *
 * - EV_ANY makes the task pend in disjunctive mode (i.e. OR); this
 * means that the request is fulfilled when at least one bit set into
 * @a mask is set in the current event mask.
 *
 * - EV_ALL makes the task pend in conjunctive mode (i.e. AND); this
 * means that the request is fulfilled when at all bits set into @a
 * mask are set in the current event mask.
 *
 * @param abs_timeout An absolute date expressed in seconds / nanoseconds,
 * based on the Alchemy clock, specifying a time limit to wait for the
 * request to be satisfied. Passing NULL causes the caller to block
 * indefinitely until the request is satisfied. Passing
 * { .tv_sec = 0, .tv_nsec = 0 } causes the service to return without
 * blocking in case the request cannot be satisfied immediately.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -ETIMEDOUT is returned if @a abs_timeout is reached before the
 * request is satisfied.
 *
 * - -EWOULDBLOCK is returned if @a abs_timeout is { .tv_sec = 0,
 * .tv_nsec = 0 } and the requested flags are not set on entry to the
 * call.
 
 * - -EINTR is returned if rt_task_unblock() was called for the
 * current task before the request is satisfied.
 *
 * - -EINVAL is returned if @a mode is invalid, or @a event is not a
 * valid event flag group descriptor.
 *
 * - -EIDRM is returned if @a event is deleted while the caller was
 * sleeping on it. In such a case, @a event is no more valid upon
 * return of this service.
 *
 * - -EPERM is returned if this service should block, but was not
 * called from a Xenomai thread.
 *
 * @apitags{xthread-nowait, switch-primary}
 */
int rt_event_wait_timed(RT_EVENT *event,
           unsigned int mask, unsigned int *mask_r,
           int mode, const struct timespec *abs_timeout)
{
   int evobj_mode = 0, ret = 0;
   struct alchemy_event *evcb;
   struct service svc;
 
   if (!threadobj_current_p() && !alchemy_poll_mode(abs_timeout))
       return -EPERM;
 
   if (mode & ~EVOBJ_ANY)
       return -EINVAL;
 
   CANCEL_DEFER(svc);
 
   evcb = find_alchemy_event(event, &ret);
   if (evcb == NULL)
       goto out;
 
   if (mode & EV_ANY)
       evobj_mode = EVOBJ_ANY;
 
   ret = eventobj_wait(&evcb->evobj, mask, mask_r,
               evobj_mode, abs_timeout);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_event_signal(RT_EVENT *event, unsigned int mask)
 * @brief Signal an event.
 *
 * Post a set of flags to @a event. All tasks having their wait
 * request satisfied as a result of this operation are immediately
 * readied.
 *
 * @param event The event descriptor.
 *
 * @param mask The set of events to be posted.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a event is not an event flag group
 * descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
#ifndef DOXYGEN_CPP
CURRENT_IMPL(int, rt_event_signal,
        (RT_EVENT *event, unsigned int mask))
#else
int rt_event_signal(RT_EVENT *event, unsigned int mask)
#endif
{
   struct alchemy_event *evcb;
   struct service svc;
   int ret = 0;
 
   CANCEL_DEFER(svc);
 
   evcb = find_alchemy_event(event, &ret);
   if (evcb == NULL)
       goto out;
 
   ret = eventobj_post(&evcb->evobj, mask);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_event_clear(RT_EVENT *event,unsigned int mask,unsigned int *mask_r)
 * @brief Clear event flags.
 *
 * This routine clears a set of flags from @a event.
 *
 * @param event The event descriptor.
 *
 * @param mask The set of event flags to be cleared.
 *
 * @param mask_r If non-NULL, @a mask_r is the address of a memory
 * location which will receive the previous value of the event flag
 * group before the flags are cleared.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a event is not a valid event flag group
 * descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
#ifndef DOXYGEN_CPP
CURRENT_IMPL(int, rt_event_clear,
        (RT_EVENT *event, unsigned int mask, unsigned int *mask_r))
#else
int rt_event_clear(RT_EVENT *event,
          unsigned int mask, unsigned int *mask_r)
#endif
{
   struct alchemy_event *evcb;
   struct service svc;
   int ret = 0;
 
   CANCEL_DEFER(svc);
 
   evcb = find_alchemy_event(event, &ret);
   if (evcb == NULL)
       goto out;
 
   ret = eventobj_clear(&evcb->evobj, mask, mask_r);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_event_inquire(RT_EVENT *event, RT_EVENT_INFO *info)
 * @brief Query event flag group status.
 *
 * This routine returns the status information about @a event.
 *
 * @param event The event descriptor.
 *
 * @param info A pointer to the @ref RT_EVENT_INFO "return
 * buffer" to copy the information to.
 *
 * @return Zero is returned and status information is written to the
 * structure pointed at by @a info upon success. Otherwise:
 *
 * - -EINVAL is returned if @a event is not a valid event flag group
 * descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
int rt_event_inquire(RT_EVENT *event, RT_EVENT_INFO *info)
{
   struct alchemy_event *evcb;
   struct service svc;
   int ret = 0;
 
   CANCEL_DEFER(svc);
 
   evcb = find_alchemy_event(event, &ret);
   if (evcb == NULL)
       goto out;
 
   ret = eventobj_inquire(&evcb->evobj, 0, NULL, &info->value);
   if (ret < 0)
       goto out;
 
   strcpy(info->name, evcb->name);
   info->nwaiters = ret;
   ret = 0;
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_event_bind(RT_EVENT *event, const char *name, RTIME timeout)
 * @brief Bind to an event flag group.
 *
 * This routine creates a new descriptor to refer to an existing event
 * flag group identified by its symbolic name. If the object does not
 * exist on entry, the caller may block until an event flag group of
 * the given name is created.
 *
 * @param event The address of an event flag group descriptor filled
 * in by the operation. Contents of this memory is undefined upon
 * failure.
 *
 * @param name A valid NULL-terminated name which identifies the event
 * flag group to bind to. This string should match the object name
 * argument passed to rt_event_create().
 *
 * @param timeout The number of clock ticks to wait for the
 * registration to occur (see note). Passing TM_INFINITE causes the
 * caller to block indefinitely until the object is
 * registered. Passing TM_NONBLOCK causes the service to return
 * immediately without waiting if the object is not registered on
 * entry.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINTR is returned if rt_task_unblock() was called for the
 * current task before the retrieval has completed.
 *
 * - -EWOULDBLOCK is returned if @a timeout is equal to TM_NONBLOCK
 * and the searched object is not registered on entry.
 *
 * - -ETIMEDOUT is returned if the object cannot be retrieved within
 * the specified amount of time.
 *
 * - -EPERM is returned if this service should block, but was not
 * called from a Xenomai thread.
 *
 * @apitags{xthread-nowait, switch-primary}
 *
 * @note The @a timeout value is interpreted as a multiple of the
 * Alchemy clock resolution (see --alchemy-clock-resolution option,
 * defaults to 1 nanosecond).
 */
int rt_event_bind(RT_EVENT *event,
         const char *name, RTIME timeout)
{
   return alchemy_bind_object(name,
                  &alchemy_event_table,
                  timeout,
                  offsetof(struct alchemy_event, cobj),
                  &event->handle);
}
 
/**
 * @fn int rt_event_unbind(RT_EVENT *event)
 * @brief Unbind from an event flag group.
 *
 * @param event The event descriptor.
 *
 * This routine releases a previous binding to an event flag
 * group. After this call has returned, the descriptor is no more
 * valid for referencing this object.
 *
 * @apitags{thread-unrestricted}
 */
int rt_event_unbind(RT_EVENT *event)
{
   event->handle = 0;
   return 0;
}
 
/** @} */