; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Process management library. Win32 part.
;
;  Target OS: Win32
;
;  Dependencies:
;
;  Notes:
;_________________________________________________________________________________________

uses kernel32, shell32

__mutex_not_initialized = 0
__mutex_mutex = 1
__mutex_critical_section = 2

struct TMutex
  .type   dd ?
  .handle dd ?
  .critical_section CRITICAL_SECTION
ends


body Terminate
begin
        invoke ExitThread, [.exit_code]
        return
endp


; Terminate current process
; Returns: nothing.

body TerminateAll
begin
        invoke ExitProcess, [.exit_code]
        return
endp



body SetForcedTerminateHandler
begin
        pushad
        invoke  SetConsoleCtrlHandler, [.procHandler], TRUE
        popad
        return
endp


; no-op for compatibility with Linux library, where this debug
; function is implemented in very slopy manner. :)

proc SetSegmentationFaultHandler, .procHandler
begin

        return
endp


body GetCmdArguments
.argc dd ?
.array dd ?
begin
        pushad
        invoke  GetCommandLineW
        lea     ecx, [.argc]
        invoke  CommandLineToArgvW, eax, ecx
        test    eax, eax
        jz      .error

        push    eax
        mov     esi, eax

        stdcall CreateArray, 4
        mov     [.array], eax

.argloop:
        cmp     [.argc], 0
        je      .endarg

        stdcall WideCharToUtf8, [esi]
        push    eax
        stdcall AddArrayItems, [.array], 1
        mov     [.array], edx
        pop     dword [eax]

        add     esi, 4
        dec     [.argc]
        jmp     .argloop

.endarg:
        invoke  LocalFree ; from the stack...
        popad
        mov     eax, [.array]
        clc
        return

.error:
        popad
        stc
        return
endp



; Creates a new execution thread
; Returns: CF=1 if error.
;          CF=0, eax = pid of the new process being created

body ThreadCreate
begin
        push    ecx edx

        invoke  CreateThread, NULL, 0, [.ptrFunction], [.ptrArguments], 0, 0
        test    eax, eax
        jz      .error

        clc
        pop     edx ecx
        return

.error:
        stc
        pop     edx ecx
        return
endp



body FreeThreadID
begin
        push    eax ecx edx
        invoke  CloseHandle, [.ThreadID]
        pop     edx ecx eax
        return
endp




body Exec
.startup STARTUPINFO
.process PROCESS_INFORMATION
begin
        push    ecx esi edi

        stdcall utf8ToWideChar, [.hCommand]
        mov     esi, eax

        lea     edi, [.startup]
        mov     ecx, (sizeof.STARTUPINFO + sizeof.PROCESS_INFORMATION)/4
        xor     eax, eax
        rep     stosd

        mov     [.startup.cb], sizeof.STARTUPINFO

        lea     ecx, [.startup]
        lea     edx, [.process]
        invoke  CreateProcessW, 0, esi, 0, 0, FALSE, NORMAL_PRIORITY_CLASS, 0, 0, ecx, edx
        stdcall FreeMem, esi
        test    eax, eax
        jz      .error

        mov     eax, [.process.hProcess]
        mov     edx, [.process.hThread]

        clc
        pop     edi esi ecx
        return

.error:
        xor     eax, eax
        xor     edx, edx
        stc
        pop     edi esi ecx
        return
endp


body FreeProcessID
begin
        push    eax ecx edx
        invoke  CloseHandle, [.ProcessID]
        pop     edx ecx eax
        return
endp


; Waits for the process to exit and returns the exit code.
; Returns:
;    CF = 0; eax=exit code
;    CF = 1; eax=0 - the process is running
;    CF = 1; eax=-1 - another error.
;
body WaitProcessExit
.exit_code dd ?
begin
        push    ecx edx
        invoke  WaitForSingleObject, [.ProcessID], [.timeout]
        cmp     eax, WAIT_TIMEOUT
        je      .err_time
        cmp     eax, WAIT_FAILED
        je      .error

        lea     eax, [.exit_code]
        invoke  GetExitCodeProcess, [.ProcessID], eax
        test    eax, eax
        jz      .error

        mov     eax, [.exit_code]
        clc

.finish:
        pop     edx ecx
        return

.err_time:
        xor     eax, eax
        stc
        jmp     .finish

.error:
        or      eax, -1
        stc
        jmp     .finish
endp



; MUTEX functions
;_________________________________________________________________________________________

body MutexCreate
begin
        push    eax ecx edx

        cmp     [.ptrName], 0
        je      .critical_section

.mutex:
        invoke  CreateMutexW, 0, TRUE, [.ptrName]
        mov     ecx, [.ptrMutex]
        mov     [ecx+TMutex.handle], eax
        mov     [ecx+TMutex.type], __mutex_mutex

.finish:
        pop     edx ecx eax
        return

.critical_section:
        mov     eax, [.ptrMutex]
        mov     [eax+TMutex.type], __mutex_critical_section
        lea     eax, [eax+TMutex.critical_section]
        push    eax
        invoke  InitializeCriticalSectionAndSpinCount, eax, 4000
        invoke  EnterCriticalSection ; from the stack
        jmp     .finish
endp




body WaitForMutex
begin
        push    eax ecx edx

        mov     ecx, [.ptrMutex]
        cmp     [ecx+TMutex.type], __mutex_mutex
        je      .mutex

        lea     eax, [ecx+TMutex.critical_section]
        invoke  EnterCriticalSection, eax
        clc
        jmp     .finish

.mutex:
        invoke  WaitForSingleObject, [ecx+TMutex.handle], [.timeout]
        cmp     eax, WAIT_TIMEOUT
        je      .error
        cmp     eax, WAIT_FAILED
        je      .error

        clc
.finish:
        pop     edx ecx eax
        return

.error:
        stc
        jmp     .finish
endp




body MutexRelease
begin
        pushf
        push    eax ecx edx

        mov     eax, [.ptrMutex]
        cmp     [eax+TMutex.type], __mutex_mutex
        je      .mutex

        lea     eax, [eax+TMutex.critical_section]
        invoke  LeaveCriticalSection, eax
        jmp     .finish

.mutex:
        invoke  ReleaseMutex, [eax+TMutex.handle]

.finish:
        pop     edx ecx eax
        popf
        return
endp



body MutexDestroy
begin
        push    eax ecx edx
        mov     eax, [.ptrMutex]

        mov     [eax+TMutex.type], __mutex_not_initialized

        cmp     [eax+TMutex.type], __mutex_mutex
        je      .mutex

        lea     eax, [eax+TMutex.critical_section]
        invoke  DeleteCriticalSection, eax
        jmp     .finish

.mutex:
        pushd   [eax+TMutex.handle]
        mov     dword [eax+TMutex.handle], 0
        invoke  CloseHandle ; from the stack

.finish:
        pop     edx ecx eax
        return
endp

