InterProcessLock.cpp 5.1 KB

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