| 1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
| 2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: |
| 3 | #ident "$Id$" |
| 4 | /*====== |
| 5 | This file is part of PerconaFT. |
| 6 | |
| 7 | |
| 8 | Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. |
| 9 | |
| 10 | PerconaFT is free software: you can redistribute it and/or modify |
| 11 | it under the terms of the GNU General Public License, version 2, |
| 12 | as published by the Free Software Foundation. |
| 13 | |
| 14 | PerconaFT is distributed in the hope that it will be useful, |
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | GNU General Public License for more details. |
| 18 | |
| 19 | You should have received a copy of the GNU General Public License |
| 20 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
| 21 | |
| 22 | ---------------------------------------- |
| 23 | |
| 24 | PerconaFT is free software: you can redistribute it and/or modify |
| 25 | it under the terms of the GNU Affero General Public License, version 3, |
| 26 | as published by the Free Software Foundation. |
| 27 | |
| 28 | PerconaFT is distributed in the hope that it will be useful, |
| 29 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 31 | GNU Affero General Public License for more details. |
| 32 | |
| 33 | You should have received a copy of the GNU Affero General Public License |
| 34 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
| 35 | ======= */ |
| 36 | |
| 37 | #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." |
| 38 | |
| 39 | #pragma once |
| 40 | |
| 41 | #include <db.h> |
| 42 | |
| 43 | #include "portability/toku_pthread.h" |
| 44 | |
| 45 | #include "locktree/locktree.h" |
| 46 | #include "locktree/txnid_set.h" |
| 47 | #include "locktree/wfg.h" |
| 48 | #include "ft/comparator.h" |
| 49 | |
| 50 | namespace toku { |
| 51 | |
| 52 | // A lock request contains the db, the key range, the lock type, and |
| 53 | // the transaction id that describes a potential row range lock. |
| 54 | // |
| 55 | // the typical use case is: |
| 56 | // - initialize a lock request |
| 57 | // - start to try to acquire the lock |
| 58 | // - do something else |
| 59 | // - wait for the lock request to be resolved on a timed condition |
| 60 | // - destroy the lock request |
| 61 | // a lock request is resolved when its state is no longer pending, or |
| 62 | // when it becomes granted, or timedout, or deadlocked. when resolved, the |
| 63 | // state of the lock request is changed and any waiting threads are awakened. |
| 64 | |
| 65 | class lock_request { |
| 66 | public: |
| 67 | enum type { |
| 68 | UNKNOWN, |
| 69 | READ, |
| 70 | WRITE |
| 71 | }; |
| 72 | |
| 73 | // effect: Initializes a lock request. |
| 74 | void create(void); |
| 75 | |
| 76 | // effect: Destroys a lock request. |
| 77 | void destroy(void); |
| 78 | |
| 79 | // effect: Resets the lock request parameters, allowing it to be reused. |
| 80 | // requires: Lock request was already created at some point |
| 81 | void set(locktree *lt, TXNID txnid, const DBT *left_key, const DBT *right_key, type lock_type, bool big_txn, void * = nullptr); |
| 82 | |
| 83 | // effect: Tries to acquire a lock described by this lock request. |
| 84 | // returns: The return code of locktree::acquire_[write,read]_lock() |
| 85 | // or DB_LOCK_DEADLOCK if this request would end up deadlocked. |
| 86 | int start(void); |
| 87 | |
| 88 | // effect: Sleeps until either the request is granted or the wait time expires. |
| 89 | // returns: The return code of locktree::acquire_[write,read]_lock() |
| 90 | // or simply DB_LOCK_NOTGRANTED if the wait time expired. |
| 91 | int wait(uint64_t wait_time_ms); |
| 92 | int wait(uint64_t wait_time_ms, uint64_t killed_time_ms, int (*killed_callback)(void), |
| 93 | void (*lock_wait_callback)(void *, TXNID, TXNID) = nullptr); |
| 94 | |
| 95 | // return: left end-point of the lock range |
| 96 | const DBT *get_left_key(void) const; |
| 97 | |
| 98 | // return: right end-point of the lock range |
| 99 | const DBT *get_right_key(void) const; |
| 100 | |
| 101 | // return: the txnid waiting for a lock |
| 102 | TXNID get_txnid(void) const; |
| 103 | |
| 104 | // return: when this lock request started, as milliseconds from epoch |
| 105 | uint64_t get_start_time(void) const; |
| 106 | |
| 107 | // return: which txnid is blocking this request (there may be more, though) |
| 108 | TXNID get_conflicting_txnid(void) const; |
| 109 | |
| 110 | // effect: Retries all of the lock requests for the given locktree. |
| 111 | // Any lock requests successfully restarted is completed and woken |
| 112 | // up. |
| 113 | // The rest remain pending. |
| 114 | static void retry_all_lock_requests( |
| 115 | locktree *lt, |
| 116 | void (*lock_wait_callback)(void *, TXNID, TXNID) = nullptr, |
| 117 | void (*after_retry_test_callback)(void) = nullptr); |
| 118 | static void retry_all_lock_requests_info(lt_lock_request_info *info, GrowableArray<TXNID> *collector); |
| 119 | |
| 120 | void set_start_test_callback(void (*f)(void)); |
| 121 | void set_start_before_pending_test_callback(void (*f)(void)); |
| 122 | void set_retry_test_callback(void (*f)(void)); |
| 123 | |
| 124 | void *(void) const; |
| 125 | |
| 126 | void kill_waiter(void); |
| 127 | static void kill_waiter(locktree *lt, void *); |
| 128 | |
| 129 | private: |
| 130 | enum state { |
| 131 | UNINITIALIZED, |
| 132 | INITIALIZED, |
| 133 | PENDING, |
| 134 | COMPLETE, |
| 135 | DESTROYED, |
| 136 | }; |
| 137 | |
| 138 | // The keys for a lock request are stored "unowned" in m_left_key |
| 139 | // and m_right_key. When the request is about to go to sleep, it |
| 140 | // copies these keys and stores them in m_left_key_copy etc and |
| 141 | // sets the temporary pointers to null. |
| 142 | TXNID m_txnid; |
| 143 | TXNID m_conflicting_txnid; |
| 144 | uint64_t m_start_time; |
| 145 | const DBT *m_left_key; |
| 146 | const DBT *m_right_key; |
| 147 | DBT m_left_key_copy; |
| 148 | DBT m_right_key_copy; |
| 149 | |
| 150 | // The lock request type and associated locktree |
| 151 | type m_type; |
| 152 | locktree *m_lt; |
| 153 | |
| 154 | // If the lock request is in the completed state, then its |
| 155 | // final return value is stored in m_complete_r |
| 156 | int m_complete_r; |
| 157 | state m_state; |
| 158 | |
| 159 | toku_cond_t m_wait_cond; |
| 160 | |
| 161 | bool m_big_txn; |
| 162 | |
| 163 | // the lock request info state stored in the |
| 164 | // locktree that this lock request is for. |
| 165 | struct lt_lock_request_info *m_info; |
| 166 | |
| 167 | void *; |
| 168 | |
| 169 | // effect: tries again to acquire the lock described by this lock request |
| 170 | // returns: 0 if retrying the request succeeded and is now complete |
| 171 | int retry(GrowableArray<TXNID> *conflict_collector); |
| 172 | |
| 173 | void complete(int complete_r); |
| 174 | |
| 175 | // effect: Finds another lock request by txnid. |
| 176 | // requires: The lock request info mutex is held |
| 177 | lock_request *find_lock_request(const TXNID &txnid); |
| 178 | |
| 179 | // effect: Insert this lock request into the locktree's set. |
| 180 | // requires: the locktree's mutex is held |
| 181 | void insert_into_lock_requests(void); |
| 182 | |
| 183 | // effect: Removes this lock request from the locktree's set. |
| 184 | // requires: The lock request info mutex is held |
| 185 | void remove_from_lock_requests(void); |
| 186 | |
| 187 | // effect: Asks this request's locktree which txnids are preventing |
| 188 | // us from getting the lock described by this request. |
| 189 | // returns: conflicts is populated with the txnid's that this request |
| 190 | // is blocked on |
| 191 | void get_conflicts(txnid_set *conflicts); |
| 192 | |
| 193 | // effect: Builds a wait-for-graph for this lock request and the given conflict set |
| 194 | void build_wait_graph(wfg *wait_graph, const txnid_set &conflicts); |
| 195 | |
| 196 | // returns: True if this lock request is in deadlock with the given conflicts set |
| 197 | bool deadlock_exists(const txnid_set &conflicts); |
| 198 | |
| 199 | void copy_keys(void); |
| 200 | |
| 201 | static int find_by_txnid(lock_request *const &request, const TXNID &txnid); |
| 202 | |
| 203 | // Report list of conflicts to lock wait callback. |
| 204 | static void report_waits(GrowableArray<TXNID> *wait_conflicts, |
| 205 | void (*lock_wait_callback)(void *, TXNID, TXNID)); |
| 206 | void add_conflicts_to_waits(txnid_set *conflicts, GrowableArray<TXNID> *wait_conflicts); |
| 207 | |
| 208 | void (*m_start_test_callback)(void); |
| 209 | void (*m_start_before_pending_test_callback)(void); |
| 210 | void (*m_retry_test_callback)(void); |
| 211 | |
| 212 | friend class lock_request_unit_test; |
| 213 | }; |
| 214 | ENSURE_POD(lock_request); |
| 215 | |
| 216 | } /* namespace toku */ |
| 217 | |