diff options
Diffstat (limited to 'storage/perfschema/pfs_lock.h')
-rw-r--r-- | storage/perfschema/pfs_lock.h | 130 |
1 files changed, 103 insertions, 27 deletions
diff --git a/storage/perfschema/pfs_lock.h b/storage/perfschema/pfs_lock.h index b74131c79e1..836c64ec992 100644 --- a/storage/perfschema/pfs_lock.h +++ b/storage/perfschema/pfs_lock.h @@ -28,8 +28,15 @@ Performance schema internal locks (declarations). */ +#include "my_global.h" + #include "pfs_atomic.h" +/* to cause bugs, testing */ +// #define MEM(X) std::memory_order_relaxed +/* correct code */ +#define MEM(X) X + /** @addtogroup Performance_schema_buffers @{ @@ -61,6 +68,16 @@ #define STATE_MASK 0x00000003 #define VERSION_INC 4 +struct pfs_optimistic_state +{ + uint32 m_version_state; +}; + +struct pfs_dirty_state +{ + uint32 m_version_state; +}; + /** A 'lock' protecting performance schema internal buffers. This lock is used to mark the state of a record. @@ -86,19 +103,34 @@ struct pfs_lock The version number is stored in the high 30 bits. The state is stored in the low 2 bits. */ - volatile uint32 m_version_state; + uint32 m_version_state; + + uint32 copy_version_state() + { + uint32 copy; + + copy= m_version_state; /* dirty read */ + + return copy; + } /** Returns true if the record is free. */ bool is_free(void) { - uint32 copy= m_version_state; /* non volatile copy, and dirty read */ + uint32 copy; + + copy= PFS_atomic::load_u32(&m_version_state); + return ((copy & STATE_MASK) == PFS_LOCK_FREE); } /** Returns true if the record contains values that can be read. */ bool is_populated(void) { - uint32 copy= m_version_state; /* non volatile copy, and dirty read */ + uint32 copy; + + copy= PFS_atomic::load_u32(&m_version_state); + return ((copy & STATE_MASK) == PFS_LOCK_ALLOCATED); } @@ -108,13 +140,28 @@ struct pfs_lock Only one writer will succeed to acquire the record. @return true if the operation succeed */ - bool free_to_dirty(void) + bool free_to_dirty(pfs_dirty_state *copy_ptr) { - 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; + uint32 old_val; + + old_val= PFS_atomic::load_u32(&m_version_state); + + if ((old_val & STATE_MASK) != PFS_LOCK_FREE) + { + return false; + } - return (PFS_atomic::cas_u32(&m_version_state, &old_val, new_val)); + uint32 new_val= (old_val & VERSION_MASK) + PFS_LOCK_DIRTY; + bool pass; + + pass= PFS_atomic::cas_u32(&m_version_state, &old_val, new_val); + + if (pass) + { + copy_ptr->m_version_state= new_val; + } + + return pass; } /** @@ -122,15 +169,18 @@ struct pfs_lock This transition should be executed by the writer that owns the record, before the record is modified. */ - void allocated_to_dirty(void) + void allocated_to_dirty(pfs_dirty_state *copy_ptr) { - uint32 copy= PFS_atomic::load_u32(&m_version_state); + uint32 copy= copy_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); + + copy_ptr->m_version_state= new_val; } /** @@ -138,13 +188,13 @@ struct pfs_lock This transition should be executed by the writer that owns the record, after the record is in a state ready to be read. */ - void dirty_to_allocated(void) + void dirty_to_allocated(const pfs_dirty_state *copy) { - uint32 copy= PFS_atomic::load_u32(&m_version_state); /* Make sure the record was DIRTY. */ - DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_DIRTY); + DBUG_ASSERT((copy->m_version_state & STATE_MASK) == PFS_LOCK_DIRTY); /* Increment the version, set the ALLOCATED state */ - uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED; + uint32 new_val= (copy->m_version_state & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED; + PFS_atomic::store_u32(&m_version_state, new_val); } @@ -156,35 +206,52 @@ struct pfs_lock void set_allocated(void) { /* Do not set the version to 0, read the previous value. */ - uint32 copy= PFS_atomic::load_u32(&m_version_state); + uint32 copy= copy_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); } /** Initialize a lock to dirty. */ - void set_dirty(void) + void set_dirty(pfs_dirty_state *copy_ptr) { /* 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 DIRTY state */ uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_DIRTY; PFS_atomic::store_u32(&m_version_state, new_val); + + copy_ptr->m_version_state= new_val; + } + + /** + Initialize a lock to dirty. + */ + void set_dirty(pfs_dirty_state *copy_ptr) + { + /* 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 DIRTY state */ + uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_DIRTY; + PFS_atomic::store_u32(&m_version_state, new_val); + + copy_ptr->m_version_state= new_val; } /** Execute a dirty to free transition. This transition should be executed by the writer that owns the record. */ - void dirty_to_free(void) + void dirty_to_free(const pfs_dirty_state *copy) { - uint32 copy= PFS_atomic::load_u32(&m_version_state); /* Make sure the record was DIRTY. */ - DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_DIRTY); + DBUG_ASSERT((copy->m_version_state & STATE_MASK) == PFS_LOCK_DIRTY); /* Keep the same version, set the FREE state */ - uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_FREE; + uint32 new_val= (copy->m_version_state & VERSION_MASK) + PFS_LOCK_FREE; + PFS_atomic::store_u32(&m_version_state, new_val); } @@ -198,22 +265,22 @@ struct pfs_lock If this record is not in the ALLOCATED state and the caller is trying to free it, this is a bug: the caller is confused, and potentially damaging data owned by another thread or object. - The correct assert to use here to guarantee data integrity is simply: - DBUG_ASSERT(m_state == PFS_LOCK_ALLOCATED); */ - uint32 copy= PFS_atomic::load_u32(&m_version_state); + uint32 copy= copy_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); } /** Start an optimistic read operation. + @param [out] copy Saved lock state @sa end_optimist_lock. */ - void begin_optimistic_lock(struct pfs_lock *copy) + void begin_optimistic_lock(struct pfs_optimistic_state *copy) { copy->m_version_state= PFS_atomic::load_u32(&m_version_state); } @@ -221,16 +288,21 @@ struct pfs_lock /** End an optimistic read operation. @sa begin_optimist_lock. + @param copy Saved lock state @return true if the data read is safe to use. */ - bool end_optimistic_lock(struct pfs_lock *copy) + bool end_optimistic_lock(const struct pfs_optimistic_state *copy) { + uint32 version_state; + /* Check there was valid data to look at. */ if ((copy->m_version_state & STATE_MASK) != PFS_LOCK_ALLOCATED) return false; + version_state= PFS_atomic::load_u32(&m_version_state); + /* Check the version + state has not changed. */ - if (copy->m_version_state != PFS_atomic::load_u32(&m_version_state)) + if (copy->m_version_state != version_state) return false; return true; @@ -238,7 +310,11 @@ struct pfs_lock uint32 get_version() { - return (PFS_atomic::load_u32(&m_version_state) & VERSION_MASK); + uint32 version_state; + + version_state= PFS_atomic::load_u32(&m_version_state); + + return (version_state & VERSION_MASK); } }; |