1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2010 Couchbase, Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdint.h>
21
22 #include "libforestdb/forestdb.h"
23 #include "fdb_internal.h"
24 #include "internal_types.h"
25 #include "filemgr.h"
26 #include "common.h"
27 #include "list.h"
28 #include "wal.h"
29 #include "memleak.h"
30
31 // Global static variables
32 static atomic_uint64_t transaction_id(0); // unique & monotonically increasing
33
34 LIBFDB_API
fdb_begin_transaction(fdb_file_handle *fhandle, fdb_isolation_level_t isolation_level)35 fdb_status fdb_begin_transaction(fdb_file_handle *fhandle,
36 fdb_isolation_level_t isolation_level)
37 {
38 if (!fhandle || !fhandle->root) {
39 return FDB_RESULT_INVALID_HANDLE;
40 }
41
42 file_status_t fstatus;
43 fdb_kvs_handle *handle = fhandle->root;
44 struct filemgr *file;
45
46 if (handle->txn) {
47 // transaction already exists
48 return FDB_RESULT_TRANSACTION_FAIL;
49 }
50 if (handle->kvs) {
51 if (handle->kvs->type == KVS_SUB) {
52 // deny transaction on sub handle
53 return FDB_RESULT_INVALID_HANDLE;
54 }
55 }
56
57 if (!atomic_cas_uint8_t(&handle->handle_busy, 0, 1)) {
58 return FDB_RESULT_HANDLE_BUSY;
59 }
60
61 do { // repeat until file status is not REMOVED_PENDING
62 fdb_check_file_reopen(handle, NULL);
63 filemgr_mutex_lock(handle->file);
64 fdb_sync_db_header(handle);
65
66 if (filemgr_is_rollback_on(handle->file)) {
67 // deny beginning transaction during rollback
68 filemgr_mutex_unlock(handle->file);
69 atomic_cas_uint8_t(&handle->handle_busy, 1, 0);
70 return FDB_RESULT_FAIL_BY_ROLLBACK;
71 }
72
73 file = handle->file;
74 fstatus = filemgr_get_file_status(file);
75 if (fstatus == FILE_REMOVED_PENDING) {
76 // we must not create transaction on this file
77 // file status was changed by other thread .. start over
78 filemgr_mutex_unlock(file);
79 }
80 } while (fstatus == FILE_REMOVED_PENDING);
81
82 handle->txn = (fdb_txn*)malloc(sizeof(fdb_txn));
83 handle->txn->wrapper = (struct wal_txn_wrapper *)
84 malloc(sizeof(struct wal_txn_wrapper));
85 handle->txn->wrapper->txn = handle->txn;
86 handle->txn->handle = handle;
87 handle->txn->txn_id = ++transaction_id;
88
89 if (filemgr_get_file_status(handle->file) != FILE_COMPACT_OLD) {
90 // keep previous header's BID
91 handle->txn->prev_hdr_bid = handle->last_hdr_bid;
92 } else {
93 // if file status is COMPACT_OLD,
94 // then this transaction will work on new file, and
95 // there is no previous header until the compaction is done.
96 handle->txn->prev_hdr_bid = BLK_NOT_FOUND;
97 }
98 handle->txn->prev_revnum = handle->cur_header_revnum;
99 handle->txn->items = (struct list *)malloc(sizeof(struct list));
100 handle->txn->isolation = isolation_level;
101 list_init(handle->txn->items);
102 wal_add_transaction(file, handle->txn);
103
104 filemgr_mutex_unlock(file);
105
106 atomic_cas_uint8_t(&handle->handle_busy, 1, 0);
107 return FDB_RESULT_SUCCESS;
108 }
109
110 LIBFDB_API
fdb_abort_transaction(fdb_file_handle *fhandle)111 fdb_status fdb_abort_transaction(fdb_file_handle *fhandle)
112 {
113 if (!fhandle) {
114 return FDB_RESULT_INVALID_HANDLE;
115 }
116
117 return _fdb_abort_transaction(fhandle->root);
118 }
119
_fdb_abort_transaction(fdb_kvs_handle *handle)120 fdb_status _fdb_abort_transaction(fdb_kvs_handle *handle)
121 {
122 if (!handle) {
123 return FDB_RESULT_INVALID_HANDLE;
124 }
125
126 file_status_t fstatus;
127 struct filemgr *file;
128
129 if (handle->txn == NULL) {
130 // there is no transaction started
131 return FDB_RESULT_TRANSACTION_FAIL;
132 }
133 if (handle->kvs) {
134 if (handle->kvs->type == KVS_SUB) {
135 // deny transaction on sub handle
136 return FDB_RESULT_INVALID_HANDLE;
137 }
138 }
139
140 if (!atomic_cas_uint8_t(&handle->handle_busy, 0, 1)) {
141 return FDB_RESULT_HANDLE_BUSY;
142 }
143
144 do { // repeat until file status is not REMOVED_PENDING
145 fdb_check_file_reopen(handle, NULL);
146
147 file = handle->file;
148 filemgr_mutex_lock(file);
149 fdb_sync_db_header(handle);
150
151 fstatus = filemgr_get_file_status(file);
152 if (fstatus == FILE_REMOVED_PENDING) {
153 // we must not abort transaction on this file
154 // file status was changed by other thread .. start over
155 filemgr_mutex_unlock(file);
156 }
157 } while (fstatus == FILE_REMOVED_PENDING);
158
159 wal_discard(file, handle->txn);
160 wal_remove_transaction(file, handle->txn);
161
162 free(handle->txn->items);
163 free(handle->txn->wrapper);
164 free(handle->txn);
165 handle->txn = NULL;
166
167 filemgr_mutex_unlock(file);
168
169 atomic_cas_uint8_t(&handle->handle_busy, 1, 0);
170 return FDB_RESULT_SUCCESS;
171 }
172
173 LIBFDB_API
fdb_end_transaction(fdb_file_handle *fhandle, fdb_commit_opt_t opt)174 fdb_status fdb_end_transaction(fdb_file_handle *fhandle,
175 fdb_commit_opt_t opt)
176 {
177 if (!fhandle || !fhandle->root) {
178 return FDB_RESULT_INVALID_HANDLE;
179 }
180
181 file_status_t fstatus;
182 fdb_kvs_handle *handle = fhandle->root;
183 struct filemgr *file;
184
185 if (handle->txn == NULL) {
186 // there is no transaction started
187 return FDB_RESULT_TRANSACTION_FAIL;
188 }
189 if (handle->kvs) {
190 if (handle->kvs->type == KVS_SUB) {
191 // deny transaction on sub handle
192 return FDB_RESULT_INVALID_HANDLE;
193 }
194 }
195
196 fdb_status fs = FDB_RESULT_SUCCESS;
197 if (list_begin(handle->txn->items)) {
198 fs = _fdb_commit(handle, opt,
199 !(handle->config.durability_opt & FDB_DRB_ASYNC));
200 }
201
202 if (fs == FDB_RESULT_SUCCESS) {
203
204 do { // repeat until file status is not REMOVED_PENDING
205 fdb_check_file_reopen(handle, NULL);
206
207 file = handle->file;
208 filemgr_mutex_lock(file);
209 fdb_sync_db_header(handle);
210
211 fstatus = filemgr_get_file_status(file);
212 if (fstatus == FILE_REMOVED_PENDING) {
213 // we must not commit transaction on this file
214 // file status was changed by other thread .. start over
215 filemgr_mutex_unlock(file);
216 }
217 } while (fstatus == FILE_REMOVED_PENDING);
218
219 wal_remove_transaction(file, handle->txn);
220
221 free(handle->txn->items);
222 free(handle->txn->wrapper);
223 free(handle->txn);
224 handle->txn = NULL;
225
226 filemgr_mutex_unlock(file);
227 }
228
229 return fs;
230 }
231