summaryrefslogtreecommitdiff
path: root/storage/perfschema/pfs_lock.h
diff options
context:
space:
mode:
Diffstat (limited to 'storage/perfschema/pfs_lock.h')
-rw-r--r--storage/perfschema/pfs_lock.h130
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);
}
};