added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / RLMRealmUtil.mm
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
19 #import "RLMRealmUtil.hpp"
20
21 #import "RLMObjectSchema_Private.hpp"
22 #import "RLMObservation.hpp"
23 #import "RLMRealm_Private.hpp"
24 #import "RLMUtil.hpp"
25
26 #import <Realm/RLMConstants.h>
27 #import <Realm/RLMSchema.h>
28
29 #import "binding_context.hpp"
30
31 #import <map>
32 #import <mutex>
33 #import <sys/event.h>
34 #import <sys/stat.h>
35 #import <sys/time.h>
36 #import <unistd.h>
37
38 // Global realm state
39 static std::mutex& s_realmCacheMutex = *new std::mutex();
40 static std::map<std::string, NSMapTable *>& s_realmsPerPath = *new std::map<std::string, NSMapTable *>();
41
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];
45     if (!realms) {
46         s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality|NSPointerFunctionsOpaqueMemory
47                                                                valueOptions:NSPointerFunctionsWeakMemory];
48     }
49     [realms setObject:realm forKey:(__bridge id)pthread_self()];
50 }
51
52 RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) {
53     std::lock_guard<std::mutex> lock(s_realmCacheMutex);
54     return [s_realmsPerPath[path] objectEnumerator].nextObject;
55 }
56
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()];
60 }
61
62 void RLMClearRealmCache() {
63     std::lock_guard<std::mutex> lock(s_realmCacheMutex);
64     s_realmsPerPath.clear();
65 }
66
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
70     // be in the future
71     if ([NSThread isMainThread]) {
72         return true;
73     }
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())) {
77         CFRelease(mode);
78         return true;
79     }
80     return false;
81 }
82
83 namespace {
84 class RLMNotificationHelper : public realm::BindingContext {
85 public:
86     RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { }
87
88     bool can_deliver_notifications() const noexcept override {
89         return RLMIsInRunLoop();
90     }
91
92     void changes_available() override {
93         @autoreleasepool {
94             auto realm = _realm;
95             if (realm && !realm.autorefresh) {
96                 [realm sendNotifications:RLMRealmRefreshRequiredNotification];
97             }
98         }
99     }
100
101     std::vector<ObserverState> get_observed_rows() override {
102         @autoreleasepool {
103             if (auto realm = _realm) {
104                 [realm detachAllEnumerators];
105                 return RLMGetObservedRows(realm->_info);
106             }
107             return {};
108         }
109     }
110
111     void will_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated) override {
112         @autoreleasepool {
113             RLMWillChange(observed, invalidated);
114         }
115     }
116
117     void did_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated, bool version_changed) override {
118         try {
119             @autoreleasepool {
120                 RLMDidChange(observed, invalidated);
121                 if (version_changed) {
122                     [_realm sendNotifications:RLMRealmDidChangeNotification];
123                 }
124             }
125         }
126         catch (...) {
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];
133             }
134             throw;
135         }
136     }
137
138 private:
139     // This is owned by the realm, so it needs to not retain the realm
140     __weak RLMRealm *const _realm;
141 };
142 } // anonymous namespace
143
144
145 std::unique_ptr<realm::BindingContext> RLMCreateBindingContext(__unsafe_unretained RLMRealm *const realm) {
146     return std::unique_ptr<realm::BindingContext>(new RLMNotificationHelper(realm));
147 }