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 | /* Purpose of this file is to implement xids list of nested transactions |
40 | * ids. |
41 | * |
42 | * See design documentation for nested transactions at |
43 | * TokuWiki/Imp/TransactionsOverview. |
44 | * |
45 | * NOTE: xids are always stored in disk byte order. |
46 | * Accessors are responsible for transposing bytes to |
47 | * host order. |
48 | */ |
49 | |
50 | #include <errno.h> |
51 | #include <string.h> |
52 | |
53 | #include "portability/memory.h" |
54 | #include "portability/toku_assert.h" |
55 | #include "portability/toku_htod.h" |
56 | #include "portability/toku_portability.h" |
57 | |
58 | #include "ft/txn/xids.h" |
59 | |
60 | ///////////////////////////////////////////////////////////////////////////////// |
61 | // This layer of abstraction (xids_xxx) understands xids<> and nothing else. |
62 | // It contains all the functions that understand xids<> |
63 | // |
64 | // xids<> do not store the implicit transaction id of 0 at index 0. |
65 | // The accessor functions make the id of 0 explicit at index 0. |
66 | // The number of xids physically stored in the xids array is in |
67 | // the variable num_xids. |
68 | // |
69 | // The xids struct is immutable. The caller gets an initial version of XIDS |
70 | // by calling toku_xids_get_root_xids(), which returns the constant struct |
71 | // representing the root transaction (id 0). When a transaction begins, |
72 | // a new XIDS is created with the id of the current transaction appended to |
73 | // the list. |
74 | // |
75 | // |
76 | |
77 | // This is the xids list for a transactionless environment. |
78 | // It is also the initial state of any xids list created for |
79 | // nested transactions. |
80 | |
81 | XIDS |
82 | toku_xids_get_root_xids(void) { |
83 | static const struct XIDS_S root_xids = { |
84 | .num_xids = 0 |
85 | }; |
86 | |
87 | XIDS rval = (XIDS)&root_xids; |
88 | return rval; |
89 | } |
90 | |
91 | bool |
92 | toku_xids_can_create_child(XIDS xids) { |
93 | invariant(xids->num_xids < MAX_TRANSACTION_RECORDS); |
94 | return (xids->num_xids + 1) != MAX_TRANSACTION_RECORDS; |
95 | } |
96 | |
97 | int |
98 | toku_xids_create_unknown_child(XIDS parent_xids, XIDS *xids_p) { |
99 | // Postcondition: |
100 | // xids_p points to an xids that is an exact copy of parent_xids, but with room for one more xid. |
101 | int rval; |
102 | invariant(parent_xids); |
103 | uint32_t num_child_xids = parent_xids->num_xids + 1; |
104 | // assumes that caller has verified that num_child_xids will |
105 | // be less than MAX_TRANSACTIN_RECORDS |
106 | invariant(num_child_xids < MAX_TRANSACTION_RECORDS); |
107 | size_t new_size = sizeof(*parent_xids) + num_child_xids*sizeof(parent_xids->ids[0]); |
108 | XIDS CAST_FROM_VOIDP(xids, toku_xmalloc(new_size)); |
109 | // Clone everything (parent does not have the newest xid). |
110 | memcpy(xids, parent_xids, new_size - sizeof(xids->ids[0])); |
111 | *xids_p = xids; |
112 | rval = 0; |
113 | return rval; |
114 | } |
115 | |
116 | void |
117 | toku_xids_finalize_with_child(XIDS xids, TXNID this_xid) { |
118 | // Precondition: |
119 | // - xids was created by toku_xids_create_unknown_child |
120 | TXNID this_xid_disk = toku_htod64(this_xid); |
121 | uint32_t num_child_xids = ++xids->num_xids; |
122 | xids->ids[num_child_xids - 1] = this_xid_disk; |
123 | } |
124 | |
125 | // xids is immutable. This function creates a new xids by copying the |
126 | // parent's list and then appending the xid of the new transaction. |
127 | int |
128 | toku_xids_create_child(XIDS parent_xids, // xids list for parent transaction |
129 | XIDS *xids_p, // xids list created |
130 | TXNID this_xid) { // xid of this transaction (new innermost) |
131 | bool can_create_child = toku_xids_can_create_child(parent_xids); |
132 | if (!can_create_child) { |
133 | return EINVAL; |
134 | } |
135 | toku_xids_create_unknown_child(parent_xids, xids_p); |
136 | toku_xids_finalize_with_child(*xids_p, this_xid); |
137 | return 0; |
138 | } |
139 | |
140 | void |
141 | toku_xids_create_from_buffer(struct rbuf *rb, // xids list for parent transaction |
142 | XIDS *xids_p) { // xids list created |
143 | uint8_t num_xids = rbuf_char(rb); |
144 | invariant(num_xids < MAX_TRANSACTION_RECORDS); |
145 | XIDS CAST_FROM_VOIDP(xids, toku_xmalloc(sizeof(*xids) + num_xids*sizeof(xids->ids[0]))); |
146 | xids->num_xids = num_xids; |
147 | uint8_t index; |
148 | for (index = 0; index < xids->num_xids; index++) { |
149 | rbuf_TXNID(rb, &xids->ids[index]); |
150 | } |
151 | *xids_p = xids; |
152 | } |
153 | |
154 | void |
155 | toku_xids_destroy(XIDS *xids_p) { |
156 | if (*xids_p != toku_xids_get_root_xids()) toku_free(*xids_p); |
157 | *xids_p = NULL; |
158 | } |
159 | |
160 | // Return xid at requested position. |
161 | // If requesting an xid out of range (which will be the case if xids array is empty) |
162 | // then return 0, the xid of the root transaction. |
163 | TXNID |
164 | toku_xids_get_xid(XIDS xids, uint8_t index) { |
165 | invariant(index < toku_xids_get_num_xids(xids)); |
166 | TXNID rval = xids->ids[index]; |
167 | rval = toku_dtoh64(rval); |
168 | return rval; |
169 | } |
170 | |
171 | uint8_t |
172 | toku_xids_get_num_xids(XIDS xids) { |
173 | uint8_t rval = xids->num_xids; |
174 | return rval; |
175 | } |
176 | |
177 | // Return innermost xid |
178 | TXNID |
179 | toku_xids_get_innermost_xid(XIDS xids) { |
180 | TXNID rval = TXNID_NONE; |
181 | if (toku_xids_get_num_xids(xids)) { |
182 | // if clause above makes this cast ok |
183 | uint8_t innermost_xid = (uint8_t) (toku_xids_get_num_xids(xids) - 1); |
184 | rval = toku_xids_get_xid(xids, innermost_xid); |
185 | } |
186 | return rval; |
187 | } |
188 | |
189 | TXNID |
190 | toku_xids_get_outermost_xid(XIDS xids) { |
191 | TXNID rval = TXNID_NONE; |
192 | if (toku_xids_get_num_xids(xids)) { |
193 | rval = toku_xids_get_xid(xids, 0); |
194 | } |
195 | return rval; |
196 | } |
197 | |
198 | void |
199 | toku_xids_cpy(XIDS target, XIDS source) { |
200 | size_t size = toku_xids_get_size(source); |
201 | memcpy(target, source, size); |
202 | } |
203 | |
204 | // return size in bytes |
205 | uint32_t |
206 | toku_xids_get_size(XIDS xids) { |
207 | uint32_t rval; |
208 | uint8_t num_xids = xids->num_xids; |
209 | rval = sizeof(*xids) + num_xids * sizeof(xids->ids[0]); |
210 | return rval; |
211 | } |
212 | |
213 | uint32_t |
214 | toku_xids_get_serialize_size(XIDS xids) { |
215 | uint32_t rval; |
216 | uint8_t num_xids = xids->num_xids; |
217 | rval = 1 + //num xids |
218 | 8 * num_xids; |
219 | return rval; |
220 | } |
221 | |
222 | unsigned char * |
223 | toku_xids_get_end_of_array(XIDS xids) { |
224 | TXNID *r = xids->ids + xids->num_xids; |
225 | return (unsigned char*)r; |
226 | } |
227 | |
228 | void wbuf_nocrc_xids(struct wbuf *wb, XIDS xids) { |
229 | wbuf_nocrc_char(wb, (unsigned char)xids->num_xids); |
230 | uint8_t index; |
231 | for (index = 0; index < xids->num_xids; index++) { |
232 | wbuf_nocrc_TXNID(wb, xids->ids[index]); |
233 | } |
234 | } |
235 | |
236 | void |
237 | toku_xids_fprintf(FILE *fp, XIDS xids) { |
238 | uint8_t index; |
239 | unsigned num_xids = toku_xids_get_num_xids(xids); |
240 | fprintf(fp, "[|%u| " , num_xids); |
241 | for (index = 0; index < toku_xids_get_num_xids(xids); index++) { |
242 | if (index) fprintf(fp, "," ); |
243 | fprintf(fp, "%" PRIx64, toku_xids_get_xid(xids, index)); |
244 | } |
245 | fprintf(fp, "]" ); |
246 | } |
247 | |
248 | |