diff options
author | Michael Widenius <monty@askmonty.org> | 2013-03-26 00:03:13 +0200 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2013-03-26 00:03:13 +0200 |
commit | 068c61978e3a81836d52b8caf11e044290159ad1 (patch) | |
tree | 2cbca861ab2cebe3bd99379ca9668bb483ca0d2a /storage/perfschema/pfs_lock.h | |
parent | 35bc8f9f4353b64da215e52ff6f1612a8ce66f43 (diff) | |
download | mariadb-git-068c61978e3a81836d52b8caf11e044290159ad1.tar.gz |
Temporary commit of 10.0-merge
Diffstat (limited to 'storage/perfschema/pfs_lock.h')
-rw-r--r-- | storage/perfschema/pfs_lock.h | 107 |
1 files changed, 70 insertions, 37 deletions
diff --git a/storage/perfschema/pfs_lock.h b/storage/perfschema/pfs_lock.h index 65937e94ece..09efecd1c5f 100644 --- a/storage/perfschema/pfs_lock.h +++ b/storage/perfschema/pfs_lock.h @@ -33,7 +33,7 @@ Values of a free record should not be read by a reader. Writers can concurrently attempt to allocate a free record. */ -#define PFS_LOCK_FREE 0 +#define PFS_LOCK_FREE 0x00 /** State of a dirty record. Values of a dirty record should not be read by a reader, @@ -41,14 +41,18 @@ Only one writer, the writer which owns the record, should modify the record content. */ -#define PFS_LOCK_DIRTY 1 +#define PFS_LOCK_DIRTY 0x01 /** State of an allocated record. Values of an allocated record are safe to read by a reader. A writer may modify some but not all properties of the record: only modifying values that can never cause the reader to crash is allowed. */ -#define PFS_LOCK_ALLOCATED 2 +#define PFS_LOCK_ALLOCATED 0x02 + +#define VERSION_MASK 0xFFFFFFFC +#define STATE_MASK 0x00000003 +#define VERSION_INC 4 /** A 'lock' protecting performance schema internal buffers. @@ -60,15 +64,11 @@ struct pfs_lock { /** - The record internal state. + The record internal version and state @sa PFS_LOCK_FREE @sa PFS_LOCK_DIRTY @sa PFS_LOCK_ALLOCATED - */ - volatile int32 m_state; - /** - The record internal version number. - This version number is to transform the 'ABA' problem + The version number is to transform the 'ABA' problem (see http://en.wikipedia.org/wiki/ABA_problem) into an 'A(n)BA(n + 1)' problem, where 'n' is the m_version number. When the performance schema instrumentation deletes a record, @@ -76,21 +76,23 @@ struct pfs_lock the version number is incremented, so that a reader can detect that the record was changed. Note that the version number is never reset to zero when a new record is created. + The version number is stored in the high 30 bits. + The state is stored in the low 2 bits. */ - volatile uint32 m_version; + volatile uint32 m_version_state; /** Returns true if the record is free. */ bool is_free(void) { - /* This is a dirty read */ - return (m_state == PFS_LOCK_FREE); + uint32 copy= m_version_state; /* non volatile copy, and dirty read */ + return ((copy & STATE_MASK) == PFS_LOCK_FREE); } /** Returns true if the record contains values that can be read. */ bool is_populated(void) { - int32 copy= m_state; /* non volatile copy, and dirty read */ - return (copy == PFS_LOCK_ALLOCATED); + uint32 copy= m_version_state; /* non volatile copy, and dirty read */ + return ((copy & STATE_MASK) == PFS_LOCK_ALLOCATED); } /** @@ -101,10 +103,11 @@ struct pfs_lock */ bool free_to_dirty(void) { - int32 old_state= PFS_LOCK_FREE; - int32 new_state= PFS_LOCK_DIRTY; + uint32 copy= m_version_state; /* non volatile copy, and dirty read */ + uint32 old_val= (copy & VERSION_MASK) + PFS_LOCK_FREE; + uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_DIRTY; - return (PFS_atomic::cas_32(&m_state, &old_state, new_state)); + return (PFS_atomic::cas_u32(&m_version_state, &old_val, new_val)); } /** @@ -114,8 +117,13 @@ struct pfs_lock */ void allocated_to_dirty(void) { - DBUG_ASSERT(m_state == PFS_LOCK_ALLOCATED); - PFS_atomic::store_32(&m_state, PFS_LOCK_DIRTY); + uint32 copy= PFS_atomic::load_u32(&m_version_state); + /* Make sure the record was ALLOCATED. */ + DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_ALLOCATED); + /* Keep the same version, set the DIRTY state */ + uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_DIRTY; + /* We own the record, no need to use compare and swap. */ + PFS_atomic::store_u32(&m_version_state, new_val); } /** @@ -125,9 +133,26 @@ struct pfs_lock */ void dirty_to_allocated(void) { - DBUG_ASSERT(m_state == PFS_LOCK_DIRTY); - PFS_atomic::add_u32(&m_version, 1); - PFS_atomic::store_32(&m_state, PFS_LOCK_ALLOCATED); + uint32 copy= PFS_atomic::load_u32(&m_version_state); + /* Make sure the record was DIRTY. */ + DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_DIRTY); + /* Increment the version, set the ALLOCATED state */ + uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED; + PFS_atomic::store_u32(&m_version_state, new_val); + } + + /** + Initialize a lock to allocated. + This transition should be executed by the writer that owns the record and the lock, + after the record is in a state ready to be read. + */ + void set_allocated(void) + { + /* Do not set the version to 0, read the previous value. */ + uint32 copy= PFS_atomic::load_u32(&m_version_state); + /* Increment the version, set the ALLOCATED state */ + uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED; + PFS_atomic::store_u32(&m_version_state, new_val); } /** @@ -136,8 +161,12 @@ struct pfs_lock */ void dirty_to_free(void) { - DBUG_ASSERT(m_state == PFS_LOCK_DIRTY); - PFS_atomic::store_32(&m_state, PFS_LOCK_FREE); + uint32 copy= PFS_atomic::load_u32(&m_version_state); + /* Make sure the record was DIRTY. */ + DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_DIRTY); + /* Keep the same version, set the FREE state */ + uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_FREE; + PFS_atomic::store_u32(&m_version_state, new_val); } /** @@ -153,8 +182,12 @@ struct pfs_lock The correct assert to use here to guarantee data integrity is simply: DBUG_ASSERT(m_state == PFS_LOCK_ALLOCATED); */ - DBUG_ASSERT(m_state == PFS_LOCK_ALLOCATED); - PFS_atomic::store_32(&m_state, PFS_LOCK_FREE); + uint32 copy= PFS_atomic::load_u32(&m_version_state); + /* Make sure the record was ALLOCATED. */ + DBUG_ASSERT(((copy & STATE_MASK) == PFS_LOCK_ALLOCATED)); + /* Keep the same version, set the FREE state */ + uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_FREE; + PFS_atomic::store_u32(&m_version_state, new_val); } /** @@ -163,8 +196,7 @@ struct pfs_lock */ void begin_optimistic_lock(struct pfs_lock *copy) { - copy->m_version= PFS_atomic::load_u32(&m_version); - copy->m_state= PFS_atomic::load_32(&m_state); + copy->m_version_state= PFS_atomic::load_u32(&m_version_state); } /** @@ -174,19 +206,20 @@ struct pfs_lock */ bool end_optimistic_lock(struct pfs_lock *copy) { - /* - return true if: - - the version + state has not changed - - and there was valid data to look at - */ - return ((copy->m_version == PFS_atomic::load_u32(&m_version)) && - (copy->m_state == PFS_atomic::load_32(&m_state)) && - (copy->m_state == PFS_LOCK_ALLOCATED)); + /* Check there was valid data to look at. */ + if ((copy->m_version_state & STATE_MASK) != PFS_LOCK_ALLOCATED) + return false; + + /* Check the version + state has not changed. */ + if (copy->m_version_state != PFS_atomic::load_u32(&m_version_state)) + return false; + + return true; } uint32 get_version() { - return PFS_atomic::load_u32(&m_version); + return (PFS_atomic::load_u32(&m_version_state) & VERSION_MASK); } }; |