Merge changes I875ac5a7,I2b13369d,I9ceb47a9

* changes:
  update vp9_thread.[hc]
  vp9_thread_test: remove unnecessary c_str()'s
  vp9_thread_test: factorize decode loop
This commit is contained in:
James Zern 2014-07-01 20:46:53 -07:00 committed by Gerrit Code Review
commit 8aafd34050
6 changed files with 246 additions and 130 deletions

View File

@ -28,11 +28,11 @@ class VP9WorkerThreadTest : public ::testing::TestWithParam<bool> {
protected:
virtual ~VP9WorkerThreadTest() {}
virtual void SetUp() {
vp9_worker_init(&worker_);
vp9_get_worker_interface()->init(&worker_);
}
virtual void TearDown() {
vp9_worker_end(&worker_);
vp9_get_worker_interface()->end(&worker_);
}
VP9Worker worker_;
@ -45,10 +45,11 @@ int ThreadHook(void* data, void* return_value) {
}
TEST_P(VP9WorkerThreadTest, HookSuccess) {
EXPECT_NE(vp9_worker_sync(&worker_), 0); // should be a no-op.
// should be a no-op.
EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
for (int i = 0; i < 2; ++i) {
EXPECT_NE(vp9_worker_reset(&worker_), 0);
EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);
int hook_data = 0;
int return_value = 1; // return successfully from the hook
@ -58,20 +59,21 @@ TEST_P(VP9WorkerThreadTest, HookSuccess) {
const bool synchronous = GetParam();
if (synchronous) {
vp9_worker_execute(&worker_);
vp9_get_worker_interface()->execute(&worker_);
} else {
vp9_worker_launch(&worker_);
vp9_get_worker_interface()->launch(&worker_);
}
EXPECT_NE(vp9_worker_sync(&worker_), 0);
EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
EXPECT_FALSE(worker_.had_error);
EXPECT_EQ(5, hook_data);
EXPECT_NE(vp9_worker_sync(&worker_), 0); // should be a no-op.
// should be a no-op.
EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
}
}
TEST_P(VP9WorkerThreadTest, HookFailure) {
EXPECT_NE(vp9_worker_reset(&worker_), 0);
EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);
int hook_data = 0;
int return_value = 0; // return failure from the hook
@ -81,26 +83,49 @@ TEST_P(VP9WorkerThreadTest, HookFailure) {
const bool synchronous = GetParam();
if (synchronous) {
vp9_worker_execute(&worker_);
vp9_get_worker_interface()->execute(&worker_);
} else {
vp9_worker_launch(&worker_);
vp9_get_worker_interface()->launch(&worker_);
}
EXPECT_FALSE(vp9_worker_sync(&worker_));
EXPECT_FALSE(vp9_get_worker_interface()->sync(&worker_));
EXPECT_EQ(1, worker_.had_error);
// Ensure _reset() clears the error and _launch() can be called again.
return_value = 1;
EXPECT_NE(vp9_worker_reset(&worker_), 0);
EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);
EXPECT_FALSE(worker_.had_error);
vp9_worker_launch(&worker_);
EXPECT_NE(vp9_worker_sync(&worker_), 0);
vp9_get_worker_interface()->launch(&worker_);
EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
EXPECT_FALSE(worker_.had_error);
}
TEST(VP9WorkerThreadTest, TestInterfaceAPI) {
EXPECT_EQ(0, vp9_set_worker_interface(NULL));
EXPECT_TRUE(vp9_get_worker_interface() != NULL);
for (int i = 0; i < 6; ++i) {
VP9WorkerInterface winterface = *vp9_get_worker_interface();
switch (i) {
default:
case 0: winterface.init = NULL; break;
case 1: winterface.reset = NULL; break;
case 2: winterface.sync = NULL; break;
case 3: winterface.launch = NULL; break;
case 4: winterface.execute = NULL; break;
case 5: winterface.end = NULL; break;
}
EXPECT_EQ(0, vp9_set_worker_interface(&winterface));
}
}
// -----------------------------------------------------------------------------
// Multi-threaded decode tests
#if CONFIG_WEBM_IO
struct FileList {
const char *name;
const char *expected_md5;
};
// Decodes |filename| with |num_threads|. Returns the md5 of the decoded frames.
string DecodeFile(const string& filename, int num_threads) {
libvpx_test::WebMVideoSource video(filename);
@ -130,39 +155,77 @@ string DecodeFile(const string& filename, int num_threads) {
return string(md5.Get());
}
void DecodeFiles(const FileList files[]) {
for (const FileList *iter = files; iter->name != NULL; ++iter) {
SCOPED_TRACE(iter->name);
for (int t = 2; t <= 8; ++t) {
EXPECT_EQ(iter->expected_md5, DecodeFile(iter->name, t))
<< "threads = " << t;
}
}
}
// Trivial serialized thread worker interface implementation.
// Note any worker that requires synchronization between other workers will
// hang.
namespace impl {
void Init(VP9Worker *const worker) { memset(worker, 0, sizeof(*worker)); }
int Reset(VP9Worker *const /*worker*/) { return 1; }
int Sync(VP9Worker *const worker) { return !worker->had_error; }
void Execute(VP9Worker *const worker) {
worker->had_error |= worker->hook(worker->data1, worker->data2);
}
void Launch(VP9Worker *const worker) { Execute(worker); }
void End(VP9Worker *const /*worker*/) {}
} // namespace impl
TEST(VP9WorkerThreadTest, TestSerialInterface) {
static const VP9WorkerInterface serial_interface = {
impl::Init, impl::Reset, impl::Sync, impl::Launch, impl::Execute, impl::End
};
// TODO(jzern): Avoid using a file that will use the row-based thread
// loopfilter, with the simple serialized implementation it will hang. This is
// due to its expectation that rows will be run in parallel as they wait on
// progress in the row above before proceeding.
static const char expected_md5[] = "b35a1b707b28e82be025d960aba039bc";
static const char filename[] = "vp90-2-03-size-226x226.webm";
VP9WorkerInterface default_interface = *vp9_get_worker_interface();
EXPECT_NE(vp9_set_worker_interface(&serial_interface), 0);
EXPECT_EQ(expected_md5, DecodeFile(filename, 2));
// Reset the interface.
EXPECT_NE(vp9_set_worker_interface(&default_interface), 0);
EXPECT_EQ(expected_md5, DecodeFile(filename, 2));
}
TEST(VP9DecodeMultiThreadedTest, Decode) {
// no tiles or frame parallel; this exercises loop filter threading.
EXPECT_STREQ("b35a1b707b28e82be025d960aba039bc",
DecodeFile("vp90-2-03-size-226x226.webm", 2).c_str());
EXPECT_EQ("b35a1b707b28e82be025d960aba039bc",
DecodeFile("vp90-2-03-size-226x226.webm", 2));
}
TEST(VP9DecodeMultiThreadedTest, Decode2) {
static const struct {
const char *name;
const char *expected_md5;
} files[] = {
static const FileList files[] = {
{ "vp90-2-08-tile_1x2_frame_parallel.webm",
"68ede6abd66bae0a2edf2eb9232241b6" },
{ "vp90-2-08-tile_1x4_frame_parallel.webm",
"368ebc6ebf3a5e478d85b2c3149b2848" },
{ "vp90-2-08-tile_1x8_frame_parallel.webm",
"17e439da2388aff3a0f69cb22579c6c1" },
{ NULL, NULL }
};
for (int i = 0; i < static_cast<int>(sizeof(files) / sizeof(files[0])); ++i) {
for (int t = 2; t <= 8; ++t) {
EXPECT_STREQ(files[i].expected_md5, DecodeFile(files[i].name, t).c_str())
<< "threads = " << t;
}
}
DecodeFiles(files);
}
// Test tile quantity changes within one file.
TEST(VP9DecodeMultiThreadedTest, Decode3) {
static const struct {
const char *name;
const char *expected_md5;
} files[] = {
static const FileList files[] = {
{ "vp90-2-14-resize-fp-tiles-1-16.webm",
"0cd5e632c326297e975f38949c31ea94" },
{ "vp90-2-14-resize-fp-tiles-1-2-4-8-16.webm",
@ -207,14 +270,10 @@ TEST(VP9DecodeMultiThreadedTest, Decode3) {
"ae96f21f21b6370cc0125621b441fc52" },
{ "vp90-2-14-resize-fp-tiles-8-4.webm",
"3eb4f24f10640d42218f7fd7b9fd30d4" },
{ NULL, NULL }
};
for (int i = 0; i < static_cast<int>(sizeof(files) / sizeof(files[0])); ++i) {
for (int t = 2; t <= 8; ++t) {
EXPECT_STREQ(files[i].expected_md5, DecodeFile(files[i].name, t).c_str())
<< "threads = " << t;
}
}
DecodeFiles(files);
}
#endif // CONFIG_WEBM_IO

View File

@ -771,6 +771,7 @@ static const uint8_t *decode_tiles(VP9Decoder *pbi,
const uint8_t *data,
const uint8_t *data_end) {
VP9_COMMON *const cm = &pbi->common;
const VP9WorkerInterface *const winterface = vp9_get_worker_interface();
const int aligned_cols = mi_cols_aligned_to_sb(cm->mi_cols);
const int tile_cols = 1 << cm->log2_tile_cols;
const int tile_rows = 1 << cm->log2_tile_rows;
@ -783,7 +784,7 @@ static const uint8_t *decode_tiles(VP9Decoder *pbi,
CHECK_MEM_ERROR(cm, pbi->lf_worker.data1,
vpx_memalign(32, sizeof(LFWorkerData)));
pbi->lf_worker.hook = (VP9WorkerHook)vp9_loop_filter_worker;
if (pbi->max_threads > 1 && !vp9_worker_reset(&pbi->lf_worker)) {
if (pbi->max_threads > 1 && !winterface->reset(&pbi->lf_worker)) {
vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
"Loop filter thread creation failed");
}
@ -869,13 +870,13 @@ static const uint8_t *decode_tiles(VP9Decoder *pbi,
// decoding has completed: finish up the loop filter in this thread.
if (mi_row + MI_BLOCK_SIZE >= cm->mi_rows) continue;
vp9_worker_sync(&pbi->lf_worker);
winterface->sync(&pbi->lf_worker);
lf_data->start = lf_start;
lf_data->stop = mi_row;
if (pbi->max_threads > 1) {
vp9_worker_launch(&pbi->lf_worker);
winterface->launch(&pbi->lf_worker);
} else {
vp9_worker_execute(&pbi->lf_worker);
winterface->execute(&pbi->lf_worker);
}
}
}
@ -884,10 +885,10 @@ static const uint8_t *decode_tiles(VP9Decoder *pbi,
// Loopfilter remaining rows in the frame.
if (cm->lf.filter_level) {
LFWorkerData *const lf_data = (LFWorkerData*)pbi->lf_worker.data1;
vp9_worker_sync(&pbi->lf_worker);
winterface->sync(&pbi->lf_worker);
lf_data->start = lf_data->stop;
lf_data->stop = cm->mi_rows;
vp9_worker_execute(&pbi->lf_worker);
winterface->execute(&pbi->lf_worker);
}
// Get last tile data.
@ -931,6 +932,7 @@ static const uint8_t *decode_tiles_mt(VP9Decoder *pbi,
const uint8_t *data,
const uint8_t *data_end) {
VP9_COMMON *const cm = &pbi->common;
const VP9WorkerInterface *const winterface = vp9_get_worker_interface();
const uint8_t *bit_reader_end = NULL;
const int aligned_mi_cols = mi_cols_aligned_to_sb(cm->mi_cols);
const int tile_cols = 1 << cm->log2_tile_cols;
@ -957,11 +959,11 @@ static const uint8_t *decode_tiles_mt(VP9Decoder *pbi,
VP9Worker *const worker = &pbi->tile_workers[i];
++pbi->num_tile_workers;
vp9_worker_init(worker);
winterface->init(worker);
CHECK_MEM_ERROR(cm, worker->data1,
vpx_memalign(32, sizeof(TileWorkerData)));
CHECK_MEM_ERROR(cm, worker->data2, vpx_malloc(sizeof(TileInfo)));
if (i < num_threads - 1 && !vp9_worker_reset(worker)) {
if (i < num_threads - 1 && !winterface->reset(worker)) {
vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
"Tile decoder thread creation failed");
}
@ -1024,9 +1026,9 @@ static const uint8_t *decode_tiles_mt(VP9Decoder *pbi,
worker->had_error = 0;
if (i == num_workers - 1 || n == tile_cols - 1) {
vp9_worker_execute(worker);
winterface->execute(worker);
} else {
vp9_worker_launch(worker);
winterface->launch(worker);
}
if (buf->col == tile_cols - 1) {
@ -1038,7 +1040,7 @@ static const uint8_t *decode_tiles_mt(VP9Decoder *pbi,
for (; i > 0; --i) {
VP9Worker *const worker = &pbi->tile_workers[i - 1];
pbi->mb.corrupted |= !vp9_worker_sync(worker);
pbi->mb.corrupted |= !winterface->sync(worker);
}
if (final_worker > -1) {
TileWorkerData *const tile_data =

View File

@ -76,7 +76,7 @@ VP9Decoder *vp9_decoder_create() {
cm->error.setjmp = 0;
vp9_worker_init(&pbi->lf_worker);
vp9_get_worker_interface()->init(&pbi->lf_worker);
return pbi;
}
@ -86,12 +86,12 @@ void vp9_decoder_remove(VP9Decoder *pbi) {
int i;
vp9_remove_common(cm);
vp9_worker_end(&pbi->lf_worker);
vp9_get_worker_interface()->end(&pbi->lf_worker);
vpx_free(pbi->lf_worker.data1);
vpx_free(pbi->tile_data);
for (i = 0; i < pbi->num_tile_workers; ++i) {
VP9Worker *const worker = &pbi->tile_workers[i];
vp9_worker_end(worker);
vp9_get_worker_interface()->end(worker);
vpx_free(worker->data1);
vpx_free(worker->data2);
}

View File

@ -138,6 +138,7 @@ void vp9_loop_filter_frame_mt(YV12_BUFFER_CONFIG *frame,
int frame_filter_level,
int y_only) {
VP9LfSync *const lf_sync = &pbi->lf_row_sync;
const VP9WorkerInterface *const winterface = vp9_get_worker_interface();
// Number of superblock rows and cols
const int sb_rows = mi_cols_aligned_to_sb(cm->mi_rows) >> MI_BLOCK_SIZE_LOG2;
const int tile_cols = 1 << cm->log2_tile_cols;
@ -197,15 +198,15 @@ void vp9_loop_filter_frame_mt(YV12_BUFFER_CONFIG *frame,
// Start loopfiltering
if (i == num_workers - 1) {
vp9_worker_execute(worker);
winterface->execute(worker);
} else {
vp9_worker_launch(worker);
winterface->launch(worker);
}
}
// Wait till all rows are finished
for (i = 0; i < num_workers; ++i) {
vp9_worker_sync(&pbi->tile_workers[i]);
winterface->sync(&pbi->tile_workers[i]);
}
}

View File

@ -11,71 +11,79 @@
//
// Original source:
// http://git.chromium.org/webm/libwebp.git
// 100644 blob eff8f2a8c20095aade3c292b0e9292dac6cb3587 src/utils/thread.c
// 100644 blob 08ad4e1fecba302bf1247645e84a7d2779956bc3 src/utils/thread.c
#include <assert.h>
#include <string.h> // for memset()
#include "./vp9_thread.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include "vpx_mem/vpx_mem.h"
#if CONFIG_MULTITHREAD
struct VP9WorkerImpl {
pthread_mutex_t mutex_;
pthread_cond_t condition_;
pthread_t thread_;
};
//------------------------------------------------------------------------------
static THREADFN thread_loop(void *ptr) { // thread loop
VP9Worker* const worker = (VP9Worker*)ptr;
static void execute(VP9Worker *const worker); // Forward declaration.
static THREADFN thread_loop(void *ptr) {
VP9Worker *const worker = (VP9Worker*)ptr;
int done = 0;
while (!done) {
pthread_mutex_lock(&worker->mutex_);
pthread_mutex_lock(&worker->impl_->mutex_);
while (worker->status_ == OK) { // wait in idling mode
pthread_cond_wait(&worker->condition_, &worker->mutex_);
pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
}
if (worker->status_ == WORK) {
vp9_worker_execute(worker);
execute(worker);
worker->status_ = OK;
} else if (worker->status_ == NOT_OK) { // finish the worker
done = 1;
}
// signal to the main thread that we're done (for Sync())
pthread_cond_signal(&worker->condition_);
pthread_mutex_unlock(&worker->mutex_);
// signal to the main thread that we're done (for sync())
pthread_cond_signal(&worker->impl_->condition_);
pthread_mutex_unlock(&worker->impl_->mutex_);
}
return THREAD_RETURN(NULL); // Thread is finished
}
// main thread state control
static void change_state(VP9Worker* const worker,
static void change_state(VP9Worker *const worker,
VP9WorkerStatus new_status) {
// no-op when attempting to change state on a thread that didn't come up
if (worker->status_ < OK) return;
// No-op when attempting to change state on a thread that didn't come up.
// Checking status_ without acquiring the lock first would result in a data
// race.
if (worker->impl_ == NULL) return;
pthread_mutex_lock(&worker->mutex_);
// wait for the worker to finish
while (worker->status_ != OK) {
pthread_cond_wait(&worker->condition_, &worker->mutex_);
pthread_mutex_lock(&worker->impl_->mutex_);
if (worker->status_ >= OK) {
// wait for the worker to finish
while (worker->status_ != OK) {
pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
}
// assign new status and release the working thread if needed
if (new_status != OK) {
worker->status_ = new_status;
pthread_cond_signal(&worker->impl_->condition_);
}
}
// assign new status and release the working thread if needed
if (new_status != OK) {
worker->status_ = new_status;
pthread_cond_signal(&worker->condition_);
}
pthread_mutex_unlock(&worker->mutex_);
pthread_mutex_unlock(&worker->impl_->mutex_);
}
#endif // CONFIG_MULTITHREAD
//------------------------------------------------------------------------------
void vp9_worker_init(VP9Worker* const worker) {
static void init(VP9Worker *const worker) {
memset(worker, 0, sizeof(*worker));
worker->status_ = NOT_OK;
}
int vp9_worker_sync(VP9Worker* const worker) {
static int sync(VP9Worker *const worker) {
#if CONFIG_MULTITHREAD
change_state(worker, OK);
#endif
@ -83,59 +91,93 @@ int vp9_worker_sync(VP9Worker* const worker) {
return !worker->had_error;
}
int vp9_worker_reset(VP9Worker* const worker) {
static int reset(VP9Worker *const worker) {
int ok = 1;
worker->had_error = 0;
if (worker->status_ < OK) {
#if CONFIG_MULTITHREAD
if (pthread_mutex_init(&worker->mutex_, NULL) ||
pthread_cond_init(&worker->condition_, NULL)) {
worker->impl_ = (VP9WorkerImpl*)vpx_calloc(1, sizeof(*worker->impl_));
if (worker->impl_ == NULL) {
return 0;
}
pthread_mutex_lock(&worker->mutex_);
ok = !pthread_create(&worker->thread_, NULL, thread_loop, worker);
if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
goto Error;
}
if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
pthread_mutex_destroy(&worker->impl_->mutex_);
goto Error;
}
pthread_mutex_lock(&worker->impl_->mutex_);
ok = !pthread_create(&worker->impl_->thread_, NULL, thread_loop, worker);
if (ok) worker->status_ = OK;
pthread_mutex_unlock(&worker->mutex_);
pthread_mutex_unlock(&worker->impl_->mutex_);
if (!ok) {
pthread_mutex_destroy(&worker->impl_->mutex_);
pthread_cond_destroy(&worker->impl_->condition_);
Error:
vpx_free(worker->impl_);
worker->impl_ = NULL;
return 0;
}
#else
worker->status_ = OK;
#endif
} else if (worker->status_ > OK) {
ok = vp9_worker_sync(worker);
ok = sync(worker);
}
assert(!ok || (worker->status_ == OK));
return ok;
}
void vp9_worker_execute(VP9Worker* const worker) {
static void execute(VP9Worker *const worker) {
if (worker->hook != NULL) {
worker->had_error |= !worker->hook(worker->data1, worker->data2);
}
}
void vp9_worker_launch(VP9Worker* const worker) {
static void launch(VP9Worker *const worker) {
#if CONFIG_MULTITHREAD
change_state(worker, WORK);
#else
vp9_worker_execute(worker);
execute(worker);
#endif
}
void vp9_worker_end(VP9Worker* const worker) {
static void end(VP9Worker *const worker) {
if (worker->status_ >= OK) {
#if CONFIG_MULTITHREAD
change_state(worker, NOT_OK);
pthread_join(worker->thread_, NULL);
pthread_mutex_destroy(&worker->mutex_);
pthread_cond_destroy(&worker->condition_);
pthread_join(worker->impl_->thread_, NULL);
pthread_mutex_destroy(&worker->impl_->mutex_);
pthread_cond_destroy(&worker->impl_->condition_);
#else
worker->status_ = NOT_OK;
#endif
}
vpx_free(worker->impl_);
worker->impl_ = NULL;
assert(worker->status_ == NOT_OK);
}
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
static VP9WorkerInterface g_worker_interface = {
init, reset, sync, launch, execute, end
};
int vp9_set_worker_interface(const VP9WorkerInterface* const winterface) {
if (winterface == NULL ||
winterface->init == NULL || winterface->reset == NULL ||
winterface->sync == NULL || winterface->launch == NULL ||
winterface->execute == NULL || winterface->end == NULL) {
return 0;
}
g_worker_interface = *winterface;
return 1;
}
const VP9WorkerInterface *vp9_get_worker_interface(void) {
return &g_worker_interface;
}
//------------------------------------------------------------------------------

View File

@ -11,8 +11,7 @@
//
// Original source:
// http://git.chromium.org/webm/libwebp.git
// 100644 blob 13a61a4c84194c3374080cbf03d881d3cd6af40d src/utils/thread.h
// 100644 blob 7bd451b124ae3b81596abfbcc823e3cb129d3a38 src/utils/thread.h
#ifndef VP9_DECODER_VP9_THREAD_H_
#define VP9_DECODER_VP9_THREAD_H_
@ -163,40 +162,53 @@ typedef enum {
// arguments (data1 and data2), and should return false in case of error.
typedef int (*VP9WorkerHook)(void*, void*);
// Synchronize object used to launch job in the worker thread
// Platform-dependent implementation details for the worker.
typedef struct VP9WorkerImpl VP9WorkerImpl;
// Synchronization object used to launch job in the worker thread
typedef struct {
#if CONFIG_MULTITHREAD
pthread_mutex_t mutex_;
pthread_cond_t condition_;
pthread_t thread_;
#endif
VP9WorkerImpl *impl_;
VP9WorkerStatus status_;
VP9WorkerHook hook; // hook to call
void* data1; // first argument passed to 'hook'
void* data2; // second argument passed to 'hook'
void *data1; // first argument passed to 'hook'
void *data2; // second argument passed to 'hook'
int had_error; // return value of the last call to 'hook'
} VP9Worker;
// Must be called first, before any other method.
void vp9_worker_init(VP9Worker* const worker);
// Must be called to initialize the object and spawn the thread. Re-entrant.
// Will potentially launch the thread. Returns false in case of error.
int vp9_worker_reset(VP9Worker* const worker);
// Makes sure the previous work is finished. Returns true if worker->had_error
// was not set and no error condition was triggered by the working thread.
int vp9_worker_sync(VP9Worker* const worker);
// Triggers the thread to call hook() with data1 and data2 argument. These
// hook/data1/data2 can be changed at any time before calling this function,
// but not be changed afterward until the next call to vp9_worker_sync().
void vp9_worker_launch(VP9Worker* const worker);
// This function is similar to vp9_worker_launch() except that it calls the
// hook directly instead of using a thread. Convenient to bypass the thread
// mechanism while still using the VP9Worker structs. vp9_worker_sync() must
// still be called afterward (for error reporting).
void vp9_worker_execute(VP9Worker* const worker);
// Kill the thread and terminate the object. To use the object again, one
// must call vp9_worker_reset() again.
void vp9_worker_end(VP9Worker* const worker);
// The interface for all thread-worker related functions. All these functions
// must be implemented.
typedef struct {
// Must be called first, before any other method.
void (*init)(VP9Worker *const worker);
// Must be called to initialize the object and spawn the thread. Re-entrant.
// Will potentially launch the thread. Returns false in case of error.
int (*reset)(VP9Worker *const worker);
// Makes sure the previous work is finished. Returns true if worker->had_error
// was not set and no error condition was triggered by the working thread.
int (*sync)(VP9Worker *const worker);
// Triggers the thread to call hook() with data1 and data2 arguments. These
// hook/data1/data2 values can be changed at any time before calling this
// function, but not be changed afterward until the next call to Sync().
void (*launch)(VP9Worker *const worker);
// This function is similar to launch() except that it calls the
// hook directly instead of using a thread. Convenient to bypass the thread
// mechanism while still using the VP9Worker structs. sync() must
// still be called afterward (for error reporting).
void (*execute)(VP9Worker *const worker);
// Kill the thread and terminate the object. To use the object again, one
// must call reset() again.
void (*end)(VP9Worker *const worker);
} VP9WorkerInterface;
// Install a new set of threading functions, overriding the defaults. This
// should be done before any workers are started, i.e., before any encoding or
// decoding takes place. The contents of the interface struct are copied, it
// is safe to free the corresponding memory after this call. This function is
// not thread-safe. Return false in case of invalid pointer or methods.
int vp9_set_worker_interface(const VP9WorkerInterface *const winterface);
// Retrieve the currently set thread worker interface.
const VP9WorkerInterface *vp9_get_worker_interface(void);
//------------------------------------------------------------------------------