You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
5.0 KiB
147 lines
5.0 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2014 Realm Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#import "RLMRealmUtil.hpp"
|
|
|
|
#import "RLMObjectSchema_Private.hpp"
|
|
#import "RLMObservation.hpp"
|
|
#import "RLMRealm_Private.hpp"
|
|
#import "RLMUtil.hpp"
|
|
|
|
#import <Realm/RLMConstants.h>
|
|
#import <Realm/RLMSchema.h>
|
|
|
|
#import "binding_context.hpp"
|
|
|
|
#import <map>
|
|
#import <mutex>
|
|
#import <sys/event.h>
|
|
#import <sys/stat.h>
|
|
#import <sys/time.h>
|
|
#import <unistd.h>
|
|
|
|
// Global realm state
|
|
static std::mutex& s_realmCacheMutex = *new std::mutex();
|
|
static std::map<std::string, NSMapTable *>& s_realmsPerPath = *new std::map<std::string, NSMapTable *>();
|
|
|
|
void RLMCacheRealm(std::string const& path, __unsafe_unretained RLMRealm *const realm) {
|
|
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
|
NSMapTable *realms = s_realmsPerPath[path];
|
|
if (!realms) {
|
|
s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality|NSPointerFunctionsOpaqueMemory
|
|
valueOptions:NSPointerFunctionsWeakMemory];
|
|
}
|
|
[realms setObject:realm forKey:(__bridge id)pthread_self()];
|
|
}
|
|
|
|
RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) {
|
|
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
|
return [s_realmsPerPath[path] objectEnumerator].nextObject;
|
|
}
|
|
|
|
RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path) {
|
|
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
|
return [s_realmsPerPath[path] objectForKey:(__bridge id)pthread_self()];
|
|
}
|
|
|
|
void RLMClearRealmCache() {
|
|
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
|
s_realmsPerPath.clear();
|
|
}
|
|
|
|
bool RLMIsInRunLoop() {
|
|
// The main thread may not be in a run loop yet if we're called from
|
|
// something like `applicationDidFinishLaunching:`, but it presumably will
|
|
// be in the future
|
|
if ([NSThread isMainThread]) {
|
|
return true;
|
|
}
|
|
// Current mode indicates why the current callout from the runloop was made,
|
|
// and is null if a runloop callout isn't currently being processed
|
|
if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) {
|
|
CFRelease(mode);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
class RLMNotificationHelper : public realm::BindingContext {
|
|
public:
|
|
RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { }
|
|
|
|
bool can_deliver_notifications() const noexcept override {
|
|
return RLMIsInRunLoop();
|
|
}
|
|
|
|
void changes_available() override {
|
|
@autoreleasepool {
|
|
auto realm = _realm;
|
|
if (realm && !realm.autorefresh) {
|
|
[realm sendNotifications:RLMRealmRefreshRequiredNotification];
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<ObserverState> get_observed_rows() override {
|
|
@autoreleasepool {
|
|
if (auto realm = _realm) {
|
|
[realm detachAllEnumerators];
|
|
return RLMGetObservedRows(realm->_info);
|
|
}
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void will_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated) override {
|
|
@autoreleasepool {
|
|
RLMWillChange(observed, invalidated);
|
|
}
|
|
}
|
|
|
|
void did_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated, bool version_changed) override {
|
|
try {
|
|
@autoreleasepool {
|
|
RLMDidChange(observed, invalidated);
|
|
if (version_changed) {
|
|
[_realm sendNotifications:RLMRealmDidChangeNotification];
|
|
}
|
|
}
|
|
}
|
|
catch (...) {
|
|
// This can only be called during a write transaction if it was
|
|
// called due to the transaction beginning, so cancel it to ensure
|
|
// exceptions thrown here behave the same as exceptions thrown when
|
|
// actually beginning the write
|
|
if (_realm.inWriteTransaction) {
|
|
[_realm cancelWriteTransaction];
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private:
|
|
// This is owned by the realm, so it needs to not retain the realm
|
|
__weak RLMRealm *const _realm;
|
|
};
|
|
} // anonymous namespace
|
|
|
|
|
|
std::unique_ptr<realm::BindingContext> RLMCreateBindingContext(__unsafe_unretained RLMRealm *const realm) {
|
|
return std::unique_ptr<realm::BindingContext>(new RLMNotificationHelper(realm));
|
|
}
|
|
|