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


struct TMutex
  .value        dd ?
  .threadID     dd ?
  .counter      dd ?
  .reserved     dd ?
ends


struct TThreadInfo
  .cpu_usage   dd ?
  .zposWindow  dw ?
  .nWindowSlot dw ?
  .reserve1    dw ?
  .ProcessName rb 11
  .reserve2    db ?
  .processAddr dd ?
  .processSize dd ?
  .TID         dd ?
  .wndPosX     dd ?
  .wndPosY     dd ?
  .wndSizeX    dd ?
  .wndSizeY    dd ?
  .ThreadState dw ?
  .reserve3    dw ?
  .ClientPosX  dd ?
  .ClientPosY  dd ?
  .ClientWidth dd ?
  .ClientHeight dd ?
  .WindowState  db ?
  .EventMask    dd ?
  .min_size = $$ - $
                rb 1024 - .min_size
ends


struct TThreadRecord
  .ThreadID dd ?
  .ptrStack dd ?
ends


uglobal
  __ThreadList TThreadRecord
               rb 255*sizeof.TThreadRecord

  var __ThreadLock = ?
endg


; This procedure should be removed. But only after the file managers and the shell
; of KolibriOS start to set the current directory properly. :)

initialize InitDefaultPath
begin
        mov     esi, bufferPath
        mov     edi, esi
.len:
        lodsb
        cmp     al, '/'
        jne     @f
        mov     edi, esi
@@:
        test    al, al
        jnz     .len

        push    dword [edi]
        mov     dword [edi], 0

        mov     eax, sys_current_directory
        mov     ebx, 1
        mov     ecx, bufferPath
        int     $40

        pop     dword [edi]
        return
endp



body Terminate
begin
; lock the thread list.
        xor     eax, eax
        inc     eax

.wait_loop:
        xchg    eax, [__ThreadLock]
        test    eax, eax
        jz      .locked

        mov     eax, sys_f68    ; switch to another thread.
        mov     ebx, 1
        int     $40
        jmp     .wait_loop

.locked:
        DebugMsg "Thread terminate locked."

        mov     esi, __ThreadList - sizeof.TThreadRecord
        mov     ecx, $100

        call    __GetThreadID

.loop:
        dec     ecx
        jz      .error

        add     esi, sizeof.TThreadRecord
        cmp     [esi+TThreadRecord.ThreadID], eax
        jne     .loop

        xor     ecx, ecx
        mov     [esi+TThreadRecord.ThreadID], ecx
        xchg    ecx, [esi+TThreadRecord.ptrStack]

        mov     [__ThreadLock], 0       ; no need to be locked anymore.

        DebugMsg "Thread will be terminated now."

; free the stack

        mov     eax, sys_f68
        mov     ebx, 13
        int     $40

; terminate the thread

        mov     eax,-1                  ; Function -1 : close the thread
        int     $40

.error:                         ; something very wrong.
        DebugMsg "Thread terminate error. Stop it with INT3"
        int3
endp


; Terminate current process
; Returns: nothing.

body TerminateAll
begin

.wait_loop:
        mov     eax, 1
        xchg    eax, [__ThreadLock]
        test    eax, eax
        jz      .locked

        mov     eax, sys_f68    ; switch to another thread.
        mov     ebx, 1
        int     $40

        jmp     .wait_loop

.locked:
        mov     esi, __ThreadList - sizeof.TThreadRecord
        mov     edx, $100

.loop:
        dec     edx
        jz      .threads_ok

        lea     esi, [esi+sizeof.TThreadRecord]
        cmp     [esi+TThreadRecord.ThreadID], 0
        je      .loop

        mov     eax, sys_system
        mov     ebx, 18
        mov     ecx, [esi+TThreadRecord.ThreadID]
        int     $40
        jmp     .loop

; then terminate the main thread, but what if TerminateAll is called from another thread???
.threads_ok:
        mov     [__ThreadLock], 0

        mov     eax,-1
        int     $40
        int3
endp



body GetCmdArguments
begin
        push    ebx ecx edx esi
        stdcall CreateArray, 4
        mov     edx, eax

        stdcall StrDup, bufferPath
        push    eax
        stdcall AddArrayItems, edx, 1
        pop     dword [eax]

        mov     esi, bufferArguments

.main_loop:
        mov     al, [esi]
        test    al, al
        jz      .end_of_arg

        cmp     al, ' '
        ja      .nospace

        inc     esi
        jmp     .main_loop

.nospace:
        xor     ah, ah
        cmp     al, '"'
        jne     .begin

        mov     ah, al
        inc     esi

.begin:
        mov     ebx, esi

.argloop:
        mov     al, [esi]
        test    al, al
        jz      .end_found

        cmp     al, ah
        je      .end_found

        cmp     al, ' '
        jbe     .end_found

        inc     esi
        jmp     .argloop

.end_found:
        mov     ecx, esi
        sub     ecx, ebx

        test    al, al
        jz      .endok

        test    ah,ah
        jz      .endok
        inc     esi
.endok:
        stdcall StrExtract, ebx, 0, ecx

        push    eax
        stdcall AddArrayItems, edx, 1
        pop     dword [eax]
        jmp     .main_loop

