1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2014 Realm Inc.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 ////////////////////////////////////////////////////////////////////////////
19 #import "RLMRealmUtil.hpp"
21 #import "RLMObjectSchema_Private.hpp"
22 #import "RLMObservation.hpp"
23 #import "RLMRealm_Private.hpp"
26 #import <Realm/RLMConstants.h>
27 #import <Realm/RLMSchema.h>
29 #import "binding_context.hpp"
39 static std::mutex& s_realmCacheMutex = *new std::mutex();
40 static std::map<std::string, NSMapTable *>& s_realmsPerPath = *new std::map<std::string, NSMapTable *>();
42 void RLMCacheRealm(std::string const& path, __unsafe_unretained RLMRealm *const realm) {
43 std::lock_guard<std::mutex> lock(s_realmCacheMutex);
44 NSMapTable *realms = s_realmsPerPath[path];
46 s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality|NSPointerFunctionsOpaqueMemory
47 valueOptions:NSPointerFunctionsWeakMemory];
49 [realms setObject:realm forKey:(__bridge id)pthread_self()];
52 RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) {
53 std::lock_guard<std::mutex> lock(s_realmCacheMutex);
54 return [s_realmsPerPath[path] objectEnumerator].nextObject;
57 RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path) {
58 std::lock_guard<std::mutex> lock(s_realmCacheMutex);
59 return [s_realmsPerPath[path] objectForKey:(__bridge id)pthread_self()];
62 void RLMClearRealmCache() {
63 std::lock_guard<std::mutex> lock(s_realmCacheMutex);
64 s_realmsPerPath.clear();
67 bool RLMIsInRunLoop() {
68 // The main thread may not be in a run loop yet if we're called from
69 // something like `applicationDidFinishLaunching:`, but it presumably will
71 if ([NSThread isMainThread]) {
74 // Current mode indicates why the current callout from the runloop was made,
75 // and is null if a runloop callout isn't currently being processed
76 if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) {
84 class RLMNotificationHelper : public realm::BindingContext {
86 RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { }
88 bool can_deliver_notifications() const noexcept override {
89 return RLMIsInRunLoop();
92 void changes_available() override {
95 if (realm && !realm.autorefresh) {
96 [realm sendNotifications:RLMRealmRefreshRequiredNotification];
101 std::vector<ObserverState> get_observed_rows() override {
103 if (auto realm = _realm) {
104 [realm detachAllEnumerators];
105 return RLMGetObservedRows(realm->_info);
111 void will_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated) override {
113 RLMWillChange(observed, invalidated);
117 void did_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated, bool version_changed) override {
120 RLMDidChange(observed, invalidated);
121 if (version_changed) {
122 [_realm sendNotifications:RLMRealmDidChangeNotification];
127 // This can only be called during a write transaction if it was
128 // called due to the transaction beginning, so cancel it to ensure
129 // exceptions thrown here behave the same as exceptions thrown when
130 // actually beginning the write
131 if (_realm.inWriteTransaction) {
132 [_realm cancelWriteTransaction];
139 // This is owned by the realm, so it needs to not retain the realm
140 __weak RLMRealm *const _realm;
142 } // anonymous namespace
145 std::unique_ptr<realm::BindingContext> RLMCreateBindingContext(__unsafe_unretained RLMRealm *const realm) {
146 return std::unique_ptr<realm::BindingContext>(new RLMNotificationHelper(realm));