libfoedus-core
FOEDUS Core Library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Retrospective Lock List

Retrospective Lock List (RLL) to avoid deadlocks. More...

Detailed Description

Retrospective Lock List (RLL) to avoid deadlocks.

What it is
Retrospective Lock List (RLL) is a thread-private list of locks the thread took or tried to take in a previous transaction on the same thread. It's used when the previous transaction aborted due to a read-conflict (OCC verification failure) or a lock-conflict (giving up acquiring a write-lock for possible deadlocks). The thread uses the list to pro-actively take locks in a canonical-mode during the next transaction, expecting that the next transaction will need almost the same accesses. This protocol rules out deadlocks, thus dramatically improves concurrency for highly contended workload. It's also very simple and scalable on many-cores.
Canonical Mode
When a thread is trying to acquire a lock, the thread is said to be in a Canonical Mode if and only if all locks the thread has already taken, either read-lock or write-lock, are universally-ordered before the lock the thread is trying to take.
Universal Order of locks
We can use arbitrary ordering scheme as far as it's universally consistent. For example, we can just order by the address of locks (except, we need to be a bit careful on shared memory and virtual addresses). In the example, a lock X is said to be ordered before another lock Y if and only if addr(X) < addr(Y).
Benefits of Canonical Mode
When one is taking a lock in canonical mode, the lock-acquire is guaranteed to be deadlock-free, thus the thread can simply wait for the lock unconditionally.
How we keep Canonical Mode as much as possible
We do a couple of things to keep the transaction in canonical mode as much as possible:
  • Following to the SILO protocol, we always acquire write-locks during pre-commit in a universal order. As far as we don't take read-locks (pure OCC), then we are always in canonical mode.
  • When we want to take a read-lock in a retry of a transaction, we use the retrospective list to take locks who are ordered before the lock the thread is taking. We keep doing this until the end of transaction.
  • When we find ourselves risking deadlocks during pre-commit, we might release some of the locks we have taken so that we go back to canonical mode. This is done in best-effort.
When Canonical Mode is violated
Initially, every thread is trivially in canonical mode because it has taken zero locks. The thread goes out of canonical mode over time for various reasons.
  • The thread has no retrospective lock list, probably because it's the first trial of the xct.
  • The thread has used the retrospective lock list, but the thread for some reason accessed different physical pages/records due to page splits, concurrent updates, and many other reasons.
  • The thread needs to take a page-lock for page-split etc. In this case, to avoid deadlock, we might have to discard retrospective locks.
  • Despite the best-effort retry to keep the canonical mode, there always is a chance that we better give up. This case most likely resulting in aborts and just leaving retrospective list for next trial.
When this happens, still fine, it's not a correctness issue. It just goes back to conditional locking mode with potential timeout, then even in the worst case aborts and leaves the RLL for next run.

What if the thread keeps issuing locks in very different orders and it needs to take read-locks for avoiding verification failure? Umm, then this scheme might keep aborting, but I can't think of any existing scheme that works well for such a case either... except being VERY pessimisitic (take table lock!).

Ohter uses of RLL
We can also use RLL to guide our decision to take read-locks or not. If we got an OCC verification failure, we leave an entry in RLL and take a read-lock in the next run. Or, potentially, we can leave an opposite note saying we probably don't need a read-lock on the record. In many cases the per-page stat is enough to guide us, but this is one option.
Collaboration diagram for Retrospective Lock List:

Classes

struct  foedus::xct::LockEntry::LessThan
 for std::binary_search() etc without creating the object More...
 
struct  foedus::xct::LockEntry
 An entry in CLL and RLL, representing a lock that is taken or will be taken. More...
 
class  foedus::xct::RetrospectiveLockList
 Retrospective lock list. More...
 
class  foedus::xct::CurrentLockList
 Sorted list of all locks, either read-lock or write-lock, taken in the current run. More...
 
struct  foedus::xct::CurrentLockListIteratorForWriteSet
 An iterator over CurrentLockList to find entries along with sorted write-set. More...
 

Functions

bool foedus::thread::Thread::is_hot_page (const storage::Page *page) const
 

Function Documentation

bool foedus::thread::Thread::is_hot_page ( const storage::Page page) const
Returns
whether the given page had enough aborts to justify pessimisitic locking.

Definition at line 142 of file thread.cpp.

References foedus::thread::ThreadPimpl::current_xct_, foedus::storage::Page::get_header(), foedus::xct::Xct::get_hot_threshold_for_this_xct(), foedus::storage::PageHeader::hotness_, and foedus::assorted::ProbCounter::value_.

Referenced by foedus::xct::Xct::on_record_read_take_locks_if_needed().

142  {
143  const uint16_t threshold = pimpl_->current_xct_.get_hot_threshold_for_this_xct();
144  return page->get_header().hotness_.value_ >= threshold;
145 }
uint16_t get_hot_threshold_for_this_xct() const
Definition: xct.hpp:128
xct::Xct current_xct_
Current transaction this thread is conveying.

Here is the call graph for this function:

Here is the caller graph for this function: