Merge pull request #66 from realm/al-per-realm

Store schema/defaults per Realm, Cleanup per Realm state and threads
This commit is contained in:
Ari Lazier 2015-10-19 15:29:04 -07:00
commit 3b5c78d29f
10 changed files with 81 additions and 57 deletions

View File

@ -21,6 +21,7 @@
#import "RJSResults.hpp" #import "RJSResults.hpp"
#import "RJSSchema.hpp" #import "RJSSchema.hpp"
#import "RJSList.hpp" #import "RJSList.hpp"
#import "RJSRealm.hpp"
#import "object_store.hpp" #import "object_store.hpp"
#import "object_accessor.hpp" #import "object_accessor.hpp"
@ -63,7 +64,7 @@ JSClassRef RJSObjectClass() {
} }
JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) { JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) {
JSValueRef prototype = RJSPrototypeForClassName(object.object_schema.name); JSValueRef prototype = RJSPrototypes(object.realm.get())[object.object_schema.name];
JSObjectRef jsObject = RJSWrapObject(ctx, RJSObjectClass(), new Object(object), prototype); JSObjectRef jsObject = RJSWrapObject(ctx, RJSObjectClass(), new Object(object), prototype);
return jsObject; return jsObject;
} }
@ -86,13 +87,13 @@ template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueR
return ret; return ret;
} }
template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, const ObjectSchema &object_schema, const std::string &prop_name) { template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name); ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
return defaults.find(prop_name) != defaults.end(); return defaults.find(prop_name) != defaults.end();
} }
template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, const ObjectSchema &object_schema, const std::string &prop_name) { template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name); ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
return defaults[prop_name]; return defaults[prop_name];
} }

View File

@ -17,6 +17,12 @@
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
#import "RJSUtil.hpp" #import "RJSUtil.hpp"
#include <map>
namespace realm {
class Realm;
using ObjectDefaults = std::map<std::string, JSValueRef>;
}
extern const JSStaticFunction RJSRealmFuncs[]; extern const JSStaticFunction RJSRealmFuncs[];
@ -28,3 +34,6 @@ std::string RJSDefaultPath();
void RJSSetDefaultPath(std::string path); void RJSSetDefaultPath(std::string path);
JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException); JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException);
std::map<std::string, realm::ObjectDefaults> &RJSDefaults(realm::Realm *realm);
std::map<std::string, JSValueRef> &RJSPrototypes(realm::Realm *realm);

View File

