Due to the nature of multithreading, using a "ready check" mechanism may introduce a deadlock. For example: Suppose all tasks have been submitted to the executor, and the last thread checks the entire list and finds no ready tasks. It then goes to sleep, waiting for a new task. However, for some multithreading-related reason, a task becomes ready after the check. Since no other thread is aware of this and no new tasks are being added to the executor, a deadlock occurs. In VVC, this function is unnecessary because we use a scoreboard. All tasks submitted to the executor are ready tasks.
Unified · Split
| @@ -79,10 +79,8 @@ static void add_task(FFTask **prev, FFTask *t) · 78 unmodified lines | |||
| 79 | static int run_one_task(FFExecutor *e, void *lc) | 79 | static int run_one_task(FFExecutor *e, void *lc) |
| 80 | { | 80 | { |
| 81 | FFTaskCallbacks *cb = &e->cb; | 81 | FFTaskCallbacks *cb = &e->cb; |
| 82 | FFTask **prev; | 82 | FFTask **prev = &e->tasks; |
| 83 | 83 | ||
| 84 | for (prev = &e->tasks; *prev && !cb->ready(*prev, cb->user_data); prev = &(*prev)->next) | ||
| 85 | /* nothing */; | ||
| 86 | if (*prev) { | 84 | if (*prev) { |
| 87 | FFTask *t = remove_task(prev, *prev); | 85 | FFTask *t = remove_task(prev, *prev); |
| 88 | if (e->thread_count > 0) | 86 | if (e->thread_count > 0) |
| @@ -143,7 +141,7 @@ FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count) · 54 unmodified lines | |||
| 143 | { | 141 | { |
| 144 | FFExecutor *e; | 142 | FFExecutor *e; |
| 145 | int has_lock = 0, has_cond = 0; | 143 | int has_lock = 0, has_cond = 0; |
| 146 | if (!cb || !cb->user_data || !cb->ready || !cb->run || !cb->priority_higher) | 144 | if (!cb || !cb->user_data || !cb->run || !cb->priority_higher) |
| 147 | return NULL; | 145 | return NULL; |
| 148 | 146 | ||
| 149 | e = av_mallocz(sizeof(*e)); | 147 | e = av_mallocz(sizeof(*e)); |
| @@ -42,9 +42,6 @@ typedef struct FFTaskCallbacks { · 41 unmodified lines | |||
| 42 | // return 1 if a's priority > b's priority | 42 | // return 1 if a's priority > b's priority |
| 43 | int (*priority_higher)(const FFTask *a, const FFTask *b); | 43 | int (*priority_higher)(const FFTask *a, const FFTask *b); |
| 44 | 44 | ||
| 45 | // task is ready for run | ||
| 46 | int (*ready)(const FFTask *t, void *user_data); | ||
| 47 | |||
| 48 | // run the task | 45 | // run the task |
| 49 | int (*run)(FFTask *t, void *local_context, void *user_data); | 46 | int (*run)(FFTask *t, void *local_context, void *user_data); |
| 50 | } FFTaskCallbacks; | 47 | } FFTaskCallbacks; |
| @@ -372,13 +372,6 @@ static int task_is_stage_ready(VVCTask *t, int add) · 371 unmodified lines | |||
| 372 | return task_has_target_score(t, stage, score); | 372 | return task_has_target_score(t, stage, score); |
| 373 | } | 373 | } |
| 374 | 374 | ||
| 375 | static int task_ready(const FFTask *_t, void *user_data) | ||
| 376 | { | ||
| 377 | VVCTask *t = (VVCTask*)_t; | ||
| 378 | |||
| 379 | return task_is_stage_ready(t, 0); | ||
| 380 | } | ||
| 381 | |||
| 382 | #define CHECK(a, b) \ | 375 | #define CHECK(a, b) \ |
| 383 | do { \ | 376 | do { \ |
| 384 | if ((a) != (b)) \ | 377 | if ((a) != (b)) \ |
| @@ -689,7 +682,6 @@ FFExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count) · 304 unmodified lines | |||
| 689 | s, | 682 | s, |
| 690 | sizeof(VVCLocalContext), | 683 | sizeof(VVCLocalContext), |
| 691 | task_priority_higher, | 684 | task_priority_higher, |
| 692 | task_ready, | ||
| 693 | task_run, | 685 | task_run, |
| 694 | }; | 686 | }; |
| 695 | return ff_executor_alloc(&callbacks, thread_count); | 687 | return ff_executor_alloc(&callbacks, thread_count); |