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