/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
*
* Data Differential YATL (i.e. libtest) library
*
* Copyright (C) 2012 Data Differential, http://datadifferential.com/
*
* 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.
*
* * The names of its contributors may not 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.
*
*/
#pragma once
#include <pthread.h>
namespace libtest
{
namespace thread
{
class Mutex
{
public:
Mutex() :
_err(0)
{
_err= pthread_mutex_init(&_mutex, NULL);
}
~Mutex()
{
if ((_err= pthread_mutex_destroy(&_mutex)))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_destroy: %s", strerror(_err));
}
}
pthread_mutex_t* handle()
{
if (_err != 0)
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_init: %s", strerror(_err));
}
return &_mutex;
}
private:
int _err;
pthread_mutex_t _mutex;
};
class ScopedLock
{
public:
ScopedLock(Mutex& mutex_) :
_mutex(mutex_)
{
init();
}
~ScopedLock()
{
int err;
if ((err= pthread_mutex_unlock(_mutex.handle())))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_unlock: %s", strerror(err));
}
}
Mutex* handle()
{
return &_mutex;
}
private:
void init()
{
int err;
if ((err= pthread_mutex_lock(_mutex.handle())))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_lock: %s", strerror(err));
}
}
private:
Mutex& _mutex;
};
class Condition
{
public:
Condition()
{
int err;
if ((err= pthread_cond_init(&_cond, NULL)))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_init: %s", strerror(err));
}
}
~Condition()
{
int err;
if ((err= pthread_cond_destroy(&_cond)))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_destroy: %s", strerror(err));
}
}
void broadcast()
{
int err;
if ((err= pthread_cond_broadcast(&_cond)))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_broadcast: %s", strerror(err));
}
}
void signal()
{
int err;
if ((err= pthread_cond_signal(&_cond)))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_broadcast: %s", strerror(err));
}
}
void wait(ScopedLock& lock_)
{
int err;
if ((err= pthread_cond_wait(&_cond, lock_.handle()->handle())))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_wait: %s", strerror(err));
}
}
private:
pthread_cond_t _cond;
};
class Barrier
{
public:
explicit Barrier(uint32_t count):
_threshold(count),
_count(count),
_generation(0)
{
if (_count == 0)
{
fatal_assert("Zero is an invalid value");
}
}
~Barrier()
{
}
bool wait()
{
ScopedLock l(_mutex);
uint32_t gen = _generation;
if (--_count == 0)
{
_generation++;
_count = _threshold;
_cond.broadcast();
return true;
}
while (gen == _generation)
{
_cond.wait(l);
}
return false;
}
private:
Mutex _mutex;
Condition _cond;
uint32_t _threshold;
uint32_t _count;
uint32_t _generation;
};
class Thread
{
private:
typedef void *(*start_routine_fn) (void *);
public:
template <class Function,class Arg1>
Thread(Function func, Arg1 arg):
_joined(false),
_func((start_routine_fn)func),
_context(arg)
{
int err;
if ((err= pthread_create(&_thread, NULL, entry_func, (void*)this)))
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_create: %s", strerror(err));
}
_owner= pthread_self();
}
bool running() const
{
return (pthread_kill(_thread, 0) == 0);
}
bool detached()
{
if (EDEADLK == pthread_join(_thread, NULL))
{
return true;
}
/* Result of pthread_join was EINVAL == detached thread */
return false;
}
bool join()
{
if (_thread == pthread_self())
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "Thread cannot join on itself");
}
if (_owner != pthread_self())
{
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "Attempt made by a non-owner thead to join on thread");
}
bool ret= false;
{
ScopedLock l(_join_mutex);
if (_joined == false)
{
int err;
if ((err= pthread_join(_thread, NULL)))
{
switch(err)
{
case EINVAL:
break;
case ESRCH:
ret= true;
break;
case EDEADLK:
default:
throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_join: %s", strerror(err));
}
}
else
{
ret= true;
}
_joined= true;
}
}
return ret;
}
~Thread()
{
join();
}
protected:
void run()
{
_func(_context);
}
private:
static void * entry_func(void* This)
{
((Thread *)This)->run();
return NULL;
}
private:
bool _joined;
pthread_t _thread;
pthread_t _owner;
start_routine_fn _func;
void* _context;
Mutex _join_mutex;
};
} // namespace thread
} // namespace libtest