InterProcessLock.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*
  2. * Tencent is pleased to support the open source community by making
  3. * MMKV available.
  4. *
  5. * Copyright (C) 2018 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 "InterProcessLock.h"
  21. #include "MMKVLog.h"
  22. #ifndef MMKV_WIN32
  23. # include <sys/file.h>
  24. #endif
  25. namespace mmkv {
  26. FileLock::~FileLock() {
  27. if (isFileLockValid()) {
  28. if (m_exclusiveLockCount > 0) {
  29. #ifdef MMKV_WIN32
  30. // only win32 file lock requires double unlock
  31. if (m_sharedLockCount > 0) {
  32. platformUnLock(true);
  33. }
  34. #endif
  35. m_sharedLockCount = 0;
  36. m_exclusiveLockCount = 0;
  37. platformUnLock(false);
  38. } else if (m_sharedLockCount > 0) {
  39. m_sharedLockCount = 0;
  40. platformUnLock(false);
  41. }
  42. }
  43. }
  44. bool FileLock::lock(LockType lockType) {
  45. return doLock(lockType, true);
  46. }
  47. bool FileLock::try_lock(LockType lockType, bool *tryAgain) {
  48. return doLock(lockType, false, tryAgain);
  49. }
  50. bool FileLock::doLock(LockType lockType, bool wait, bool *tryAgain) {
  51. if (!isFileLockValid()) {
  52. return false;
  53. }
  54. bool unLockFirstIfNeeded = false;
  55. if (lockType == SharedLockType) {
  56. // don't want shared-lock to break any existing locks
  57. if (m_sharedLockCount > 0 || m_exclusiveLockCount > 0) {
  58. m_sharedLockCount++;
  59. return true;
  60. }
  61. } else {
  62. // don't want exclusive-lock to break existing exclusive-locks
  63. if (m_exclusiveLockCount > 0) {
  64. m_exclusiveLockCount++;
  65. return true;
  66. }
  67. // prevent deadlock
  68. if (m_sharedLockCount > 0) {
  69. unLockFirstIfNeeded = true;
  70. }
  71. }
  72. auto ret = platformLock(lockType, wait, unLockFirstIfNeeded, tryAgain);
  73. if (ret) {
  74. if (lockType == SharedLockType) {
  75. m_sharedLockCount++;
  76. } else {
  77. m_exclusiveLockCount++;
  78. }
  79. }
  80. return ret;
  81. }
  82. #ifndef MMKV_WIN32
  83. static int32_t LockType2FlockType(LockType lockType) {
  84. switch (lockType) {
  85. case SharedLockType:
  86. return LOCK_SH;
  87. case ExclusiveLockType:
  88. return LOCK_EX;
  89. }
  90. return LOCK_EX;
  91. }
  92. bool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded, bool *tryAgain) {
  93. # ifdef MMKV_ANDROID
  94. if (m_isAshmem) {
  95. return ashmemLock(lockType, wait, unLockFirstIfNeeded, tryAgain);
  96. }
  97. # endif
  98. auto realLockType = LockType2FlockType(lockType);
  99. auto cmd = wait ? realLockType : (realLockType | LOCK_NB);
  100. if (unLockFirstIfNeeded) {
  101. // try lock
  102. auto ret = flock(m_fd, realLockType | LOCK_NB);
  103. if (ret == 0) {
  104. return true;
  105. }
  106. // let's be gentleman: unlock my shared-lock to prevent deadlock
  107. ret = flock(m_fd, LOCK_UN);
  108. if (ret != 0) {
  109. MMKVError("fail to try unlock first fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
  110. }
  111. }
  112. auto ret = flock(m_fd, cmd);
  113. if (ret != 0) {
  114. if (tryAgain) {
  115. *tryAgain = (errno == EWOULDBLOCK);
  116. }
  117. if (wait) {
  118. MMKVError("fail to lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
  119. }
  120. // try recover my shared-lock
  121. if (unLockFirstIfNeeded) {
  122. ret = flock(m_fd, LockType2FlockType(SharedLockType));
  123. if (ret != 0) {
  124. // let's hope this never happen
  125. MMKVError("fail to recover shared-lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
  126. }
  127. }
  128. return false;
  129. } else {
  130. return true;
  131. }
  132. }
  133. bool FileLock::platformUnLock(bool unlockToSharedLock) {
  134. # ifdef MMKV_ANDROID
  135. if (m_isAshmem) {
  136. return ashmemUnLock(unlockToSharedLock);
  137. }
  138. # endif
  139. int cmd = unlockToSharedLock ? LOCK_SH : LOCK_UN;
  140. if (flock(m_fd, cmd) != 0) {
  141. MMKVError("fail to unlock fd=%d, error:%d(%s)", m_fd, errno, strerror(errno));
  142. return false;
  143. }
  144. return true;
  145. }
  146. #endif // MMKV_WIN32
  147. bool FileLock::unlock(LockType lockType) {
  148. if (!isFileLockValid()) {
  149. return false;
  150. }
  151. bool unlockToSharedLock = false;
  152. if (lockType == SharedLockType) {
  153. if (m_sharedLockCount == 0) {
  154. return false;
  155. }
  156. // don't want shared-lock to break any existing locks
  157. if (m_sharedLockCount > 1 || m_exclusiveLockCount > 0) {
  158. m_sharedLockCount--;
  159. return true;
  160. }
  161. } else {
  162. if (m_exclusiveLockCount == 0) {
  163. return false;
  164. }
  165. if (m_exclusiveLockCount > 1) {
  166. m_exclusiveLockCount--;
  167. return true;
  168. }
  169. // restore shared-lock when all exclusive-locks are done
  170. if (m_sharedLockCount > 0) {
  171. unlockToSharedLock = true;
  172. }
  173. }
  174. auto ret = platformUnLock(unlockToSharedLock);
  175. if (ret) {
  176. if (lockType == SharedLockType) {
  177. m_sharedLockCount--;
  178. } else {
  179. m_exclusiveLockCount--;
  180. }
  181. }
  182. return ret;
  183. }
  184. void FileLock::destroyAndUnLock() {
  185. platformUnLock(false);
  186. m_sharedLockCount = 0;
  187. m_exclusiveLockCount = 0;
  188. m_fd = MMKVFileHandleInvalidValue;
  189. }
  190. } // namespace mmkv