- Normal - fast and efficient mutex - Recursive - it may be less efficient on some platformsmaster
@@ -121,6 +121,43 @@ namespace booster { | |||
}; | |||
/// | |||
/// \brief Recursuve Shared mutex or a.k.a. Read-Write Lock that can be recursively locked by \b readers | |||
/// | |||
/// This class provides two options of locking unique - nobody but me can use the object | |||
/// shared anybody with shared lock can use the object. | |||
/// | |||
class BOOSTER_API recursive_shared_mutex : public noncopyable { | |||
public: | |||
recursive_shared_mutex(); | |||
~recursive_shared_mutex(); | |||
/// | |||
/// Same as unique_lock() | |||
/// | |||
/// \note this function is not recursive | |||
/// | |||
void lock() { unique_lock(); } | |||
/// | |||
/// Acquire a unique lock on the object. \see booster::unique_lock | |||
/// | |||
/// \note this function is not recursive | |||
/// | |||
void unique_lock(); | |||
/// | |||
/// Acquire a shared lock on the object. \see booster::shared_lock | |||
/// | |||
/// \note the shared_lock() member function is recursive, that means that same thread may acquire | |||
/// it multiple times. | |||
/// | |||
void shared_lock(); | |||
/// | |||
/// Release the lock | |||
/// | |||
void unlock(); | |||
private: | |||
struct data; | |||
hold_ptr<data> d; | |||
}; | |||
/// | |||
/// \brief Shared mutex or a.k.a. Read-Write Lock | |||
/// | |||
/// This class provides two options of locking unique - nobody but me can use the object | |||
@@ -141,8 +178,7 @@ namespace booster { | |||
/// | |||
/// Acquire a shared lock on the object. \see booster::shared_lock | |||
/// | |||
/// Note the shared_lock() member function is recursive, that means that same thread may acquire | |||
/// it multiple times. | |||
/// Note this function is not recursive | |||
/// | |||
void shared_lock(); | |||
/// | |||
@@ -139,9 +139,6 @@ namespace booster { | |||
void recursive_mutex::lock() { pthread_mutex_lock(&d->m); } | |||
void recursive_mutex::unlock() { pthread_mutex_unlock(&d->m); } | |||
#ifndef __APPLE__ | |||
/// This is normal implementation | |||
struct shared_mutex::data { pthread_rwlock_t m; }; | |||
shared_mutex::shared_mutex() : d(new data) | |||
{ | |||
@@ -155,9 +152,29 @@ namespace booster { | |||
void shared_mutex::unique_lock() { pthread_rwlock_wrlock(&d->m); } | |||
void shared_mutex::unlock() { pthread_rwlock_unlock(&d->m); } | |||
#ifndef __APPLE__ | |||
// | |||
// This is normal implementation | |||
// | |||
// Same as shared mutex under "Most platforms" | |||
// | |||
struct recursive_shared_mutex::data { pthread_rwlock_t m; }; | |||
recursive_shared_mutex::recursive_shared_mutex() : d(new data) | |||
{ | |||
pthread_rwlock_init(&d->m,0); | |||
} | |||
recursive_shared_mutex::~recursive_shared_mutex() | |||
{ | |||
pthread_rwlock_destroy(&d->m); | |||
} | |||
void recursive_shared_mutex::shared_lock() { pthread_rwlock_rdlock(&d->m); } | |||
void recursive_shared_mutex::unique_lock() { pthread_rwlock_wrlock(&d->m); } | |||
void recursive_shared_mutex::unlock() { pthread_rwlock_unlock(&d->m); } | |||
#else // Darwin has broken recursive RW Lock | |||
struct shared_mutex::data { | |||
struct recursive_shared_mutex::data { | |||
thread_specific_ptr<int> k; | |||
pthread_rwlock_t m; | |||
}; | |||
@@ -174,24 +191,24 @@ namespace booster { | |||
} | |||
} | |||
shared_mutex::shared_mutex() : d(new data) | |||
recursive_shared_mutex::recursive_shared_mutex() : d(new data) | |||
{ | |||
pthread_rwlock_init(&d->m,0); | |||
} | |||
shared_mutex::~shared_mutex() | |||
recursive_shared_mutex::~recursive_shared_mutex() | |||
{ | |||
pthread_rwlock_destroy(&d->m); | |||
} | |||
void shared_mutex::shared_lock() | |||
void recursive_shared_mutex::shared_lock() | |||
{ | |||
int &counter = specific_key(d->k); | |||
if(counter++ == 0) | |||
pthread_rwlock_rdlock(&d->m); | |||
} | |||
void shared_mutex::unique_lock() { | |||
void recursive_shared_mutex::unique_lock() { | |||
pthread_rwlock_wrlock(&d->m); | |||
} | |||
void shared_mutex::unlock() { | |||
void recursive_shared_mutex::unlock() { | |||
int &counter = specific_key(d->k); | |||
if(counter > 1) { | |||
counter --; | |||
@@ -49,6 +49,67 @@ namespace booster { | |||
LeaveCriticalSection(&d->m); | |||
} | |||
struct shared_mutex::data { | |||
mutex lock; | |||
condition_variable can_lock; | |||
int read_lock; | |||
int write_lock; | |||
int pending_lock; | |||
}; | |||
shared_mutex::shared_mutex() : d(new data) | |||
{ | |||
d->read_lock = 0; | |||
d->write_lock = 0; | |||
d->pending_lock = 0; | |||
} | |||
shared_mutex::~shared_mutex() | |||
{ | |||
} | |||
void shared_mutex::shared_lock() | |||
{ | |||
booster::unique_lock<mutex> g(d->lock); | |||
for(;;) { | |||
if(d->write_lock == 0 && d->pending_lock == 0) { | |||
d->read_lock++; | |||
break; | |||
} | |||
else | |||
d->can_lock.wait(g); | |||
} | |||
} | |||
void shared_mutex::unique_lock() | |||
{ | |||
booster::unique_lock<mutex> g(d->lock); | |||
for(;;) { | |||
if(d->write_lock == 0 && d->read_lock==0) { | |||
d->write_lock = 1; | |||
d->pending_lock = 0; | |||
break; | |||
} | |||
else { | |||
if(d->read_lock) | |||
d->pending_lock = 1; | |||
d->can_lock.wait(g); | |||
} | |||
} | |||
} | |||
void shared_mutex::unlock() | |||
{ | |||
booster::unique_lock<mutex> g(d->lock); | |||
if(d->write_lock) { | |||
d->write_lock = 0; | |||
d->pending_lock = 0; | |||
d->can_lock.notify_all(); | |||
} | |||
else if(d->read_lock) { | |||
d->read_lock--; | |||
if(d->read_lock == 0) | |||
d->can_lock.notify_all(); | |||
} | |||
} | |||
namespace details { | |||
struct event { | |||
@@ -48,6 +48,32 @@ namespace booster { | |||
LeaveCriticalSection(&d->m); | |||
} | |||
struct shared_mutex::data { SRWLOCK m; bool ex; }; | |||
shared_mutex::shared_mutex() : d(new data) | |||
{ | |||
d->ex=false; | |||
InitializeSRWLock(&d->m); | |||
} | |||
shared_mutex::~shared_mutex() | |||
{ | |||
} | |||
void shared_mutex::shared_lock() { | |||
AcquireSRWLockShared(&d->m); | |||
} | |||
void shared_mutex::unique_lock() { | |||
AcquireSRWLockExclusive(&d->m); | |||
d->ex=true; | |||
} | |||
void shared_mutex::unlock() { | |||
bool ex = d->ex; | |||
if(ex) { | |||
d->ex=false; | |||
ReleaseSRWLockExclusive(&d->m); | |||
} | |||
else | |||
ReleaseSRWLockShared(&d->m); | |||
} | |||
struct condition_variable::data { CONDITION_VARIABLE c; }; | |||
condition_variable::condition_variable() : d(new data) | |||
@@ -269,7 +269,7 @@ namespace booster { | |||
void recursive_mutex::unlock() { LeaveCriticalSection(&d->m); } | |||
struct shared_mutex::data { | |||
struct recursive_shared_mutex::data { | |||
mutex lock; | |||
condition_variable can_lock; | |||
@@ -287,17 +287,17 @@ namespace booster { | |||
} | |||
}; | |||
shared_mutex::shared_mutex() : d(new data) | |||
recursive_shared_mutex::recursive_shared_mutex() : d(new data) | |||
{ | |||
d->read_lock = 0; | |||
d->write_lock = 0; | |||
d->pending_lock = 0; | |||
memset(&d->recursive_locks,0,sizeof(d->recursive_locks)); | |||
} | |||
shared_mutex::~shared_mutex() | |||
recursive_shared_mutex::~recursive_shared_mutex() | |||
{ | |||
} | |||
void shared_mutex::shared_lock() | |||
void recursive_shared_mutex::shared_lock() | |||
{ | |||
unsigned id = data::id(); | |||
booster::unique_lock<mutex> g(d->lock); | |||
@@ -311,7 +311,7 @@ namespace booster { | |||
} | |||
} | |||
void shared_mutex::unique_lock() | |||
void recursive_shared_mutex::unique_lock() | |||
{ | |||
booster::unique_lock<mutex> g(d->lock); | |||
for(;;) { | |||
@@ -327,7 +327,7 @@ namespace booster { | |||
} | |||
} | |||
} | |||
void shared_mutex::unlock() | |||
void recursive_shared_mutex::unlock() | |||
{ | |||
unsigned id = data::id(); | |||
booster::unique_lock<mutex> g(d->lock); | |||
@@ -100,11 +100,12 @@ struct cond_incrementer { | |||
}; | |||
template<typename ShM> | |||
struct rw_executor { | |||
bool *flag; | |||
bool read; | |||
booster::mutex *flags_mutex; | |||
booster::shared_mutex *mutex; | |||
ShM *mutex; | |||
void operator()() const | |||
{ | |||
for(int i=0;i<20;i++) { | |||
@@ -136,7 +137,7 @@ struct rw_executor { | |||
struct rw_shared_thread { | |||
booster::shared_mutex *lp; | |||
booster::recursive_shared_mutex *lp; | |||
bool *done; | |||
void operator()() const | |||
{ | |||
@@ -154,7 +155,7 @@ struct rw_shared_thread { | |||
}; | |||
struct rw_unique_thread { | |||
booster::shared_mutex *lp; | |||
booster::recursive_shared_mutex *lp; | |||
bool *done; | |||
void operator()() const | |||
{ | |||
@@ -293,7 +294,7 @@ int main() | |||
t2.join(); | |||
t3.join(); | |||
} | |||
std::cout << "Test rw_lock write lock" << std::endl; | |||
std::cout << "Test shared_mutex write lock" << std::endl; | |||
{ | |||
variable = 0; | |||
booster::shared_mutex m; | |||
@@ -304,7 +305,18 @@ int main() | |||
t2.join(); | |||
TEST(variable == 10); | |||
} | |||
std::cout << "Test rw_lock shared/write lock" << std::endl; | |||
std::cout << "Test recursive_shared_mutex write lock" << std::endl; | |||
{ | |||
variable = 0; | |||
booster::shared_mutex m; | |||
incrementer<booster::shared_mutex> inc = { &m }; | |||
booster::thread t1(inc); | |||
booster::thread t2(inc); | |||
t1.join(); | |||
t2.join(); | |||
TEST(variable == 10); | |||
} | |||
std::cout << "Test shared_mutex shared/write lock" << std::endl; | |||
{ | |||
booster::mutex fm; | |||
booster::shared_mutex sm; | |||
@@ -312,9 +324,45 @@ int main() | |||
bool mread_happened = false; | |||
bool write_happened = false; | |||
bool error_occured = false ; | |||
rw_executor exec1 = { flags + 0, true, &fm, &sm }; | |||
rw_executor exec2 = { flags + 1, true, &fm, &sm }; | |||
rw_executor exec3 = { flags + 2, true, &fm, &sm }; | |||
rw_executor<booster::shared_mutex> exec1 = { flags + 0, true, &fm, &sm }; | |||
rw_executor<booster::shared_mutex> exec2 = { flags + 1, true, &fm, &sm }; | |||
rw_executor<booster::shared_mutex> exec3 = { flags + 2, true, &fm, &sm }; | |||
booster::thread t1(exec1); | |||
booster::thread t2(exec2); | |||
booster::thread t3(exec3); | |||
for(int i=0;i<100;i++) { | |||
booster::ptime::millisleep(1); | |||
{ | |||
booster::unique_lock<booster::mutex> l(fm); | |||
if(flags[0] && flags[1]) | |||
mread_happened = true; | |||
if(flags[2]) | |||
write_happened = true; | |||
if((flags[0] || flags[1]) && flags[2]) | |||
error_occured = true; | |||
} | |||
} | |||
t1.join(); | |||
t2.join(); | |||
t3.join(); | |||
TEST(mread_happened); | |||
TEST(write_happened); | |||
TEST(error_occured); | |||
} | |||
std::cout << "Test recursive_shared_mutex shared/write lock" << std::endl; | |||
{ | |||
booster::mutex fm; | |||
booster::recursive_shared_mutex sm; | |||
bool flags[3] = {false,false,false}; | |||
bool mread_happened = false; | |||
bool write_happened = false; | |||
bool error_occured = false ; | |||
rw_executor<booster::recursive_shared_mutex> exec1 = { flags + 0, true, &fm, &sm }; | |||
rw_executor<booster::recursive_shared_mutex> exec2 = { flags + 1, true, &fm, &sm }; | |||
rw_executor<booster::recursive_shared_mutex> exec3 = { flags + 2, true, &fm, &sm }; | |||
booster::thread t1(exec1); | |||
booster::thread t2(exec2); | |||
booster::thread t3(exec3); | |||
@@ -340,9 +388,9 @@ int main() | |||
TEST(write_happened); | |||
TEST(error_occured); | |||
} | |||
std::cout << "Test rw_lock recursive shared lock" << std::endl; | |||
std::cout << "Test recursive_shared_mutex recursive shared lock" << std::endl; | |||
{ | |||
booster::shared_mutex l; | |||
booster::recursive_shared_mutex l; | |||
bool read = false; | |||
bool write = false; | |||
rw_shared_thread t1c = { &l, &read }; | |||
@@ -68,7 +68,7 @@ std::auto_ptr<base_view> generator::create( std::string const &view_name, | |||
// class pool | |||
struct pool::data { | |||
booster::shared_mutex lock; | |||
booster::recursive_shared_mutex lock; | |||
typedef std::map<std::string,generator const *> generators_type; | |||
generators_type generators; | |||
}; | |||
@@ -78,7 +78,7 @@ void pool::add(generator const &g) | |||
generator const *ptr = &g; | |||
std::string name = ptr->name(); | |||
booster::unique_lock<booster::shared_mutex> guard(d->lock); | |||
booster::unique_lock<booster::recursive_shared_mutex> guard(d->lock); | |||
for(data::generators_type::iterator p=d->generators.begin();p!=d->generators.end();++p) { | |||
if(p->second == ptr) | |||
return; | |||
@@ -92,7 +92,7 @@ void pool::remove(generator const &g) | |||
{ | |||
generator const *ptr = &g; | |||
booster::unique_lock<booster::shared_mutex> guard(d->lock); | |||
booster::unique_lock<booster::recursive_shared_mutex> guard(d->lock); | |||
for(data::generators_type::iterator p=d->generators.begin();p!=d->generators.end();++p) { | |||
if(p->second == ptr) { | |||
d->generators.erase(p); | |||
@@ -103,7 +103,7 @@ void pool::remove(generator const &g) | |||
void pool::render(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) | |||
{ | |||
booster::shared_lock<booster::shared_mutex> guard(d->lock); | |||
booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); | |||
data::generators_type::iterator p=d->generators.find(skin); | |||
if(p==d->generators.end()) | |||
throw cppcms_error("cppcms::views::pool: no such skin:" + skin); | |||
@@ -118,7 +118,7 @@ void pool::render(std::string const &skin,std::string const &template_name,std:: | |||
std::vector<std::string> pool::enumerate() | |||
{ | |||
booster::shared_lock<booster::shared_mutex> guard(d->lock); | |||
booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); | |||
std::vector<std::string> all; | |||
all.reserve(d->generators.size()); | |||
for(data::generators_type::iterator p=d->generators.begin(),e=d->generators.end();p!=e;++p) { | |||
@@ -254,7 +254,7 @@ struct manager::data { | |||
bool auto_reload; | |||
std::string default_skin; | |||
std::vector<impl::skin> skins; | |||
booster::shared_mutex lock; | |||
booster::recursive_shared_mutex lock; | |||
data() : auto_reload(false) | |||
{ | |||
} | |||
@@ -320,7 +320,7 @@ void manager::render(std::string const &skin_name,std::string const &template_na | |||
if(d->auto_reload) { | |||
{ // Check if update | |||
bool reload_required = false; | |||
booster::shared_lock<booster::shared_mutex> guard(d->lock); | |||
booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); | |||
for(size_t i=0;i<d->skins.size();i++) { | |||
time_t mtime = impl::get_mtime(d->skins[i].file_name); | |||
if(mtime != d->skins[i].mtime) { | |||
@@ -334,7 +334,7 @@ void manager::render(std::string const &skin_name,std::string const &template_na | |||
} | |||
} | |||
// reload all if needed | |||
booster::unique_lock<booster::shared_mutex> lock(d->lock); | |||
booster::unique_lock<booster::recursive_shared_mutex> lock(d->lock); | |||
for(size_t i=0;i<d->skins.size();i++) { | |||
impl::skin ¤t = d->skins[i]; | |||
time_t mtime = impl::get_mtime(current.file_name); | |||