@ -59,8 +59,36 @@ public:
std::vector<void*> const& invalidated) { std::vector<void*> const& invalidated) {
} }
JSGlobalContextRef m_context;
std::map<std::string, ObjectDefaults> m_defaults;
std::map<std::string, JSValueRef> m_prototypes;
RJSRealmDelegate(JSGlobalContextRef ctx) : m_context(ctx) {
JSGlobalContextRetain(m_context);
}
~RJSRealmDelegate() {
for (auto prototype : m_prototypes) {
JSValueUnprotect(m_context, prototype.second);
}
for (auto objectDefaults : m_defaults) {
for (auto value : objectDefaults.second) {
JSValueUnprotect(m_context, value.second);
}
}
JSGlobalContextRelease(m_context);
}
}; };
std::map<std::string, ObjectDefaults> &RJSDefaults(Realm *realm) {
return static_cast<RJSRealmDelegate *>(realm->m_delegate.get())->m_defaults;
}
std::map<std::string, JSValueRef> &RJSPrototypes(Realm *realm) {
return static_cast<RJSRealmDelegate *>(realm->m_delegate.get())->m_prototypes;
}
std::string writeablePathForFile(const std::string &fileName) { std::string writeablePathForFile(const std::string &fileName) {
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
// On iOS the Documents directory isn't user-visible, so put files there // On iOS the Documents directory isn't user-visible, so put files there
@ -114,6 +142,8 @@ static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef pro
JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try { try {
Realm::Config config; Realm::Config config;
std::map<std::string, realm::ObjectDefaults> defaults;
std::map<std::string, JSValueRef> prototypes;
switch (argumentCount) { switch (argumentCount) {
case 0: case 0:
config.path = RJSDefaultPath(); config.path = RJSDefaultPath();
@ -139,7 +169,7 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a
static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema");
JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString);
if (!JSValueIsUndefined(ctx, schemaValue)) { if (!JSValueIsUndefined(ctx, schemaValue)) {
config.schema = std::make_unique<Schema>(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue))); config.schema = std::make_unique<Schema>(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, prototypes));
} }
static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion");
@ -159,8 +189,10 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a
} }
SharedRealm realm = Realm::get_shared_realm(config); SharedRealm realm = Realm::get_shared_realm(config);
if (!realm->m_delegate) { if (!realm->m_delegate) {
realm->m_delegate = std::make_unique<RJSRealmDelegate>(); realm->m_delegate = std::make_unique<RJSRealmDelegate>(JSContextGetGlobalContext(ctx));
} }
RJSDefaults(realm.get()) = defaults;
RJSPrototypes(realm.get()) = prototypes;
return RJSWrapObject<SharedRealm *>(ctx, RJSRealmClass(), new SharedRealm(realm)); return RJSWrapObject<SharedRealm *>(ctx, RJSRealmClass(), new SharedRealm(realm));
} }
catch (std::exception &ex) { catch (std::exception &ex) {
@ -422,8 +454,9 @@ JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb
try { try {
RJSValidateArgumentCount(argumentCount, 0); RJSValidateArgumentCount(argumentCount, 0);
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject); SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
realm->invalidate(); realm->close();
realm::Realm::s_global_cache.remove(realm->config().path, realm->thread_id()); realm::Realm::s_global_cache.remove(realm->config().path, realm->thread_id());
} }
catch (std::exception &exp) { catch (std::exception &exp) {
if (jsException) { if (jsException) {

View File

@ -27,8 +27,4 @@ namespace realm {
JSClassRef RJSSchemaClass(); JSClassRef RJSSchemaClass();
JSObjectRef RJSSchemaCreate(JSContextRef ctx, realm::Schema *schema); JSObjectRef RJSSchemaCreate(JSContextRef ctx, realm::Schema *schema);
realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject); realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map<std::string, realm::ObjectDefaults> &defaults, std::map<std::string, JSValueRef> &prototypes);
JSValueRef RJSPrototypeForClassName(const std::string &className);
realm::ObjectDefaults &RJSDefaultsForClassName(const std::string &className);
void RJSSchemaClearState(JSContextRef ctx);

View File

@ -45,30 +45,6 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) {
return RJSWrapObject(ctx, RJSSchemaClass(), wrapper); return RJSWrapObject(ctx, RJSSchemaClass(), wrapper);
} }
static std::map<std::string, ObjectDefaults> s_defaults;
ObjectDefaults &RJSDefaultsForClassName(const std::string &className) {
return s_defaults[className];
}
static std::map<std::string, JSValueRef> s_prototypes;
JSValueRef RJSPrototypeForClassName(const std::string &className) {
return s_prototypes[className];
}
void RJSSchemaClearState(JSContextRef ctx) {
for (auto prototype : s_prototypes) {
JSValueUnprotect(ctx, prototype.second);
}
s_prototypes.clear();
for (auto defaults : s_defaults) {
for (auto value : defaults.second) {
JSValueUnprotect(ctx, value.second);
}
}
s_defaults.clear();
}
static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyObject) { static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyObject) {
static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); static JSStringRef nameString = JSStringCreateWithUTF8CString("name");
static JSStringRef typeString = JSStringCreateWithUTF8CString("type"); static JSStringRef typeString = JSStringCreateWithUTF8CString("type");
@ -116,7 +92,7 @@ static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyOb
return prop; return prop;
} }
static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef objectSchemaObject) { static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef objectSchemaObject, std::map<std::string, realm::ObjectDefaults> &defaults, std::map<std::string, JSValueRef> &prototypes) {
static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema");
static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype"); static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype");
JSObjectRef prototypeObject = NULL; JSObjectRef prototypeObject = NULL;
@ -136,8 +112,7 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema object must have a 'properties' array."); JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema object must have a 'properties' array.");
ObjectSchema objectSchema; ObjectSchema objectSchema;
ObjectDefaults defaults; ObjectDefaults objectDefaults;
static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); static JSStringRef nameString = JSStringCreateWithUTF8CString("name");
objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString); objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString);
@ -150,10 +125,10 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
JSValueRef defaultValue = JSObjectGetProperty(ctx, property, defaultString, NULL); JSValueRef defaultValue = JSObjectGetProperty(ctx, property, defaultString, NULL);
if (!JSValueIsUndefined(ctx, defaultValue)) { if (!JSValueIsUndefined(ctx, defaultValue)) {
JSValueProtect(ctx, defaultValue); JSValueProtect(ctx, defaultValue);
defaults.emplace(objectSchema.properties.back().name, defaultValue); objectDefaults.emplace(objectSchema.properties.back().name, defaultValue);
} }
} }
s_defaults.emplace(objectSchema.name, std::move(defaults)); defaults.emplace(objectSchema.name, std::move(objectDefaults));
static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey"); static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey");
JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString); JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString);
@ -169,18 +144,18 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
// store prototype // store prototype
if (prototypeObject) { if (prototypeObject) {
JSValueProtect(ctx, prototypeObject); JSValueProtect(ctx, prototypeObject);
s_prototypes[objectSchema.name] = std::move(prototypeObject); prototypes[objectSchema.name] = std::move(prototypeObject);
} }
return objectSchema; return objectSchema;
} }
realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject) { realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map<std::string, realm::ObjectDefaults> &defaults, std::map<std::string, JSValueRef> &prototypes) {
std::vector<ObjectSchema> schema; std::vector<ObjectSchema> schema;
size_t length = RJSValidatedListLength(ctx, jsonObject); size_t length = RJSValidatedListLength(ctx, jsonObject);
for (unsigned int i = 0; i < length; i++) { for (unsigned int i = 0; i < length; i++) {
JSObjectRef jsonObjectSchema = RJSValidatedObjectAtIndex(ctx, jsonObject, i); JSObjectRef jsonObjectSchema = RJSValidatedObjectAtIndex(ctx, jsonObject, i);
ObjectSchema objectSchema = RJSParseObjectSchema(ctx, jsonObjectSchema); ObjectSchema objectSchema = RJSParseObjectSchema(ctx, jsonObjectSchema, defaults, prototypes);
schema.emplace_back(std::move(objectSchema)); schema.emplace_back(std::move(objectSchema));
} }

View File

@ -24,7 +24,6 @@
// add realm apis to the given js context // add realm apis to the given js context
+ (void)initializeContext:(JSContextRef)ctx; + (void)initializeContext:(JSContextRef)ctx;
+ (void)clearTestState; + (void)clearTestState;
@end @end

View File

@ -58,7 +58,6 @@ NSString *RealmFileDirectory() {
static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
[RealmJS clearTestState]; [RealmJS clearTestState];
//RJSSchemaClearState(ctx);
return NULL; return NULL;
} }
@ -84,7 +83,7 @@ static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjec
} }
+ (void)clearTestState { + (void)clearTestState {
realm::Realm::s_global_cache.invalidate_all(); realm::Realm::s_global_cache.close_all();
realm::Realm::s_global_cache.clear(); realm::Realm::s_global_cache.clear();
NSFileManager *manager = [NSFileManager defaultManager]; NSFileManager *manager = [NSFileManager defaultManager];

View File

@ -60,8 +60,8 @@ namespace realm {
static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name);
static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name);
static bool has_default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); static bool has_default_value_for_property(ContextType ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name);
static ValueType default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); static ValueType default_value_for_property(ContextType ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name);
static bool to_bool(ContextType, ValueType &); static bool to_bool(ContextType, ValueType &);
static ValueType from_bool(ContextType, bool); static ValueType from_bool(ContextType, bool);
@ -260,8 +260,8 @@ namespace realm {
object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update); object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update);
} }
else if (created) { else if (created) {
if (Accessor::has_default_value_for_property(ctx, object_schema, prop.name)) { if (Accessor::has_default_value_for_property(ctx, realm.get(), object_schema, prop.name)) {
object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, object_schema, prop.name), try_update); object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, realm.get(), object_schema, prop.name), try_update);
} }
else { else {
throw std::runtime_error("Missing property value for property " + prop.name); throw std::runtime_error("Missing property value for property " + prop.name);

View File

@ -295,6 +295,16 @@ void Realm::invalidate()
m_group = nullptr; m_group = nullptr;
} }
void Realm::close()
{
invalidate();
if (m_notifier) {
m_notifier->remove_realm(this);
m_notifier = nullptr;
}
m_delegate = nullptr;
}
bool Realm::compact() bool Realm::compact()
{ {
verify_thread(); verify_thread();
@ -440,14 +450,15 @@ void RealmCache::cache_realm(SharedRealm &realm, std::thread::id thread_id)
} }
} }
void RealmCache::invalidate_all() void RealmCache::close_all(std::thread::id thread_id)
{ {
std::lock_guard<std::mutex> lock(m_mutex); std::lock_guard<std::mutex> lock(m_mutex);
for (auto &path_realms : m_cache) { for (auto &path_realms : m_cache) {
for (auto &realm_iter : path_realms.second) { auto thread_realm = path_realms.second.find(thread_id);
if (auto realm = realm_iter.second.lock()) { if (thread_realm != path_realms.second.end()) {
realm->invalidate(); if (auto realm = thread_realm->second.lock()) {
realm->close();
} }
} }
} }

View File

@ -94,6 +94,7 @@ namespace realm {
void invalidate(); void invalidate();
bool compact(); bool compact();
void close();
std::thread::id thread_id() const { return m_thread_id; } std::thread::id thread_id() const { return m_thread_id; }
void verify_thread() const; void verify_thread() const;
@ -131,7 +132,7 @@ namespace realm {
SharedRealm get_any_realm(const std::string &path); SharedRealm get_any_realm(const std::string &path);
void remove(const std::string &path, std::thread::id thread_id); void remove(const std::string &path, std::thread::id thread_id);
void cache_realm(SharedRealm &realm, std::thread::id thread_id = std::this_thread::get_id()); void cache_realm(SharedRealm &realm, std::thread::id thread_id = std::this_thread::get_id());
void invalidate_all(); void close_all(std::thread::id thread_id = std::this_thread::get_id());
void clear(); void clear();
private: private: