UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
worker.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2013-2024 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <algorithm>
39#include <cassert>
40#include <pthread.h>
41#include <queue>
42#include <set>
43#include <vector>
44
45#include "utils/macros.h" // for MAX_CPU_CORES
46#include "utils/misc.h" // get_cpu_core_count
47#include "utils/thread.h"
48#include "utils/worker.h"
49
50using std::min;
51using std::queue;
52using std::set;
53using std::vector;
54
55struct wp_worker;
56
59 virtual void notify(wp_worker *) = 0;
60};
61
66 wp_task_data(runnable_t task, void *data, wp_worker *w, bool detached) : m_task(task), m_data(data),
67 m_result(0), m_returned(false), m_w(w), m_detached(detached) {}
69 void *m_data;
70 void *m_result;
72 struct wp_worker *m_w;
74};
75
79struct wp_worker {
81 m_state_observer(observer)
82 {
83 int ret;
84 ret = pthread_mutex_init(&m_lock, NULL);
85 assert(ret == 0);
86 ret = pthread_cond_init(&m_task_ready_cv, NULL);
87 assert(ret == 0);
88 ret = pthread_cond_init(&m_task_completed_cv, NULL);
89 assert(ret == 0);
90
91 ret = pthread_create(&m_thread_id, NULL, wp_worker::enter_loop, this);
92 assert(ret == 0);
93 }
95 wp_task_data *poisoned = new wp_task_data(NULL, NULL, this, false);
96 this->push(poisoned);
97
98 pthread_join(m_thread_id, NULL);
99 pthread_mutex_destroy(&m_lock);
100 pthread_cond_destroy(&m_task_ready_cv);
101 pthread_cond_destroy(&m_task_completed_cv);
102 }
103 static void *enter_loop(void *args);
104 void run();
105
106 void push(wp_task_data *);
107 void *pop(wp_task_data *);
108
109 queue<wp_task_data*> m_data;
110 pthread_mutex_t m_lock;
111 pthread_cond_t m_task_ready_cv;
112 pthread_cond_t m_task_completed_cv;
113 pthread_t m_thread_id;
114
116};
117
118void *wp_worker::enter_loop(void *args) {
119 set_thread_name("worker");
120 wp_worker *instance = (wp_worker *) args;
121 instance->run();
122
123 return NULL;
124}
125
127 while(1) {
128 struct wp_task_data *data;
129 pthread_mutex_lock(&m_lock);
130 while(m_data.empty()) {
131 pthread_cond_wait(&m_task_ready_cv, &m_lock);
132 }
133 data = m_data.front();
134 m_data.pop();
135 pthread_mutex_unlock(&m_lock);
136
137 // poisoned pill
138 if(data->m_task == NULL) {
139 delete data;
140 return;
141 }
142
143 void *res = data->m_task(data->m_data);
144
145 pthread_mutex_lock(&m_lock);
146 data->m_result = res;
147 data->m_returned = true;
148 pthread_cond_signal(&m_task_completed_cv);
150 if (data->m_detached) {
151 delete data;
152 }
153 pthread_mutex_unlock(&m_lock);
154 }
155}
156
158 pthread_mutex_lock(&m_lock);
159 assert(m_data.size() == 0);
160 m_data.push(data);
161 pthread_mutex_unlock(&m_lock);
162 pthread_cond_signal(&m_task_ready_cv);
163}
164
166 void *res = NULL;
167
168 pthread_mutex_lock(&m_lock);
169 while(!d->m_returned) {
170 pthread_cond_wait(&m_task_completed_cv, &m_lock);
171 }
172 res = d->m_result;
173 delete d;
174 pthread_mutex_unlock(&m_lock);
175
176 return res;
177}
178
179static void func_delete(wp_worker *arg) {
180 delete arg;
181}
182
184{
185 public:
187 pthread_mutex_init(&m_lock, NULL);
188 pthread_cond_init(&m_worker_finished, NULL);
189 }
190
192 pthread_mutex_lock(&m_lock);
193 while (m_occupied_workers.size() > 0) {
194 pthread_cond_wait(&m_worker_finished, &m_lock);
195 }
196 pthread_mutex_unlock(&m_lock);
197
198 for_each(m_empty_workers.begin(),
200 pthread_cond_destroy(&m_worker_finished);
201 pthread_mutex_destroy(&m_lock);
202 }
203
204 void notify(wp_worker *w) {
205 pthread_mutex_lock(&m_lock);
206 m_occupied_workers.erase(w);
207 m_empty_workers.insert(w);
208 pthread_mutex_unlock(&m_lock);
209 pthread_cond_signal(&m_worker_finished);
210 }
211
212 task_result_handle_t run_async(runnable_t task, void *data, bool detached);
213 void *wait_task(task_result_handle_t handle);
214
215 private:
216 set<wp_worker*> m_empty_workers;
217 set<wp_worker*> m_occupied_workers;
218 pthread_mutex_t m_lock;
219 pthread_cond_t m_worker_finished;
220};
221
223{
224 wp_worker *w;
225 pthread_mutex_lock(&m_lock);
226 if(m_empty_workers.empty()) {
227 m_empty_workers.insert(new wp_worker(*this));
228 }
229 set<wp_worker*>::iterator it = m_empty_workers.begin();
230 assert(it != m_empty_workers.end());
231 w = *it;
233 m_empty_workers.erase(w);
234 m_occupied_workers.insert(w);
235
236 wp_task_data *d = new wp_task_data(task, data, w, detached);
237 w->push(d);
238 pthread_mutex_unlock(&m_lock);
239
240 return d;
241}
242
244{
245 wp_task_data *d = (wp_task_data *) handle;
246 wp_worker *w = d->m_w;
247 return w->pop(d);
248}
249
251
263{
264 return instance.run_async(task, data, false);
265}
266
276{
277 instance.run_async(task, data, true);
278}
279
281{
282 return instance.wait_task(handle);
283}
284
294void task_run_parallel(runnable_t task, int worker_count, void *data, size_t data_size, void **res)
295{
296 if (worker_count == 1) {
297 task(data);
298 return;
299 }
300
301 vector<task_result_handle_t> tasks(worker_count);
302 for (int i = 0; i < worker_count; ++i) {
303 tasks[i] = task_run_async(task, (void *)((char *) data + i * data_size));
304 }
305 for (int i = 0; i < worker_count; ++i) {
306 if (res != nullptr) {
307 res[i] = wait_task(tasks[i]);
308 } else {
309 wait_task(tasks[i]);
310 }
311 }
312}
313
321static void *respawn_parallel_task(void *arg) {
322 auto data = (struct respawn_parallel_data *) arg;
323 data->c(data->in, data->out, data->data_len, data->udata);
324 return NULL;
325}
332void respawn_parallel(void *in, void *out, size_t nmemb, size_t size, respawn_parallel_callback_t c, void *udata)
333{
334 const int threads = min<int>(get_cpu_core_count(), MAX_CPU_CORES);
336
337 for (int i = 0; i < threads; ++i) {
338 data[i].c = c;
339 data[i].in = (char *) in + i * (nmemb / threads) * size;
340 data[i].out = (char *) out + i * (nmemb / threads) * size;
341 data[i].data_len = (nmemb / threads) * size;
342 data[i].udata = udata;
343 if (i == threads - 1) {
344 data[i].data_len = size * (nmemb - (threads - 1) * (nmemb / threads));
345 }
346 }
347
348 task_run_parallel(respawn_parallel_task, threads, data, sizeof data[0], NULL);
349}
350
pthread_mutex_t m_lock
Definition worker.cpp:218
pthread_cond_t m_worker_finished
Definition worker.cpp:219
void * wait_task(task_result_handle_t handle)
Definition worker.cpp:243
task_result_handle_t run_async(runnable_t task, void *data, bool detached)
Definition worker.cpp:222
set< wp_worker * > m_empty_workers
Definition worker.cpp:216
set< wp_worker * > m_occupied_workers
Definition worker.cpp:217
void notify(wp_worker *w)
Definition worker.cpp:204
@ MAX_CPU_CORES
maximal expected CPU core count
Definition macros.h:132
int get_cpu_core_count(void)
Definition misc.cpp:218
#define min(a, b)
Definition rtp.c:99
respawn_parallel_callback_t c
Definition worker.cpp:315
virtual void notify(wp_worker *)=0
virtual ~worker_state_observer()
Definition worker.cpp:58
Holds data to be passed to worker.
Definition worker.cpp:65
struct wp_worker * m_w
Definition worker.cpp:72
void * m_result
Definition worker.cpp:70
void * m_data
Definition worker.cpp:69
bool m_detached
Definition worker.cpp:73
bool m_returned
Definition worker.cpp:71
runnable_t m_task
Definition worker.cpp:68
wp_task_data(runnable_t task, void *data, wp_worker *w, bool detached)
Definition worker.cpp:66
This class represents a worker that is called from within the pool.
Definition worker.cpp:79
void run()
Definition worker.cpp:126
worker_state_observer & m_state_observer
Definition worker.cpp:115
pthread_cond_t m_task_ready_cv
Definition worker.cpp:111
void * pop(wp_task_data *)
Definition worker.cpp:165
pthread_t m_thread_id
Definition worker.cpp:113
wp_worker(worker_state_observer &observer)
Definition worker.cpp:80
pthread_cond_t m_task_completed_cv
Definition worker.cpp:112
static void * enter_loop(void *args)
Definition worker.cpp:118
pthread_mutex_t m_lock
Definition worker.cpp:110
~wp_worker()
Definition worker.cpp:94
void push(wp_task_data *)
Definition worker.cpp:157
queue< wp_task_data * > m_data
Definition worker.cpp:109
void set_thread_name(const char *name)
Definition thread.c:72
void task_run_async_detached(runnable_t task, void *data)
Runs task asynchronously in a detached state.
Definition worker.cpp:275
void * wait_task(task_result_handle_t handle)
Definition worker.cpp:280
void respawn_parallel(void *in, void *out, size_t nmemb, size_t size, respawn_parallel_callback_t c, void *udata)
Definition worker.cpp:332
void task_run_parallel(runnable_t task, int worker_count, void *data, size_t data_size, void **res)
Definition worker.cpp:294
task_result_handle_t task_run_async(runnable_t task, void *data)
Runs task asynchronously.
Definition worker.cpp:262
static void func_delete(wp_worker *arg)
Definition worker.cpp:179
static void * respawn_parallel_task(void *arg)
Definition worker.cpp:321
static class worker_pool instance
Definition worker.cpp:250
void * task_result_handle_t
Definition worker.h:49
void(* respawn_parallel_callback_t)(void *in, void *out, size_t data_len, void *udata)
Definition worker.h:61
void *(* runnable_t)(void *)
Definition worker.h:50