98 lines
2.7 KiB
C
98 lines
2.7 KiB
C
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "error.h"
|
|
#include "nt.h"
|
|
#include "reflock.h"
|
|
#include "util.h"
|
|
#include "win.h"
|
|
|
|
/* clang-format off */
|
|
static const uint32_t _REF = 0x00000001;
|
|
static const uint32_t _REF_MASK = 0x0fffffff;
|
|
static const uint32_t _DESTROY = 0x10000000;
|
|
static const uint32_t _DESTROY_MASK = 0xf0000000;
|
|
static const uint32_t _POISON = 0x300DEAD0;
|
|
/* clang-format on */
|
|
|
|
static HANDLE _keyed_event = NULL;
|
|
|
|
int reflock_global_init(void) {
|
|
NTSTATUS status =
|
|
NtCreateKeyedEvent(&_keyed_event, ~(ACCESS_MASK) 0, NULL, 0);
|
|
if (status != STATUS_SUCCESS)
|
|
return_error(-1, RtlNtStatusToDosError(status));
|
|
return 0;
|
|
}
|
|
|
|
void reflock_init(reflock_t* reflock) {
|
|
reflock->state = 0;
|
|
}
|
|
|
|
static void _signal_event(const void* address) {
|
|
NTSTATUS status =
|
|
NtReleaseKeyedEvent(_keyed_event, (PVOID) address, FALSE, NULL);
|
|
if (status != STATUS_SUCCESS)
|
|
abort();
|
|
}
|
|
|
|
static void _await_event(const void* address) {
|
|
NTSTATUS status =
|
|
NtWaitForKeyedEvent(_keyed_event, (PVOID) address, FALSE, NULL);
|
|
if (status != STATUS_SUCCESS)
|
|
abort();
|
|
}
|
|
|
|
static inline uint32_t _sync_add_and_fetch(volatile uint32_t* target,
|
|
uint32_t value) {
|
|
static_assert(sizeof(*target) == sizeof(long), "");
|
|
return InterlockedAdd((volatile long*) target, value);
|
|
}
|
|
|
|
static inline uint32_t _sync_sub_and_fetch(volatile uint32_t* target,
|
|
uint32_t value) {
|
|
uint32_t add_value = -(int32_t) value;
|
|
return _sync_add_and_fetch(target, add_value);
|
|
}
|
|
|
|
static inline uint32_t _sync_fetch_and_set(volatile uint32_t* target,
|
|
uint32_t value) {
|
|
static_assert(sizeof(*target) == sizeof(long), "");
|
|
return InterlockedExchange((volatile long*) target, value);
|
|
}
|
|
|
|
void reflock_ref(reflock_t* reflock) {
|
|
uint32_t state = _sync_add_and_fetch(&reflock->state, _REF);
|
|
unused(state);
|
|
assert((state & _DESTROY_MASK) == 0); /* Overflow or destroyed. */
|
|
}
|
|
|
|
void reflock_unref(reflock_t* reflock) {
|
|
uint32_t state = _sync_sub_and_fetch(&reflock->state, _REF);
|
|
uint32_t ref_count = state & _REF_MASK;
|
|
uint32_t destroy = state & _DESTROY_MASK;
|
|
|
|
unused(ref_count);
|
|
unused(destroy);
|
|
|
|
if (state == _DESTROY)
|
|
_signal_event(reflock);
|
|
else
|
|
assert(destroy == 0 || ref_count > 0);
|
|
}
|
|
|
|
void reflock_unref_and_destroy(reflock_t* reflock) {
|
|
uint32_t state = _sync_add_and_fetch(&reflock->state, _DESTROY - _REF);
|
|
uint32_t ref_count = state & _REF_MASK;
|
|
|
|
assert((state & _DESTROY_MASK) ==
|
|
_DESTROY); /* Underflow or already destroyed. */
|
|
|
|
if (ref_count != 0)
|
|
_await_event(reflock);
|
|
|
|
state = _sync_fetch_and_set(&reflock->state, _POISON);
|
|
assert(state == _DESTROY);
|
|
}
|