My Project
Classes | Public Types | Public Member Functions | Static Public Member Functions | Public Attributes
MDL_lock Class Reference
Inheritance diagram for MDL_lock:
MDL_object_lock MDL_scoped_lock

List of all members.


class  Ticket_list

Public Types

typedef unsigned short bitmap_t
typedef Ticket_list::List::Iterator Ticket_iterator

Public Member Functions

bool is_empty () const
virtual const bitmap_t * incompatible_granted_types_bitmap () const =0
virtual const bitmap_t * incompatible_waiting_types_bitmap () const =0
bool has_pending_conflicting_lock (enum_mdl_type type)
bool can_grant_lock (enum_mdl_type type, MDL_context *requstor_ctx, bool ignore_lock_priority) const
void reschedule_waiters ()
void remove_ticket (Ticket_list MDL_lock::*queue, MDL_ticket *ticket)
bool visit_subgraph (MDL_ticket *waiting_ticket, MDL_wait_for_graph_visitor *gvisitor)
virtual bool needs_notification (const MDL_ticket *ticket) const =0
virtual void notify_conflicting_locks (MDL_context *ctx)=0
virtual bitmap_t hog_lock_types_bitmap () const =0
 MDL_lock (const MDL_key *key_arg, MDL_map_partition *map_part)

Static Public Member Functions

static MDL_lockcreate (const MDL_key *key, MDL_map_partition *map_part)
static void destroy (MDL_lock *lock)

Public Attributes

MDL_key key
mysql_prlock_t m_rwlock
Ticket_list m_granted
Ticket_list m_waiting
ulong m_hog_lock_count
uint m_ref_usage
uint m_ref_release
bool m_is_destroyed
ulonglong m_version

Detailed Description

The lock context. Created internally for an acquired lock. For a given name, there exists only one MDL_lock instance, and it exists only when the lock has been granted. Can be seen as an MDL subsystem's version of TABLE_SHARE.

This is an abstract class which lacks information about compatibility rules for lock types. They should be specified in its descendants.

Member Function Documentation

bool MDL_lock::can_grant_lock ( enum_mdl_type  type_arg,
MDL_context requestor_ctx,
bool  ignore_lock_priority 
) const

Check if request for the metadata lock can be satisfied given its current state.

type_argThe requested lock type.
requestor_ctxThe MDL context of the requestor.
ignore_lock_priorityIgnore lock priority.
Return values:
TRUELock request can be satisfied
FALSEThere is some conflicting lock.
In cases then current context already has "stronger" type of lock on the object it will be automatically granted thanks to usage of the MDL_context::find_ticket() method.
MDL_lock * MDL_lock::create ( const MDL_key mdl_key,
MDL_map_partition map_part 
) [inline, static]

Auxiliary functions needed for creation/destruction of MDL_lock objects.

Also chooses an MDL_lock descendant appropriate for object namespace.
bool MDL_lock::has_pending_conflicting_lock ( enum_mdl_type  type)

Check if we have any pending locks which conflict with existing shared lock.

The ticket must match an acquired lock.
TRUE if there is a conflicting lock request, FALSE otherwise.
void MDL_lock::remove_ticket ( Ticket_list MDL_lock::*  list,
MDL_ticket ticket 

Remove a ticket from waiting or pending queue and wakeup up waiters.

Determine waiting contexts which requests for the lock can be satisfied, grant lock to them and wake them up.

Together with MDL_lock::add_ticket() this method implements fair scheduling among requests with the same priority. It tries to grant lock from the head of waiters list, while add_ticket() adds new requests to the back of this list.
bool MDL_lock::visit_subgraph ( MDL_ticket waiting_ticket,
MDL_wait_for_graph_visitor gvisitor 

A fragment of recursive traversal of the wait-for graph in search for deadlocks. Direct the deadlock visitor to all contexts that own the lock the current node in the wait-for graph is waiting for. As long as the initial node is remembered in the visitor, a deadlock is found when the same node is seen twice.

Member Data Documentation

The key of the object (data) being protected.

List of granted tickets for this lock.

Number of times high priority lock requests have been granted while low priority lock requests were waiting.

Partition of MDL_map where the lock is stored.

These three members are used to make it possible to separate the MDL_map_partition::m_mutex mutex and MDL_lock::m_rwlock in MDL_map::find_or_insert() for increased scalability. The 'm_is_destroyed' member is only set by destroyers that have both the MDL_map_partition::m_mutex and MDL_lock::m_rwlock, thus holding any of the mutexes is sufficient to read it. The 'm_ref_usage; is incremented under protection by MDL_map_partition::m_mutex, but when 'm_is_destroyed' is set to TRUE, this member is moved to be protected by the MDL_lock::m_rwlock. This means that the MDL_map::find_or_insert() which only holds the MDL_lock::m_rwlock can compare it to 'm_ref_release' without acquiring MDL_map_partition::m_mutex again and if equal it can also destroy the lock object safely. The 'm_ref_release' is incremented under protection by MDL_lock::m_rwlock. Note since we are only interested in equality of these two counters we don't have to worry about overflows as long as their size is big enough to hold maximum number of concurrent threads on the system.

mysql_prlock_t MDL_lock::m_rwlock

Read-write lock protecting this lock context.

The fact that we use read-write lock prefers readers here is important as deadlock detector won't work correctly otherwise.

For example, imagine that we have following waiters graph:

ctxA -> obj1 -> ctxB -> obj1 -| ^ | |----------------------------|

and both ctxA and ctxB start deadlock detection process:

ctxA read-locks obj1 ctxB read-locks obj2 ctxA goes deeper ctxB goes deeper

Now ctxC comes in who wants to start waiting on obj1, also ctxD comes in who wants to start waiting on obj2.

ctxC tries to write-lock obj1 ctxD tries to write-lock obj2 ctxC is blocked ctxD is blocked

Now ctxA and ctxB resume their search:

ctxA tries to read-lock obj2 ctxB tries to read-lock obj1

If m_rwlock prefers writes (or fair) both ctxA and ctxB would be blocked because of pending write locks from ctxD and ctxC correspondingly. Thus we will get a deadlock in deadlock detector. If m_wrlock prefers readers (actually ignoring pending writers is enough) ctxA and ctxB will continue and no deadlock will occur.

We use the same idea and an additional version counter to support caching of unused MDL_lock object for further re-use. This counter is incremented while holding both MDL_map_partition::m_mutex and MDL_lock::m_rwlock locks each time when a MDL_lock is moved from the partitioned hash to the paritioned unused objects list (or destroyed). A thread, which has found a MDL_lock object for the key in the hash and then released the MDL_map_partition::m_mutex before acquiring the MDL_lock::m_rwlock, can determine that this object was moved to the unused objects list (or destroyed) while it held no locks by comparing the version value which it read while holding the MDL_map_partition::m_mutex with the value read after acquiring the MDL_lock::m_rwlock. Note that since it takes several years to overflow this counter such theoretically possible overflows should not have any practical effects.

Tickets for contexts waiting to acquire a lock.

The documentation for this class was generated from the following file:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines