xref: /aosp_15_r20/external/libavc/common/ih264_list.c (revision 495ae853bb871d1e5a258cb02c2cc13cde8ddb9a)
1 /******************************************************************************
2  *
3  * Copyright (C) 2015 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *****************************************************************************
18  * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
19 */
20 
21 /**
22 *******************************************************************************
23 * @file
24 *  ih264_list.c
25 *
26 * @brief
27 *  Contains functions for buf queue
28 *
29 * @author
30 *  ittiam
31 *
32 * @par List of Functions:
33 *  - ih264_list_size
34 *  - ih264_list_lock
35 *  - ih264_list_unlock
36 *  - ih264_list_yield
37 *  - ih264_list_free
38 *  - ih264_list_init
39 *  - ih264_list_reset
40 *  - ih264_list_deinit
41 *  - ih264_list_terminate
42 *  - ih264_list_queue
43 *  - ih264_list_dequeue
44 *
45 * @remarks
46 *  none
47 *
48 *******************************************************************************
49 */
50 
51 /*****************************************************************************/
52 /* File Includes                                                             */
53 /*****************************************************************************/
54 
55 /* System Include Files */
56 #include <stdio.h>
57 #include <stddef.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <assert.h>
61 
62 /* User Include Files */
63 #include "ih264_typedefs.h"
64 #include "ithread.h"
65 #include "ih264_debug.h"
66 #include "ih264_macros.h"
67 #include "ih264_error.h"
68 #include "ih264_list.h"
69 #include "ih264_platform_macros.h"
70 
71 /*****************************************************************************/
72 /* Function Definitions                                                      */
73 /*****************************************************************************/
74 
75 /**
76 *******************************************************************************
77 *
78 * @brief Returns size for job queue context.
79 *
80 * @par Description
81 *  Returns size for job queue context.
82 *
83 * @param[in] num_entries
84 *  max number of jobs that can be queued
85 *
86 * @param[in] entry_size
87 *  memory needed for a single job
88 *
89 * @returns Size of the job queue context
90 *
91 * @remarks
92 *
93 *******************************************************************************
94 */
ih264_list_size(WORD32 num_entries,WORD32 entry_size)95 WORD32 ih264_list_size(WORD32 num_entries, WORD32 entry_size)
96 {
97     WORD32 size;
98     WORD32 clz;
99 
100     size = sizeof(list_t);
101     size += ithread_get_mutex_lock_size();
102 
103     /* Use next power of two number of entries*/
104     clz = CLZ(num_entries);
105     num_entries = 1 << (32 - clz);
106 
107     size += num_entries * entry_size;
108     return size;
109 }
110 
111 /**
112 *******************************************************************************
113 *
114 * @brief Locks the list context
115 *
116 * @par Description
117 *  Locks the list context by calling ithread_mutex_lock()
118 *
119 * @param[in] ps_list
120 *  Pointer to job queue context
121 *
122 * @returns IH264_FAIL if mutex lock fails else IH264_SUCCESS
123 *
124 * @remarks
125 *
126 *******************************************************************************
127 */
ih264_list_lock(list_t * ps_list)128 IH264_ERROR_T ih264_list_lock(list_t *ps_list)
129 {
130     WORD32 retval;
131 
132     retval = ithread_mutex_lock(ps_list->pv_mutex);
133     if(retval)
134         return IH264_FAIL;
135     return IH264_SUCCESS;
136 }
137 
138 /**
139 *******************************************************************************
140 *
141 * @brief Unlocks the list context
142 *
143 * @par Description
144 *  Unlocks the list context by calling ithread_mutex_unlock()
145 *
146 * @param[in] ps_list
147 *  Pointer to job queue context
148 *
149 * @returns IH264_FAIL if mutex unlock fails else IH264_SUCCESS
150 *
151 * @remarks
152 *
153 *******************************************************************************
154 */
ih264_list_unlock(list_t * ps_list)155 IH264_ERROR_T ih264_list_unlock(list_t *ps_list)
156 {
157     WORD32 retval;
158 
159     retval = ithread_mutex_unlock(ps_list->pv_mutex);
160     if(retval)
161         return IH264_FAIL;
162     return IH264_SUCCESS;
163 }
164 
165 /**
166 *******************************************************************************
167 *
168 * @brief Yields the thread
169 *
170 * @par Description
171 *  Unlocks the list context by calling  ih264_list_unlock(), ithread_yield()
172 *  and then ih264_list_lock(). List is unlocked before to ensure its
173 *  access by other threads. If unlock is not done before calling yield then
174 *  no other thread can access the list functions and update list.
175 *
176 * @param[in] ps_list
177 *  pointer to Job Queue context
178 *
179 * @returns IH264_FAIL if mutex lock unlock or yield fails else IH264_SUCCESS
180 *
181 * @remarks
182 *
183 *******************************************************************************
184 */
ih264_list_yield(list_t * ps_list)185 IH264_ERROR_T ih264_list_yield(list_t *ps_list)
186 {
187     IH264_ERROR_T ret;
188 
189     ret = ih264_list_unlock(ps_list);
190     RETURN_IF((ret != IH264_SUCCESS), ret);
191 
192     ithread_yield();
193 
194     if(ps_list->i4_yield_interval_us > 0)
195         ithread_usleep(ps_list->i4_yield_interval_us);
196 
197     ret = ih264_list_lock(ps_list);
198     RETURN_IF((ret != IH264_SUCCESS), ret);
199     return IH264_SUCCESS;
200 }
201 
202 /**
203 *******************************************************************************
204 *
205 * @brief free the list context
206 *
207 * @par Description
208 *  Frees the list context
209 *
210 * @param[in] ps_list
211 *  pointer to Job Queue context
212 *
213 * @returns IH264_FAIL if mutex desttroy fails else IH264_SUCCESS
214 *
215 * @remarks
216 *  Since it will be called only once by master thread this is not thread safe.
217 *
218 *******************************************************************************
219 */
ih264_list_free(list_t * ps_list)220 IH264_ERROR_T ih264_list_free(list_t *ps_list)
221 {
222     WORD32 ret;
223 
224     ret = ithread_mutex_destroy(ps_list->pv_mutex);
225     if(0 == ret)
226         return IH264_SUCCESS;
227     return IH264_FAIL;
228 }
229 
230 /**
231 *******************************************************************************
232 *
233 * @brief Initialize the buf queue
234 *
235 * @par Description
236 *  Initializes the list context and sets write and read pointers to start of
237 *  buf queue buffer
238 *
239 * @param[in] pv_buf
240 *  Memory for job queue context
241 *
242 * @param[in] buf_size
243 *  Size of the total memory allocated
244 *
245 * @param[in] num_entries
246 *  max number of jobs that can be queued
247 *
248 * @param[in] entry_size
249 *  memory needed for a single job
250 *
251 * @param[in] yield_interval_us
252 *  Thread sleep duration
253 *
254 * @returns Pointer to job queue context
255 *
256 * @remarks
257 *  Since it will be called only once by master thread this is not thread safe.
258 *
259 *******************************************************************************
260 */
ih264_list_init(void * pv_buf,WORD32 buf_size,WORD32 num_entries,WORD32 entry_size,WORD32 yield_interval_us)261 void* ih264_list_init(void *pv_buf,
262                       WORD32 buf_size,
263                       WORD32 num_entries,
264                       WORD32 entry_size,
265                       WORD32 yield_interval_us)
266 {
267     list_t *ps_list = (list_t *)pv_buf;
268     UWORD8 *pu1_buf = (UWORD8 *)pv_buf;
269 
270     pu1_buf += sizeof(list_t);
271     buf_size -= sizeof(list_t);
272 
273     ps_list->pv_mutex = pu1_buf;
274     pu1_buf += ithread_get_mutex_lock_size();
275     buf_size -= ithread_get_mutex_lock_size();
276 
277     if (buf_size <= 0)
278       return NULL;
279 
280     ithread_mutex_init(ps_list->pv_mutex);
281 
282     /* Ensure num_entries is power of two */
283     ASSERT(0 == (num_entries & (num_entries - 1)));
284 
285     /* Ensure remaining buffer is large enough to hold given number of entries */
286     ASSERT((num_entries * entry_size) <= buf_size);
287 
288     ps_list->pv_buf_base = pu1_buf;
289     ps_list->i4_terminate = 0;
290     ps_list->i4_entry_size = entry_size;
291     ps_list->i4_buf_rd_idx = 0;
292     ps_list->i4_buf_wr_idx = 0;
293     ps_list->i4_log2_buf_max_idx = 32 - CLZ(num_entries);
294     ps_list->i4_buf_max_idx = num_entries;
295     ps_list->i4_yield_interval_us = yield_interval_us;
296 
297     return ps_list;
298 }
299 
300 /**
301 *******************************************************************************
302 *
303 * @brief Resets the list context
304 *
305 * @par Description
306 *  Resets the list context by initializing buf queue context elements
307 *
308 * @param[in] ps_list
309 *  Pointer to job queue context
310 *
311 * @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS
312 *
313 * @remarks
314 *
315 *******************************************************************************
316 */
ih264_list_reset(list_t * ps_list)317 IH264_ERROR_T ih264_list_reset(list_t *ps_list)
318 {
319     IH264_ERROR_T ret = IH264_SUCCESS;
320 
321     ret = ih264_list_lock(ps_list);
322     RETURN_IF((ret != IH264_SUCCESS), ret);
323 
324     ps_list->i4_terminate  = 0;
325     ps_list->i4_buf_rd_idx = 0;
326     ps_list->i4_buf_wr_idx = 0;
327 
328     ret = ih264_list_unlock(ps_list);
329     RETURN_IF((ret != IH264_SUCCESS), ret);
330 
331     return ret;
332 }
333 
334 /**
335 *******************************************************************************
336 *
337 * @brief De-initializes the list context
338 *
339 * @par Description
340 *  De-initializes the list context by calling ih264_list_reset() and then
341 *  destroying the mutex created
342 *
343 * @param[in] ps_list
344 *  Pointer to job queue context
345 *
346 * @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS
347 *
348 * @remarks
349 *
350 *******************************************************************************
351 */
ih264_list_deinit(list_t * ps_list)352 IH264_ERROR_T ih264_list_deinit(list_t *ps_list)
353 {
354     WORD32 retval;
355     IH264_ERROR_T ret = IH264_SUCCESS;
356 
357     ret = ih264_list_reset(ps_list);
358     RETURN_IF((ret != IH264_SUCCESS), ret);
359 
360     retval = ithread_mutex_destroy(ps_list->pv_mutex);
361     if(retval)
362         return IH264_FAIL;
363     return IH264_SUCCESS;
364 }
365 
366 /**
367 *******************************************************************************
368 *
369 * @brief Terminates the list
370 *
371 * @par Description
372 *  Terminates the list by setting a flag in context.
373 *
374 * @param[in] ps_list
375 *  Pointer to job queue context
376 *
377 * @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS
378 *
379 * @remarks
380 *
381 *******************************************************************************
382 */
ih264_list_terminate(list_t * ps_list)383 IH264_ERROR_T ih264_list_terminate(list_t *ps_list)
384 {
385     IH264_ERROR_T ret = IH264_SUCCESS;
386 
387     ret = ih264_list_lock(ps_list);
388     RETURN_IF((ret != IH264_SUCCESS), ret);
389 
390     ps_list->i4_terminate = 1;
391 
392     ret = ih264_list_unlock(ps_list);
393     RETURN_IF((ret != IH264_SUCCESS), ret);
394     return ret;
395 }
396 
397 /**
398 *******************************************************************************
399 *
400 * @brief Adds a job to the queue
401 *
402 * @par Description
403 *  Adds a buffer to the queue and updates write address to next location.
404 *
405 * @param[in] ps_list
406 *  Pointer to job queue context
407 *
408 * @param[in] pv_buf
409 *  Pointer to the location that contains details of the job to be added
410 *
411 * @param[in] blocking
412 *  To signal if the write is blocking or non-blocking.
413 *
414 * @returns IH264_SUCCESS on success and IH264_FAIL on fail
415 *
416 * @remarks
417 *  Job Queue buffer is assumed to be allocated to handle worst case number of
418 *  buffers. Wrap around is not supported
419 *
420 *******************************************************************************
421 */
ih264_list_queue(list_t * ps_list,void * pv_buf,WORD32 blocking)422 IH264_ERROR_T ih264_list_queue(list_t *ps_list, void *pv_buf, WORD32 blocking)
423 {
424     IH264_ERROR_T ret = IH264_SUCCESS;
425     IH264_ERROR_T rettmp;
426     WORD32 diff;
427     void *pv_buf_wr;
428     volatile WORD32 *pi4_wr_idx, *pi4_rd_idx;
429     WORD32 buf_size = ps_list->i4_entry_size;
430 
431 
432     rettmp = ih264_list_lock(ps_list);
433     RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
434 
435     while(1)
436     {
437         /* Ensure wr idx does not go beyond rd idx by more than number of entries
438          */
439         pi4_wr_idx = &ps_list->i4_buf_wr_idx;
440         pi4_rd_idx = &ps_list->i4_buf_rd_idx;
441         diff = *pi4_wr_idx - *pi4_rd_idx;
442 
443         if(diff < ps_list->i4_buf_max_idx)
444         {
445             WORD32 wr_idx;
446             wr_idx = ps_list->i4_buf_wr_idx & (ps_list->i4_buf_max_idx - 1);
447             pv_buf_wr = (UWORD8 *)ps_list->pv_buf_base + wr_idx * buf_size;
448 
449             memcpy(pv_buf_wr, pv_buf, buf_size);
450             ps_list->i4_buf_wr_idx++;
451             break;
452         }
453         else
454         {
455             /* wr is ahead, so wait for rd to consume */
456             if(blocking)
457             {
458                 ih264_list_yield(ps_list);
459             }
460             else
461             {
462                 ret = IH264_FAIL;
463                 break;
464             }
465         }
466     }
467     ps_list->i4_terminate = 0;
468 
469     rettmp = ih264_list_unlock(ps_list);
470     RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
471 
472     return ret;
473 }
474 
475 /**
476 *******************************************************************************
477 *
478 * @brief Gets next job from the job queue
479 *
480 * @par   Description
481 *  Gets next job from the job queue and updates rd address to next location.
482 *  If it is a blocking call and if there is no new buf then this functions
483 *  unlocks the mutex and calls yield and then locks it back and continues
484 *  till a buf is available or terminate is set
485 *
486 * @param[in] ps_list
487 *  Pointer to Job Queue context
488 *
489 * @param[out] pv_buf
490 *  Pointer to the location that contains details of the buf to be written
491 *
492 * @param[in] blocking
493 *  To signal if the read is blocking or non-blocking.
494 *
495 * @returns
496 *
497 * @remarks
498 *  Job Queue buffer is assumed to be allocated to handle worst case number of
499 *  buffers. Wrap around is not supported
500 *
501 *******************************************************************************
502 */
ih264_list_dequeue(list_t * ps_list,void * pv_buf,WORD32 blocking)503 IH264_ERROR_T ih264_list_dequeue(list_t *ps_list, void *pv_buf, WORD32 blocking)
504 {
505     IH264_ERROR_T ret = IH264_SUCCESS;
506     IH264_ERROR_T rettmp;
507     WORD32 buf_size = ps_list->i4_entry_size;
508     WORD32 diff;
509     void *pv_buf_rd;
510     volatile WORD32 *pi4_wr_idx, *pi4_rd_idx;
511 
512     rettmp = ih264_list_lock(ps_list);
513     RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
514 
515     while(1)
516     {
517         /* Ensure wr idx is ahead of rd idx and
518          * wr idx does not go beyond rd idx by more than number of entries
519          */
520         pi4_wr_idx = &ps_list->i4_buf_wr_idx;
521         pi4_rd_idx = &ps_list->i4_buf_rd_idx;
522         diff = *pi4_wr_idx - *pi4_rd_idx;
523 
524         if(diff > 0)
525         {
526             WORD32 rd_idx;
527             rd_idx = ps_list->i4_buf_rd_idx & (ps_list->i4_buf_max_idx - 1);
528             pv_buf_rd = (UWORD8 *)ps_list->pv_buf_base + rd_idx * buf_size;
529 
530             memcpy(pv_buf, pv_buf_rd, buf_size);
531             ps_list->i4_buf_rd_idx++;
532             break;
533         }
534         else
535         {
536             /* If terminate is signaled then break */
537             if(ps_list->i4_terminate)
538             {
539                 ret = IH264_FAIL;
540                 break;
541             }
542             /* wr is ahead, so wait for rd to consume */
543             if(blocking)
544             {
545                 ih264_list_yield(ps_list);
546             }
547             else
548             {
549                 ret = IH264_FAIL;
550                 break;
551             }
552         }
553     }
554 
555     rettmp = ih264_list_unlock(ps_list);
556     RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
557 
558     return ret;
559 }
560