/** * 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 #include #include #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 static bool isInitialized(const T& t){ return ((char*)t)[0]=='i'; } template static bool isDestroyed(const T& t){ return ((char*)t)[0]=='x'; } template static int getDestroyCounter(const T& t){ return ((char*)t)[1]; } template 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 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 Entry; typedef ThreadInfo Entry; typedef std::map ThreadMap; static ThreadMap tmap_; static ThreadMap& getMap(const TypeOp::BareT&){return tmap_;} typedef std::map MutexMap; static MutexMap mmap_; static MutexMap& getMap(const TypeOp::BareT&){return mmap_;} typedef std::map CVMap; static CVMap cvmap_; static CVMap& getMap(const TypeOp::BareT&){return cvmap_;} static Mutex mx; template static void markDestroyed(T& t){ typedef typename TypeOp::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 static void markCreated(T& t){ typedef typename TypeOp::BareT Type; Entry e; synchronized(mx); if(!getValue(getMap(Type()),t,e)) putValue(getMap(Type()),t,Entry()); } template static void checkAccessed(T& t){ typedef typename TypeOp::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 static bool isInitialized(const T& t){ typedef typename TypeOp::BareT Type; Entry e; synchronized(mx); return getValue(getMap(Type()),t,e) && e.destructionCounter_==0; } template static bool isDestroyed(const T& t){ typedef typename TypeOp::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 static int getDestroyCounter(const T& t){ typedef typename TypeOp::BareT Type; Entry e; synchronized(mx); return getValue(getMap(Type()),t,e)?e.destructionCounter_:-1; } template static int getInvalidAccessCounter(const T& t){ typedef typename TypeOp::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_*/