.end_of_arg:
        mov     eax, edx
        pop     esi edx ecx ebx
        clc
        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    ebx ecx edx edi

.wait_loop:
        mov     ebx, 1
        xchg    ebx, [__ThreadLock]
        test    ebx, ebx
        jz      .locked

        mov     eax, sys_f68    ; switch to another thread.
        mov     ebx, 1
        int     $40

        jmp     .wait_loop


; search for free slot:

.locked:
        mov     edi, __ThreadList - sizeof.TThreadRecord
        mov     ecx, $100
        xor     eax, eax

.loop:
        dec     ecx
        jz      .error         ; no empty slot.

        lea     edi, [edi+sizeof.TThreadRecord]
        cmp     [edi+TThreadRecord.ThreadID], eax
        jne     .loop

; edi points to the TThreadRecord structure

        mov     eax, sys_f68
        mov     ebx, 12
        mov     ecx, STACK_SIZE
        int     $40
        test    eax, eax
        jz      .error

        mov     [edi+TThreadRecord.ptrStack], eax
        lea     edx, [eax + STACK_SIZE - 8]

        mov     eax, [.ptrArguments]
        mov     ebx, [.ptrFunction]
        mov     [edx+4], eax
        mov     [edx], ebx

        mov     eax, sys_threads
        mov     ebx, 1          ; create thread
        mov     ecx, __CommonThreadProc
        int     $40

        cmp     eax, -1
        je      .error

        DebugMsg "Thread created."

        mov     [edi+TThreadRecord.ThreadID], eax
        clc

.finish:
        mov     [__ThreadLock], 0
        pop     edi edx ecx ebx
        return

.error:
        DebugMsg "ThreadCreate ERROR!"
        stc
        jmp     .finish
endp



proc __CommonThreadProc
begin
        DebugMsg "Thread start!"

        pop     eax
        call    eax     ; the argument is already in the stack.

        DebugMsg "Thread terminate."

        stdcall Terminate, eax
endp



body FreeThreadID
begin
        return
endp


; returns the threadID of the current thread.
proc __GetThreadID
.info TThreadInfo
begin
        push    ebx ecx
; what is TID?
        mov     eax, sys_cpuusage
        lea     ebx, [.info]
        mov     ecx, -1
        int     $40

        mov     eax, [.info.TID]
        pop     ecx ebx
        return
endp



; MUTEX functions
;_________________________________________________________________________________________

body MutexCreate
begin
        push    eax ecx
        mov     ecx, [.ptrMutex]
        xor     eax, eax
        mov     dword [ecx+TMutex.value], eax
        mov     dword [ecx+TMutex.threadID], eax
        mov     dword [ecx+TMutex.counter], 0
        clc
        pop     ecx eax
        return
endp




body WaitForMutex
begin
        pushad

        mov     eax, [.timeout]
        test    eax, eax
        jns     .compute_timeout

        mov     ecx, -1
        jmp     .timeout_ok


.compute_timeout:
        cdq
        mov     ebx, 10
        div     ebx
        mov     edx, eax        ; wait_to
        xor     ecx, ecx        ; wait_to_carry

        mov     eax, sys_getsetup
        mov     ebx, 9
        int     $40

        add     edx, eax
        adc     ecx, ecx

.timeout_ok:
        call    __GetThreadID
        mov     edi, eax

        mov     esi, [.ptrMutex]
        mov     eax, 1

.loop:
        xchg    eax, [esi+TMutex.value]
        test    eax, eax
        jz      .lockok                 ; now it is locked and it was unlocked

        cmp     [esi+TMutex.threadID], edi
        je      .lockok                 ; it is locked by the same thread before.

; check the timeout
        test    ecx, ecx
        js      .wait_more              ; the timeout is disabled.

        mov     eax, sys_getsetup
        mov     ebx, 9
        int     $40

        cmp     eax, edx                ; is the current time above the waitto time?
        ja      .maybetimeout

        xor     ecx, ecx                ; reset the carry.
        jmp     .wait_more

.maybetimeout:
        test    ecx, ecx                ; there is a carry in the counter.
        jz      .error

.wait_more:
        mov     eax, sys_f68    ; switch to another thread.
        mov     ebx, 1
        int     $40
        jmp     .loop

.lockok:
        inc     [esi+TMutex.counter]
        mov     [esi+TMutex.threadID], edi
        clc
        popad
        return

.error:
        stc
        popad
        return
endp




body MutexRelease
begin
        pushf
        pushad

        stdcall __GetThreadID
        mov     edi, eax
        mov     esi, [.ptrMutex]

        cmp     edi, [esi+TMutex.threadID]
        jne     .unlock_ok

        cmp     [esi+TMutex.counter], 0
        jz      .dec_ok                 ; it is error actually, but...

        dec     [esi+TMutex.counter]
        jnz     .unlock_ok

.dec_ok:
        mov     [esi+TMutex.threadID], 0
        mov     [esi+TMutex.value], 0

.unlock_ok:
        popad
        popf
        return
endp



body MutexDestroy
begin
        return
endp

