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 
38    Licensed under the Apache License, Version 2.0 (the "License");
39    you may not use this file except in compliance with the License.
40    You may obtain a copy of the License at
41 
42        http://www.apache.org/licenses/LICENSE-2.0
43 
44    Unless required by applicable law or agreed to in writing, software
45    distributed under the License is distributed on an "AS IS" BASIS,
46    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47    See the License for the specific language governing permissions and
48    limitations under the License.
49 ======= */
50 
51 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
52 
53 #pragma once
54 
55 #include <db.h>
56 
57 #include "portability/toku_pthread.h"
58 
59 #include "locktree/locktree.h"
60 #include "locktree/txnid_set.h"
61 #include "locktree/wfg.h"
62 #include "ft/comparator.h"
63 
64 namespace toku {
65 
66 // A lock request contains the db, the key range, the lock type, and
67 // the transaction id that describes a potential row range lock.
68 //
69 // the typical use case is:
70 // - initialize a lock request
71 // - start to try to acquire the lock
72 // - do something else
73 // - wait for the lock request to be resolved on a timed condition
74 // - destroy the lock request
75 // a lock request is resolved when its state is no longer pending, or
76 // when it becomes granted, or timedout, or deadlocked. when resolved, the
77 // state of the lock request is changed and any waiting threads are awakened.
78 
79 class lock_request {
80 public:
81     enum type {
82         UNKNOWN,
83         READ,
84         WRITE
85     };
86 
87     // effect: Initializes a lock request.
88     void create(void);
89 
90     // effect: Destroys a lock request.
91     void destroy(void);
92     void clearmem(char c);
93 
94     // effect: Resets the lock request parameters, allowing it to be reused.
95     // requires: Lock request was already created at some point
96     void set(locktree *lt, TXNID txnid, const DBT *left_key, const DBT *right_key, type lock_type, bool big_txn, void *extra = nullptr);
97 
98     // effect: Tries to acquire a lock described by this lock request.
99     // returns: The return code of locktree::acquire_[write,read]_lock()
100     //          or DB_LOCK_DEADLOCK if this request would end up deadlocked.
101     int start(void);
102 
103     // effect: Sleeps until either the request is granted or the wait time expires.
104     // returns: The return code of locktree::acquire_[write,read]_lock()
105     //          or simply DB_LOCK_NOTGRANTED if the wait time expired.
106     int wait(uint64_t wait_time_ms);
107     int wait(uint64_t wait_time_ms, uint64_t killed_time_ms, int (*killed_callback)(void),
108              void (*lock_wait_callback)(void *, TXNID, TXNID) = nullptr);
109 
110     // return: left end-point of the lock range
111     const DBT *get_left_key(void) const;
112 
113     // return: right end-point of the lock range
114     const DBT *get_right_key(void) const;
115 
116     // return: the txnid waiting for a lock
117     TXNID get_txnid(void) const;
118 
119     // return: when this lock request started, as milliseconds from epoch
120     uint64_t get_start_time(void) const;
121 
122     // return: which txnid is blocking this request (there may be more, though)
123     TXNID get_conflicting_txnid(void) const;
124 
125     // effect: Retries all of the lock requests for the given locktree.
126     //         Any lock requests successfully restarted is completed and woken
127     //         up.
128     //         The rest remain pending.
129     static void retry_all_lock_requests(
130         locktree *lt,
131         void (*lock_wait_callback)(void *, TXNID, TXNID) = nullptr,
132         void (*after_retry_test_callback)(void) = nullptr);
133     static void retry_all_lock_requests_info(lt_lock_request_info *info, GrowableArray<TXNID> *collector);
134 
135     void set_start_test_callback(void (*f)(void));
136     void set_start_before_pending_test_callback(void (*f)(void));
137     void set_retry_test_callback(void (*f)(void));
138 
139     void *get_extra(void) const;
140 
141     void kill_waiter(void);
142     static void kill_waiter(locktree *lt, void *extra);
143 
144    private:
145     enum state {
146         UNINITIALIZED,
147         INITIALIZED,
148         PENDING,
149         COMPLETE,
150         DESTROYED,
151     };
152 
153     // The keys for a lock request are stored "unowned" in m_left_key
154     // and m_right_key. When the request is about to go to sleep, it
155     // copies these keys and stores them in m_left_key_copy etc and
156     // sets the temporary pointers to null.
157     TXNID m_txnid;
158     TXNID m_conflicting_txnid;
159     uint64_t m_start_time;
160     const DBT *m_left_key;
161     const DBT *m_right_key;
162     DBT m_left_key_copy;
163     DBT m_right_key_copy;
164 
165     // The lock request type and associated locktree
166     type m_type;
167     locktree *m_lt;
168 
169     // If the lock request is in the completed state, then its
170     // final return value is stored in m_complete_r
171     int m_complete_r;
172     state m_state;
173 
174     toku_cond_t m_wait_cond;
175 
176     bool m_big_txn;
177 
178     // the lock request info state stored in the
179     // locktree that this lock request is for.
180     struct lt_lock_request_info *m_info;
181 
182     void *m_extra;
183 
184     // effect: tries again to acquire the lock described by this lock request
185     // returns: 0 if retrying the request succeeded and is now complete
186     int retry(GrowableArray<TXNID> *conflict_collector);
187 
188     void complete(int complete_r);
189 
190     // effect: Finds another lock request by txnid.
191     // requires: The lock request info mutex is held
192     lock_request *find_lock_request(const TXNID &txnid);
193 
194     // effect: Insert this lock request into the locktree's set.
195     // requires: the locktree's mutex is held
196     void insert_into_lock_requests(void);
197 
198     // effect: Removes this lock request from the locktree's set.
199     // requires: The lock request info mutex is held
200     void remove_from_lock_requests(void);
201 
202     // effect: Asks this request's locktree which txnids are preventing
203     //         us from getting the lock described by this request.
204     // returns: conflicts is populated with the txnid's that this request
205     //          is blocked on
206     void get_conflicts(txnid_set *conflicts);
207 
208     // effect: Builds a wait-for-graph for this lock request and the given conflict set
209     void build_wait_graph(wfg *wait_graph, const txnid_set &conflicts);
210 
211     // returns: True if this lock request is in deadlock with the given conflicts set
212     bool deadlock_exists(const txnid_set &conflicts);
213 
214     void copy_keys(void);
215 
216     static int find_by_txnid(lock_request *const &request, const TXNID &txnid);
217 
218     // Report list of conflicts to lock wait callback.
219     static void report_waits(GrowableArray<TXNID> *wait_conflicts,
220                              void (*lock_wait_callback)(void *, TXNID, TXNID));
221     void add_conflicts_to_waits(txnid_set *conflicts, GrowableArray<TXNID> *wait_conflicts);
222 
223     void (*m_start_test_callback)(void);
224     void (*m_start_before_pending_test_callback)(void);
225     void (*m_retry_test_callback)(void);
226 
227     friend class lock_request_unit_test;
228 };
229 ENSURE_POD(lock_request);
230 
231 } /* namespace toku */
232