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 "RJSSchema.hpp"
#import "RJSList.hpp"
#import "RJSRealm.hpp"
#import "object_store.hpp"
#import "object_accessor.hpp"
@ -63,7 +64,7 @@ JSClassRef RJSObjectClass() {
}
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);
return jsObject;
}
@ -86,13 +87,13 @@ template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueR
return ret;
}
template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, const ObjectSchema &object_schema, const std::string &prop_name) {
ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name);
template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
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) {
ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name);
template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
return defaults[prop_name];
}

View File

@ -17,6 +17,12 @@
////////////////////////////////////////////////////////////////////////////
#import "RJSUtil.hpp"
#include <map>
namespace realm {
class Realm;
using ObjectDefaults = std::map<std::string, JSValueRef>;
}
extern const JSStaticFunction RJSRealmFuncs[];
@ -28,3 +34,6 @@ std::string RJSDefaultPath();
void RJSSetDefaultPath(std::string path);
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) {
}
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) {
#if TARGET_OS_IPHONE
// 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) {
try {
Realm::Config config;
std::map<std::string, realm::ObjectDefaults> defaults;
std::map<std::string, JSValueRef> prototypes;
switch (argumentCount) {
case 0:
config.path = RJSDefaultPath();
@ -139,7 +169,7 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a
static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema");
JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString);
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");
@ -159,8 +189,10 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a
}
SharedRealm realm = Realm::get_shared_realm(config);
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));
}
catch (std::exception &ex) {
@ -422,8 +454,9 @@ JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb
try {
RJSValidateArgumentCount(argumentCount, 0);
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
realm->invalidate();
realm->close();
realm::Realm::s_global_cache.remove(realm->config().path, realm->thread_id());
}
catch (std::exception &exp) {
if (jsException) {

View File

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

View File

@ -45,30 +45,6 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) {
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 JSStringRef nameString = JSStringCreateWithUTF8CString("name");
static JSStringRef typeString = JSStringCreateWithUTF8CString("type");
@ -116,7 +92,7 @@ static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyOb
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 prototypeString = JSStringCreateWithUTF8CString("prototype");
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.");
ObjectSchema objectSchema;
ObjectDefaults defaults;
ObjectDefaults objectDefaults;
static JSStringRef nameString = JSStringCreateWithUTF8CString("name");
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);
if (!JSValueIsUndefined(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");
JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString);
@ -169,18 +144,18 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
// store prototype
if (prototypeObject) {
JSValueProtect(ctx, prototypeObject);
s_prototypes[objectSchema.name] = std::move(prototypeObject);
prototypes[objectSchema.name] = std::move(prototypeObject);
}
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;
size_t length = RJSValidatedListLength(ctx, jsonObject);
for (unsigned int i = 0; i < length; 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));
}

View File

@ -24,7 +24,6 @@
// add realm apis to the given js context
+ (void)initializeContext:(JSContextRef)ctx;
+ (void)clearTestState;
@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) {
[RealmJS clearTestState];
//RJSSchemaClearState(ctx);
return NULL;
}
@ -84,7 +83,7 @@ static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjec
}
+ (void)clearTestState {
realm::Realm::s_global_cache.invalidate_all();
realm::Realm::s_global_cache.close_all();
realm::Realm::s_global_cache.clear();
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 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 ValueType 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, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name);
static bool to_bool(ContextType, ValueType &);
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);
}
else if (created) {
if (Accessor::has_default_value_for_property(ctx, object_schema, prop.name)) {
object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, object_schema, prop.name), try_update);
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, realm.get(), object_schema, prop.name), try_update);
}
else {
throw std::runtime_error("Missing property value for property " + prop.name);

View File

@ -295,6 +295,16 @@ void Realm::invalidate()
m_group = nullptr;
}
void Realm::close()
{
invalidate();
if (m_notifier) {
m_notifier->remove_realm(this);
m_notifier = nullptr;
}
m_delegate = nullptr;
}
bool Realm::compact()
{
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);
for (auto &path_realms : m_cache) {
for (auto &realm_iter : path_realms.second) {
if (auto realm = realm_iter.second.lock()) {
realm->invalidate();
auto thread_realm = path_realms.second.find(thread_id);
if (thread_realm != path_realms.second.end()) {
if (auto realm = thread_realm->second.lock()) {
realm->close();
}
}
}

View File

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