diff options
Diffstat (limited to 'src/core/basetypes/MCObject.cpp')
-rw-r--r-- | src/core/basetypes/MCObject.cpp | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/src/core/basetypes/MCObject.cpp b/src/core/basetypes/MCObject.cpp new file mode 100644 index 00000000..7c1d83bb --- /dev/null +++ b/src/core/basetypes/MCObject.cpp @@ -0,0 +1,449 @@ +#include "MCObject.h" + +#include <stdlib.h> +#include <typeinfo> +#ifndef _MSC_VER +#include <cxxabi.h> +#endif +#include <libetpan/libetpan.h> +#include <string.h> +#if __APPLE__ +#include <Block.h> +#endif + +#include "MCAutoreleasePool.h" +#include "MCString.h" +#include "MCHash.h" +#include "MCLog.h" +#include "MCUtils.h" +#include "MCAssert.h" +#include "MCMainThread.h" +#include "MCLog.h" +#include "MCHashMap.h" + +using namespace mailcore; + +bool mailcore::zombieEnabled = 0; + +Object::Object() +{ + init(); +} + +Object::~Object() +{ +} + +void Object::init() +{ + pthread_mutex_init(&mLock, NULL); + mCounter = 1; +} + +int Object::retainCount() +{ + pthread_mutex_lock(&mLock); + int value = mCounter; + pthread_mutex_unlock(&mLock); + + return value; +} + +Object * Object::retain() +{ + pthread_mutex_lock(&mLock); + mCounter ++; + pthread_mutex_unlock(&mLock); + return this; +} + +void Object::release() +{ + bool shouldRelease = false; + + pthread_mutex_lock(&mLock); + mCounter --; + if (mCounter == 0) { + shouldRelease = true; + } + if (mCounter < 0) { + MCLog("release too much %p %s", this, MCUTF8(className())); + MCAssert(0); + } + pthread_mutex_unlock(&mLock); + + if (shouldRelease && !zombieEnabled) { + //int status; + //char * unmangled = abi::__cxa_demangle(typeid(* this).name(), NULL, NULL, &status); + //MCLog("dealloc %p %s", this, unmangled); + delete this; + } +} + +#ifndef __APPLE__ +Object * Object::autorelease() +{ + AutoreleasePool::autorelease(this); + return this; +} +#endif + +String * Object::className() +{ + int status; +#ifdef _MSC_VER + String * result = String::uniquedStringWithUTF8Characters(typeid(*this).name()); +#else + char * unmangled = abi::__cxa_demangle(typeid(* this).name(), NULL, NULL, &status); + String * result = String::uniquedStringWithUTF8Characters(unmangled); + free(unmangled); +#endif + return result; +} + +String * Object::description() +{ + return String::stringWithUTF8Format("<%s:%p>", className()->UTF8Characters(), this); +} + +bool Object::isEqual(Object * otherObject) +{ + return this == otherObject; +} + +unsigned int Object::hash() +{ + return hashCompute((const char *) this, sizeof(this)); +} + +Object * Object::copy() +{ + MCAssert(0); + return NULL; +} + +void Object::performMethod(Object::Method method, void * context) +{ + (this->*method)(context); +} + +struct mainThreadCallData { + Object * obj; + void * context; + Object::Method method; + void * caller; +}; + +static pthread_once_t delayedPerformOnce = PTHREAD_ONCE_INIT; +static chash * delayedPerformHash = NULL; +static pthread_mutex_t delayedPerformLock = PTHREAD_MUTEX_INITIALIZER; + +static void reallyInitDelayedPerform() +{ + delayedPerformHash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY); +} + +static void initDelayedPerform() +{ + pthread_once(&delayedPerformOnce, reallyInitDelayedPerform); +} + +struct mainThreadCallKeyData { + Object * dispatchQueueIdentifier; + Object * obj; + void * context; + Object::Method method; +}; + +static void removeFromPerformHash(Object * obj, Object::Method method, void * context, void * targetDispatchQueue) +{ + chashdatum key; + struct mainThreadCallKeyData keyData; + Object * queueIdentifier = NULL; +#if __APPLE__ + queueIdentifier = (String *) dispatch_queue_get_specific((dispatch_queue_t) targetDispatchQueue, "MCDispatchQueueID"); +#endif + memset(&keyData, 0, sizeof(keyData)); + keyData.dispatchQueueIdentifier = queueIdentifier; + keyData.obj = obj; + keyData.context = context; + keyData.method = method; + key.data = &keyData; + key.len = sizeof(keyData); + + pthread_mutex_lock(&delayedPerformLock); + chash_delete(delayedPerformHash, (chashdatum *) &key, NULL); + pthread_mutex_unlock(&delayedPerformLock); +} + +static void queueIdentifierDestructor(void * identifier) +{ + Object * obj = (Object *) identifier; + MC_SAFE_RELEASE(obj); +} + +static void addToPerformHash(Object * obj, Object::Method method, void * context, void * targetDispatchQueue, + void * performValue) +{ + chashdatum key; + chashdatum value; + struct mainThreadCallKeyData keyData; + Object * queueIdentifier = NULL; +#if __APPLE__ + queueIdentifier = (String *) dispatch_queue_get_specific((dispatch_queue_t) targetDispatchQueue, "MCDispatchQueueID"); + if (queueIdentifier == NULL) { + queueIdentifier = new Object(); + dispatch_queue_set_specific((dispatch_queue_t) targetDispatchQueue, "MCDispatchQueueID", queueIdentifier, queueIdentifierDestructor); + } +#endif + memset(&keyData, 0, sizeof(keyData)); + keyData.dispatchQueueIdentifier = queueIdentifier; + keyData.obj = obj; + keyData.context = context; + keyData.method = method; + key.data = &keyData; + key.len = sizeof(keyData); + value.data = performValue; + value.len = 0; + pthread_mutex_lock(&delayedPerformLock); + chash_set(delayedPerformHash, &key, &value, NULL); + pthread_mutex_unlock(&delayedPerformLock); +} + +static void * getFromPerformHash(Object * obj, Object::Method method, void * context, void * targetDispatchQueue) +{ + chashdatum key; + chashdatum value; + struct mainThreadCallKeyData keyData; + int r; + + Object * queueIdentifier = NULL; +#if __APPLE__ + MCAssert(targetDispatchQueue != NULL); + queueIdentifier = (String *) dispatch_queue_get_specific((dispatch_queue_t) targetDispatchQueue, "MCDispatchQueueID"); + if (queueIdentifier == NULL) + return NULL; +#endif + memset(&keyData, 0, sizeof(keyData)); + keyData.dispatchQueueIdentifier = queueIdentifier; + keyData.obj = obj; + keyData.context = context; + keyData.method = method; + key.data = &keyData; + key.len = sizeof(keyData); + + pthread_mutex_lock(&delayedPerformLock); + r = chash_get(delayedPerformHash, &key, &value); + pthread_mutex_unlock(&delayedPerformLock); + if (r < 0) + return NULL; + + return value.data; +} + +static void performOnMainThread(void * info) +{ + struct mainThreadCallData * data; + void * context; + Object * obj; + Object::Method method; + + data = (struct mainThreadCallData *) info; + obj = data->obj; + context = data->context; + method = data->method; + + (obj->*method)(context); + + free(data); +} + +static void performAfterDelay(void * info) +{ + struct mainThreadCallData * data; + void * context; + Object * obj; + Object::Method method; + + data = (struct mainThreadCallData *) info; + obj = data->obj; + context = data->context; + method = data->method; + + removeFromPerformHash(obj, method, context, NULL); + (obj->*method)(context); + + free(data); +} + +void Object::performMethodOnMainThread(Method method, void * context, bool waitUntilDone) +{ + struct mainThreadCallData * data; + + data = (struct mainThreadCallData *) calloc(sizeof(* data), 1); + data->obj = this; + data->context = context; + data->method = method; + data->caller = NULL; + + if (waitUntilDone) { + callOnMainThreadAndWait(performOnMainThread, data); + } + else { + callOnMainThread(performOnMainThread, data); + } +} + +#if __APPLE__ +void Object::performMethodOnDispatchQueue(Method method, void * context, void * targetDispatchQueue, bool waitUntilDone) +{ + if (waitUntilDone) { + dispatch_sync((dispatch_queue_t) targetDispatchQueue, ^{ + (this->*method)(context); + }); + } + else { + dispatch_async((dispatch_queue_t) targetDispatchQueue, ^{ + (this->*method)(context); + }); + } +} + +struct cancellableBlock { + void (^block)(void); + bool cancelled; +}; + +void Object::performMethodOnDispatchQueueAfterDelay(Method method, void * context, void * targetDispatchQueue, double delay) +{ + initDelayedPerform(); + + __block bool cancelled = false; + + void (^cancelableBlock)(bool cancel) = ^(bool cancel) { + if (cancel) { + cancelled = true; + return; + } + if (!cancelled) { + (this->*method)(context); + } + }; + + void (^dupCancelableBlock)(bool cancel) = Block_copy(cancelableBlock); + + retain(); + addToPerformHash(this, method, context, targetDispatchQueue, (void *) dupCancelableBlock); + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)); + dispatch_after(popTime, (dispatch_queue_t) targetDispatchQueue, ^(void) { + if (!cancelled) { + removeFromPerformHash(this, method, context, targetDispatchQueue); + } + dupCancelableBlock(false); + Block_release(dupCancelableBlock); + release(); + }); +} + +void Object::cancelDelayedPerformMethodOnDispatchQueue(Method method, void * context, void * targetDispatchQueue) +{ + initDelayedPerform(); + void (^dupCancelableBlock)(bool cancel) = (void (^)(bool)) getFromPerformHash(this, method, context, targetDispatchQueue); + if (dupCancelableBlock == NULL) { + return; + } + removeFromPerformHash(this, method, context, targetDispatchQueue); + dupCancelableBlock(true); +} +#endif + +void Object::performMethodAfterDelay(Method method, void * context, double delay) +{ +#if __APPLE__ + performMethodOnDispatchQueueAfterDelay(method, context, dispatch_get_main_queue(), delay); +#else + initDelayedPerform(); + + struct mainThreadCallData * data; + + data = (struct mainThreadCallData *) calloc(sizeof(* data), 1); + data->obj = this; + data->context = context; + data->method = method; + data->caller = callAfterDelay(performAfterDelay, data, delay); + addToPerformHash(this, method, context, NULL, data); +#endif +} + +void Object::cancelDelayedPerformMethod(Method method, void * context) +{ +#if __APPLE__ + cancelDelayedPerformMethodOnDispatchQueue(method, context, dispatch_get_main_queue()); +#else + initDelayedPerform(); + + struct mainThreadCallData * data = (struct mainThreadCallData *) getFromPerformHash(this, method, context, NULL); + if (data == NULL) + return; + + removeFromPerformHash(this, method, context, NULL); + cancelDelayedCall(data->caller); + free(data); +#endif +} + +HashMap * Object::serializable() +{ + HashMap * result = HashMap::hashMap(); + result->setObjectForKey(MCSTR("class"), className()); + return result; +} + +void Object::importSerializable(HashMap * serializable) +{ + MCAssert(0); +} + +static chash * constructors = NULL; + +void Object::initObjectConstructors() +{ + constructors = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY); +} + +void Object::registerObjectConstructor(const char * className, void * (* objectConstructor)(void)) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once(&once, initObjectConstructors); + + chashdatum key; + chashdatum value; + key.data = (void *) className; + key.len = (unsigned int) strlen(className); + value.data = (void *) objectConstructor; + value.len = 0; + chash_set(constructors, &key, &value, NULL); +} + +Object * Object::objectWithSerializable(HashMap * serializable) +{ + if (serializable == NULL) + return NULL; + + void * (* objectConstructor)(void) = NULL; + + chashdatum key; + chashdatum value; + const char * className = ((String *) serializable->objectForKey(MCSTR("class")))->UTF8Characters(); + key.data = (void *) className; + key.len = (unsigned int) strlen(className); + int r = chash_get(constructors, &key, &value); + if (r < 0) + return NULL; + + objectConstructor = (void * (*)()) value.data; + Object * obj = (Object *) objectConstructor(); + obj->importSerializable(serializable); + return obj->autorelease(); +} + |