Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2017  Z. Gilboa                 */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/nt_object.h>
#include <ntapi/nt_sync.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

static void __sync_block_memset(
	__in	nt_sync_block *	sync_block,
	__in	intptr_t	value)
{
	intptr_t * sptr = sync_block->cache_line;

	at_store(&sptr[0x0],value);
	at_store(&sptr[0x1],value);
	at_store(&sptr[0x2],value);
	at_store(&sptr[0x3],value);
	at_store(&sptr[0x4],value);
	at_store(&sptr[0x5],value);
	at_store(&sptr[0x6],value);
	at_store(&sptr[0x7],value);

	if (sizeof(intptr_t) == 4) {
		at_store(&sptr[0x8],value);
		at_store(&sptr[0x9],value);
		at_store(&sptr[0xa],value);
		at_store(&sptr[0xb],value);
		at_store(&sptr[0xc],value);
		at_store(&sptr[0xd],value);
		at_store(&sptr[0xe],value);
		at_store(&sptr[0xf],value);
	}
}

void __stdcall __ntapi_tt_sync_block_init(
	__in	nt_sync_block *	sync_block,
	__in	uint32_t	flags			__optional,
	__in	int32_t		srvtid			__optional,
	__in	int32_t		default_lock_tries	__optional,
	__in	int64_t		default_lock_wait	__optional,
	__in	void *		hsignal			__optional)
{
	__sync_block_memset(
		sync_block,0);

	at_store_32(
		&sync_block->lock_tries,
		default_lock_tries
			? default_lock_tries
			: __NT_SYNC_BLOCK_LOCK_TRIES);

	at_store_64(
		&sync_block->lock_wait.quad,
		default_lock_wait
			? default_lock_wait
			: (-1));

	at_store_32(
		(int32_t *)&sync_block->flags,
		flags);

	at_store_32(
		(int32_t *)&sync_block->srvtid,
		srvtid);

	at_store(
		(intptr_t *)&sync_block->hsignal,
		(intptr_t)hsignal);
}


int32_t __stdcall __ntapi_tt_sync_block_lock(
	__in	nt_sync_block *	sync_block,
	__in	int32_t		lock_tries	__optional,
	__in	int64_t		lock_wait	__optional,
	__in	uint32_t *	sig_flag	__optional)
{
	int32_t		status;
	int32_t		tid;
	intptr_t	lock;
	void *		hwait[2];
	nt_timeout	timeout;

	/* validation */
	if (sync_block->invalid)
		return NT_STATUS_INVALID_HANDLE;

	/* already owned? */
	if ((tid = pe_get_current_thread_id()) == sync_block->tid)
		return NT_STATUS_SUCCESS;

	/* yield to server? */
	if ((sync_block->flags & NT_SYNC_BLOCK_YIELD_TO_SERVER)
			&& ((uint32_t)tid != sync_block->srvtid)) {
		hwait[0] = sync_block->hserver;
		hwait[1] = sync_block->hsignal;

		/* signal support */
		if (sig_flag && *sig_flag)
			return NT_STATUS_ALERTED;

		/* wait */
		status = __ntapi->zw_wait_for_multiple_objects(
			2,hwait,
			NT_WAIT_ANY,
			NT_SYNC_NON_ALERTABLE,
			0);

		/* signal support */
		if (sig_flag && *sig_flag)
			return NT_STATUS_ALERTED;
	}

	/* first try */
	lock = at_locked_cas_32(
		&sync_block->tid,
		0,tid);

	if (lock && (lock_tries == 1))
		return NT_STATUS_NOT_LOCKED;

	/* first-time contended case? */
	if (lock && !sync_block->hwait) {
		if ((status = __ntapi->tt_create_inheritable_event(
				&hwait[0],
				NT_NOTIFICATION_EVENT,
				NT_EVENT_NOT_SIGNALED)))
			return status;

		lock = at_locked_cas(
			(intptr_t *)&sync_block->hwait,
			0,(intptr_t)hwait[0]);

		if (lock)
			__ntapi->zw_close(hwait[0]);

		/* try again without a wait */
		lock = at_locked_cas_32(
			&sync_block->tid,
			0,tid);
	}

	/* contended case? */
	if (lock) {
		hwait[0] = sync_block->hwait;
		hwait[1] = sync_block->hsignal;

		lock_tries = lock_tries
			? lock_tries
			: sync_block->lock_tries;

		timeout.quad = lock_wait
			? lock_wait
			: sync_block->lock_wait.quad;

		for (; lock && lock_tries; lock_tries--) {
			/* signal support */
			if (sig_flag && *sig_flag)
				return NT_STATUS_ALERTED;

			/* wait */
			status = __ntapi->zw_wait_for_multiple_objects(
				2,hwait,
				NT_WAIT_ANY,
				NT_SYNC_NON_ALERTABLE,
				&timeout);

			/* check status */
			if ((status >= NT_STATUS_WAIT_0) && (status < NT_STATUS_WAIT_CAP))
				(void)0;
			else if (status == NT_STATUS_TIMEOUT)
				(void)0;
			else
				return status;

			/* signal support */
			if (sig_flag && *sig_flag)
				return NT_STATUS_ALERTED;

			/* try again */
			lock = at_locked_cas_32(
				&sync_block->tid,
				0,tid);
		};
	}

	if (lock)
		return NT_STATUS_NOT_LOCKED;

	/* shared section support */
	at_store_32(
		&sync_block->pid,
		pe_get_current_process_id());

	return NT_STATUS_SUCCESS;
}


