1 /*
2 * Copyright (C) 2024 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 "ringtone_rdb_transaction.h"
17
18 #include "ringtone_log.h"
19
20 namespace OHOS::Media {
21 using namespace std;
22 constexpr int32_t E_HAS_DB_ERROR = -222;
23 constexpr int32_t E_OK = 0;
24 constexpr int RDB_TRANSACTION_WAIT_MS = 1000;
25 std::mutex RingtoneRdbTransaction::transactionMutex_;
26 std::condition_variable RingtoneRdbTransaction::transactionCV_;
27 std::atomic<bool> RingtoneRdbTransaction::isInTransaction_(false);
28 constexpr int32_t MAX_TRY_TIMES = 30;
29 constexpr int32_t TRANSACTION_WAIT_INTERVAL = 50; // in milliseconds.
30
RingtoneRdbTransaction(const shared_ptr<OHOS::NativeRdb::RdbStore> & rdbStore)31 RingtoneRdbTransaction::RingtoneRdbTransaction(
32 const shared_ptr<OHOS::NativeRdb::RdbStore> &rdbStore) : rdbStore_(rdbStore) {}
33
~RingtoneRdbTransaction()34 RingtoneRdbTransaction::~RingtoneRdbTransaction()
35 {
36 if (isStart && !isFinish) {
37 TransactionRollback();
38 }
39 }
40
Start()41 int32_t RingtoneRdbTransaction::Start()
42 {
43 if (isStart || isFinish) {
44 return 0;
45 }
46 int32_t errCode = BeginTransaction();
47 if (errCode == 0) {
48 isStart = true;
49 }
50 return errCode;
51 }
52
Finish()53 void RingtoneRdbTransaction::Finish()
54 {
55 if (!isStart) {
56 return;
57 }
58 if (!isFinish) {
59 int32_t ret = TransactionCommit();
60 if (ret == 0) {
61 isFinish = true;
62 }
63 }
64 }
65
BeginTransaction()66 int32_t RingtoneRdbTransaction::BeginTransaction()
67 {
68 if (rdbStore_ == nullptr) {
69 RINGTONE_ERR_LOG("Pointer rdbStore_ is nullptr. Maybe it didn't init successfully.");
70 return E_HAS_DB_ERROR;
71 }
72 RINGTONE_INFO_LOG("Start transaction");
73
74 unique_lock<mutex> cvLock(transactionMutex_);
75 if (isInTransaction_.load()) {
76 transactionCV_.wait_for(cvLock, chrono::milliseconds(RDB_TRANSACTION_WAIT_MS),
77 [this] () { return !(this->isInTransaction_.load()); });
78 }
79
80 int curTryTime = 0;
81 while (curTryTime < MAX_TRY_TIMES) {
82 if (rdbStore_->IsInTransaction()) {
83 this_thread::sleep_for(chrono::milliseconds(TRANSACTION_WAIT_INTERVAL));
84 if (isInTransaction_.load() || rdbStore_->IsInTransaction()) {
85 curTryTime++;
86 RINGTONE_INFO_LOG("RdbStore is in transaction, try %{public}d times...", curTryTime);
87 continue;
88 }
89 }
90
91 int32_t errCode = rdbStore_->BeginTransaction();
92 if (errCode == SQLITE3_DATABASE_LOCKER) {
93 curTryTime++;
94 RINGTONE_ERR_LOG("Sqlite database file is locked! try %{public}d times...", curTryTime);
95 continue;
96 } else if (errCode != NativeRdb::E_OK) {
97 RINGTONE_ERR_LOG("Start Transaction failed, errCode=%{public}d", errCode);
98 isInTransaction_.store(false);
99 transactionCV_.notify_one();
100 return E_HAS_DB_ERROR;
101 } else {
102 isInTransaction_.store(true);
103 return E_OK;
104 }
105 }
106
107 RINGTONE_ERR_LOG("RdbStore is still in transaction after try %{public}d times, abort.", MAX_TRY_TIMES);
108 return E_HAS_DB_ERROR;
109 }
110
TransactionCommit()111 int32_t RingtoneRdbTransaction::TransactionCommit()
112 {
113 if (rdbStore_ == nullptr) {
114 return E_HAS_DB_ERROR;
115 }
116 RINGTONE_INFO_LOG("Try commit transaction");
117
118 if (!(isInTransaction_.load()) || !(rdbStore_->IsInTransaction())) {
119 RINGTONE_ERR_LOG("no transaction now");
120 return E_HAS_DB_ERROR;
121 }
122
123 int32_t errCode = rdbStore_->Commit();
124 isInTransaction_.store(false);
125 transactionCV_.notify_all();
126 if (errCode != NativeRdb::E_OK) {
127 RINGTONE_ERR_LOG("commit failed, errCode=%{public}d", errCode);
128 return E_HAS_DB_ERROR;
129 }
130
131 return E_OK;
132 }
133
TransactionRollback()134 int32_t RingtoneRdbTransaction::TransactionRollback()
135 {
136 if (rdbStore_ == nullptr) {
137 return E_HAS_DB_ERROR;
138 }
139 RINGTONE_INFO_LOG("Try rollback transaction");
140
141 if (!(isInTransaction_.load()) || !(rdbStore_->IsInTransaction())) {
142 RINGTONE_ERR_LOG("no transaction now");
143 return E_HAS_DB_ERROR;
144 }
145
146 int32_t errCode = rdbStore_->RollBack();
147 isInTransaction_.store(false);
148 transactionCV_.notify_all();
149 if (errCode != NativeRdb::E_OK) {
150 RINGTONE_ERR_LOG("rollback failed, errCode=%{public}d", errCode);
151 return E_HAS_DB_ERROR;
152 }
153
154 return E_OK;
155 }
156 } // namespace OHOS::Media
157