aboutsummaryrefslogtreecommitdiffstats
path: root/ChibiOS_20.3.2/os/oslib/src/chdelegates.c
blob: d1c2453671f9e830c5d2c1968f7ef6d0037d70cc (plain)
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
/*
    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio.

    This file is part of ChibiOS.

    ChibiOS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    ChibiOS 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * @file    oslib/src/chdelegates.c
 * @brief   Delegate threads code.
 * @details Delegate threads.
 *          <h2>Operation mode</h2>
 *          A delegate thread is a thread performing function calls triggered
 *          by other threads. This functionality is especially useful when
 *          encapsulating a library not designed for threading into a
 *          delegate thread. Other threads have access to the library without
 *          having to worry about mutual exclusion.
 * @pre     In order to use the pipes APIs the @p CH_CFG_USE_DELEGATES
 *          option must be enabled in @p chconf.h.
 * @note    Compatible with RT and NIL.
 *
 * @addtogroup oslib_delegates
 * @{
 */

#include "ch.h"

#if (CH_CFG_USE_DELEGATES == TRUE) || defined(__DOXYGEN__)

/*===========================================================================*/
/* Module local definitions.                                                 */
/*===========================================================================*/

/*===========================================================================*/
/* Module exported variables.                                                */
/*===========================================================================*/

/*===========================================================================*/
/* Module local types.                                                       */
/*===========================================================================*/

/**
 * @brief   Type of a structure representing a delegate call.
 */
typedef struct {
  /**
   * @brief   The delegate veneer function.
   */
  delegate_veneer_t veneer;
  /**
   * @brief   Pointer to the caller @p va_list object.
   */
  va_list           *argsp;
} call_message_t;

/*===========================================================================*/
/* Module local variables.                                                   */
/*===========================================================================*/

/*===========================================================================*/
/* Module local functions.                                                   */
/*===========================================================================*/

/*===========================================================================*/
/* Module exported functions.                                                */
/*===========================================================================*/

/*lint -save -e586 [17.1] Required by design.*/

/**
 * @brief   Veneer for functions with no parameters.
 *
 * @param[in] argsp     the list of arguments
 * @return              The function return value.
 */
msg_t __ch_delegate_fn0(va_list *argsp) {
  delegate_fn0_t fn0 = (delegate_fn0_t)va_arg(*argsp, delegate_fn0_t);
  return fn0();
}

/**
 * @brief   Veneer for functions with one parameter.
 *
 * @param[in] argsp     the list of arguments
 * @return              The function return value.
 */
msg_t __ch_delegate_fn1(va_list *argsp) {
  delegate_fn1_t fn1 = (delegate_fn1_t)va_arg(*argsp, delegate_fn1_t);
  msg_t p1 = (msg_t)va_arg(*argsp, msg_t);
  return fn1(p1);
}

/**
 * @brief   Veneer for functions with two parameters.
 *
 * @param[in] argsp     the list of arguments
 * @return              The function return value.
 */
msg_t __ch_delegate_fn2(va_list *argsp) {
  delegate_fn2_t fn2 = (delegate_fn2_t)va_arg(*argsp, delegate_fn2_t);
  msg_t p1 = (msg_t)va_arg(*argsp, msg_t);
  msg_t p2 = (msg_t)va_arg(*argsp, msg_t);
  return fn2(p1, p2);
}

/**
 * @brief   Veneer for functions with three parameters.
 *
 * @param[in] argsp     the list of arguments
 * @return              The function return value.
 */
msg_t __ch_delegate_fn3(va_list *argsp) {
  delegate_fn3_t fn3 = (delegate_fn3_t)va_arg(*argsp, delegate_fn3_t);
  msg_t p1 = (msg_t)va_arg(*argsp, msg_t);
  msg_t p2 = (msg_t)va_arg(*argsp, msg_t);
  msg_t p3 = (msg_t)va_arg(*argsp, msg_t);
  return fn3(p1, p2, p3);
}

/**
 * @brief   Veneer for functions with four parameters.
 *
 * @param[in] argsp     the list of arguments
 * @return              The function return value.
 */
msg_t __ch_delegate_fn4(va_list *argsp) {
  delegate_fn4_t fn4 = (delegate_fn4_t)va_arg(*argsp, delegate_fn4_t);
  msg_t p1 = (msg_t)va_arg(*argsp, msg_t);
  msg_t p2 = (msg_t)va_arg(*argsp, msg_t);
  msg_t p3 = (msg_t)va_arg(*argsp, msg_t);
  msg_t p4 = (msg_t)va_arg(*argsp, msg_t);
  return fn4(p1, p2, p3, p4);
}

/**
 * @brief   Triggers a function call on a delegate thread.
 * @note    The thread must be executing @p chDelegateDispatchTimeout() in
 *          order to have the functions called.
 *
 * @param[in] tp        pointer to the delegate thread
 * @param[in] veneer    pointer to the veneer function to be called
 * @param[in] ...       variable number of parameters
 * @return              The function return value casted to msg_t. It is
 *                      garbage for functions returning @p void.
 */
msg_t chDelegateCallVeneer(thread_t *tp, delegate_veneer_t veneer, ...) {
  va_list args;
  call_message_t cm;
  msg_t msg;

  va_start(args, veneer);

  /* Preparing the call message.*/
  cm.veneer = veneer;
  cm.argsp  = &args;
  (void)cm; /* Suppresses a lint warning.*/

  /* Sending the message to the dispatcher thread, the return value is
     contained in the returned message.*/
  msg = chMsgSend(tp, (msg_t)&cm);

  va_end(args);

  return msg;
}

/*lint -restore*/

/**
 * @brief   Call messages dispatching.
 * @details The function awaits for an incoming call messages and calls the
 *          specified functions, then it returns. In case multiple threads
 *          are sending messages then the requests are served in priority
 *          order.
 *
 * @api
 */
void chDelegateDispatch(void) {
  thread_t *tp;
  const call_message_t *cmp;
  msg_t ret;

  tp = chMsgWait();
  cmp = (const call_message_t *)chMsgGet(tp);
  ret = cmp->veneer(cmp->argsp);

  chMsgRelease(tp, ret);
}

/**
 * @brief   Call messages dispatching with timeout.
 * @details The function awaits for an incoming call messages and calls the
 *          specified functions, then it returns. In case multiple threads
 *          are sending messages then the requests are served in priority
 *          order.
 *
 * @param[in] timeout   the number of ticks before the operation timeouts,
 *                      the following special values are allowed:
 *                      - @a TIME_INFINITE no timeout.
 *                      .
 * @return              The function outcome.
 * @retval MSG_OK       if a function has been called.
 * @retval MSG_TIMEOUT  if a timeout occurred.
 *
 * @api
 */
msg_t chDelegateDispatchTimeout(sysinterval_t timeout) {
  thread_t *tp;
  const call_message_t *cmp;
  msg_t ret;

  tp = chMsgWaitTimeout(timeout);
  if (tp == NULL) {
    return MSG_TIMEOUT;
  }

  cmp = (const call_message_t *)chMsgGet(tp);
  ret = cmp->veneer(cmp->argsp);

  chMsgRelease(tp, ret);

  return MSG_OK;
}

#endif /* CH_CFG_USE_DELEGATES == TRUE */

/** @} */