diff options
Diffstat (limited to 'src/process')
-rw-r--r-- | src/process/ntapi_tt_fork.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/process/ntapi_tt_fork.c b/src/process/ntapi_tt_fork.c new file mode 100644 index 0000000..9f498eb --- /dev/null +++ b/src/process/ntapi_tt_fork.c @@ -0,0 +1,230 @@ +/********************************************************/ +/* 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_status.h> +#include <ntapi/nt_object.h> +#include <ntapi/nt_memory.h> +#include <ntapi/nt_thread.h> +#include <ntapi/nt_process.h> +#include <ntapi/ntapi.h> +#include "ntapi_impl.h" + +static intptr_t __fork_retry_stats = 0; +static intptr_t __fork_resume_stats = 0; + +static int32_t __stdcall __fork_thread(void * ctx) +{ + intptr_t * pstate; + intptr_t state; + void * hready; + + pstate = (intptr_t *)ctx; + state = *pstate; + hready = (void *)state; + + at_store( + pstate, + 0); + + return __ntapi->zw_terminate_thread( + NT_CURRENT_THREAD_HANDLE, + __ntapi->zw_set_event( + hready,0)); +} + +static intptr_t __fastcall __ntapi_tt_fork_child( + void * hresumed, + void * hready) +{ + int32_t status; + nt_thread_params tparams; + nt_timeout timeout; + nt_timeout zerowait; + intptr_t state; + + at_store( + &state, + (intptr_t)hready); + + __ntapi->tt_aligned_block_memset( + &tparams,0,sizeof(tparams)); + + tparams.start = __fork_thread; + tparams.arg = &state; + tparams.stack_size_commit = 0x10000; + tparams.stack_size_reserve = 0x20000; + + status = __ntapi->tt_create_local_thread( + &tparams); + + __ntapi->zw_set_event( + hresumed,0); + + if (status) + __ntapi->zw_terminate_process( + NT_CURRENT_PROCESS_HANDLE, + status); + + if (!state) { + __ntapi->zw_close(hresumed); + __ntapi->zw_close(hready); + __ntapi->zw_close(tparams.hthread); + return 0; + } + + timeout.quad = (-1) * 10 * 1000 * 250; + + status = __ntapi->zw_wait_for_single_object( + hready, + NT_SYNC_NON_ALERTABLE, + &timeout); + + if (status == NT_STATUS_SUCCESS) { + __ntapi->zw_close(hresumed); + __ntapi->zw_close(hready); + __ntapi->zw_close(tparams.hthread); + return 0; + } + + __ntapi->zw_terminate_thread( + tparams.hthread, + NT_STATUS_MORE_PROCESSING_REQUIRED); + + zerowait.quad = 0; + + status = __ntapi->zw_wait_for_single_object( + hready, + NT_SYNC_NON_ALERTABLE, + &zerowait); + + if (status == NT_STATUS_SUCCESS) { + __ntapi->zw_close(hresumed); + __ntapi->zw_close(hready); + __ntapi->zw_close(tparams.hthread); + return 0; + } + + return __ntapi->zw_terminate_process( + NT_CURRENT_PROCESS_HANDLE, + status); +} + +static intptr_t __fastcall __ntapi_tt_fork_parent( + void ** hprocess, + void ** hthread, + void * hresumed, + void * hready) +{ + int32_t status; + nt_timeout timeout; + nt_timeout zerowait; + uint32_t prev; + + __ntapi->zw_wait_for_single_object( + hresumed, + NT_SYNC_NON_ALERTABLE, + 0); + + timeout.quad = (-1) * 10 * 1000 * 500; + + status = __ntapi->zw_wait_for_single_object( + hready, + NT_SYNC_NON_ALERTABLE, + &timeout); + + if (status == NT_STATUS_SUCCESS) { + __ntapi->zw_close(hresumed); + __ntapi->zw_close(hready); + return NT_STATUS_SUCCESS; + } + + __ntapi->zw_suspend_thread( + *hthread,&prev); + + zerowait.quad = 0; + + status = __ntapi->zw_wait_for_single_object( + hready, + NT_SYNC_NON_ALERTABLE, + &zerowait); + + if (status == NT_STATUS_SUCCESS) { + at_locked_inc( + &__fork_resume_stats); + + __ntapi->zw_resume_thread( + *hthread,0); + + __ntapi->zw_close(hresumed); + __ntapi->zw_close(hready); + return NT_STATUS_SUCCESS; + } + + at_locked_inc( + &__fork_retry_stats); + + __ntapi->zw_terminate_process( + *hprocess, + status); + + __ntapi->zw_close(*hprocess); + __ntapi->zw_close(*hthread); + + return status; +} + +intptr_t __fastcall __ntapi_tt_fork( + __out void ** hprocess, + __out void ** hthread) +{ + int32_t status; + intptr_t pid; + void * hresumed; + void * hready; + int i; + + if ((status = __ntapi->tt_create_inheritable_event( + &hresumed, + NT_NOTIFICATION_EVENT, + NT_EVENT_NOT_SIGNALED))) + return -1; + + if ((status = __ntapi->tt_create_inheritable_event( + &hready, + NT_NOTIFICATION_EVENT, + NT_EVENT_NOT_SIGNALED))) + return -1; + + for (i=0; i<32; i++) { + if (__ntapi->zw_create_user_process) + pid = __ntapi_tt_fork_v2(hprocess,hthread); + else + pid = __ntapi_tt_fork_v2(hprocess,hthread); + + if (pid == 0) { + return __ntapi_tt_fork_child( + hresumed,hready); + + } else if (pid > 0) { + if (!(__ntapi_tt_fork_parent( + hprocess,hthread, + hresumed,hready))) + return pid; + + } else { + __ntapi->zw_close(hresumed); + __ntapi->zw_close(hready); + return -1; + } + } + + __ntapi->zw_close(hresumed); + __ntapi->zw_close(hready); + + return -1; +} |