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