aboutsummaryrefslogtreecommitdiffstats
path: root/libparc/parc/algol/parc_Object.c
diff options
context:
space:
mode:
Diffstat (limited to 'libparc/parc/algol/parc_Object.c')
-rw-r--r--libparc/parc/algol/parc_Object.c952
1 files changed, 952 insertions, 0 deletions
diff --git a/libparc/parc/algol/parc_Object.c b/libparc/parc/algol/parc_Object.c
new file mode 100644
index 00000000..578f1e1a
--- /dev/null
+++ b/libparc/parc/algol/parc_Object.c
@@ -0,0 +1,952 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * 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.
+ */
+
+/**
+ */
+#include <config.h>
+
+#include <LongBow/runtime.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <parc/algol/parc_DisplayIndented.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/concurrent/parc_AtomicUint64.h>
+
+typedef struct parc_object_locking {
+ pthread_mutex_t lock;
+ pthread_cond_t notification;
+ pthread_t locker;
+} _PARCObjectLocking;
+
+/*
+ * This is the per-object header.
+ * The size of this structure must be less than or equal to the value used in the parcObject_PrefixLength macro.
+ */
+typedef struct object_header {
+#define PARCObject_HEADER_MAGIC_GUARD_NUMBER 0x0ddFadda
+ uint32_t magicGuardNumber;
+ bool isAllocated;
+ bool barrier;
+ PARCReferenceCount references;
+ const PARCObjectDescriptor *descriptor;
+
+ // The locking member points to the locking structure or is NULL if the object does not support locking.
+ _PARCObjectLocking *locking;
+
+ // Currently every object is lockable, but at some point in the future this will be controlled by the descriptor.
+ _PARCObjectLocking lock;
+
+ void *data[];
+} _PARCObjectHeader;
+
+/*
+ * Increment/decrement a pointer by the given value.
+ *
+ * @param [in] base The base pointer.
+ * @param [in] increment The value of the pointer increment.
+ *
+ * @return void* The updated pointer
+ */
+static inline void *
+_pointerAdd(const void *base, const size_t increment)
+{
+ void *result = (void *) &((char *) base)[increment];
+ return result;
+}
+
+/*
+ * Compute the size of the prefix as the number of bytes necessary
+ * to fit the object header (with padding), and any additional per-object data, into allocated memory,
+ * such that the first address after the header and any additional per-object data,
+ * is aligned according to the value of the alignment parameter.
+ *
+ * For example, if the object header is 5 bytes long, and the alignment is 4 (bytes),
+ * then the necessary number of bytes to fit the header
+ * (and yet maintain the proper alignment for the first memory location after the header) is 8.
+ *
+ * @param [in] alignment Cache alignment
+ *
+ * @return The size of the object header as the number of bytes necessary to fit the header (with padding) into allocated memory.
+ */
+static inline size_t
+_parcObject_PrefixLength(const PARCObjectDescriptor *descriptor)
+{
+ return parcObject_PrefixLength(descriptor->objectAlignment);
+}
+
+/*
+ * Given the memory address, return a pointer to the corresponding _PARCObjectHeader structure.
+ *
+ * @param [in] object The base pointer to the object.
+ *
+ * @return A _PARCObjectHeader struct encapsulating the object's header information.
+ */
+static inline _PARCObjectHeader *
+_parcObject_Header(const PARCObject *object)
+{
+ return _pointerAdd(object, -sizeof(_PARCObjectHeader));
+}
+
+static inline const PARCObjectDescriptor *
+_parcObject_Descriptor(const PARCObject *object)
+{
+ return (_parcObject_Header(object)->descriptor);
+}
+
+static inline _PARCObjectLocking *
+_parcObjectHeader_Locking(const PARCObject *object)
+{
+ return _parcObject_Header(object)->locking;
+}
+
+static inline bool
+_parcObjectHeader_IsValid(const _PARCObjectHeader *header, const PARCObject *object)
+{
+ bool result = true;
+
+ if ((intptr_t) header >= (intptr_t) object) {
+ result = false;
+ } else if (header->magicGuardNumber != PARCObject_HEADER_MAGIC_GUARD_NUMBER) {
+ result = false;
+ } else if (header->references == 0) {
+ result = false;
+ }
+
+ return result;
+}
+
+/*
+ * Compute the origin of the allocated memory.
+ *
+ * This will be greater than the start of the object header.
+ * due to alignment requirements causing the object header to be offset from the origin.
+ */
+static inline void *
+_parcObject_Origin(const PARCObject *object)
+{
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ return _pointerAdd(object, -_parcObject_PrefixLength(header->descriptor));
+}
+
+static inline PARCObjectEquals *
+_parcObject_ResolveEquals(const PARCObjectDescriptor *descriptor)
+{
+ while (descriptor->equals == NULL) {
+ descriptor = descriptor->super;
+ }
+ return descriptor->equals;
+}
+
+static inline PARCObjectCopy *
+_parcObject_ResolveCopy(const PARCObjectDescriptor *descriptor)
+{
+ while (descriptor->copy == NULL) {
+ descriptor = descriptor->super;
+ }
+ return descriptor->copy;
+}
+
+static inline PARCObjectToString *
+_parcObject_ResolveToString(const PARCObjectDescriptor *descriptor)
+{
+ while (descriptor->toString == NULL) {
+ descriptor = descriptor->super;
+ }
+ return descriptor->toString;
+}
+
+static inline PARCObjectToJSON *
+_parcObject_ResolveToJSON(const PARCObjectDescriptor *descriptor)
+{
+ while (descriptor->toJSON == NULL) {
+ descriptor = descriptor->super;
+ }
+ return descriptor->toJSON;
+}
+
+static bool
+_parcObject_Destructor(const PARCObjectDescriptor *descriptor, PARCObject **object)
+{
+ if (descriptor != NULL) {
+ if (descriptor->destructor != NULL) {
+ return descriptor->destructor(object);
+ } else if (descriptor->destroy != NULL) {
+ descriptor->destroy(object);
+ }
+ }
+
+ return true;
+}
+
+static int
+_parcObject_Compare(const PARCObject *self, const PARCObject *other)
+{
+ _PARCObjectHeader *header = _parcObject_Header(self);
+ size_t length = header->descriptor->objectSize;
+ int result = memcmp(self, other, length);
+ return result;
+}
+
+static PARCObject *
+_parcObject_Copy(const PARCObject *object)
+{
+ _PARCObjectHeader *header = _parcObject_Header(object);
+ size_t length = header->descriptor->objectSize;
+
+ void *result = parcObject_CreateInstanceImpl(header->descriptor);
+ memcpy(result, object, length);
+ parcObject_OptionalAssertValid(result);
+ return result;
+}
+
+static bool
+_parcObject_Equals(const PARCObject *x, const PARCObject *y)
+{
+ _PARCObjectHeader *header = _parcObject_Header(x);
+
+ bool result = memcmp(x, y, header->descriptor->objectSize) == 0;
+
+ return result;
+}
+
+static char *
+_parcObject_ToString(const PARCObject *object)
+{
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ char *string;
+ int nwritten = asprintf(&string,
+ "Object@%p { .references=%" PRId64 ", .objectLength = %zd, .objectAlignment=%u } data %p\n",
+ (void *) header,
+ header->references, header->descriptor->objectSize, header->descriptor->objectAlignment, object);
+ assertTrue(nwritten >= 0, "Error calling asprintf");
+ char *result = parcMemory_StringDuplicate(string, strlen(string));
+ free(string);
+ return result;
+}
+
+static PARCJSON *
+_parcObject_ToJSON(const PARCObject *object)
+{
+ _PARCObjectHeader *prefix = _parcObject_Header(object);
+
+ PARCJSON *json = parcJSON_Create();
+
+ parcJSON_AddInteger(json, "references", prefix->references);
+ parcJSON_AddInteger(json, "objectLength", prefix->descriptor->objectSize);
+ parcJSON_AddInteger(json, "objectAlignment", prefix->descriptor->objectAlignment);
+
+ char *addressString;
+ int nwritten = asprintf(&addressString, "%p", object);
+ assertTrue(nwritten >= 0, "Error calling asprintf");
+
+ parcJSON_AddString(json, "address", addressString);
+
+ return json;
+}
+
+static PARCHashCode
+_parcObject_HashCode(const PARCObject *object)
+{
+ _PARCObjectHeader *header = _parcObject_Header(object);
+ return parcHashCode_Hash(object, header->descriptor->objectSize);
+}
+
+static void
+_parcObject_Display(const PARCObject *object, const int indentation)
+{
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ parcDisplayIndented_PrintLine(indentation, "PARCObject@%p @%p={ .name=%s .references=%zd }\n",
+ object, header, header->descriptor->name, header->references);
+}
+
+const PARCObjectDescriptor
+parcObject_DescriptorName(PARCObject) =
+{
+ .name = "PARCObject",
+ .destroy = NULL,
+ .destructor = NULL,
+ .release = NULL,
+ .copy = _parcObject_Copy,
+ .toString = _parcObject_ToString,
+ .equals = _parcObject_Equals,
+ .compare = _parcObject_Compare,
+ .hashCode = _parcObject_HashCode,
+ .toJSON = _parcObject_ToJSON,
+ .display = _parcObject_Display,
+ .super = NULL,
+ .isLockable = true,
+ .objectSize = 0,
+ .objectAlignment = sizeof(void *)
+};
+
+bool
+parcObject_IsValid(const PARCObject *object)
+{
+ bool result = true;
+
+ if (object == NULL) {
+ result = false;
+ } else {
+ if (_parcObjectHeader_IsValid(_parcObject_Header(object), object) == false) {
+ result = false;
+ }
+ }
+
+ return result;
+}
+
+#ifdef PARCLibrary_DISABLE_VALIDATION
+# define parcObjectHeader_OptionalAssertValid(_instance_)
+#else
+# define parcObjectHeader_OptionalAssertValid(_instance_) _parcObjectHeader_AssertValid(_instance_)
+#endif
+
+static inline void
+_parcObjectHeader_AssertValid(const _PARCObjectHeader *header, const PARCObject *object)
+{
+ trapIllegalValueIf(header->magicGuardNumber != PARCObject_HEADER_MAGIC_GUARD_NUMBER, "PARCObject@%p is corrupt.", object);
+ trapIllegalValueIf(header->descriptor == NULL, "PARCObject@%p descriptor cannot be NULL.", object);
+ if (header->descriptor->isLockable) {
+ trapIllegalValueIf(header->locking == NULL, "PARCObject@%p is corrupt. Is Lockable but no locking structure", object);
+ }
+}
+
+static inline void
+_parcObject_AssertValid(const PARCObject *object)
+{
+ trapIllegalValueIf(object == NULL, "PARCObject must be a non-null pointer.");
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+ _parcObjectHeader_AssertValid(header, object);
+}
+
+void
+parcObject_AssertValid(const PARCObject *object)
+{
+ _parcObject_AssertValid(object);
+}
+
+PARCObject *
+parcObject_Acquire(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ parcAtomicUint64_Increment(&header->references);
+
+ return (PARCObject *) object;
+}
+
+static inline PARCObjectCompare *
+_parcObject_ResolveCompare(const PARCObjectDescriptor *descriptor)
+{
+ while (descriptor->compare == NULL) {
+ descriptor = descriptor->super;
+ }
+ return descriptor->compare;
+}
+
+int
+parcObject_Compare(const PARCObject *x, const PARCObject *y)
+{
+ int result = 0;
+ if ((x == NULL) || (y == NULL)) {
+ result = 0;
+ if (x != NULL) {
+ result = 1;
+ } else if (y != NULL) {
+ result = -1;
+ }
+ return result;
+ }
+
+ parcObject_OptionalAssertValid(x);
+ parcObject_OptionalAssertValid(y);
+
+ PARCObjectCompare *compare = _parcObject_ResolveCompare(_parcObject_Descriptor(x));
+ result = compare(x, y);
+
+ return result;
+}
+
+bool
+parcObject_IsInstanceOf(const PARCObject *object, const PARCObjectDescriptor *descriptor)
+{
+ bool result = false;
+
+ if (object != NULL) {
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ if (_parcObjectHeader_IsValid(header, object)) {
+ const PARCObjectDescriptor *d = _parcObject_Descriptor(object);
+
+ while (result == false) {
+ if (d == descriptor) {
+ result = true;
+ }
+ d = d->super;
+ }
+ }
+ }
+
+ return result;
+}
+
+bool
+parcObject_Equals(const PARCObject *x, const PARCObject *y)
+{
+ bool result = false;
+
+ if (x == y) {
+ result = true;
+ } else if (x != NULL && y != NULL) {
+ _PARCObjectHeader *xHeader = _parcObject_Header(x);
+ _PARCObjectHeader *yHeader = _parcObject_Header(y);
+
+ if (xHeader->descriptor == yHeader->descriptor) {
+ PARCObjectEquals *equals = _parcObject_ResolveEquals(xHeader->descriptor);
+ result = equals(x, y);
+ }
+ }
+
+ return result;
+}
+
+static inline PARCObjectHashCode *
+_parcObject_ResolveHashCode(const PARCObjectDescriptor *descriptor)
+{
+ while (descriptor->hashCode == NULL) {
+ descriptor = descriptor->super;
+ }
+ return descriptor->hashCode;
+}
+
+PARCHashCode
+parcObject_HashCode(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ PARCObjectHashCode *hashCode = _parcObject_ResolveHashCode(_parcObject_Descriptor(object));
+
+ return hashCode(object);
+}
+
+static inline PARCObjectDisplay *
+_parcObject_ResolveDisplay(const PARCObjectDescriptor *descriptor)
+{
+ while (descriptor->display == NULL) {
+ descriptor = descriptor->super;
+ }
+ return descriptor->display;
+}
+
+void
+parcObject_Display(const PARCObject *object, const int indentation)
+{
+ parcObject_OptionalAssertValid(object);
+
+ PARCObjectDisplay *display = _parcObject_ResolveDisplay(_parcObject_Descriptor(object));
+
+ display(object, indentation);
+}
+
+char *
+parcObject_ToString(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ PARCObjectToString *toString = _parcObject_ResolveToString(_parcObject_Descriptor(object));
+
+ return toString(object);
+}
+
+PARCJSON *
+parcObject_ToJSON(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ PARCObjectToJSON *toJSON = _parcObject_ResolveToJSON(_parcObject_Descriptor(object));
+ return toJSON(object);
+}
+
+PARCObject *
+parcObject_CreateAndClearInstanceImpl(const PARCObjectDescriptor *descriptor)
+{
+ PARCObject *result = parcObject_CreateInstanceImpl(descriptor);
+ memset(result, 0, descriptor->objectSize);
+ return result;
+}
+
+static pthread_once_t _parcObject_GlobalLockAttributesInitialized = PTHREAD_ONCE_INIT;
+static pthread_mutexattr_t _parcObject_GlobalLockAttributes;
+
+static void
+_parcObject_InitializeGobalLockAttributes(void)
+{
+ pthread_mutexattr_init(&_parcObject_GlobalLockAttributes);
+ pthread_mutexattr_settype(&_parcObject_GlobalLockAttributes, PTHREAD_MUTEX_ERRORCHECK);
+}
+
+static inline void
+_parcObject_InitializeLocking(_PARCObjectLocking *locking)
+{
+ if (locking != NULL) {
+ pthread_once(&_parcObject_GlobalLockAttributesInitialized, _parcObject_InitializeGobalLockAttributes);
+
+ pthread_mutex_init(&locking->lock, &_parcObject_GlobalLockAttributes);
+ pthread_cond_init(&locking->notification, NULL);
+
+ locking->locker = (pthread_t) NULL;
+ }
+}
+
+static inline _PARCObjectHeader *
+_parcObjectHeader_InitAllocated(_PARCObjectHeader *header, const PARCObjectDescriptor *descriptor)
+{
+ header->magicGuardNumber = PARCObject_HEADER_MAGIC_GUARD_NUMBER;
+ header->barrier = false;
+ header->references = 1;
+ header->descriptor = (PARCObjectDescriptor *) descriptor;
+ header->isAllocated = true;
+
+ if (header->descriptor->isLockable) {
+ header->locking = &header->lock;
+ _parcObject_InitializeLocking(header->locking);
+ } else {
+ header->locking = NULL;
+ }
+
+ return header;
+}
+
+static inline _PARCObjectHeader *
+_parcObjectHeader_InitUnallocated(_PARCObjectHeader *header, const PARCObjectDescriptor *descriptor)
+{
+ _parcObjectHeader_InitAllocated(header, descriptor);
+ header->isAllocated = false;
+
+ return header;
+}
+
+PARCObject *
+parcObject_WrapImpl(void *memory, const PARCObjectDescriptor *descriptor)
+{
+ size_t prefixLength = _parcObject_PrefixLength(descriptor);
+ PARCObject *object = _pointerAdd(memory, prefixLength);
+
+ _parcObjectHeader_InitUnallocated(_parcObject_Header(object), descriptor);
+
+ return object;
+}
+
+PARCObject *
+parcObject_CreateInstanceImpl(const PARCObjectDescriptor *descriptor)
+{
+ size_t prefixLength = _parcObject_PrefixLength(descriptor);
+ size_t totalMemoryLength = prefixLength + descriptor->objectSize;
+
+ void *origin = NULL;
+ parcMemory_MemAlign(&origin, sizeof(void *), totalMemoryLength);
+
+ if (origin == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ PARCObject *object = _pointerAdd(origin, prefixLength);
+
+ _parcObjectHeader_InitAllocated(_parcObject_Header(object), descriptor);
+
+ errno = 0;
+ return object;
+}
+
+PARCObject *
+parcObject_InitInstanceImpl(PARCObject *object, const PARCObjectDescriptor *descriptor)
+{
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ _parcObjectHeader_InitUnallocated(header, descriptor);
+ return object;
+}
+
+PARCObject *
+parcObject_InitAndClearInstanceImpl(PARCObject *object, const PARCObjectDescriptor *descriptor)
+{
+ parcObject_InitInstanceImpl(object, descriptor);
+
+ memset(object, 0, descriptor->objectSize);
+ return object;
+}
+
+PARCObject *
+parcObject_Copy(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ PARCObjectCopy *copy = _parcObject_ResolveCopy(_parcObject_Descriptor(object));
+ return copy(object);
+}
+
+PARCReferenceCount
+parcObject_Release(PARCObject **objectPointer)
+{
+ PARCObject *object = *objectPointer;
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ trapIllegalValueIf(header->references == 0, "PARCObject@%p references must be > 0", object);
+
+ PARCReferenceCount result = parcAtomicUint64_Decrement(&header->references);
+
+ if (result == 0) {
+ if (_parcObject_Destructor(header->descriptor, objectPointer)) {
+ if (header->locking != NULL) {
+ pthread_cond_destroy(&header->locking->notification);
+ }
+ if (header->isAllocated) {
+ void *origin = _parcObject_Origin(object);
+ parcMemory_Deallocate(&origin);
+ }
+ assertNotNull(*objectPointer, "Class implementation unnecessarily clears the object pointer.");
+ } else {
+ assertNull(*objectPointer, "Class implementation must clear the object pointer.");
+ }
+ }
+
+ *objectPointer = NULL;
+ return result;
+}
+
+PARCReferenceCount
+parcObject_GetReferenceCount(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ return header->references;
+}
+
+const PARCObjectDescriptor *
+parcObject_GetDescriptor(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ return header->descriptor;
+}
+
+const PARCObjectDescriptor *
+parcObject_SetDescriptor(PARCObject *object, const PARCObjectDescriptor *descriptor)
+{
+ parcObject_OptionalAssertValid(object);
+ assertNotNull(descriptor, "PARCObjectDescriptor cannot be NULL.");
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ const PARCObjectDescriptor *result = header->descriptor;
+ header->descriptor = (PARCObjectDescriptor *) descriptor;
+
+ return result;
+}
+
+PARCObjectDescriptor *
+parcObjectDescriptor_Create(const char *name,
+ size_t objectSize,
+ unsigned int objectAlignment,
+ bool isLockable,
+ PARCObjectDestructor *destructor,
+ PARCObjectRelease *release,
+ PARCObjectCopy *copy,
+ PARCObjectToString *toString,
+ PARCObjectEquals *equals,
+ PARCObjectCompare *compare,
+ PARCObjectHashCode *hashCode,
+ PARCObjectToJSON *toJSON,
+ PARCObjectDisplay *display,
+ const PARCObjectDescriptor *superType,
+ PARCObjectTypeState *typeState)
+{
+ assertNotNull(superType, "Supertype descriptor cannot be NULL.");
+
+ PARCObjectDescriptor *result = parcMemory_AllocateAndClear(sizeof(PARCObjectDescriptor));
+ if (result != NULL) {
+ strncpy(result->name, name, sizeof(result->name) - 1);
+ result->name[sizeof(result->name) - 1] = 0;
+ result->destroy = NULL;
+ result->destructor = destructor;
+ result->release = release;
+ result->copy = copy;
+ result->toString = toString;
+ result->equals = equals;
+ result->compare = compare;
+ result->hashCode = hashCode;
+ result->toJSON = toJSON;
+ result->display = display;
+ result->super = superType;
+ result->objectSize = objectSize;
+ result->objectAlignment = objectAlignment;
+ result->typeState = typeState;
+ result->isLockable = isLockable;
+ }
+ return result;
+}
+
+PARCObjectDescriptor *
+parcObjectDescriptor_CreateExtension(const PARCObjectDescriptor *superType, const char *name)
+{
+ PARCObjectDescriptor *result = parcMemory_AllocateAndClear(sizeof(PARCObjectDescriptor));
+ *result = *superType;
+ strncpy(result->name, name, sizeof(result->name) - 1);
+ result->name[sizeof(result->name) - 1] = 0;
+ return result;
+}
+
+PARCObjectTypeState *
+parcObjectDescriptor_GetTypeState(const PARCObjectDescriptor *descriptor)
+{
+ return descriptor->typeState;
+}
+
+const PARCObjectDescriptor *
+parcObjectDescriptor_GetSuperType(const PARCObjectDescriptor *descriptor)
+{
+ return descriptor->super;
+}
+
+bool
+parcObjectDescriptor_Destroy(PARCObjectDescriptor **descriptorPointer)
+{
+ parcMemory_Deallocate(descriptorPointer);
+ return true;
+}
+
+bool
+parcObject_Unlock(const PARCObject *object)
+{
+ bool result = false;
+
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectHeader *header = _parcObject_Header(object);
+ if (header->references > 0) {
+ _parcObjectHeader_AssertValid(header, object);
+
+ if (object != NULL) {
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ locking->locker = (pthread_t) NULL;
+ result = (pthread_mutex_unlock(&locking->lock) == 0);
+
+ assertTrue(result, "Attempted to unlock an unowned lock.");
+ }
+ }
+ }
+ return result;
+}
+
+bool
+parcObject_Lock(const PARCObject *object)
+{
+#ifndef __ANDROID__
+ extern int errno;
+#endif
+ bool result = false;
+
+ parcObject_OptionalAssertValid(object);
+
+ if (object != NULL) {
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ trapCannotObtainLockIf(pthread_equal(locking->locker, pthread_self()),
+ "Recursive locks on %p are not supported.", object);
+
+ errno = pthread_mutex_lock(&locking->lock);
+
+ if (errno == 0) {
+ locking->locker = pthread_self();
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+bool
+parcObject_TryLock(const PARCObject *object)
+{
+ bool result = false;
+
+ if (object != NULL) {
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ trapCannotObtainLockIf(pthread_equal(locking->locker, pthread_self()), "Recursive locks are not supported.");
+
+ int lockStatus = pthread_mutex_trylock(&locking->lock);
+
+ if (lockStatus == 0) {
+ result = true;
+ locking->locker = pthread_self();
+ }
+ }
+ }
+
+ return result;
+}
+
+bool
+parcObject_IsLocked(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+ bool result = false;
+
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ result = locking->locker != (pthread_t) NULL;
+ }
+
+ return result;
+}
+
+void
+parcObject_Wait(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ pthread_cond_wait(&locking->notification, &locking->lock);
+ }
+}
+
+bool
+parcObject_WaitUntil(const PARCObject *object, const struct timespec *time)
+{
+ parcObject_OptionalAssertValid(object);
+
+ bool result = false;
+
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ int waitResult = pthread_cond_timedwait(&locking->notification, &locking->lock, time);
+ if (waitResult == 0) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool
+parcObject_WaitFor(const PARCObject *object, const uint64_t nanoSeconds)
+{
+ parcObject_OptionalAssertValid(object);
+
+ bool result = false;
+
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ // Convert timeval to timespec.
+ struct timespec time = {
+ .tv_sec = now.tv_sec,
+ .tv_nsec = (now.tv_usec * 1000)
+ };
+ time.tv_nsec += nanoSeconds;
+ time.tv_sec += time.tv_nsec / 1000000000;
+ time.tv_nsec = time.tv_nsec % 1000000000;
+
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ int waitResult = pthread_cond_timedwait(&locking->notification, &locking->lock, &time);
+
+ if (waitResult == 0) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+void
+parcObject_Notify(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ pthread_cond_signal(&locking->notification);
+ }
+}
+
+void
+parcObject_NotifyAll(const PARCObject *object)
+{
+ parcObject_OptionalAssertValid(object);
+
+ _PARCObjectLocking *locking = _parcObjectHeader_Locking(object);
+ if (locking != NULL) {
+ pthread_cond_broadcast(&locking->notification);
+ }
+}
+
+bool
+parcObject_BarrierSet(const PARCObject *object)
+{
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ while (__sync_bool_compare_and_swap(&header->barrier, false, true) == false) {
+ ;
+ }
+ return true;
+}
+
+bool
+parcObject_BarrierUnset(const PARCObject *object)
+{
+ _PARCObjectHeader *header = _parcObject_Header(object);
+
+ while (__sync_bool_compare_and_swap(&header->barrier, true, false) == false) {
+ ;
+ }
+
+ return false;
+}