/********************************************************/
/* ntapi: Native API core library */
/* Copyright (C) 2013--2016 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;
}