/*
* Copyright 2015-2018, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* benchmark_worker.cpp -- benchmark_worker module definitions
*/
#include <cassert>
#include <err.h>
#include "benchmark_worker.hpp"
#include "os_thread.h"
/*
* worker_state_wait_for_transition -- wait for transition from and to
* specified states
*/
static void
worker_state_wait_for_transition(struct benchmark_worker *worker,
enum benchmark_worker_state state,
enum benchmark_worker_state new_state)
{
while (worker->state == state)
os_cond_wait(&worker->cond, &worker->lock);
assert(worker->state == new_state);
}
/*
* worker_state_transition -- change worker state from and to specified states
*/
static void
worker_state_transition(struct benchmark_worker *worker,
enum benchmark_worker_state old_state,
enum benchmark_worker_state new_state)
{
assert(worker->state == old_state);
worker->state = new_state;
os_cond_signal(&worker->cond);
}
/*
* thread_func -- (internal) callback for os_thread
*/
static void *
thread_func(void *arg)
{
assert(arg != nullptr);
auto *worker = (struct benchmark_worker *)arg;
os_mutex_lock(&worker->lock);
worker_state_wait_for_transition(worker, WORKER_STATE_IDLE,
WORKER_STATE_INIT);
if (worker->init)
worker->ret_init = worker->init(worker->bench, worker->args,
&worker->info);
worker_state_transition(worker, WORKER_STATE_INIT,
WORKER_STATE_INITIALIZED);
if (worker->ret_init) {
os_mutex_unlock(&worker->lock);
return nullptr;
}
worker_state_wait_for_transition(worker, WORKER_STATE_INITIALIZED,
WORKER_STATE_RUN);
worker->ret = worker->func(worker->bench, &worker->info);
worker_state_transition(worker, WORKER_STATE_RUN, WORKER_STATE_END);
worker_state_wait_for_transition(worker, WORKER_STATE_END,
WORKER_STATE_EXIT);
if (worker->exit)
worker->exit(worker->bench, worker->args, &worker->info);
worker_state_transition(worker, WORKER_STATE_EXIT, WORKER_STATE_DONE);
os_mutex_unlock(&worker->lock);
return nullptr;
}
/*
* benchmark_worker_alloc -- allocate benchmark worker
*/
struct benchmark_worker *
benchmark_worker_alloc(void)
{
struct benchmark_worker *w =
(struct benchmark_worker *)calloc(1, sizeof(*w));
if (!w)
return nullptr;
if (os_mutex_init(&w->lock))
goto err_free_worker;
if (os_cond_init(&w->cond))
goto err_destroy_mutex;
if (os_thread_create(&w->thread, nullptr, thread_func, w))
goto err_destroy_cond;
return w;
err_destroy_cond:
os_cond_destroy(&w->cond);
err_destroy_mutex:
os_mutex_destroy(&w->lock);
err_free_worker:
free(w);
return nullptr;
}
/*
* benchmark_worker_free -- release benchmark worker
*/
void
benchmark_worker_free(struct benchmark_worker *w)
{
os_thread_join(&w->thread, nullptr);
os_cond_destroy(&w->cond);
os_mutex_destroy(&w->lock);
free(w);
}
/*
* benchmark_worker_init -- call init function for worker
*/
int
benchmark_worker_init(struct benchmark_worker *worker)
{
os_mutex_lock(&worker->lock);
worker_state_transition(worker, WORKER_STATE_IDLE, WORKER_STATE_INIT);
worker_state_wait_for_transition(worker, WORKER_STATE_INIT,
WORKER_STATE_INITIALIZED);
int ret = worker->ret_init;
os_mutex_unlock(&worker->lock);
return ret;
}
/*
* benchmark_worker_exit -- call exit function for worker
*/
void
benchmark_worker_exit(struct benchmark_worker *worker)
{
os_mutex_lock(&worker->lock);
worker_state_transition(worker, WORKER_STATE_END, WORKER_STATE_EXIT);
worker_state_wait_for_transition(worker, WORKER_STATE_EXIT,
WORKER_STATE_DONE);
os_mutex_unlock(&worker->lock);
}
/*
* benchmark_worker_run -- run benchmark worker
*/
int
benchmark_worker_run(struct benchmark_worker *worker)
{
int ret = 0;
os_mutex_lock(&worker->lock);
worker_state_transition(worker, WORKER_STATE_INITIALIZED,
WORKER_STATE_RUN);
os_mutex_unlock(&worker->lock);
return ret;
}
/*
* benchmark_worker_join -- join benchmark worker
*/
int
benchmark_worker_join(struct benchmark_worker *worker)
{
os_mutex_lock(&worker->lock);
worker_state_wait_for_transition(worker, WORKER_STATE_RUN,
WORKER_STATE_END);
os_mutex_unlock(&worker->lock);
return 0;
}