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/*======
5This file is part of PerconaFT.
6
7
8Copyright (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
81XIDS
82toku_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
91bool
92toku_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
97int
98toku_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
116void
117toku_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.
127int
128toku_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
140void
141toku_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
154void
155toku_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.
163TXNID
164toku_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
171uint8_t
172toku_xids_get_num_xids(XIDS xids) {
173 uint8_t rval = xids->num_xids;
174 return rval;
175}
176
177// Return innermost xid
178TXNID
179toku_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
189TXNID
190toku_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
198void
199toku_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
205uint32_t
206toku_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
213uint32_t
214toku_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
222unsigned char *
223toku_xids_get_end_of_array(XIDS xids) {
224 TXNID *r = xids->ids + xids->num_xids;
225 return (unsigned char*)r;
226}
227
228void 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
236void
237toku_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