Blame src/sync/ntapi_tt_sync_block.c

dd89bb
/********************************************************/
dd89bb
/*  ntapi: Native API core library                      */
dd89bb
/*  Copyright (C) 2013,2014,2015  Z. Gilboa             */
dd89bb
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
dd89bb
/********************************************************/
dd89bb
dd89bb
#include <psxtypes/psxtypes.h>
dd89bb
#include <ntapi/nt_object.h>
dd89bb
#include <ntapi/nt_sync.h>
dd89bb
#include <ntapi/nt_atomic.h>
dd89bb
#include <ntapi/ntapi.h>
dd89bb
#include "ntapi_impl.h"
dd89bb
dd89bb
void __stdcall __ntapi_tt_sync_block_init(
dd89bb
	__in	nt_sync_block *	sync_block,
dd89bb
	__in	uint32_t	flags			__optional,
dd89bb
	__in	int32_t		srvtid			__optional,
dd89bb
	__in	int32_t		default_lock_tries	__optional,
dd89bb
	__in	int64_t		default_lock_wait	__optional,
dd89bb
	__in	void *		hsignal			__optional)
dd89bb
{
dd89bb
	__ntapi->tt_aligned_block_memset(
dd89bb
		sync_block,
dd89bb
		0,sizeof(*sync_block));
dd89bb
dd89bb
	sync_block->lock_tries = default_lock_tries
dd89bb
		? default_lock_tries
dd89bb
		: __NT_SYNC_BLOCK_LOCK_TRIES;
dd89bb
dd89bb
	sync_block->lock_wait.quad = default_lock_wait
dd89bb
		? default_lock_wait
dd89bb
		: (-1);
dd89bb
dd89bb
	sync_block->flags   = flags;
dd89bb
	sync_block->srvtid  = srvtid;
dd89bb
	sync_block->hsignal = hsignal;
dd89bb
dd89bb
	return;
dd89bb
}
dd89bb
dd89bb
dd89bb
int32_t __stdcall __ntapi_tt_sync_block_lock(
dd89bb
	__in	nt_sync_block *	sync_block,
dd89bb
	__in	int32_t		lock_tries	__optional,
dd89bb
	__in	int64_t		lock_wait	__optional,
dd89bb
	__in	uint32_t *	sig_flag	__optional)
