RLMRealmUtil.mm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMRealmUtil.hpp"
  19. #import "RLMObjectSchema_Private.hpp"
  20. #import "RLMObservation.hpp"
  21. #import "RLMRealm_Private.hpp"
  22. #import "RLMUtil.hpp"
  23. #import <Realm/RLMConstants.h>
  24. #import <Realm/RLMSchema.h>
  25. #import "binding_context.hpp"
  26. #import "shared_realm.hpp"
  27. #import "util/scheduler.hpp"
  28. #import <map>
  29. #import <mutex>
  30. // Global realm state
  31. static auto& s_realmCacheMutex = *new std::mutex();
  32. static auto& s_realmsPerPath = *new std::map<std::string, NSMapTable *>();
  33. static auto& s_frozenRealms = *new std::map<std::string, NSMapTable *>();
  34. void RLMCacheRealm(std::string const& path, void *key, __unsafe_unretained RLMRealm *const realm) {
  35. std::lock_guard<std::mutex> lock(s_realmCacheMutex);
  36. NSMapTable *realms = s_realmsPerPath[path];
  37. if (!realms) {
  38. s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality|NSPointerFunctionsOpaqueMemory
  39. valueOptions:NSPointerFunctionsWeakMemory];
  40. }
  41. [realms setObject:realm forKey:(__bridge id)key];
  42. }
  43. RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) {
  44. std::lock_guard<std::mutex> lock(s_realmCacheMutex);
  45. return [s_realmsPerPath[path] objectEnumerator].nextObject;
  46. }
  47. RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path, void *key) {
  48. std::lock_guard<std::mutex> lock(s_realmCacheMutex);
  49. RLMRealm *realm = [s_realmsPerPath[path] objectForKey:(__bridge id)key];
  50. if (realm && !realm->_realm->scheduler()->is_on_thread()) {
  51. // We can get here in two cases: if the user is trying to open a
  52. // queue-bound Realm from the wrong queue, or if we have a stale cached
  53. // Realm which is bound to a thread that no longer exists. In the first
  54. // case we'll throw an error later on; in the second we'll just create
  55. // a new RLMRealm and replace the cache entry with one bound to the
  56. // thread that now exists.
  57. realm = nil;
  58. }
  59. return realm;
  60. }
  61. void RLMClearRealmCache() {
  62. std::lock_guard<std::mutex> lock(s_realmCacheMutex);
  63. s_realmsPerPath.clear();
  64. s_frozenRealms.clear();
  65. }
  66. RLMRealm *RLMGetFrozenRealmForSourceRealm(__unsafe_unretained RLMRealm *const sourceRealm) {
  67. std::lock_guard<std::mutex> lock(s_realmCacheMutex);
  68. auto& r = *sourceRealm->_realm;
  69. auto& path = r.config().path;
  70. NSMapTable *realms = s_realmsPerPath[path];
  71. if (!realms) {
  72. s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsIntegerPersonality|NSPointerFunctionsOpaqueMemory
  73. valueOptions:NSPointerFunctionsWeakMemory];
  74. }
  75. r.read_group();
  76. auto version = reinterpret_cast<void *>(r.read_transaction_version().version);
  77. RLMRealm *realm = [realms objectForKey:(__bridge id)version];
  78. if (!realm) {
  79. realm = [sourceRealm frozenCopy];
  80. [realms setObject:realm forKey:(__bridge id)version];
  81. }
  82. return realm;
  83. }
  84. namespace {
  85. class RLMNotificationHelper : public realm::BindingContext {
  86. public:
  87. RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { }
  88. void changes_available() override {
  89. @autoreleasepool {
  90. auto realm = _realm;
  91. if (realm && !realm.autorefresh) {
  92. [realm sendNotifications:RLMRealmRefreshRequiredNotification];
  93. }
  94. }
  95. }
  96. std::vector<ObserverState> get_observed_rows() override {
  97. @autoreleasepool {
  98. if (auto realm = _realm) {
  99. [realm detachAllEnumerators];
  100. return RLMGetObservedRows(realm->_info);
  101. }
  102. return {};
  103. }
  104. }
  105. void will_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated) override {
  106. @autoreleasepool {
  107. RLMWillChange(observed, invalidated);
  108. }
  109. }
  110. void did_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated, bool version_changed) override {
  111. try {
  112. @autoreleasepool {
  113. RLMDidChange(observed, invalidated);
  114. if (version_changed) {
  115. [_realm sendNotifications:RLMRealmDidChangeNotification];
  116. }
  117. }
  118. }
  119. catch (...) {
  120. // This can only be called during a write transaction if it was
  121. // called due to the transaction beginning, so cancel it to ensure
  122. // exceptions thrown here behave the same as exceptions thrown when
  123. // actually beginning the write
  124. if (_realm.inWriteTransaction) {
  125. [_realm cancelWriteTransaction];
  126. }
  127. throw;
  128. }
  129. }
  130. private:
  131. // This is owned by the realm, so it needs to not retain the realm
  132. __weak RLMRealm *const _realm;
  133. };
  134. } // anonymous namespace
  135. std::unique_ptr<realm::BindingContext> RLMCreateBindingContext(__unsafe_unretained RLMRealm *const realm) {
  136. return std::unique_ptr<realm::BindingContext>(new RLMNotificationHelper(realm));
  137. }