1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "medialibrary_rdb_transaction.h"
17
18 #include "media_log.h"
19
20 namespace OHOS::Media {
21 using namespace std;
22
23 constexpr int32_t E_HAS_DB_ERROR = -222;
24 constexpr int32_t E_OK = 0;
25
26 constexpr int RDB_TRANSACTION_WAIT_MS = 1000;
27 std::mutex TransactionOperations::transactionMutex_;
28 std::condition_variable TransactionOperations::transactionCV_;
29 std::atomic<bool> TransactionOperations::isInTransaction_(false);
30 constexpr int32_t MAX_TRY_TIMES = 30;
31 constexpr int32_t TRANSACTION_WAIT_INTERVAL = 50; // in milliseconds.
32
TransactionOperations(const shared_ptr<OHOS::NativeRdb::RdbStore> & rdbStore)33 TransactionOperations::TransactionOperations(
34 const shared_ptr<OHOS::NativeRdb::RdbStore> &rdbStore) : rdbStore_(rdbStore) {}
35
~TransactionOperations()36 TransactionOperations::~TransactionOperations()
37 {
38 if (isStart && !isFinish) {
39 TransactionRollback();
40 }
41 }
42
Start()43 int32_t TransactionOperations::Start()
44 {
45 if (isStart || isFinish) {
46 return 0;
47 }
48 int32_t errCode = BeginTransaction();
49 if (errCode == 0) {
50 isStart = true;
51 }
52 return errCode;
53 }
54
Finish()55 void TransactionOperations::Finish()
56 {
57 if (!isStart) {
58 return;
59 }
60 if (!isFinish) {
61 int32_t ret = TransactionCommit();
62 if (ret == 0) {
63 isFinish = true;
64 }
65 }
66 }
67
BeginTransaction()68 int32_t TransactionOperations::BeginTransaction()
69 {
70 if (rdbStore_ == nullptr) {
71 MEDIA_ERR_LOG("Pointer rdbStore_ is nullptr. Maybe it didn't init successfully.");
72 return E_HAS_DB_ERROR;
73 }
74 MEDIA_INFO_LOG("Start transaction");
75
76 unique_lock<mutex> cvLock(transactionMutex_);
77 if (isInTransaction_.load()) {
78 transactionCV_.wait_for(cvLock, chrono::milliseconds(RDB_TRANSACTION_WAIT_MS),
79 [this] () { return !(isInTransaction_.load()); });
80 }
81
82 int curTryTime = 0;
83 while (curTryTime < MAX_TRY_TIMES) {
84 if (rdbStore_->IsInTransaction()) {
85 this_thread::sleep_for(chrono::milliseconds(TRANSACTION_WAIT_INTERVAL));
86 if (isInTransaction_.load() || rdbStore_->IsInTransaction()) {
87 curTryTime++;
88 MEDIA_INFO_LOG("RdbStore is in transaction, try %{public}d times...", curTryTime);
89 continue;
90 }
91 }
92
93 int32_t errCode = rdbStore_->BeginTransaction();
94 if (errCode == SQLITE3_DATABASE_LOCKER) {
95 curTryTime++;
96 MEDIA_ERR_LOG("Sqlite database file is locked! try %{public}d times...", curTryTime);
97 continue;
98 } else if (errCode != NativeRdb::E_OK) {
99 MEDIA_ERR_LOG("Start Transaction failed, errCode=%{public}d", errCode);
100 isInTransaction_.store(false);
101 transactionCV_.notify_one();
102 return E_HAS_DB_ERROR;
103 } else {
104 isInTransaction_.store(true);
105 return E_OK;
106 }
107 }
108
109 MEDIA_ERR_LOG("RdbStore is still in transaction after try %{public}d times, abort.", MAX_TRY_TIMES);
110 return E_HAS_DB_ERROR;
111 }
112
TransactionCommit()113 int32_t TransactionOperations::TransactionCommit()
114 {
115 if (rdbStore_ == nullptr) {
116 return E_HAS_DB_ERROR;
117 }
118 MEDIA_INFO_LOG("Try commit transaction");
119
120 if (!(isInTransaction_.load()) || !(rdbStore_->IsInTransaction())) {
121 MEDIA_ERR_LOG("no transaction now");
122 return E_HAS_DB_ERROR;
123 }
124
125 int32_t errCode = rdbStore_->Commit();
126 isInTransaction_.store(false);
127 transactionCV_.notify_all();
128 if (errCode != NativeRdb::E_OK) {
129 MEDIA_ERR_LOG("commit failed, errCode=%{public}d", errCode);
130 return E_HAS_DB_ERROR;
131 }
132
133 return E_OK;
134 }
135
TransactionRollback()136 int32_t TransactionOperations::TransactionRollback()
137 {
138 if (rdbStore_ == nullptr) {
139 return E_HAS_DB_ERROR;
140 }
141 MEDIA_INFO_LOG("Try rollback transaction");
142
143 if (!(isInTransaction_.load()) || !(rdbStore_->IsInTransaction())) {
144 MEDIA_ERR_LOG("no transaction now");
145 return E_HAS_DB_ERROR;
146 }
147
148 int32_t errCode = rdbStore_->RollBack();
149 isInTransaction_.store(false);
150 transactionCV_.notify_all();
151 if (errCode != NativeRdb::E_OK) {
152 MEDIA_ERR_LOG("rollback failed, errCode=%{public}d", errCode);
153 return E_HAS_DB_ERROR;
154 }
155
156 return E_OK;
157 }
158 } // namespace OHOS::Media
159