/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #ifndef CPPCMS_POSIX_UTIL_H #define CPPCMS_POSIX_UTIL_H #include #include #include #include #include #include #include #include #include #include #include namespace cppcms { namespace impl { inline void *mmap_anonymous(size_t size) { #if defined(MAP_ANONYMOUS) void *p=::mmap(0,size,PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); int err=errno; #elif defined(MAP_ANON) void *p=::mmap(0,size,PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); int err=errno; #else int fd=::open("/dev/null",O_RDWR); if(fd < 0) throw cppcms_error(errno,"Failed to open /dev/null"); void *p=::mmap(0,size,PROT_READ | PROT_WRITE,MAP_SHARED, fd, 0); int err=errno; ::close(fd); #endif if(p==MAP_FAILED) throw cppcms_error(err,"Failed to create shared memory"); return p; } inline void create_mutex(pthread_mutex_t *m,bool pshared = false) { if(!pshared) { pthread_mutex_init(m,0); } else { #ifdef CPPCMS_HAS_THREAD_PSHARED pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); try { int res; res = pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED); if(res==0) res = pthread_mutex_init(m,&attr); if(res < 0) throw cppcms_error(errno,"Failed to create process shared mutex"); pthread_mutexattr_destroy(&attr); } catch(...) { pthread_mutexattr_destroy(&attr); throw; } #else throw cppcms_error("Process shared mutex is not supported"); #endif } } inline void destroy_mutex(pthread_mutex_t *m) { pthread_mutex_destroy(m); } inline void create_rwlock(pthread_rwlock_t *m,bool pshared=false) { if(!pshared) { pthread_rwlock_init(m,0); } else { #ifdef CPPCMS_HAS_THREAD_PSHARED pthread_rwlockattr_t attr; pthread_rwlockattr_init(&attr); try { int res; res = pthread_rwlockattr_setpshared(&attr,PTHREAD_PROCESS_SHARED); if(res==0) res = pthread_rwlock_init(m,&attr); if(res < 0) throw cppcms_error(errno,"Failed to create process shared mutex"); pthread_rwlockattr_destroy(&attr); } catch(...) { pthread_rwlockattr_destroy(&attr); throw; } #else throw cppcms_error("Process shared mutex is not supported"); #endif } } inline void destroy_rwlock(pthread_rwlock_t *m) { pthread_rwlock_destroy(m); } #ifdef CPPCMS_HAS_THREAD_PSHARED inline bool test_pthread_mutex_pshared_impl() { void *memory=mmap_anonymous(sizeof(pthread_mutex_t)); bool res=false; try { create_mutex(reinterpret_cast(memory),true); destroy_mutex(reinterpret_cast(memory)); res=true; } catch(cppcms_error const &e) { res= false; } ::munmap((char*)memory,sizeof(pthread_mutex_t)); return res; } inline bool test_pthread_mutex_pshared() { static bool has = test_pthread_mutex_pshared_impl(); return has; } #else inline bool test_pthread_mutex_pshared() { return false; } #endif class mutex : public booster::noncopyable { public: class guard; mutex() : plock_(0), flock_(0) { bool use_pthread=test_pthread_mutex_pshared(); if(use_pthread) { plock_ =reinterpret_cast(mmap_anonymous(sizeof(pthread_mutex_t))); create_mutex(plock_,true); } else { plock_=&normal_; create_mutex(plock_,false); flock_=tmpfile(); if(!flock_) { int err=errno; destroy_mutex(plock_); throw cppcms_error(err,"Failed to create temporary file"); } } } void lock() { pthread_mutex_lock(plock_); if(flock_) { struct flock lock; memset(&lock,0,sizeof(lock)); lock.l_type=F_WRLCK; lock.l_whence=SEEK_SET; while(::fcntl(fileno(flock_),F_SETLKW,&lock)!=0 && errno==EINTR) ; } } void unlock() { if(flock_) { struct flock lock; memset(&lock,0,sizeof(lock)); lock.l_type=F_UNLCK; lock.l_whence=SEEK_SET; while(::fcntl(fileno(flock_),F_SETLKW,&lock)!=0 && errno==EINTR) ; } pthread_mutex_unlock(plock_); } ~mutex() { if(flock_) ::fclose(flock_); destroy_mutex(plock_); if(plock_ != &normal_) ::munmap((char*)plock_,sizeof(pthread_mutex_t)); } private: pthread_mutex_t *plock_; FILE *flock_; pthread_mutex_t normal_; }; class mutex::guard : public booster::noncopyable { public: guard(mutex &m) : m_(m) { m_.lock(); } ~guard() { m_.unlock(); } private: mutex &m_; }; class shared_mutex : public booster::noncopyable { public: class shared_guard; class unique_guard; shared_mutex() : plock_(0), flock_(0) { bool use_pthread=test_pthread_mutex_pshared(); if(use_pthread) { plock_ =reinterpret_cast(mmap_anonymous(sizeof(pthread_rwlock_t))); create_rwlock(plock_,true); } else { plock_=&normal_; create_rwlock(plock_,false); flock_=tmpfile(); if(!flock_) { int err=errno; destroy_rwlock(plock_); throw cppcms_error(err,"Failed to create temporary file"); } } } void wrlock() { pthread_rwlock_wrlock(plock_); if(flock_) { struct flock lock; memset(&lock,0,sizeof(lock)); lock.l_type=F_WRLCK; lock.l_whence=SEEK_SET; while(::fcntl(fileno(flock_),F_SETLKW,&lock)!=0 && errno==EINTR) ; } } void rdlock() { pthread_rwlock_rdlock(plock_); if(flock_) { struct flock lock; memset(&lock,0,sizeof(lock)); lock.l_type=F_RDLCK; lock.l_whence=SEEK_SET; while(::fcntl(fileno(flock_),F_SETLKW,&lock)!=0 && errno==EINTR) ; } } void unlock() { if(flock_) { struct flock lock; memset(&lock,0,sizeof(lock)); lock.l_type=F_UNLCK; lock.l_whence=SEEK_SET; while(::fcntl(fileno(flock_),F_SETLKW,&lock)!=0 && errno==EINTR) ; } pthread_rwlock_unlock(plock_); } ~shared_mutex() { if(flock_) ::fclose(flock_); destroy_rwlock(plock_); if(plock_ != &normal_) ::munmap((char*)plock_,sizeof(pthread_rwlock_t)); } private: pthread_rwlock_t *plock_; FILE *flock_; pthread_rwlock_t normal_; }; class shared_mutex::shared_guard : public booster::noncopyable { public: shared_guard(shared_mutex &m) : m_(m) { m_.rdlock(); } ~shared_guard() { m_.unlock(); } private: shared_mutex &m_; }; class shared_mutex::unique_guard : public booster::noncopyable { public: unique_guard(shared_mutex &m) : m_(m) { m_.wrlock(); } ~unique_guard() { m_.unlock(); } private: shared_mutex &m_; }; } // impl } // cppcms #endif