diff --git a/f265/enc.c b/f265/enc.c
index b9e55f6..a752fc9 100644
--- a/f265/enc.c
+++ b/f265/enc.c
@@ -989,6 +989,11 @@ void fenc_deinit_enc(f265_enc *enc)
f265_main_data *md = &enc->md;
f265_enc_sync *es = &enc->es;
+ // Abort the encoding.
+ f265_mutex_lock(&es->mutex);
+ es->abort_flag = 1;
+ f265_mutex_unlock(&es->mutex);
+
// Terminate running threads. Check flags to avoid releasing
// uninitialized resources.
for (int n = 0; n < md->nb_enc_threads; n++)
@@ -997,11 +1002,6 @@ void fenc_deinit_enc(f265_enc *enc)
if (t->handle_flag)
{
- // Update its status.
- f265_mutex_lock(&es->mutex);
- t->status = F265_THREAD_EXIT;
- f265_mutex_unlock(&es->mutex);
-
// Wake up worker thread so it can exit properly.
f265_cond_signal(&t->worker_cv);
@@ -1374,92 +1374,76 @@ static void* fenc_encode_parallel_tile(void *args)
// Check for colleague.
if (t - md->enc_threads[0] < md->nb_enc_threads-1) next = t+1;
- // Worker loop.
- int exit_flag = 0;
- while (!exit_flag)
+ // Track whether we signaled our colleague for the current frame yet.
+ int next_signaled_flag = 0;
+
+ // Enter an infinite loop in mutual exclusion.
+ f265_mutex_lock(mutex);
+ while (1)
{
- // Wait for work. Prevent spurious wakeup.
- int status;
- f265_mutex_lock(mutex);
- do
+ // Exit requested.
+ if (es->abort_flag)
{
- // When woken up, make sure the thread is no longer set to IDLE.
- f265_cond_wait(&t->worker_cv, mutex);
- status = t->status;
+ // Tell the master that we are exiting.
+ t->status = F265_THREAD_EXIT;
+ f265_cond_signal(&es->master_cv);
- } while (status == F265_THREAD_IDLE);
+ // Leave mutual exclusion and exit.
+ f265_mutex_unlock(mutex);
+ return NULL;
+ }
+
+ // We're idle. Leave mutual exclusion and wait for something to happen.
+ else if (t->status == F265_THREAD_IDLE)
+ f265_cond_wait(&t->worker_cv, mutex);
- // Wake up colleague.
- if (status == F265_THREAD_BUSY && next)
+ // An error occurred or we're out of sections to encode.
+ else if (es->error_flag || es->next_section_idx >= t->src_frame->fmap->nb_sections)
{
- // Get colleague thread ready for work.
- // NOTE. Having each thread init its own data requires modifying the
- // parameter passed to fenc_encode_parallel_tile. It would
- // need to be a struct that would hold the encoding thread,
- // the frame and the QP for proper initialization. This
- // approach makes it uniform: the master thread wakes up the
- // first worker after having shared the info it needs to work.
- fenc_init_enc_thread(e, t->src_frame, next, t->qp[0]);
- next->status = F265_THREAD_BUSY;
-
- // Wake up call.
- f265_cond_signal(&next->worker_cv);
- }
+ // Reset the colleague signaled status.
+ next_signaled_flag = 0;
- // Stop hogging the mutex. Take a breather.
- f265_mutex_unlock(mutex);
+ // Tell the master that we're idle.
+ t->status = F265_THREAD_IDLE;
+ f265_cond_signal(&es->master_cv);
+ }
- // Tile encoding loop.
- do
+ // We have a section to encode.
+ else
{
- // Fetch next available tile.
- // NOTE. Optimize this eventually. For the first loop, this results
- // in a back-to-back unlock-lock combo.
- f265_mutex_lock(mutex);
+ // Get the index of the section to encode and update the section
+ // index.
int section_idx = es->next_section_idx++;
- exit_flag = es->abort_flag | es->error_flag | status == F265_THREAD_EXIT;
- f265_mutex_unlock(mutex);
-
- // Stop work loop entirely if any thread aborted, an error occurred,
- // or exit was requested.
- if (exit_flag) break;
- // Pause when all other tiles have been, or are being processed.
- if (section_idx >= t->src_frame->fmap->nb_sections)
+ // Initialize and change the status of our colleague in mutual
+ // exclusion. Don't signal it yet because it cannot acquire the
+ // mutex yet.
+ if (next && !next_signaled_flag)
{
- // Update status.
- f265_mutex_lock(mutex);
- status = t->status = F265_THREAD_IDLE;
- f265_mutex_unlock(mutex);
-
- // Notify master thread.
- f265_cond_signal(&es->master_cv);
+ fenc_init_enc_thread(e, t->src_frame, next, t->qp[0]);
+ next->status = F265_THREAD_BUSY;
}
- // Process tile. Signal any problems to coworkers.
- else
+ // Leave mutual exclusion.
+ f265_mutex_unlock(mutex);
+
+ // Signal our colleague now that we have released the mutex.
+ if (next && !next_signaled_flag)
{
- int res = fenc_encode_section(t, section_idx);
- if (res == F265_RET_ABORT)
- {
- f265_mutex_lock(mutex);
- es->abort_flag = 1;
- f265_mutex_unlock(mutex);
- }
+ next_signaled_flag = 1;
+ f265_cond_signal(&next->worker_cv);
}
- } while (status == F265_THREAD_BUSY);
- }
- // Clean exit. Force EXIT status in case this was caused by an abort signal
- // or an error during encoding.
- f265_mutex_lock(mutex);
- t->status = F265_THREAD_EXIT;
- f265_mutex_unlock(mutex);
+ // Process our section.
+ int res = fenc_encode_section(t, section_idx);
- // Tell the master thread about it.
- f265_cond_signal(&es->master_cv);
+ // Enter mutual exclusion.
+ f265_mutex_lock(mutex);
- return NULL;
+ // Report a problem.
+ if (res == F265_RET_ABORT) es->abort_flag = 1;
+ }
+ }
}
// Have the main thread use the worker threads. The main thread then makes sure
@@ -1483,36 +1467,38 @@ static int fenc_encode_frame_mt_tile(f265_enc *e, f265_frame *f)
// Frame re-encoding loop.
while (1)
{
- // Avoid spurious wakeup locking problems.
+ // Enter mutual exclusion.
f265_mutex_lock(mutex);
- // Init the first worker thread.
+ // Initialize the job list.
+ es->next_section_idx = 0;
+
+ // Initialize the first worker thread.
f265_enc_thread *t = md->enc_threads[0];
fenc_init_enc_thread(e, f, t, -1);
- // Update its status to BUSY.
+ // Set its status to BUSY.
t->status = F265_THREAD_BUSY;
- // Update job listing.
- es->next_section_idx = 0;
-
- f265_mutex_unlock(mutex);
-
// Wake it up. It will wake up the others.
f265_cond_signal(&t->worker_cv);
- // Wait for all the threads to finish.
- int busy_flag;
- f265_mutex_lock(mutex);
- do
+ // Wait for all the threads to become idle.
+ while (1)
{
- f265_cond_wait(&es->master_cv, mutex);
-
- busy_flag = 0;
+ // Check if a thread is busy.
+ int busy_flag = 0;
for (int n = 0; n < md->nb_enc_threads; n++)
busy_flag |= md->enc_threads[n]->status == F265_THREAD_BUSY;
- } while (busy_flag);
+ // All threads are idle, leave.
+ if (!busy_flag) break;
+
+ // Wait for threads to become idle.
+ f265_cond_wait(&es->master_cv, mutex);
+ }
+
+ // Leave mutual exclusion.
f265_mutex_unlock(mutex);
// The main thread validates the results.