262 lines
6.6 KiB
C++
262 lines
6.6 KiB
C++
/**
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef THREADINGUTIL_H_
|
|
#define THREADINGUTIL_H_
|
|
|
|
#include <vector>
|
|
|
|
#ifdef THREADED
|
|
#include "pthread.h"
|
|
#endif
|
|
|
|
// *****************************************************************************
|
|
// Threading primitives
|
|
|
|
// atomic post-increment; returns the previous value of the operand
|
|
int32_t atomic_post_incr(volatile int32_t* operand, int32_t incr);
|
|
// atomic fetch&store; returns the previous value of the operand
|
|
int32_t atomic_fetch_store(volatile int32_t *operand, int32_t value);
|
|
|
|
// a partial implementation of an atomic integer type
|
|
class AtomicInt{
|
|
public:
|
|
explicit AtomicInt(int32_t init=0):v_(init){}
|
|
AtomicInt(const AtomicInt& other):v_(other){}
|
|
// assigment
|
|
AtomicInt& operator=(const AtomicInt& lhs){
|
|
atomic_fetch_store(&v_,lhs);
|
|
return *this;
|
|
}
|
|
AtomicInt& operator=(int32_t i){
|
|
atomic_fetch_store(&v_,i);
|
|
return *this;
|
|
}
|
|
// pre-increment
|
|
AtomicInt& operator++() {
|
|
atomic_post_incr(&v_,1);
|
|
return *this;
|
|
}
|
|
// pre-decrement
|
|
AtomicInt& operator--() {
|
|
atomic_post_incr(&v_,-1);
|
|
return *this;
|
|
}
|
|
// post-increment
|
|
AtomicInt operator++(int){
|
|
return AtomicInt(atomic_post_incr(&v_,1));
|
|
}
|
|
// post-decrement
|
|
AtomicInt operator--(int){
|
|
return AtomicInt(atomic_post_incr(&v_,-1));
|
|
}
|
|
|
|
operator int() const{
|
|
return atomic_post_incr(&v_,0);
|
|
}
|
|
int get() const{
|
|
return atomic_post_incr(&v_,0);
|
|
}
|
|
private:
|
|
mutable int32_t v_;
|
|
};
|
|
|
|
#ifdef THREADED
|
|
// ****************************************************************************
|
|
#define VALIDATE_JOBS(jm) jm.validateJobs(__FILE__,__LINE__)
|
|
#define VALIDATE_JOB(j) j.validate(__FILE__,__LINE__)
|
|
|
|
class Mutex{
|
|
public:
|
|
Mutex();
|
|
~Mutex();
|
|
void acquire();
|
|
void release();
|
|
private:
|
|
Mutex(const Mutex&);
|
|
Mutex& operator=(const Mutex&);
|
|
struct Impl;
|
|
Impl* impl_;
|
|
};
|
|
|
|
class MTLock{
|
|
public:
|
|
MTLock(Mutex& m):m_(m){m.acquire();}
|
|
~MTLock(){m_.release();}
|
|
Mutex& m_;
|
|
};
|
|
|
|
#define synchronized(m) MTLock __lock(m)
|
|
|
|
// ****************************************************************************
|
|
class Latch {
|
|
public:
|
|
virtual ~Latch() {}
|
|
virtual void await() const =0;
|
|
virtual void signalAndWait() =0;
|
|
virtual void signal() =0;
|
|
};
|
|
|
|
class CountDownLatch: public Latch {
|
|
public:
|
|
CountDownLatch(int count):count_(count) {
|
|
pthread_cond_init(&cond_,0);
|
|
pthread_mutex_init(&mut_,0);
|
|
}
|
|
virtual ~CountDownLatch() {
|
|
pthread_mutex_lock(&mut_);
|
|
if(count_!=0) {
|
|
count_=0;
|
|
pthread_cond_broadcast(&cond_);
|
|
}
|
|
pthread_mutex_unlock(&mut_);
|
|
|
|
pthread_cond_destroy(&cond_);
|
|
pthread_mutex_destroy(&mut_);
|
|
}
|
|
|
|
virtual void await() const {
|
|
pthread_mutex_lock(&mut_);
|
|
awaitImpl();
|
|
pthread_mutex_unlock(&mut_);
|
|
}
|
|
virtual void signalAndWait() {
|
|
pthread_mutex_lock(&mut_);
|
|
signalImpl();
|
|
awaitImpl();
|
|
pthread_mutex_unlock(&mut_);
|
|
}
|
|
virtual void signal() {
|
|
pthread_mutex_lock(&mut_);
|
|
signalImpl();
|
|
pthread_mutex_unlock(&mut_);
|
|
}
|
|
private:
|
|
void awaitImpl() const{
|
|
while(count_!=0)
|
|
pthread_cond_wait(&cond_,&mut_);
|
|
}
|
|
void signalImpl() {
|
|
if(count_>0) {
|
|
count_--;
|
|
pthread_cond_broadcast(&cond_);
|
|
}
|
|
}
|
|
int count_;
|
|
mutable pthread_mutex_t mut_;
|
|
mutable pthread_cond_t cond_;
|
|
};
|
|
|
|
class TestJob {
|
|
public:
|
|
typedef long JobId;
|
|
TestJob():hasRun_(false),startLatch_(0),endLatch_(0) {}
|
|
virtual ~TestJob() {
|
|
join();
|
|
}
|
|
virtual TestJob* clone() const =0;
|
|
|
|
virtual void run() =0;
|
|
virtual void validate(const char* file, int line) const =0;
|
|
|
|
virtual void start(Latch* startLatch=0,Latch* endLatch=0) {
|
|
startLatch_=startLatch;endLatch_=endLatch;
|
|
hasRun_=true;
|
|
pthread_create(&thread_, 0, thread, this);
|
|
}
|
|
virtual JobId getJobId() const {
|
|
return (JobId)thread_;
|
|
}
|
|
virtual void join() {
|
|
if(!hasRun_)
|
|
return;
|
|
if(!pthread_equal(thread_,pthread_self()))
|
|
pthread_join(thread_,0);
|
|
else
|
|
pthread_detach(thread_);
|
|
}
|
|
private:
|
|
void awaitStart() {
|
|
if(startLatch_==0) return;
|
|
startLatch_->signalAndWait();
|
|
}
|
|
void signalFinished() {
|
|
if(endLatch_==0) return;
|
|
endLatch_->signal();
|
|
}
|
|
static void* thread(void* p) {
|
|
TestJob* j=(TestJob*)p;
|
|
j->awaitStart(); // wait for the start command
|
|
j->run();
|
|
j->signalFinished();
|
|
return 0;
|
|
}
|
|
bool hasRun_;
|
|
Latch* startLatch_;
|
|
Latch* endLatch_;
|
|
pthread_t thread_;
|
|
};
|
|
|
|
class TestJobManager {
|
|
typedef std::vector<TestJob*> JobList;
|
|
public:
|
|
TestJobManager(const TestJob& tj,int threadCount=1):
|
|
startLatch_(threadCount),endLatch_(threadCount)
|
|
{
|
|
for(int i=0;i<threadCount;++i)
|
|
jobs_.push_back(tj.clone());
|
|
}
|
|
virtual ~TestJobManager(){
|
|
for(unsigned i=0;i<jobs_.size();++i)
|
|
delete jobs_[i];
|
|
}
|
|
|
|
virtual void startAllJobs() {
|
|
for(unsigned i=0;i<jobs_.size();++i)
|
|
jobs_[i]->start(&startLatch_,&endLatch_);
|
|
}
|
|
virtual void startJobsImmediately() {
|
|
for(unsigned i=0;i<jobs_.size();++i)
|
|
jobs_[i]->start(0,&endLatch_);
|
|
}
|
|
virtual void wait() const {
|
|
endLatch_.await();
|
|
}
|
|
virtual void validateJobs(const char* file, int line) const{
|
|
for(unsigned i=0;i<jobs_.size();++i)
|
|
jobs_[i]->validate(file,line);
|
|
}
|
|
private:
|
|
JobList jobs_;
|
|
CountDownLatch startLatch_;
|
|
CountDownLatch endLatch_;
|
|
};
|
|
|
|
#else // THREADED
|
|
// single THREADED
|
|
class Mutex{
|
|
public:
|
|
void acquire(){}
|
|
void release(){}
|
|
};
|
|
#define synchronized(m)
|
|
|
|
#endif // THREADED
|
|
|
|
#endif /*THREADINGUTIL_H_*/
|