2021-10-19 15:30:39 +08:00

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_*/