__ThreadStackSize = 1024*1024


; 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 esi edi

        push    ebp
        mov     eax, sys_mmap2
        xor     ebx, ebx
        mov     ecx, __ThreadStackSize
        mov     edx, PROT_READ or PROT_WRITE or PROT_EXEC
        mov     esi, MAP_ANONYMOUS or MAP_PRIVATE or MAP_STACK
        mov     edi, -1
        xor     ebp, ebp
        int $80
        pop     ebp

        cmp     eax, $ffffff00
        jae     .error

; set the guard page:
        push    eax
        mov     ebx, eax
        mov     eax, sys_mprotect
        mov     ecx, 4096               ; one page
        mov     edx, PROT_NONE
        pop     eax

        lea     ecx, [eax+__ThreadStackSize-8-$c00]

; transfer arguments in the new stack.
        mov     eax, [.ptrFunction]
        mov     [ecx], eax
        mov     eax, [.ptrArguments]
        mov     [ecx+4], eax
        lea     esi, [ecx+4]

        mov     eax, sys_clone
        mov     ebx, CLONE_SIGHAND or CLONE_THREAD or CLONE_FILES or         \
                     CLONE_FS or CLONE_VM or CLONE_PARENT
        int 0x80

        test    eax, eax
        js      .error
        jz      .is_clone

;        push    eax

;; release the stack.
;        mov     eax, sys_munmap

;        mov     ebx, ecx
;        lea     ebx, [ecx-__ThreadStackSize+8]

;        mov     ecx, __ThreadStackSize
;        int $80

;        pop     eax
        clc
        pop     edi esi edx ecx ebx
        return

; The stack is not allocated or sys_clone exits with error.
.error:
        stc
        pop     edi esi edx ecx ebx
        return

; this is the clone process - call the thread.
.is_clone:

        pop     eax
        call    eax       ; the argument is already in the stack.
        mov     ebx, eax  ; the thread procedure can return exit code. Save it in esi.

; and exit the process  - the question is - how to free the stack allocated...
        mov     eax, sys_exit
        int     0x80
endp



; Terminate current thread
; Returns: eax = 0
body Terminate
begin
        mov     eax, sys_exit
        mov     ebx, [.exit_code]
        int     $80
        return
endp




body Exec
begin
        return
endp



body FreeThreadID
begin
        return
endp



; MUTEX functions
;_________________________________________________________________________________________


MUTEX_LOCK_CONTEND = $0101

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


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
.time lnx_timespec
begin
        pushad
        mov     ecx, 100                ; why 100?
        mov     ebx, [.ptrMutex]

        mov     eax, sys_gettid
        int     $80
        mov     edi, eax

        cmp     byte [ebx+TMutex.value], 0      ; locked?
        je      .loop1                          ; no, try to get it

; if locked, check who own it
        cmp     edi, [ebx+TMutex.threadID]
        je      .lock_ok

.loop1:
        mov     dl, 1
        xchg    byte [ebx+TMutex.value], dl
        test    dl, dl
        jz      .lock_ok

        rep nop                         ; ???
        loop    .loop1

; Set up syscall details
        mov      edx, MUTEX_LOCK_CONTEND
        mov      ecx, FUTEX_WAIT or FUTEX_PRIVATE_FLAG
        xor      esi, esi        ; timeout
        jmp      .check

; Wait loop
.wait:
        mov     eax, sys_futex
        int     $80

.check:
        mov     eax, edx
        xchg    eax, [ebx+TMutex.value]
        test    eax, 1
        jnz     .wait

.lock_ok:
        inc     [ebx+TMutex.counter]
        mov     [ebx+TMutex.threadID], edi
        clc
        popad
        return

.err_timeout:
        stc
        popad
        return
endp




body MutexRelease
begin
        pushf
        pushad

        mov     ebx, [.ptrMutex]
        mov     eax, sys_gettid
        int     $80
        mov     edi, eax

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

        cmp     [ebx+TMutex.counter], 0
        je      .dec_ok
        dec     [ebx+TMutex.counter]
        jnz     .unlock_ok

.dec_ok:
        cmp     dword [ebx+TMutex.value], 1
        jne     .unlock

        mov     eax, 1
        xor     ecx, ecx
        lock cmpxchg [ebx+TMutex.value], ecx
        jz      .unlock_ok

.unlock:
        mov     byte [ebx+TMutex.value], 0

; Spin, and hope someone takes the lock
        mov     ecx, 200

.wait:
        test    byte [ebx], 1
        jnz     .unlock_ok

        rep nop
        loop    .wait

; Wake up someone
        mov     byte [ebx+1], 0
        mov     eax, sys_futex
        mov     ecx, FUTEX_WAKE or FUTEX_PRIVATE_FLAG
        mov     edx, 1
        int     $80

.unlock_ok:
        popad
        popf
        clc
        return
endp



body MutexDestroy
begin
        return
endp

