2013-04-17 00:00:59 -04:00
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
2013-04-16 23:57:47 -04:00
# ifndef TOKU_RWLOCK_H
# define TOKU_RWLOCK_H
2013-04-16 23:57:48 -04:00
# ident "$Id$"
2013-04-17 00:00:59 -04:00
# ident "Copyright (c) 2007-2012 Tokutek Inc. All rights reserved."
2013-04-16 23:57:48 -04:00
# ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11 / 760379 and to the patents and / or patent applications resulting from it."
2013-04-16 23:57:53 -04:00
# include <toku_assert.h>
2013-04-16 23:57:48 -04:00
2013-04-16 23:59:09 -04:00
2013-04-16 23:57:47 -04:00
//Use case:
2013-04-16 23:57:17 -04:00
// A read lock is acquired by threads that get and pin an entry in the
// cachetable. A write lock is acquired by the writer thread when an entry
// is evicted from the cachetable and is being written storage.
2013-04-16 23:57:47 -04:00
//Use case:
// General purpose reader writer lock with properties:
2013-04-16 23:57:17 -04:00
// 1. multiple readers, no writers
// 2. one writer at a time
// 3. pending writers have priority over pending readers
// An external mutex must be locked when using these functions. An alternate
// design would bury a mutex into the rwlock itself. While this may
// increase parallelism at the expense of single thread performance, we
// are experimenting with a single higher level lock.
2013-04-16 23:57:47 -04:00
typedef struct rwlock * RWLOCK ;
struct rwlock {
int reader ; // the number of readers
int want_read ; // the number of blocked readers
2013-04-17 00:00:31 -04:00
toku_cond_t wait_read ;
2013-04-16 23:57:17 -04:00
int writer ; // the number of writers
int want_write ; // the number of blocked writers
2013-04-17 00:00:31 -04:00
toku_cond_t wait_write ;
2013-04-17 00:00:58 -04:00
toku_cond_t * wait_users_go_to_zero ;
2013-04-16 23:57:17 -04:00
} ;
2013-04-17 00:00:58 -04:00
// returns: the sum of the number of readers, pending readers, writers, and
// pending writers
static inline int rwlock_users ( RWLOCK rwlock ) {
return rwlock - > reader + rwlock - > want_read + rwlock - > writer + rwlock - > want_write ;
}
2013-04-16 23:57:17 -04:00
// initialize a read write lock
2013-04-16 23:57:20 -04:00
static __attribute__ ( ( __unused__ ) )
void
2013-04-16 23:57:47 -04:00
rwlock_init ( RWLOCK rwlock ) {
rwlock - > reader = rwlock - > want_read = 0 ;
2013-04-17 00:00:31 -04:00
toku_cond_init ( & rwlock - > wait_read , 0 ) ;
2013-04-16 23:57:17 -04:00
rwlock - > writer = rwlock - > want_write = 0 ;
2013-04-17 00:00:31 -04:00
toku_cond_init ( & rwlock - > wait_write , 0 ) ;
2013-04-17 00:00:58 -04:00
rwlock - > wait_users_go_to_zero = NULL ;
2013-04-16 23:57:17 -04:00
}
// destroy a read write lock
2013-04-16 23:57:20 -04:00
static __attribute__ ( ( __unused__ ) )
void
2013-04-16 23:57:47 -04:00
rwlock_destroy ( RWLOCK rwlock ) {
assert ( rwlock - > reader = = 0 & & rwlock - > want_read = = 0 ) ;
2013-04-16 23:57:17 -04:00
assert ( rwlock - > writer = = 0 & & rwlock - > want_write = = 0 ) ;
2013-04-17 00:00:31 -04:00
toku_cond_destroy ( & rwlock - > wait_read ) ;
toku_cond_destroy ( & rwlock - > wait_write ) ;
2013-04-16 23:57:17 -04:00
}
// obtain a read lock
// expects: mutex is locked
2013-04-17 00:00:31 -04:00
static inline void rwlock_read_lock ( RWLOCK rwlock , toku_mutex_t * mutex ) {
2013-04-17 00:00:58 -04:00
assert ( ! rwlock - > wait_users_go_to_zero ) ;
2013-04-16 23:57:17 -04:00
if ( rwlock - > writer | | rwlock - > want_write ) {
2013-04-16 23:57:47 -04:00
rwlock - > want_read + + ;
2013-04-16 23:57:17 -04:00
while ( rwlock - > writer | | rwlock - > want_write ) {
2013-04-17 00:00:31 -04:00
toku_cond_wait ( & rwlock - > wait_read , mutex ) ;
2013-04-16 23:57:17 -04:00
}
2013-04-16 23:57:47 -04:00
rwlock - > want_read - - ;
2013-04-16 23:57:17 -04:00
}
2013-04-16 23:57:47 -04:00
rwlock - > reader + + ;
2013-04-16 23:57:17 -04:00
}
// release a read lock
// expects: mutex is locked
2013-04-16 23:57:47 -04:00
static inline void rwlock_read_unlock ( RWLOCK rwlock ) {
2013-04-16 23:59:04 -04:00
assert ( rwlock - > reader > 0 ) ;
assert ( rwlock - > writer = = 0 ) ;
2013-04-16 23:57:47 -04:00
rwlock - > reader - - ;
if ( rwlock - > reader = = 0 & & rwlock - > want_write ) {
2013-04-17 00:00:31 -04:00
toku_cond_signal ( & rwlock - > wait_write ) ;
2013-04-16 23:57:17 -04:00
}
2013-04-17 00:00:58 -04:00
if ( rwlock - > wait_users_go_to_zero & & rwlock_users ( rwlock ) = = 0 ) {
toku_cond_signal ( rwlock - > wait_users_go_to_zero ) ;
}
2013-04-16 23:57:17 -04:00
}
// obtain a write lock
// expects: mutex is locked
2013-04-17 00:00:31 -04:00
static inline void rwlock_write_lock ( RWLOCK rwlock , toku_mutex_t * mutex ) {
2013-04-17 00:00:58 -04:00
assert ( ! rwlock - > wait_users_go_to_zero ) ;
2013-04-16 23:57:47 -04:00
if ( rwlock - > reader | | rwlock - > writer ) {
2013-04-16 23:57:17 -04:00
rwlock - > want_write + + ;
2013-04-16 23:57:47 -04:00
while ( rwlock - > reader | | rwlock - > writer ) {
2013-04-17 00:00:31 -04:00
toku_cond_wait ( & rwlock - > wait_write , mutex ) ;
2013-04-16 23:57:17 -04:00
}
rwlock - > want_write - - ;
}
rwlock - > writer + + ;
}
// release a write lock
// expects: mutex is locked
2013-04-16 23:57:47 -04:00
static inline void rwlock_write_unlock ( RWLOCK rwlock ) {
2013-04-16 23:59:04 -04:00
assert ( rwlock - > reader = = 0 ) ;
assert ( rwlock - > writer = = 1 ) ;
2013-04-16 23:57:17 -04:00
rwlock - > writer - - ;
2013-04-17 00:00:58 -04:00
if ( rwlock - > want_write ) {
toku_cond_signal ( & rwlock - > wait_write ) ;
} else if ( rwlock - > want_read ) {
toku_cond_broadcast ( & rwlock - > wait_read ) ;
2013-04-17 00:00:58 -04:00
}
if ( rwlock - > wait_users_go_to_zero & & rwlock_users ( rwlock ) = = 0 ) {
toku_cond_signal ( rwlock - > wait_users_go_to_zero ) ;
2013-04-16 23:57:17 -04:00
}
}
// returns: the number of readers
2013-04-16 23:57:47 -04:00
static inline int rwlock_readers ( RWLOCK rwlock ) {
return rwlock - > reader ;
2013-04-16 23:57:17 -04:00
}
2013-04-16 23:59:06 -04:00
// returns: the number of readers who are waiting for the lock
static inline int rwlock_blocked_readers ( RWLOCK rwlock ) {
return rwlock - > want_read ;
}
// returns: the number of writers who are waiting for the lock
2013-04-16 23:57:17 -04:00
2013-04-16 23:59:06 -04:00
static inline int rwlock_blocked_writers ( RWLOCK rwlock ) {
return rwlock - > want_write ;
}
// returns: the number of writers
2013-04-16 23:57:47 -04:00
static inline int rwlock_writers ( RWLOCK rwlock ) {
2013-04-16 23:57:17 -04:00
return rwlock - > writer ;
}
2013-04-17 00:01:03 -04:00
static inline bool rwlock_write_will_block ( RWLOCK rwlock ) {
return ( rwlock - > writer > 0 | | rwlock - > reader > 0 ) ;
}
static inline int rwlock_read_will_block ( RWLOCK rwlock ) {
return ( rwlock - > writer > 0 | | rwlock - > want_write > 0 ) ;
}
2013-04-17 00:00:58 -04:00
static inline void rwlock_wait_for_users (
RWLOCK rwlock ,
2013-04-17 00:00:59 -04:00
toku_mutex_t * mutex
2013-04-17 00:00:58 -04:00
)
{
assert ( ! rwlock - > wait_users_go_to_zero ) ;
2013-04-17 00:00:59 -04:00
toku_cond_t cond ;
toku_cond_init ( & cond , NULL ) ;
2013-04-17 00:00:58 -04:00
while ( rwlock_users ( rwlock ) > 0 ) {
2013-04-17 00:00:59 -04:00
rwlock - > wait_users_go_to_zero = & cond ;
toku_cond_wait ( & cond , mutex ) ;
2013-04-17 00:00:58 -04:00
}
rwlock - > wait_users_go_to_zero = NULL ;
2013-04-17 00:00:59 -04:00
toku_cond_destroy ( & cond ) ;
2013-04-16 23:57:17 -04:00
}
2013-04-16 23:57:47 -04:00
2013-04-16 23:59:09 -04:00
2013-04-16 23:57:47 -04:00
# endif