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
/*
 * 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 <copperplate/threadobj.h>
#include <copperplate/heapobj.h>
#include "internal.h"
#include "cond.h"
#include "timer.h"
#include "mutex.h"
 
/**
 * @ingroup alchemy
 * @defgroup alchemy_cond Condition variable services
 *
 * POSIXish condition variable mechanism
 *
 * A condition variable is a synchronization mechanism which allows
 * tasks to suspend execution until some predicate on some arbitrary
 * shared data is satisfied.
 *
 * The basic operations on conditions are: signal the condition (when
 * the predicate becomes true), and wait for the condition, blocking
 * the task execution until another task signals the condition.  A
 * condition variable must always be associated with a mutex, to avoid
 * a well-known race condition where a task prepares to wait on a
 * condition variable and another task signals the condition just
 * before the first task actually waits on it.
 *
 * @{
 */
 
struct syncluster alchemy_cond_table;
 
static DEFINE_NAME_GENERATOR(cond_namegen, "cond",
                struct alchemy_cond, name);
 
DEFINE_LOOKUP_PRIVATE(cond, RT_COND);
 
#ifdef CONFIG_XENO_REGISTRY
 
static ssize_t cond_registry_read(struct fsobj *fsobj,
                 char *buf, size_t size, off_t offset,
                 void *priv)
{
   return 0;        /* FIXME */
}
 
static struct registry_operations registry_ops = {
   .read    = cond_registry_read
};
 
#else /* !CONFIG_XENO_REGISTRY */
 
static struct registry_operations registry_ops;
 
#endif /* CONFIG_XENO_REGISTRY */
 
/**
 * @fn int rt_cond_create(RT_COND *cond, const char *name)
 * @brief Create a condition variable.
 *
 * Create a synchronization object which allows tasks to suspend
 * execution until some predicate on shared data is satisfied.
 *
 * @param cond The address of a condition variable 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
 * condition variable. When non-NULL and non-empty, a copy of this
 * string is used for indexing the created condition variable into the
 * object registry.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -ENOMEM is returned if the system fails to get memory from the
 * main heap in order to create the condition variable.
 *
 * - -EEXIST is returned if the @a name is conflicting with an already
 * registered condition variable.
 *
 * - -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 Condition variables can be shared by multiple processes which
 * belong to the same Xenomai session.
 *
 * @attention If the underlying threading library does not support
 * pthread_condattr_setclock(), timings with Alchemy condition
 * variables will be based on CLOCK_REALTIME, and may therefore be
 * affected by updates to the system date (e.g. NTP). This typically
 * concerns legacy setups based on the linuxthreads library.
 * In the normal case, timings are based on CLOCK_MONOTONIC.
 */
