1 /*
2 * Copyright 2017 Rockchip Electronics Co. LTD
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <assert.h>
18 #include <errno.h>
19 #include <malloc.h>
20 #include <memory.h>
21 #include <pthread.h>
22 #include <stdio.h>
23 #include <sys/time.h>
24 #include <unistd.h>
25
26 #include "mpp_event_trigger.h"
27
28 #include "mpp_debug.h"
29
30 struct event_ctx_impl {
31 int (*notify)(void *param);
32
33 event_trigger trigger;
34
35 pthread_cond_t condition;
36 pthread_mutex_t mutex;
37 RK_U32 semval;
38
39 pthread_t thr;
40 RK_S32 flag;
41
42 struct event_packet *ea;
43 int event_idx;
44
45 void *parent;
46 };
47
event_create(struct event_ctx_impl * ctx)48 static int event_create(struct event_ctx_impl *ctx)
49 {
50 int ret;
51
52 ret = pthread_cond_init(&ctx->condition, NULL);
53 if (ret != 0)
54 return -1;
55
56 ret = pthread_mutex_init(&ctx->mutex, NULL);
57 if (ret != 0)
58 return -1;
59
60 ctx->semval = ctx->ea->e[0].idx;
61 mpp_log_f("with %u\n", ctx->semval);
62
63 return 0;
64 }
65
event_destroy(struct event_ctx_impl * ctx)66 static void event_destroy(struct event_ctx_impl *ctx)
67 {
68 pthread_cond_destroy(&ctx->condition);
69 pthread_mutex_destroy(&ctx->mutex);
70 }
71
72 /* initialize and schedule next event */
event_init(struct event_ctx_impl * ctx)73 static void event_init(struct event_ctx_impl *ctx)
74 {
75 struct ievent *e_curr;
76 struct ievent *e_next;
77
78 pthread_mutex_lock(&ctx->mutex);
79 e_curr = &ctx->ea->e[ctx->event_idx % ctx->ea->cnt];
80 e_next = &ctx->ea->e[(++ctx->event_idx) % ctx->ea->cnt];
81
82 mpp_log("curr %d, next %d\n",
83 e_curr->idx, e_next->idx);
84
85 if (e_next->idx > e_curr->idx)
86 ctx->semval = e_next->idx - e_curr->idx;
87 else if (ctx->ea->loop > 0)
88 ctx->semval = e_next->idx + ctx->ea->loop - e_curr->idx;
89 else
90 ctx->flag = 0;
91
92 mpp_log_f("semval %u\n", ctx->semval);
93 pthread_mutex_unlock(&ctx->mutex);
94 }
95
96 /* wait the event trigger deadline */
97 /*static void event_wait(struct event_ctx_impl *ctx)
98 {
99 pthread_mutex_lock(&ctx->mutex);
100 while (ctx->semval > 0)
101 pthread_cond_wait(&ctx->condition, &ctx->mutex);
102
103 ctx->semval--;
104 pthread_mutex_unlock(&ctx->mutex);
105 }*/
106
107 /* wait the event trigger deadline with timeout, avoid dead lock */
event_timed_wait(struct event_ctx_impl * ctx,unsigned int milli_sec)108 static int event_timed_wait(struct event_ctx_impl *ctx, unsigned int milli_sec)
109 {
110 int err = 0;
111 struct timespec final_time;
112 struct timeval curr_time;
113 long int microdelay;
114
115 gettimeofday(&curr_time, NULL);
116 /*
117 * convert timeval to timespec and add delay in milliseconds
118 * for the timeout
119 */
120 microdelay = ((milli_sec * 1000 + curr_time.tv_usec));
121 final_time.tv_sec = curr_time.tv_sec + (microdelay / 1000000);
122 final_time.tv_nsec = (microdelay % 1000000) * 1000;
123 pthread_mutex_lock(&ctx->mutex);
124 while (ctx->semval > 0) {
125 err = pthread_cond_timedwait(&ctx->condition, &ctx->mutex, &final_time);
126 if (err != 0)
127 ctx->semval--;
128 }
129 ctx->semval--;
130 pthread_mutex_unlock(&ctx->mutex);
131 return err;
132 }
133
134 /* event heart beat */
event_down(struct event_ctx_impl * ctx)135 static void event_down(struct event_ctx_impl *ctx)
136 {
137 pthread_mutex_lock(&ctx->mutex);
138 ctx->semval--;
139 pthread_cond_signal(&ctx->condition);
140 pthread_mutex_unlock(&ctx->mutex);
141 }
142
143 /* a callback to notify the event trigger that there is a event heart beat */
event_notify(void * param)144 static int event_notify(void *param)
145 {
146 struct event_ctx_impl *ctx = (struct event_ctx_impl *)param;
147
148 if (ctx->flag)
149 event_down(ctx);
150
151 return 0;
152 }
153
event_trigger_thread(void * param)154 static void *event_trigger_thread(void *param)
155 {
156 struct event_ctx_impl *ctx = (struct event_ctx_impl *)param;
157 struct timeval curr_time;
158 long start_time, curr;
159 RK_S32 ret;
160
161 gettimeofday(&curr_time, NULL);
162
163 start_time = curr_time.tv_sec * 1000 + curr_time.tv_usec / 1000;
164 event_create(ctx);
165
166 while (1) {
167 ret = event_timed_wait(ctx, ctx->semval * 10000);
168 if (!ctx->flag)
169 break;
170
171 if (ret == ETIMEDOUT) {
172 mpp_err("wait event timeout\n");
173 break;
174 }
175
176 gettimeofday(&curr_time, NULL);
177
178 curr = curr_time.tv_sec * 1000 + curr_time.tv_usec / 1000;
179 /* TODO, trigger event */
180 mpp_log("[%ld ms] event idx %d triggered\n",
181 curr - start_time, ctx->event_idx);
182 mpp_log("cnt %d\n", ctx->ea->cnt);
183 ctx->trigger(ctx->parent, ctx->ea->e[ctx->event_idx % ctx->ea->cnt].event);
184
185 /* schedule next event */
186 event_init(ctx);
187 if (!ctx->flag)
188 break;
189 }
190
191 event_destroy(ctx);
192
193 mpp_log_f("exit\n");
194
195 return NULL;
196 }
197
event_ctx_create(struct event_packet * ea,event_trigger trigger,void * parent)198 struct event_ctx* event_ctx_create(struct event_packet *ea,
199 event_trigger trigger, void *parent)
200 {
201 struct event_ctx_impl *ctx = (struct event_ctx_impl *)malloc(sizeof(*ctx));
202
203 if (ctx == NULL) {
204 mpp_err("allocate event ctx failed\n");
205 return NULL;
206 }
207
208 assert(ea->cnt <= 128);
209
210 ctx->event_idx = 0;
211 ctx->ea = ea;
212
213 ctx->notify = event_notify;
214 ctx->trigger = trigger;
215 ctx->parent = parent;
216
217 ctx->flag = 1;
218 pthread_create(&ctx->thr, NULL, event_trigger_thread, ctx);
219
220 return (struct event_ctx *)ctx;
221 }
222
event_ctx_release(struct event_ctx * ctx)223 void event_ctx_release(struct event_ctx *ctx)
224 {
225 void *ret;
226 struct event_ctx_impl *ictx = (struct event_ctx_impl *)ctx;
227
228 assert(ctx != NULL);
229
230 if (ictx->flag) {
231 ictx->flag = 0;
232 ictx->semval = 0;
233 pthread_cond_signal(&ictx->condition);
234 }
235
236 pthread_join(ictx->thr, &ret);
237
238 free(ictx);
239 }
240
241 #ifdef EVENT_TRIGGER_TEST
242 /*
243 * the following codes is the sample to use the event trigger
244 */
245 struct event_impl {
246 int cnt;
247 int loop;
248 int idx[128];
249 int event[128];
250 };
251
252 /* a callback to notify test that a event occur */
event_occur(void * parent,void * event)253 static void event_occur(void *parent, void *event)
254 {
255 int *e = (int *)event;
256 mpp_log("event %d occur\n", *e);
257 }
258
main(int argc,char ** argv)259 int main(int argc, char **argv)
260 {
261 struct event_ctx *ctx;
262 int i = 0;
263 struct event_impl ie;
264 struct event_packet ea;
265
266 ie.cnt = 4;
267 ie.loop = 25;
268 ie.event[0] = ie.idx[0] = 4;
269 ie.event[1] = ie.idx[1] = 8;
270 ie.event[2] = ie.idx[2] = 18;
271 ie.event[3] = ie.idx[3] = 20;
272
273 ea.cnt = ie.cnt;
274 ea.loop = ie.loop;
275
276 for (i = 0; i < ie.cnt; ++i) {
277 ea.e[i].idx = ie.idx[i];
278 ea.e[i].event = &ie.event[i];
279 }
280
281 ctx = event_ctx_create(&ea, event_occur, NULL);
282
283 while (i++ < 100) {
284 usleep(10 * 1000);
285 mpp_log("%03d:\n", i);
286 ctx->notify((void*)ctx);
287 }
288
289 event_ctx_release(ctx);
290
291 return 0;
292 }
293 #endif
294