int32_t __stdcall __ntapi_tt_sync_block_server_lock(
	__in	nt_sync_block *	sync_block,
	__in	int32_t		lock_tries	__optional,
	__in	int64_t		lock_wait	__optional,
	__in	uint32_t *	sig_flag	__optional)
{
	int32_t status;

	/* validation */
	if (sync_block->invalid)
		return NT_STATUS_INVALID_HANDLE;

	else if (sync_block->srvtid != pe_get_current_thread_id())
		return NT_STATUS_RESOURCE_NOT_OWNED;

	/* try once without yield request */
	status = __ntapi_tt_sync_block_lock(
		sync_block,
		1,lock_wait,
		sig_flag);

	if (status == NT_STATUS_SUCCESS)
		return status;

	/* hserver */
	if (!sync_block->hserver) {
		if ((status = __ntapi->tt_create_inheritable_event(
				&sync_block->hserver,
				NT_NOTIFICATION_EVENT,
				NT_EVENT_NOT_SIGNALED)))
			return status;
	} else {
		if ((status = __ntapi->zw_reset_event(
				sync_block->hserver,0)))
			return status;
	}

	/* yield request: set */
	at_locked_or_32(
		(int32_t *)&sync_block->flags,
		NT_SYNC_BLOCK_YIELD_TO_SERVER);

	/* try again */
	status = __ntapi_tt_sync_block_lock(
		sync_block,
		lock_tries,
		lock_wait,
		sig_flag);

	/* yield request: unset */
	at_locked_xor_32(
		(int32_t *)&sync_block->flags,
		NT_SYNC_BLOCK_YIELD_TO_SERVER);

	__ntapi->zw_set_event(
		sync_block->hserver,
		0);

	/* (locking not guaranteed) */
	return status;
}


int32_t __stdcall __ntapi_tt_sync_block_unlock(
	__in	nt_sync_block *	sync_block)
{
	union {
		int64_t			i64;
		nt_large_integer	nti64;
	} cmp;

	if (sync_block->invalid)
		return NT_STATUS_INVALID_HANDLE;

	cmp.nti64.ihigh = pe_get_current_process_id();
	cmp.nti64.ulow  = pe_get_current_thread_id();

	if (cmp.i64 != at_locked_cas_64(
			(int64_t *)&sync_block->tid,
			cmp.i64,0))
		return NT_STATUS_RESOURCE_NOT_OWNED;

	return NT_STATUS_SUCCESS;
}


int32_t __stdcall __ntapi_tt_sync_block_discard(
	__in	nt_sync_block *	sync_block)
{
	if (sync_block->hwait)
		__ntapi->zw_close(
			sync_block->hwait);

	if (sync_block->hserver)
		__ntapi->zw_close(
			sync_block->hserver);

	__sync_block_memset(
		sync_block,-1);

	return NT_STATUS_SUCCESS;
}