dd89bb
{
dd89bb
	int32_t		status;
dd89bb
	int32_t		tid;
dd89bb
	intptr_t	lock;
dd89bb
	void *		hwait[2];
dd89bb
	nt_timeout	timeout;
dd89bb
dd89bb
	/* validation */
dd89bb
	if (sync_block->invalid)
dd89bb
		return NT_STATUS_INVALID_HANDLE;
dd89bb
dd89bb
	/* already owned? */
dd89bb
	tid = pe_get_current_thread_id();
dd89bb
	if (sync_block->tid == tid) return NT_STATUS_SUCCESS;
dd89bb
dd89bb
	/* yield to server? */
dd89bb
	if ((sync_block->flags & NT_SYNC_BLOCK_YIELD_TO_SERVER) && (tid != sync_block->srvtid)) {
dd89bb
		hwait[0] = sync_block->hserver;
dd89bb
		hwait[1] = sync_block->hsignal;
dd89bb
dd89bb
		/* signal support */
dd89bb
		if (sig_flag && *sig_flag)
dd89bb
			return NT_STATUS_ALERTED;
dd89bb
dd89bb
		/* wait */
dd89bb
		status = __ntapi->zw_wait_for_multiple_objects(
dd89bb
			2,
dd89bb
			hwait,
dd89bb
			NT_WAIT_ANY,
dd89bb
			NT_SYNC_NON_ALERTABLE,
dd89bb
			(nt_timeout *)0);
dd89bb
dd89bb
		/* signal support */
dd89bb
		if (sig_flag && *sig_flag)
dd89bb
			return NT_STATUS_ALERTED;
dd89bb
	}
dd89bb
dd89bb
	/* first try */
dd89bb
	lock = at_locked_cas_32(&sync_block->tid,0,tid);
dd89bb
	if (lock && !--lock_tries) return NT_STATUS_NOT_LOCKED;
dd89bb
dd89bb
	/* first-time contended case? */
dd89bb
	if (lock && !sync_block->hwait) {
dd89bb
		status = __ntapi->tt_create_inheritable_event(
dd89bb
			&hwait[0],
dd89bb
			NT_NOTIFICATION_EVENT,
dd89bb
			NT_EVENT_NOT_SIGNALED);
dd89bb
dd89bb
		if (status) return status;
dd89bb
dd89bb
		lock = at_locked_cas(
dd89bb
			(intptr_t *)&sync_block->hwait,
dd89bb
			0,(intptr_t)hwait);
dd89bb
dd89bb
		if (lock)
dd89bb
			__ntapi->zw_close(hwait);
dd89bb
dd89bb
		/* try again without a wait */
dd89bb
		lock = at_locked_cas_32(&sync_block->tid,0,tid);
dd89bb
	}
dd89bb
dd89bb
	/* contended case? */
dd89bb
	if (lock) {
dd89bb
		hwait[0] = sync_block->hwait;
dd89bb
		hwait[1] = sync_block->hsignal;
dd89bb
dd89bb
		lock_tries = lock_tries
dd89bb
			? lock_tries
dd89bb
			: sync_block->lock_tries;
dd89bb
dd89bb
		timeout.quad = lock_wait
dd89bb
			? lock_wait
dd89bb
			: sync_block->lock_wait.quad;
dd89bb
dd89bb
		for (; lock && lock_tries; lock_tries--) {
dd89bb
			/* signal support */
dd89bb
			if (sig_flag && *sig_flag)
dd89bb
				return NT_STATUS_ALERTED;
dd89bb
dd89bb
			/* wait */
dd89bb
			status = __ntapi->zw_wait_for_multiple_objects(
dd89bb
				2,
dd89bb
				&sync_block->hwait,
dd89bb
				NT_WAIT_ANY,
dd89bb
				NT_SYNC_NON_ALERTABLE,
dd89bb
				&timeout);
dd89bb
dd89bb
			/* check status */
dd89bb
			if ((status != NT_STATUS_TIMEOUT) && ((uint32_t)status >= NT_STATUS_WAIT_CAP))
dd89bb
				return status;
dd89bb
dd89bb
			/* signal support */
dd89bb
			if (sig_flag && *sig_flag)
dd89bb
				return NT_STATUS_ALERTED;
dd89bb
dd89bb
			/* try again */
dd89bb
			lock = at_locked_cas_32(&sync_block->tid,0,tid);
dd89bb
		};
dd89bb
	}
dd89bb
dd89bb
	if (lock) return NT_STATUS_NOT_LOCKED;
dd89bb
dd89bb
	/* shared section support */
dd89bb
	sync_block->pid = pe_get_current_process_id();
dd89bb
dd89bb
	return NT_STATUS_SUCCESS;
dd89bb
}
dd89bb
dd89bb
dd89bb
int32_t __stdcall __ntapi_tt_sync_block_server_lock(
dd89bb
	__in	nt_sync_block *	sync_block,
dd89bb
	__in	int32_t		lock_tries	__optional,
dd89bb
	__in	int64_t		lock_wait	__optional,
dd89bb
	__in	uint32_t *	sig_flag	__optional)
