MMKV_Android.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. * Tencent is pleased to support the open source community by making
  3. * MMKV available.
  4. *
  5. * Copyright (C) 2019 THL A29 Limited, a Tencent company.
  6. * All rights reserved.
  7. *
  8. * Licensed under the BSD 3-Clause License (the "License"); you may not use
  9. * this file except in compliance with the License. You may obtain a copy of
  10. * the License at
  11. *
  12. * https://opensource.org/licenses/BSD-3-Clause
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. #include "MMKV.h"
  21. #ifdef MMKV_ANDROID
  22. # include "InterProcessLock.h"
  23. # include "KeyValueHolder.h"
  24. # include "MMKVLog.h"
  25. # include "MMKVMetaInfo.hpp"
  26. # include "MemoryFile.h"
  27. # include "ScopedLock.hpp"
  28. # include "ThreadLock.h"
  29. # include <unistd.h>
  30. # include "MMKV_IO.h"
  31. using namespace std;
  32. using namespace mmkv;
  33. extern unordered_map<string, MMKV *> *g_instanceDic;
  34. extern ThreadLock *g_instanceLock;
  35. MMKV::MMKV(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath)
  36. : m_mmapID((mode & MMKV_BACKUP) ? mmapID : mmapedKVKey(mmapID, rootPath)) // historically Android mistakenly use mmapKey as mmapID
  37. , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
  38. , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
  39. , m_dic(nullptr)
  40. , m_dicCrypt(nullptr)
  41. , m_file(new MemoryFile(m_path, size, (mode & MMKV_ASHMEM) ? MMFILE_TYPE_ASHMEM : MMFILE_TYPE_FILE))
  42. , m_metaFile(new MemoryFile(m_crcPath, DEFAULT_MMAP_SIZE, m_file->m_fileType))
  43. , m_metaInfo(new MMKVMetaInfo())
  44. , m_crypter(nullptr)
  45. , m_lock(new ThreadLock())
  46. , m_fileLock(new FileLock(m_metaFile->getFd(), (mode & MMKV_ASHMEM)))
  47. , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
  48. , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
  49. , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0 || (mode & CONTEXT_MODE_MULTI_PROCESS) != 0) {
  50. m_actualSize = 0;
  51. m_output = nullptr;
  52. // force use fcntl(), otherwise will conflict with MemoryFile::reloadFromFile()
  53. m_fileModeLock = new FileLock(m_file->getFd(), true);
  54. m_sharedProcessModeLock = new InterProcessLock(m_fileModeLock, SharedLockType);
  55. m_exclusiveProcessModeLock = nullptr;
  56. # ifndef MMKV_DISABLE_CRYPT
  57. if (cryptKey && cryptKey->length() > 0) {
  58. m_dicCrypt = new MMKVMapCrypt();
  59. m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
  60. } else
  61. # endif
  62. {
  63. m_dic = new MMKVMap();
  64. }
  65. m_needLoadFromFile = true;
  66. m_hasFullWriteback = false;
  67. m_crcDigest = 0;
  68. m_sharedProcessLock->m_enable = m_isInterProcess;
  69. m_exclusiveProcessLock->m_enable = m_isInterProcess;
  70. // sensitive zone
  71. {
  72. SCOPED_LOCK(m_sharedProcessLock);
  73. loadFromFile();
  74. }
  75. }
  76. MMKV::MMKV(const string &mmapID, int ashmemFD, int ashmemMetaFD, string *cryptKey)
  77. : m_mmapID(mmapID)
  78. , m_path(mappedKVPathWithID(m_mmapID, MMKV_ASHMEM, nullptr))
  79. , m_crcPath(crcPathWithID(m_mmapID, MMKV_ASHMEM, nullptr))
  80. , m_dic(nullptr)
  81. , m_dicCrypt(nullptr)
  82. , m_file(new MemoryFile(ashmemFD))
  83. , m_metaFile(new MemoryFile(ashmemMetaFD))
  84. , m_metaInfo(new MMKVMetaInfo())
  85. , m_crypter(nullptr)
  86. , m_lock(new ThreadLock())
  87. , m_fileLock(new FileLock(m_metaFile->getFd(), true))
  88. , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
  89. , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
  90. , m_isInterProcess(true) {
  91. m_actualSize = 0;
  92. m_output = nullptr;
  93. // force use fcntl(), otherwise will conflict with MemoryFile::reloadFromFile()
  94. m_fileModeLock = new FileLock(m_file->getFd(), true);
  95. m_sharedProcessModeLock = new InterProcessLock(m_fileModeLock, SharedLockType);
  96. m_exclusiveProcessModeLock = nullptr;
  97. # ifndef MMKV_DISABLE_CRYPT
  98. if (cryptKey && cryptKey->length() > 0) {
  99. m_dicCrypt = new MMKVMapCrypt();
  100. m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
  101. } else
  102. # endif
  103. {
  104. m_dic = new MMKVMap();
  105. }
  106. m_needLoadFromFile = true;
  107. m_hasFullWriteback = false;
  108. m_crcDigest = 0;
  109. m_sharedProcessLock->m_enable = m_isInterProcess;
  110. m_exclusiveProcessLock->m_enable = m_isInterProcess;
  111. // sensitive zone
  112. {
  113. SCOPED_LOCK(m_sharedProcessLock);
  114. loadFromFile();
  115. }
  116. }
  117. MMKV *MMKV::mmkvWithID(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath) {
  118. if (mmapID.empty()) {
  119. return nullptr;
  120. }
  121. SCOPED_LOCK(g_instanceLock);
  122. auto mmapKey = mmapedKVKey(mmapID, rootPath);
  123. auto itr = g_instanceDic->find(mmapKey);
  124. if (itr != g_instanceDic->end()) {
  125. MMKV *kv = itr->second;
  126. return kv;
  127. }
  128. if (rootPath) {
  129. if (!isFileExist(*rootPath)) {
  130. if (!mkPath(*rootPath)) {
  131. return nullptr;
  132. }
  133. }
  134. MMKVInfo("prepare to load %s (id %s) from rootPath %s", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());
  135. }
  136. auto kv = new MMKV(mmapID, size, mode, cryptKey, rootPath);
  137. (*g_instanceDic)[mmapKey] = kv;
  138. return kv;
  139. }
  140. MMKV *MMKV::mmkvWithAshmemFD(const string &mmapID, int fd, int metaFD, string *cryptKey) {
  141. if (fd < 0) {
  142. return nullptr;
  143. }
  144. SCOPED_LOCK(g_instanceLock);
  145. auto itr = g_instanceDic->find(mmapID);
  146. if (itr != g_instanceDic->end()) {
  147. MMKV *kv = itr->second;
  148. # ifndef MMKV_DISABLE_CRYPT
  149. kv->checkReSetCryptKey(fd, metaFD, cryptKey);
  150. # endif
  151. return kv;
  152. }
  153. auto kv = new MMKV(mmapID, fd, metaFD, cryptKey);
  154. (*g_instanceDic)[mmapID] = kv;
  155. return kv;
  156. }
  157. int MMKV::ashmemFD() {
  158. return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_file->getFd() : -1;
  159. }
  160. int MMKV::ashmemMetaFD() {
  161. return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_metaFile->getFd() : -1;
  162. }
  163. # ifndef MMKV_DISABLE_CRYPT
  164. void MMKV::checkReSetCryptKey(int fd, int metaFD, string *cryptKey) {
  165. SCOPED_LOCK(m_lock);
  166. checkReSetCryptKey(cryptKey);
  167. if (m_file->m_fileType & MMFILE_TYPE_ASHMEM) {
  168. if (m_file->getFd() != fd) {
  169. ::close(fd);
  170. }
  171. if (m_metaFile->getFd() != metaFD) {
  172. ::close(metaFD);
  173. }
  174. }
  175. }
  176. # endif // MMKV_DISABLE_CRYPT
  177. bool MMKV::checkProcessMode() {
  178. // avoid exception on open() error
  179. if (!m_file->isFileValid()) {
  180. return true;
  181. }
  182. if (m_isInterProcess) {
  183. if (!m_exclusiveProcessModeLock) {
  184. m_exclusiveProcessModeLock = new InterProcessLock(m_fileModeLock, ExclusiveLockType);
  185. }
  186. // avoid multiple processes get shared lock at the same time, https://github.com/Tencent/MMKV/issues/523
  187. auto tryAgain = false;
  188. auto exclusiveLocked = m_exclusiveProcessModeLock->try_lock(&tryAgain);
  189. if (exclusiveLocked) {
  190. return true;
  191. }
  192. auto shareLocked = m_sharedProcessModeLock->try_lock();
  193. if (!shareLocked) {
  194. // this call will fail on most case, just do it to make sure
  195. m_exclusiveProcessModeLock->try_lock();
  196. return true;
  197. } else {
  198. if (!tryAgain) {
  199. // something wrong with the OS/filesystem, let's try again
  200. exclusiveLocked = m_exclusiveProcessModeLock->try_lock(&tryAgain);
  201. if (!exclusiveLocked && !tryAgain) {
  202. // still something wrong, we have to give up and assume it passed the test
  203. MMKVWarning("Got a shared lock, but fail to exclusive lock [%s], assume it's ok", m_mmapID.c_str());
  204. exclusiveLocked = true;
  205. }
  206. }
  207. if (!exclusiveLocked) {
  208. MMKVError("Got a shared lock, but fail to exclusive lock [%s]", m_mmapID.c_str());
  209. }
  210. return exclusiveLocked;
  211. }
  212. } else {
  213. auto tryAgain = false;
  214. auto shareLocked = m_sharedProcessModeLock->try_lock(&tryAgain);
  215. if (!shareLocked && !tryAgain) {
  216. // something wrong with the OS/filesystem, we have to give up and assume it passed the test
  217. MMKVWarning("Fail to shared lock [%s], assume it's ok", m_mmapID.c_str());
  218. shareLocked = true;
  219. }
  220. if (!shareLocked) {
  221. MMKVError("Fail to share lock [%s]", m_mmapID.c_str());
  222. }
  223. return shareLocked;
  224. }
  225. }
  226. #endif // MMKV_ANDROID