#include "moar.h" /* This representation's function pointer table. */ static const MVMREPROps ReentrantMutex_this_repr; /* Populates the object body with a mutex. */ static void initialize_mutex(MVMThreadContext *tc, MVMReentrantMutexBody *rm) { int init_stat; rm->mutex = MVM_malloc(sizeof(uv_mutex_t)); if ((init_stat = uv_mutex_init(rm->mutex)) < 0) { MVM_free(rm->mutex); MVM_exception_throw_adhoc(tc, "Failed to initialize mutex: %s", uv_strerror(init_stat)); } } /* Creates a new type object of this representation, and associates it with * the given HOW. */ static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) { MVMSTable *st = MVM_gc_allocate_stable(tc, &ReentrantMutex_this_repr, HOW); MVMROOT(tc, st, { MVMObject *obj = MVM_gc_allocate_type_object(tc, st); MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj); st->size = sizeof(MVMReentrantMutex); }); return st->WHAT; } /* Initializes a new instance. */ static void initialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) { initialize_mutex(tc, (MVMReentrantMutexBody *)data); } /* Copies the body of one object to another. */ static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) { MVM_exception_throw_adhoc(tc, "Cannot copy object with representation ReentrantMutex"); } /* Called by the VM in order to free memory associated with this object. */ static void gc_free(MVMThreadContext *tc, MVMObject *obj) { /* The ThreadContext has already been destroyed by the GC. */ MVMReentrantMutex *rm = (MVMReentrantMutex *)obj; if (rm->body.lock_count) MVM_panic(1, "Tried to garbage-collect a locked mutex"); if (rm->body.mutex) { /* Cope with incomplete object initialization */ uv_mutex_destroy(rm->body.mutex); MVM_free(rm->body.mutex); } } static const MVMStorageSpec storage_spec = { MVM_STORAGE_SPEC_REFERENCE, /* inlineable */ 0, /* bits */ 0, /* align */ MVM_STORAGE_SPEC_BP_NONE, /* boxed_primitive */ 0, /* can_box */ 0, /* is_unsigned */ }; /* Gets the storage specification for this representation. */ static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) { return &storage_spec; } /* Compose the representation. */ static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info) { /* Nothing to do for this REPR. */ } /* Set the size of the STable. */ static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { st->size = sizeof(MVMReentrantMutex); } /* Serializing a mutex doesn't save anything; we will re-create it upon * deserialization. Makes data structures that just happen to have a lock in * them serializable. */ static void serialize(MVMThreadContext *tc, MVMSTable *st, void *data, MVMSerializationWriter *writer) { } static void deserialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMSerializationReader *reader) { initialize_mutex(tc, (MVMReentrantMutexBody *)data); } /* Initializes the representation. */ const MVMREPROps * MVMReentrantMutex_initialize(MVMThreadContext *tc) { return &ReentrantMutex_this_repr; } static const MVMREPROps ReentrantMutex_this_repr = { type_object_for, MVM_gc_allocate_object, initialize, copy_to, MVM_REPR_DEFAULT_ATTR_FUNCS, MVM_REPR_DEFAULT_BOX_FUNCS, MVM_REPR_DEFAULT_POS_FUNCS, MVM_REPR_DEFAULT_ASS_FUNCS, MVM_REPR_DEFAULT_ELEMS, get_storage_spec, NULL, /* change_type */ serialize, deserialize, NULL, /* serialize_repr_data */ NULL, /* deserialize_repr_data */ deserialize_stable_size, NULL, /* gc_mark */ gc_free, NULL, /* gc_cleanup */ NULL, /* gc_mark_repr_data */ NULL, /* gc_free_repr_data */ compose, NULL, /* spesh */ "ReentrantMutex", /* name */ MVM_REPR_ID_ReentrantMutex, NULL, /* unmanaged_size */ NULL, /* describe_refs */ }; /* Locks the mutex. */ void MVM_reentrantmutex_lock_checked(MVMThreadContext *tc, MVMObject *lock) { if (REPR(lock)->ID == MVM_REPR_ID_ReentrantMutex && IS_CONCRETE(lock)) MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)lock); else MVM_exception_throw_adhoc(tc, "lock requires a concrete object with REPR ReentrantMutex"); } void MVM_reentrantmutex_lock(MVMThreadContext *tc, MVMReentrantMutex *rm) { /*unsigned int interval_id;*/ /* Atomic access must be aligned, otherwise the lock will not work. */ MVM_ASSERT_ALIGNED(&rm->body.holder_id, ALIGNOF(AO_t)); MVM_ASSERT_ALIGNED(&rm->body.lock_count, ALIGNOF(AO_t)); if (MVM_load(&rm->body.holder_id) == tc->thread_id) { /* We already hold the lock; bump the count. */ MVM_incr(&rm->body.lock_count); } else { /* Not holding the lock; obtain it. */ /*interval_id = MVM_telemetry_interval_start(tc, "ReentrantMutex obtains lock");*/ /*MVM_telemetry_interval_annotate(rm->body.mutex, interval_id, "lock in question");*/ MVMROOT(tc, rm, { MVM_gc_mark_thread_blocked(tc); uv_mutex_lock(rm->body.mutex); MVM_gc_mark_thread_unblocked(tc); }); MVM_store(&rm->body.holder_id, tc->thread_id); MVM_store(&rm->body.lock_count, 1); tc->num_locks++; /*MVM_telemetry_interval_stop(tc, interval_id, "ReentrantMutex obtained lock");*/ } } /* Unlocks the mutex. */ void MVM_reentrantmutex_unlock_checked(MVMThreadContext *tc, MVMObject *lock) { if (REPR(lock)->ID == MVM_REPR_ID_ReentrantMutex && IS_CONCRETE(lock)) MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)lock); else MVM_exception_throw_adhoc(tc, "unlock requires a concrete object with REPR ReentrantMutex"); } void MVM_reentrantmutex_unlock(MVMThreadContext *tc, MVMReentrantMutex *rm) { /* Ensure we hold the lock. */ if (MVM_load(&rm->body.holder_id) == tc->thread_id) { if (MVM_decr(&rm->body.lock_count) == 1) { /* Decremented the last recursion count; really unlock. */ MVM_store(&rm->body.holder_id, 0); uv_mutex_unlock(rm->body.mutex); tc->num_locks--; /*MVM_telemetry_timestamp(rm->body.mutex, "this ReentrantMutex unlocked");*/ } } else { MVM_exception_throw_adhoc(tc, "Attempt to unlock mutex by thread not holding it"); } }