dd89bb
{
dd89bb
	int32_t status;
dd89bb
dd89bb
	/* validation */
dd89bb
	 if (sync_block->invalid)
dd89bb
		return NT_STATUS_INVALID_HANDLE;
dd89bb
dd89bb
	else if (sync_block->srvtid != pe_get_current_thread_id())
dd89bb
		return NT_STATUS_RESOURCE_NOT_OWNED;
dd89bb
dd89bb
	/* try once without yield request */
dd89bb
	status = __ntapi_tt_sync_block_lock(
dd89bb
		sync_block,
dd89bb
		1,
dd89bb
		lock_wait,
dd89bb
		sig_flag);
dd89bb
dd89bb
	if (status == NT_STATUS_SUCCESS)
dd89bb
		return status;
dd89bb
dd89bb
	/* hserver */
dd89bb
	if (!sync_block->hserver) {
dd89bb
		status = __ntapi->tt_create_inheritable_event(
dd89bb
			&sync_block->hserver,
dd89bb
			NT_NOTIFICATION_EVENT,
dd89bb
			NT_EVENT_NOT_SIGNALED);
dd89bb
dd89bb
		if (status) return status;
dd89bb
	} else {
dd89bb
		status = __ntapi->zw_reset_event(
dd89bb
			&sync_block->hserver,
dd89bb
			(int32_t *)0);
dd89bb
dd89bb
		if (status) return status;
dd89bb
	}
dd89bb
dd89bb
	/* yield request: set */
dd89bb
	sync_block->flags |= NT_SYNC_BLOCK_YIELD_TO_SERVER;
dd89bb
dd89bb
	/* try again */
dd89bb
	status = __ntapi_tt_sync_block_lock(
dd89bb
		sync_block,
dd89bb
		lock_tries,
dd89bb
		lock_wait,
dd89bb
		sig_flag);
dd89bb
dd89bb
	/* yield request: unset */
dd89bb
	sync_block->flags ^= NT_SYNC_BLOCK_YIELD_TO_SERVER;
dd89bb
dd89bb
	__ntapi->zw_set_event(
dd89bb
		sync_block->hserver,
dd89bb
		(int32_t *)0);
dd89bb
dd89bb
	/* (locking not guaranteed) */
dd89bb
	return status;
dd89bb
}
dd89bb
dd89bb
dd89bb
int32_t __stdcall __ntapi_tt_sync_block_unlock(
dd89bb
	__in	nt_sync_block *	sync_block)
dd89bb
{
c0bd29
	union {
c0bd29
		int64_t			i64;
c0bd29
		nt_large_integer	nti64;
c0bd29
	} cmp;
dd89bb
dd89bb
	if (sync_block->invalid)
dd89bb
		return NT_STATUS_INVALID_HANDLE;
dd89bb
c0bd29
	cmp.nti64.ihigh = pe_get_current_process_id();
c0bd29
	cmp.nti64.ulow  = pe_get_current_thread_id();
dd89bb
c0bd29
	if (cmp.i64 != at_locked_cas_64(
dd89bb
			(int64_t *)&sync_block->tid,
c0bd29
			cmp.i64,0))
dd89bb
		return NT_STATUS_RESOURCE_NOT_OWNED;
dd89bb
dd89bb
	return NT_STATUS_SUCCESS;
dd89bb
}
dd89bb
dd89bb
dd89bb
void __stdcall __ntapi_tt_sync_block_validate(
dd89bb
	__in	nt_sync_block *	sync_block)
dd89bb
{
dd89bb
	at_store_32(&sync_block->invalid,0);
dd89bb
dd89bb
	return;
dd89bb
}
dd89bb
dd89bb
dd89bb
int32_t __stdcall __ntapi_tt_sync_block_invalidate(
dd89bb
	__in	nt_sync_block *	sync_block)
dd89bb
{
dd89bb
	int32_t invalid;
dd89bb
dd89bb
	if (!sync_block)
dd89bb
		return NT_STATUS_INVALID_PARAMETER;
dd89bb
dd89bb
	invalid = at_locked_cas_32(
dd89bb
		&sync_block->invalid,
dd89bb
		0,
dd89bb
		1);
dd89bb
dd89bb
	if (invalid)
dd89bb
		return NT_STATUS_INVALID_HANDLE;
dd89bb
dd89bb
	return NT_STATUS_SUCCESS;
dd89bb
}
dd89bb
dd89bb
dd89bb
int32_t __stdcall __ntapi_tt_sync_block_discard(
dd89bb
	__in	nt_sync_block *	sync_block)
dd89bb
{
dd89bb
	if (!sync_block)
dd89bb
		return NT_STATUS_INVALID_PARAMETER;
dd89bb
dd89bb
	if (sync_block->hwait)
dd89bb
		__ntapi->zw_close(sync_block->hwait);
dd89bb
dd89bb
	if (sync_block->hserver)
dd89bb
		__ntapi->zw_close(sync_block->hserver);
dd89bb
dd89bb
	__ntapi->tt_aligned_block_memset(sync_block,-1,sizeof(*sync_block));
dd89bb
dd89bb
	return NT_STATUS_SUCCESS;
dd89bb
}