1 /* taskset.c
2 
3    Copyright (c) 2003-2021 HandBrake Team
4    This file is part of the HandBrake source code
5    Homepage: <http://handbrake.fr/>.
6    It may be used under the terms of the GNU General Public License v2.
7    For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8  */
9 
10 #include "handbrake/handbrake.h"
11 #include "handbrake/ports.h"
12 #include "handbrake/taskset.h"
13 
14 int
taskset_init(taskset_t * ts,int thread_count,size_t arg_size)15 taskset_init( taskset_t *ts, int thread_count, size_t arg_size )
16 {
17     int init_step;
18 
19     init_step = 0;
20     memset( ts, 0, sizeof( *ts ) );
21     ts->thread_count = thread_count;
22     ts->arg_size = arg_size;
23     ts->bitmap_elements = ( ts->thread_count + 31 ) / 32;
24     ts->task_threads = malloc( sizeof( hb_thread_t* ) * ts->thread_count );
25     if( ts->task_threads == NULL )
26         goto fail;
27     init_step++;
28 
29     if( arg_size != 0 )
30     {
31         ts->task_threads_args = malloc( arg_size * ts->thread_count );
32         if( ts->task_threads == NULL )
33             goto fail;
34     }
35     init_step++;
36 
37     ts->task_begin_bitmap = malloc( sizeof( uint32_t  ) * ts->bitmap_elements );
38     if( ts->task_begin_bitmap == NULL )
39         goto fail;
40     init_step++;
41 
42     ts->task_complete_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements );
43     if( ts->task_complete_bitmap == NULL )
44         goto fail;
45     init_step++;
46 
47     ts->task_stop_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements );
48     if( ts->task_stop_bitmap == NULL )
49         goto fail;
50     init_step++;
51 
52     ts->task_cond_lock = hb_lock_init();
53     if( ts->task_cond_lock == NULL)
54         goto fail;
55     init_step++;
56 
57     ts->task_begin = hb_cond_init();
58     if( ts->task_begin == NULL)
59         goto fail;
60     init_step++;
61 
62     ts->task_complete = hb_cond_init();
63     if( ts->task_complete == NULL)
64         goto fail;
65     init_step++;
66 
67     /*
68      * Initialize all arg data to 0.
69      */
70     memset(ts->task_threads_args, 0, ts->arg_size * ts->thread_count );
71 
72     /*
73      * Initialize bitmaps to all bits set.  This means that any unused bits
74      * in the bitmap are already in the "condition satisfied" state allowing
75      * us to test the bitmap 32bits at a time without having to mask off
76      * the end.
77      */
78     memset(ts->task_begin_bitmap, 0xFF, sizeof( uint32_t ) * ts->bitmap_elements );
79     memset(ts->task_complete_bitmap, 0xFF, sizeof( uint32_t ) * ts->bitmap_elements );
80     memset(ts->task_stop_bitmap, 0, sizeof( uint32_t ) * ts->bitmap_elements );
81 
82     /*
83      * Important to start off with the threads locked waiting
84      * on input, no work completed, and not asked to stop.
85      */
86     bit_nclear( ts->task_begin_bitmap, 0, ts->thread_count - 1 );
87     bit_nclear( ts->task_complete_bitmap, 0, ts->thread_count - 1 );
88     bit_nclear( ts->task_stop_bitmap, 0, ts->thread_count - 1 );
89     return (1);
90 
91 fail:
92     switch (init_step)
93     {
94         default:
95             hb_cond_close( &ts->task_complete );
96             /* FALL THROUGH */
97         case 7:
98             hb_cond_close( &ts->task_begin );
99             /* FALL THROUGH */
100         case 6:
101             hb_lock_close( &ts->task_cond_lock );
102             /* FALL THROUGH */
103         case 5:
104             free( ts->task_stop_bitmap );
105             /* FALL THROUGH */
106         case 4:
107             free( ts->task_complete_bitmap );
108             /* FALL THROUGH */
109         case 3:
110             free( ts->task_begin_bitmap );
111             /* FALL THROUGH */
112         case 2:
113             free( ts->task_threads_args );
114             /* FALL THROUGH */
115         case 1:
116             free( ts->task_threads );
117             /* FALL THROUGH */
118         case 0:
119             break;
120     }
121     return (0);
122 }
123 
124 int
taskset_thread_spawn(taskset_t * ts,int thr_idx,const char * descr,thread_func_t * func,int priority)125 taskset_thread_spawn( taskset_t *ts, int thr_idx, const char *descr,
126                       thread_func_t *func, int priority )
127 {
128     ts->task_threads[thr_idx] = hb_thread_init( descr, func,
129                                                 taskset_thread_args( ts, thr_idx ),
130                                                 priority);
131     return( ts->task_threads[thr_idx] != NULL );
132 }
133 
134 void
taskset_cycle(taskset_t * ts)135 taskset_cycle( taskset_t *ts )
136 {
137     hb_lock( ts->task_cond_lock );
138 
139     /*
140      * Signal all threads that their work is available.
141      */
142     bit_nset( ts->task_begin_bitmap, 0, ts->thread_count - 1 );
143     hb_cond_broadcast( ts->task_begin );
144 
145     /*
146      * Wait until all threads have completed.  Note that we must
147      * loop here as hb_cond_wait() on some platforms (e.g pthread_cond_wait)
148      * may unblock prematurely.
149      */
150     do
151     {
152         hb_cond_wait( ts->task_complete, ts->task_cond_lock );
153     } while ( !allbits_set( ts->task_complete_bitmap, ts->bitmap_elements ) );
154 
155     /*
156      * Clear completion indications for next time.
157      */
158     bit_nclear( ts->task_complete_bitmap, 0, ts->thread_count - 1 );
159 
160     hb_unlock( ts->task_cond_lock );
161 }
162 
163 /*
164  * Block current thread until work is available for it.
165  */
166 void
taskset_thread_wait4start(taskset_t * ts,int thr_idx)167 taskset_thread_wait4start( taskset_t *ts, int thr_idx )
168 {
169     hb_lock( ts->task_cond_lock );
170     while ( bit_is_clear( ts->task_begin_bitmap, thr_idx ) )
171         hb_cond_wait( ts->task_begin, ts->task_cond_lock );
172 
173     /*
174      * We've been released for one run.  Insure we block the next
175      * time through the loop.
176      */
177     bit_clear( ts->task_begin_bitmap, thr_idx );
178     hb_unlock( ts->task_cond_lock );
179 }
180 
181 /*
182  * Current thread has completed its work.  Indicate completion,
183  * and if all threads in this task set have completed, wakeup
184  * anyone waiting for this condition.
185  */
186 void
taskset_thread_complete(taskset_t * ts,int thr_idx)187 taskset_thread_complete( taskset_t *ts, int thr_idx )
188 {
189     hb_lock( ts->task_cond_lock );
190     bit_set( ts->task_complete_bitmap, thr_idx );
191     if( allbits_set( ts->task_complete_bitmap, ts->bitmap_elements ) )
192     {
193         hb_cond_signal( ts->task_complete );
194     }
195     hb_unlock( ts->task_cond_lock );
196 }
197 
198 void
taskset_fini(taskset_t * ts)199 taskset_fini( taskset_t *ts )
200 {
201     int i;
202 
203     hb_lock( ts->task_cond_lock );
204     /*
205      * Tell each thread to stop, and then cleanup.
206      */
207     bit_nset( ts->task_stop_bitmap, 0, ts->thread_count - 1 );
208     bit_nset( ts->task_begin_bitmap, 0, ts->thread_count - 1 );
209     hb_cond_broadcast( ts->task_begin );
210 
211     /*
212      * Wait for all threads to exit.
213      */
214     hb_cond_wait( ts->task_complete, ts->task_cond_lock );
215     hb_unlock( ts->task_cond_lock );
216 
217     /*
218      * Clean up taskset memory.
219      */
220     for( i = 0; i < ts->thread_count; i++)
221     {
222         hb_thread_close( &ts->task_threads[i] );
223     }
224     hb_lock_close( &ts->task_cond_lock );
225     hb_cond_close( &ts->task_begin );
226     hb_cond_close( &ts->task_complete );
227     free( ts->task_threads );
228     if( ts->task_threads_args != NULL )
229         free( ts->task_threads_args );
230     free( ts->task_begin_bitmap );
231     free( ts->task_complete_bitmap );
232     free( ts->task_stop_bitmap );
233 }
234