| /** |
| * 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 PTHREADMOCKS_H_ |
| #define PTHREADMOCKS_H_ |
| |
| #include <pthread.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "src/zk_adaptor.h" |
| |
| #include "Util.h" |
| #include "MocksBase.h" |
| #include "LibCSymTable.h" |
| #include "ThreadingUtil.h" |
| |
| // an ABC for pthreads |
| class MockPthreadsBase: public Mock |
| { |
| public: |
| MockPthreadsBase(){mock_=this;} |
| virtual ~MockPthreadsBase(){mock_=0;} |
| |
| virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, |
| void *(*f)(void *), void *d) =0; |
| virtual int pthread_join(pthread_t t, void ** r) =0; |
| virtual int pthread_detach(pthread_t t) =0; |
| virtual int pthread_cond_broadcast(pthread_cond_t *c) =0; |
| virtual int pthread_cond_destroy(pthread_cond_t *c) =0; |
| virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a) =0; |
| virtual int pthread_cond_signal(pthread_cond_t *c) =0; |
| virtual int pthread_cond_timedwait(pthread_cond_t *c, |
| pthread_mutex_t *m, const struct timespec *t) =0; |
| virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) =0; |
| virtual int pthread_mutex_destroy(pthread_mutex_t *m) =0; |
| virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a) =0; |
| virtual int pthread_mutex_lock(pthread_mutex_t *m) =0; |
| virtual int pthread_mutex_trylock(pthread_mutex_t *m) =0; |
| virtual int pthread_mutex_unlock(pthread_mutex_t *m) =0; |
| |
| static MockPthreadsBase* mock_; |
| }; |
| |
| // all pthread functions simply return an error code |
| // and increment their invocation counter. No actual threads are spawned. |
| class MockPthreadsNull: public MockPthreadsBase |
| { |
| public: |
| MockPthreadsNull(): |
| pthread_createReturns(0),pthread_createCounter(0), |
| pthread_joinReturns(0),pthread_joinCounter(0),pthread_joinResultReturn(0), |
| pthread_detachReturns(0),pthread_detachCounter(0), |
| pthread_cond_broadcastReturns(0),pthread_cond_broadcastCounter(0), |
| pthread_cond_destroyReturns(0),pthread_cond_destroyCounter(0), |
| pthread_cond_initReturns(0),pthread_cond_initCounter(0), |
| pthread_cond_signalReturns(0),pthread_cond_signalCounter(0), |
| pthread_cond_timedwaitReturns(0),pthread_cond_timedwaitCounter(0), |
| pthread_cond_waitReturns(0),pthread_cond_waitCounter(0), |
| pthread_mutex_destroyReturns(0),pthread_mutex_destroyCounter(0), |
| pthread_mutex_initReturns(0),pthread_mutex_initCounter(0), |
| pthread_mutex_lockReturns(0),pthread_mutex_lockCounter(0), |
| pthread_mutex_trylockReturns(0),pthread_mutex_trylockCounter(0), |
| pthread_mutex_unlockReturns(0),pthread_mutex_unlockCounter(0) |
| { |
| memset(threads,0,sizeof(threads)); |
| } |
| |
| short threads[512]; |
| |
| int pthread_createReturns; |
| int pthread_createCounter; |
| virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, |
| void *(*f)(void *), void *d){ |
| char* p=(char*)&threads[pthread_createCounter++]; |
| p[0]='i'; // mark as created |
| *t=(pthread_t)p; |
| return pthread_createReturns; |
| } |
| int pthread_joinReturns; |
| int pthread_joinCounter; |
| void* pthread_joinResultReturn; |
| virtual int pthread_join(pthread_t t, void ** r){ |
| pthread_joinCounter++; |
| if(r!=0) |
| *r=pthread_joinResultReturn; |
| char* p=(char*)t; |
| p[0]='x';p[1]+=1; |
| return pthread_joinReturns; |
| } |
| int pthread_detachReturns; |
| int pthread_detachCounter; |
| virtual int pthread_detach(pthread_t t){ |
| pthread_detachCounter++; |
| char* p=(char*)t; |
| p[0]='x';p[1]+=1; |
| return pthread_detachReturns; |
| } |
| |
| template<class T> |
| static bool isInitialized(const T& t){ |
| return ((char*)t)[0]=='i'; |
| } |
| template<class T> |
| static bool isDestroyed(const T& t){ |
| return ((char*)t)[0]=='x'; |
| } |
| template<class T> |
| static int getDestroyCounter(const T& t){ |
| return ((char*)t)[1]; |
| } |
| template<class T> |
| static int getInvalidAccessCounter(const T& t){ |
| return ((char*)t)[2]; |
| } |
| int pthread_cond_broadcastReturns; |
| int pthread_cond_broadcastCounter; |
| virtual int pthread_cond_broadcast(pthread_cond_t *c){ |
| pthread_cond_broadcastCounter++; |
| if(isDestroyed(c))((char*)c)[2]++; |
| return pthread_cond_broadcastReturns; |
| } |
| int pthread_cond_destroyReturns; |
| int pthread_cond_destroyCounter; |
| virtual int pthread_cond_destroy(pthread_cond_t *c){ |
| pthread_cond_destroyCounter++; |
| char* p=(char*)c; |
| p[0]='x';p[1]+=1; |
| return pthread_cond_destroyReturns; |
| } |
| int pthread_cond_initReturns; |
| int pthread_cond_initCounter; |
| virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a){ |
| pthread_cond_initCounter++; |
| char* p=(char*)c; |
| p[0]='i'; // mark as created |
| p[1]=0; // destruction counter |
| p[2]=0; // access after destruction counter |
| return pthread_cond_initReturns; |
| } |
| int pthread_cond_signalReturns; |
| int pthread_cond_signalCounter; |
| virtual int pthread_cond_signal(pthread_cond_t *c){ |
| pthread_cond_signalCounter++; |
| if(isDestroyed(c))((char*)c)[2]++; |
| return pthread_cond_signalReturns; |
| } |
| int pthread_cond_timedwaitReturns; |
| int pthread_cond_timedwaitCounter; |
| virtual int pthread_cond_timedwait(pthread_cond_t *c, |
| pthread_mutex_t *m, const struct timespec *t){ |
| pthread_cond_timedwaitCounter++; |
| if(isDestroyed(c))((char*)c)[2]++; |
| return pthread_cond_timedwaitReturns; |
| } |
| int pthread_cond_waitReturns; |
| int pthread_cond_waitCounter; |
| virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m){ |
| pthread_cond_waitCounter++; |
| if(isDestroyed(c))((char*)c)[2]++; |
| return pthread_cond_waitReturns; |
| } |
| int pthread_mutex_destroyReturns; |
| int pthread_mutex_destroyCounter; |
| virtual int pthread_mutex_destroy(pthread_mutex_t *m){ |
| pthread_mutex_destroyCounter++; |
| char* p=(char*)m; |
| p[0]='x';p[1]+=1; |
| return pthread_mutex_destroyReturns; |
| } |
| int pthread_mutex_initReturns; |
| int pthread_mutex_initCounter; |
| virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a){ |
| pthread_mutex_initCounter++; |
| char* p=(char*)m; |
| p[0]='i'; // mark as created |
| p[1]=0; // destruction counter |
| p[2]=0; // access after destruction counter |
| return pthread_mutex_initReturns; |
| } |
| int pthread_mutex_lockReturns; |
| int pthread_mutex_lockCounter; |
| virtual int pthread_mutex_lock(pthread_mutex_t *m){ |
| pthread_mutex_lockCounter++; |
| if(isDestroyed(m))((char*)m)[2]++; |
| return pthread_mutex_lockReturns; |
| } |
| int pthread_mutex_trylockReturns; |
| int pthread_mutex_trylockCounter; |
| virtual int pthread_mutex_trylock(pthread_mutex_t *m){ |
| pthread_mutex_trylockCounter++; |
| if(isDestroyed(m))((char*)m)[2]++; |
| return pthread_mutex_trylockReturns; |
| } |
| int pthread_mutex_unlockReturns; |
| int pthread_mutex_unlockCounter; |
| virtual int pthread_mutex_unlock(pthread_mutex_t *m){ |
| pthread_mutex_unlockCounter++; |
| if(isDestroyed(m))((char*)m)[2]++; |
| return pthread_mutex_unlockReturns; |
| } |
| }; |
| |
| // simulates the way zookeeper threads make use of api_prolog/epilog and |
| // |
| class MockPthreadZKNull: public MockPthreadsNull |
| { |
| typedef std::map<pthread_t,zhandle_t*> Map; |
| Map map_; |
| public: |
| virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, |
| void *(*f)(void *), void *d){ |
| int ret=MockPthreadsNull::pthread_create(t,a,f,d); |
| zhandle_t* zh=(zhandle_t*)d; |
| adaptor_threads* ad=(adaptor_threads*)zh->adaptor_priv; |
| api_prolog(zh); |
| ad->threadsToWait--; |
| putValue(map_,*t,zh); |
| return ret; |
| } |
| virtual int pthread_join(pthread_t t, void ** r){ |
| zhandle_t* zh=0; |
| if(getValue(map_,t,zh)) |
| api_epilog(zh,0); |
| return MockPthreadsNull::pthread_join(t,r); |
| } |
| }; |
| |
| struct ThreadInfo{ |
| typedef enum {RUNNING,TERMINATED} ThreadState; |
| |
| ThreadInfo(): |
| destructionCounter_(0),invalidAccessCounter_(0),state_(RUNNING) |
| { |
| } |
| |
| ThreadInfo& incDestroyed() { |
| destructionCounter_++; |
| return *this; |
| } |
| ThreadInfo& incInvalidAccess(){ |
| invalidAccessCounter_++; |
| return *this; |
| } |
| ThreadInfo& setTerminated(){ |
| state_=TERMINATED; |
| return *this; |
| } |
| int destructionCounter_; |
| int invalidAccessCounter_; |
| ThreadState state_; |
| }; |
| |
| class CheckedPthread: public MockPthreadsBase |
| { |
| // first => destruction counter |
| // second => invalid access counter |
| //typedef std::pair<int,int> Entry; |
| typedef ThreadInfo Entry; |
| typedef std::map<pthread_t,Entry> ThreadMap; |
| static ThreadMap tmap_; |
| static ThreadMap& getMap(const TypeOp<pthread_t>::BareT&){return tmap_;} |
| typedef std::map<pthread_mutex_t*,Entry> MutexMap; |
| static MutexMap mmap_; |
| static MutexMap& getMap(const TypeOp<pthread_mutex_t>::BareT&){return mmap_;} |
| typedef std::map<pthread_cond_t*,Entry> CVMap; |
| static CVMap cvmap_; |
| static CVMap& getMap(const TypeOp<pthread_cond_t>::BareT&){return cvmap_;} |
| |
| static Mutex mx; |
| |
| template<class T> |
| static void markDestroyed(T& t){ |
| typedef typename TypeOp<T>::BareT Type; |
| Entry e; |
| synchronized(mx); |
| if(getValue(getMap(Type()),t,e)){ |
| putValue(getMap(Type()),t,Entry(e).incDestroyed()); |
| }else{ |
| putValue(getMap(Type()),t,Entry().incDestroyed()); |
| } |
| } |
| template<class T> |
| static void markCreated(T& t){ |
| typedef typename TypeOp<T>::BareT Type; |
| Entry e; |
| synchronized(mx); |
| if(!getValue(getMap(Type()),t,e)) |
| putValue(getMap(Type()),t,Entry()); |
| } |
| template<class T> |
| static void checkAccessed(T& t){ |
| typedef typename TypeOp<T>::BareT Type; |
| Entry e; |
| synchronized(mx); |
| if(getValue(getMap(Type()),t,e) && e.destructionCounter_>0) |
| putValue(getMap(Type()),t,Entry(e).incInvalidAccess()); |
| } |
| static void setTerminated(pthread_t t){ |
| Entry e; |
| synchronized(mx); |
| if(getValue(tmap_,t,e)) |
| putValue(tmap_,t,Entry(e).setTerminated()); |
| } |
| public: |
| bool verbose; |
| CheckedPthread():verbose(false){ |
| tmap_.clear(); |
| mmap_.clear(); |
| cvmap_.clear(); |
| mx.release(); |
| } |
| template <class T> |
| static bool isInitialized(const T& t){ |
| typedef typename TypeOp<T>::BareT Type; |
| Entry e; |
| synchronized(mx); |
| return getValue(getMap(Type()),t,e) && e.destructionCounter_==0; |
| } |
| template <class T> |
| static bool isDestroyed(const T& t){ |
| typedef typename TypeOp<T>::BareT Type; |
| Entry e; |
| synchronized(mx); |
| return getValue(getMap(Type()),t,e) && e.destructionCounter_>0; |
| } |
| static bool isTerminated(pthread_t t){ |
| Entry e; |
| synchronized(mx); |
| return getValue(tmap_,t,e) && e.state_==ThreadInfo::TERMINATED; |
| } |
| template <class T> |
| static int getDestroyCounter(const T& t){ |
| typedef typename TypeOp<T>::BareT Type; |
| Entry e; |
| synchronized(mx); |
| return getValue(getMap(Type()),t,e)?e.destructionCounter_:-1; |
| } |
| template<class T> |
| static int getInvalidAccessCounter(const T& t){ |
| typedef typename TypeOp<T>::BareT Type; |
| Entry e; |
| synchronized(mx); |
| return getValue(getMap(Type()),t,e)?e.invalidAccessCounter_:-1; |
| } |
| |
| struct ThreadContext{ |
| typedef void *(*ThreadFunc)(void *); |
| |
| ThreadContext(ThreadFunc func,void* param):func_(func),param_(param){} |
| ThreadFunc func_; |
| void* param_; |
| }; |
| static void* threadFuncWrapper(void* v){ |
| ThreadContext* ctx=(ThreadContext*)v; |
| pthread_t t=pthread_self(); |
| markCreated(t); |
| void* res=ctx->func_(ctx->param_); |
| setTerminated(pthread_self()); |
| delete ctx; |
| return res; |
| } |
| virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, |
| void *(*f)(void *), void *d) |
| { |
| int ret=LIBC_SYMBOLS.pthread_create(t,a,threadFuncWrapper, |
| new ThreadContext(f,d)); |
| if(verbose) |
| TEST_TRACE(("thread created %p",*t)); |
| return ret; |
| } |
| virtual int pthread_join(pthread_t t, void ** r){ |
| if(verbose) TEST_TRACE(("thread joined %p",t)); |
| int ret=LIBC_SYMBOLS.pthread_join(t,r); |
| if(ret==0) |
| markDestroyed(t); |
| return ret; |
| } |
| virtual int pthread_detach(pthread_t t){ |
| if(verbose) TEST_TRACE(("thread detached %p",t)); |
| int ret=LIBC_SYMBOLS.pthread_detach(t); |
| if(ret==0) |
| markDestroyed(t); |
| return ret; |
| } |
| virtual int pthread_cond_broadcast(pthread_cond_t *c){ |
| checkAccessed(c); |
| return LIBC_SYMBOLS.pthread_cond_broadcast(c); |
| } |
| virtual int pthread_cond_destroy(pthread_cond_t *c){ |
| markDestroyed(c); |
| return LIBC_SYMBOLS.pthread_cond_destroy(c); |
| } |
| virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a){ |
| markCreated(c); |
| return LIBC_SYMBOLS.pthread_cond_init(c,a); |
| } |
| virtual int pthread_cond_signal(pthread_cond_t *c){ |
| checkAccessed(c); |
| return LIBC_SYMBOLS.pthread_cond_signal(c); |
| } |
| virtual int pthread_cond_timedwait(pthread_cond_t *c, |
| pthread_mutex_t *m, const struct timespec *t){ |
| checkAccessed(c); |
| return LIBC_SYMBOLS.pthread_cond_timedwait(c,m,t); |
| } |
| virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m){ |
| checkAccessed(c); |
| return LIBC_SYMBOLS.pthread_cond_wait(c,m); |
| } |
| virtual int pthread_mutex_destroy(pthread_mutex_t *m){ |
| markDestroyed(m); |
| return LIBC_SYMBOLS.pthread_mutex_destroy(m); |
| } |
| virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a){ |
| markCreated(m); |
| return LIBC_SYMBOLS.pthread_mutex_init(m,a); |
| } |
| virtual int pthread_mutex_lock(pthread_mutex_t *m){ |
| checkAccessed(m); |
| return LIBC_SYMBOLS.pthread_mutex_lock(m); |
| } |
| virtual int pthread_mutex_trylock(pthread_mutex_t *m){ |
| checkAccessed(m); |
| return LIBC_SYMBOLS.pthread_mutex_trylock(m); |
| } |
| virtual int pthread_mutex_unlock(pthread_mutex_t *m){ |
| checkAccessed(m); |
| return LIBC_SYMBOLS.pthread_mutex_unlock(m); |
| } |
| }; |
| |
| #endif /*PTHREADMOCKS_H_*/ |
| |