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 | |