int rt_cond_create(RT_COND *cond, const char *name)
{
   struct alchemy_cond *ccb;
   pthread_condattr_t cattr;
   struct service svc;
   int ret = 0;
 
   if (threadobj_irq_p())
       return -EPERM;
 
   CANCEL_DEFER(svc);
 
   ccb = xnmalloc(sizeof(*ccb));
   if (ccb == NULL) {
       ret = -ENOMEM;
       goto out;
   }
 
   /*
    * XXX: Alchemy condvars are paired with Alchemy mutex
    * objects, so we must rely on POSIX condvars directly.
    */
   generate_name(ccb->name, name, &cond_namegen);
   pthread_condattr_init(&cattr);
   pthread_condattr_setpshared(&cattr, mutex_scope_attribute);
   /*
    * pthread_condattr_setclock() may return ENOSYS over Cobalt
    * if not actually implemented by the threading library, but
    * only by the compat placeholder. In such a case, timings
    * will be based on CLOCK_REALTIME, which is an accepted
    * restriction.
    */
   pthread_condattr_setclock(&cattr, CLOCK_COPPERPLATE);
   __RT(pthread_cond_init(&ccb->cond, &cattr));
   pthread_condattr_destroy(&cattr);
   ccb->magic = cond_magic;
 
   registry_init_file(&ccb->fsobj, &registry_ops, 0);
   ret = __bt(registry_add_file(&ccb->fsobj, O_RDONLY,
                    "/alchemy/condvars/%s", ccb->name));
   if (ret) {
       warning("failed to export condvar %s to registry, %s",
           ccb->name, symerror(ret));
       ret = 0;
   }
 
   ret = syncluster_addobj(&alchemy_cond_table, ccb->name, &ccb->cobj);
   if (ret) {
       registry_destroy_file(&ccb->fsobj);
       __RT(pthread_cond_destroy(&ccb->cond));
       xnfree(ccb);
   } else
       cond->handle = mainheap_ref(ccb, uintptr_t);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_cond_delete(RT_COND *cond)
 * @brief Delete a condition variable.
 *
 * This routine deletes a condition variable object previously created
 * by a call to rt_cond_create().
 *
 * @param cond The condition variable descriptor.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a alarm is not a valid condition variable
 * descriptor.
 *
 * - -EPERM is returned if this service was called from an
 * asynchronous context.
 *
 * - -EBUSY is returned upon an attempt to destroy the object
 * referenced by @a cond while it is referenced (for example, while
 * being used in a rt_cond_wait(), rt_cond_wait_timed() or
 * rt_cond_wait_until() by another task).
 *
 * @apitags{mode-unrestricted, switch-secondary}
 */
int rt_cond_delete(RT_COND *cond)
{
   struct alchemy_cond *ccb;
   struct service svc;
   int ret = 0;
 
   if (threadobj_irq_p())
       return -EPERM;
 
   CANCEL_DEFER(svc);
 
   ccb = find_alchemy_cond(cond, &ret);
   if (ccb == NULL)
       goto out;
 
   ret = -__RT(pthread_cond_destroy(&ccb->cond));
   if (ret)
       goto out;
 
   ccb->magic = ~cond_magic;
   registry_destroy_file(&ccb->fsobj);
   syncluster_delobj(&alchemy_cond_table, &ccb->cobj);
   xnfree(ccb);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_cond_signal(RT_COND *cond)
 * @brief Signal a condition variable.
 *
 * If the condition variable @a cond is pended, this routine
 * immediately unblocks the first waiting task (by queuing priority
 * order).
 *
 * @param cond The condition variable descriptor.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a cond is not a valid condition variable
 * descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
int rt_cond_signal(RT_COND *cond)
{
   struct alchemy_cond *ccb;
   int ret = 0;
 
   ccb = find_alchemy_cond(cond, &ret);
   if (ccb == NULL)
       return ret;
 
   return -__RT(pthread_cond_signal(&ccb->cond));
}
 
/**
 * @fn int rt_cond_broadcast(RT_COND *cond)
 * @brief Broadcast a condition variable
 *
 * All tasks currently waiting on the condition variable are
 * immediately unblocked.
 *
 * @param cond The condition variable descriptor.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a cond is not a valid condition variable
 * descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
int rt_cond_broadcast(RT_COND *cond)
{
   struct alchemy_cond *ccb;
   int ret = 0;
 
   ccb = find_alchemy_cond(cond, &ret);
   if (ccb == NULL)
       return ret;
 
   return -__RT(pthread_cond_broadcast(&ccb->cond));
}
 
/**
 * @fn int rt_cond_wait(RT_COND *cond, RT_MUTEX *mutex, RTIME timeout)
 * @brief Wait on a condition variable (with relative scalar timeout).
 *
 * This routine is a variant of rt_cond_wait_timed() accepting a
 * relative timeout specification expressed as a scalar value.
 *
 * @param cond The condition variable descriptor.
 *
 * @param mutex The address of the mutex serializing the access to the
 * shared data.
 *
 * @param timeout A delay expressed in clock ticks. Passing
 * TM_INFINITE causes the caller to block indefinitely. Passing
 * TM_NONBLOCK causes the caller to return immediately without block.
 *
 * @apitags{xthread-only, switch-primary}
 */
 
/**
 * @fn int rt_cond_wait_until(RT_COND *cond, RT_MUTEX *mutex, RTIME abs_timeout)
 * @brief Wait on a condition variable (with absolute scalar timeout).
 *
 * This routine is a variant of rt_cond_wait_timed() accepting an
 * abs_timeout specification expressed as a scalar value.
 *
 * @param cond The condition variable descriptor.
 *
 * @param mutex The address of the mutex serializing the access to the
 * shared data.
 *
 * @param abs_timeout An absolute date expressed in clock ticks.
 * Passing TM_INFINITE causes the caller to block indefinitely.
 * Passing TM_NONBLOCK causes the caller to return immediately
 * without block.
 *
 * @apitags{xthread-only, switch-primary}
 */
 
/**
 * @fn int rt_cond_wait_timed(RT_COND *cond, RT_MUTEX *mutex, const struct timespec *abs_timeout)
 * @brief Wait on a condition variable.
 *
 * This service atomically releases the mutex and blocks the calling
 * task, until the condition variable @a cond is signaled or a timeout
 * occurs, whichever comes first. The mutex is re-acquired before
 * returning from this service.
 *
 * @param cond The condition variable descriptor.
 *
 * @param mutex The address of the mutex serializing the access to the
 * shared data.
 *
 * @param abs_timeout An absolute date expressed in seconds / nanoseconds,
 * based on the Alchemy clock, specifying a time limit to wait for the
 * condition variable to be signaled. Passing NULL causes the caller to
 * block indefinitely. Passing { .tv_sec = 0, .tv_nsec = 0 } causes the
 * caller to return immediately without block.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -ETIMEDOUT is returned if @a abs_timeout is reached before the
 * condition variable is signaled.
 *
 * - -EWOULDBLOCK is returned if @a abs_timeout is { .tv_sec = 0,
 * .tv_nsec = 0 } .
 *
 * - -EINTR is returned if rt_task_unblock() was called for the
 * current task.
 *
 * - -EINVAL is returned if @a cond is not a valid condition variable
 * descriptor.
 *
 * - -EIDRM is returned if @a cond is deleted while the caller was
 * waiting on the condition variable. In such event, @a cond 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-only, switch-primary}
 */
int rt_cond_wait_timed(RT_COND *cond, RT_MUTEX *mutex,
              const struct timespec *abs_timeout)
{
   struct alchemy_mutex *mcb;
   struct alchemy_cond *ccb;
   int ret = 0;
 
   if (alchemy_poll_mode(abs_timeout))
       return -EWOULDBLOCK;
 
   ccb = find_alchemy_cond(cond, &ret);
   if (ccb == NULL)
       return ret;
 
   mcb = find_alchemy_mutex(mutex, &ret);
   if (mcb == NULL)
       return ret;
 
   if (abs_timeout)
       ret = -__RT(pthread_cond_timedwait(&ccb->cond,
                          &mcb->lock, abs_timeout));
   else
       ret = -__RT(pthread_cond_wait(&ccb->cond, &mcb->lock));
 
   return ret;
}
 
/**
 * @fn int rt_cond_inquire(RT_COND *cond, RT_COND_INFO *info)
 * @brief Query condition variable status.
 *
 * This routine returns the status information about the specified
 * condition variable.
 *
 * @param cond The condition variable descriptor.
 *
 * @param info A pointer to the @ref RT_COND_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 cond is not a valid condition variable
 * descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
int rt_cond_inquire(RT_COND *cond, RT_COND_INFO *info)
{
   struct alchemy_cond *ccb;
   int ret = 0;
 
   ccb = find_alchemy_cond(cond, &ret);
   if (ccb == NULL)
       return ret;
 
   strcpy(info->name, ccb->name);
 
   return ret;
}
 
/**
 * @fn int rt_cond_bind(RT_COND *cond, const char *name, RTIME timeout)
 * @brief Bind to a condition variable.
 *
 * This routine creates a new descriptor to refer to an existing
 * condition variable identified by its symbolic name. If the object
 * not exist on entry, the caller may block until a condition variable
 * of the given name is created.
 *
 * @param cond The address of a condition variable descriptor filled
 * in by the operation. Contents of this memory is undefined upon
 * failure.
 *
 * @param name A valid NULL-terminated name which identifies the
 * condition variable to bind to. This string should match the object
 * name argument passed to rt_cond_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_cond_bind(RT_COND *cond,
        const char *name, RTIME timeout)
{
   return alchemy_bind_object(name,
                  &alchemy_cond_table,
                  timeout,
                  offsetof(struct alchemy_cond, cobj),
                  &cond->handle);
}
 
/**
 * @fn int rt_cond_unbind(RT_COND *cond)
 * @brief Unbind from a condition variable.
 *
 * @param cond The condition variable descriptor.
 *
 * This routine releases a previous binding to a condition
 * variable. After this call has returned, the descriptor is no more
 * valid for referencing this object.
 *
 * @apitags{thread-unrestricted}
 */
int rt_cond_unbind(RT_COND *cond)
{
   cond->handle = 0;
   return 0;
}
 
/** @} */