pipe.inc
kolibrios-nextgen_kolibrios-kernel/kernel/posix/pipe.inc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2022. All rights reserved. ;;
;;  Distributed under terms of the GNU General Public License.  ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

F_READ              = 0x0001  ; file opened for reading
F_WRITE             = 0x0002  ; file opened for writing

O_CLOEXEC           = 0x40000
PIPE_BUFFER_SIZE    = 4096


iglobal
align 4
pipe_file_ops:
        dd pipe_close       ;0
        dd pipe_read        ;1
        dd pipe_write       ;2
endg

;int pipe2(int pipefd[2], int flags);
;ecx pipefd
;edx flags

align 4
sys_pipe2:
.pipeflags equ  esp+16
.pipefd    equ  esp+12
.fdread    equ  esp+8
.fdwrite   equ  esp+4
.intpipe   equ  esp

        push    ebp
        test    ecx, ecx
        mov     ebp, -EFAULT
        js      .fail

        test    edx, not O_CLOEXEC
        mov     ebp, -EINVAL
        jnz     .fail

        push    edx
        push    ecx
        sub     esp, (5-2)*4

        mov     ecx, sizeof.FILED
        call    create_object
        mov     [.fdread], eax
        test    eax, eax
        mov     ebp, -EMFILE
        jz      .err_0

        mov     ecx, sizeof.FILED
        call    create_object
        mov     [.fdwrite], eax
        test    eax, eax
        jz      .err_1

        mov     eax, sizeof.PIPE
        call    malloc
        test    eax, eax
        mov     ebp, -ENFILE
        jz      .err_2

        mov     ebp, eax

        stdcall create_ring_buffer, PIPE_BUFFER_SIZE, PG_SWR
        test    eax, eax
        jz      .err_3

        mov     [ebp + PIPE.pipe_ops], pipe_file_ops
        mov     [ebp + PIPE.buffer], eax

        xor     eax, eax
        mov     [ebp + PIPE.count], eax
        mov     [ebp + PIPE.read_end], eax
        mov     [ebp + PIPE.write_end], eax

        inc     eax
        mov     [ebp + PIPE.readers], eax
        mov     [ebp + PIPE.writers], eax

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_init

        lea     ecx, [ebp + PIPE.rlist]
        list_init ecx

        lea     ecx, [ebp + PIPE.wlist]
        list_init ecx

        mov     eax, [.fdread]
        mov     edx, [.fdwrite]
        mov     ecx, [.pipefd]

        mov     [eax + FILED.magic], 'PIPE'
        mov     [eax + FILED.destroy], 0
        mov     [eax + FILED.mode], F_READ
        mov     [eax + FILED.file], ebp

        mov     [edx + FILED.magic], 'PIPE'
        mov     [edx + FILED.destroy], 0
        mov     [edx + FILED.mode], F_WRITE
        mov     [edx + FILED.file], ebp

        mov     eax, [eax + FILED.handle]
        mov     edx, [edx + FILED.handle]

        mov     [ecx], eax
        mov     [ecx+4], edx
        add     esp, 5*4
        pop     ebp
        xor     eax, eax
        mov     [esp + SYSCALL_STACK.eax], eax
        ret
.err_3:
        mov     eax, ebp
        call    free
        mov     ebp, -ENFILE
.err_2:
        mov     ecx, [.fdwrite]
        call    destroy_object
.err_1:
        mov     ecx, [.fdread]
        call    destroy_object
.err_0:
        add     esp, 5*4
.fail:
        mov     eax, ebp
        pop     ebp
        mov     [esp + SYSCALL_STACK.eax], eax
        ret

purge .pipeflags
purge .filefd
purge .fdread
purge .fdwrite
purge .intpipe


; edx dst_buf
; esi read count
; ebp pipe

align 4
pipe_read:

        mov     edi, edx

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_lock
.again:
        xor     eax, eax
        cmp     eax, [ebp + PIPE.writers]
        je      .eof

        mov     ecx, [ebp + PIPE.count]
        test    ecx, ecx
        jz      .wait

.check_count:
        cmp     ecx, esi
        jb      .read
        mov     ecx, esi
.read:
        mov     esi, [ebp + PIPE.buffer]
        add     esi, [ebp + PIPE.read_end]
        mov     [esp + SYSCALL_STACK.eax], ecx
        sub     [ebp + PIPE.count], ecx
        cld
        rep movsb
        and     esi, 0xFFF
        mov     [ebp + PIPE.read_end], esi

        lea     ecx, [ebp + PIPE.wlist]
        cmp     ecx, [ebp + PIPE.wlist.next]
        je      @F

        mov     ecx, [ecx + MUTEX_WAITER.task]
        mov     [ecx + APPDATA.state], TSTATE_RUNNING         ;activate writer task
@@:
        cmp     [ebp + PIPE.count], 0
        je      @F

        lea     eax, [ebp + PIPE.rlist]
        cmp     eax, [ebp + PIPE.rlist.next]
        je      @F

        mov     eax, [eax + MUTEX_WAITER.task]
        mov     [eax + APPDATA.state], TSTATE_RUNNING         ;activate reader task
@@:
        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_unlock
        ret

.wait:
        pushfd
        cli

        sub     esp, sizeof.MUTEX_WAITER
        mov     ebx, [current_slot]
        mov     [esp + MUTEX_WAITER.task], ebx
        lea     edx, [ebp + PIPE.rlist]

        list_add_tail esp, edx                  ;esp= new waiter, edx= list head

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_unlock

        mov     [ebx + APPDATA.state], TSTATE_RUN_SUSPENDED
        call    change_task

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_lock

        list_del esp
        add     esp, sizeof.MUTEX_WAITER
        popfd
        jmp     .again

.eof:
        mov     [esp + SYSCALL_STACK.eax], eax
        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_unlock
        ret

; edx src_buf
; esi write count
; ebp pipe

align 4
pipe_write:

.written    equ esp

        push    0                               ;written
        mov     ebx, esi                        ;ebx = write count
        mov     esi, edx                        ;esi = src

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_lock
.again:
        xor     eax, eax
        cmp     eax, [ebp + PIPE.readers]
        je      .epipe

        mov     ecx, 4096
        sub     ecx, [ebp + PIPE.count]
        jz      .wait                           ;wait if buffer full

.check_count:
        cmp     ecx, ebx
        jb      .write
        mov     ecx, ebx
.write:
        mov     edi, [ebp + PIPE.buffer]
        add     edi, [ebp + PIPE.write_end]
        add     [.written], ecx
        sub     ebx, ecx
        add     [ebp + PIPE.count], ecx

        cld
        rep movsb
        and     edi, 0xFFF
        mov     [ebp + PIPE.write_end], edi

        pushfd
        cli

        lea     eax, [ebp + PIPE.rlist]
        cmp     eax, [ebp + PIPE.rlist.next]
        je      @F

        mov     eax, [eax + MUTEX_WAITER.task]
        mov     [eax + APPDATA.state], TSTATE_RUNNING         ;activate reader task
@@:
        cmp     [ebp + PIPE.count], 4096
        je      @F

        lea     ecx, [ebp + PIPE.wlist]
        cmp     ecx, [ebp + PIPE.wlist.next]
        je      @F

        mov     ecx, [eax + MUTEX_WAITER.task]
        mov     [ecx + APPDATA.state], TSTATE_RUNNING         ;activate writer task
@@:
        popfd

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_unlock

        test    ebx, ebx
        jnz     .again

        pop     eax                             ; written
        mov     [esp + SYSCALL_STACK.eax], eax
        ret

.wait:
        pushfd
        cli

        sub     esp, sizeof.MUTEX_WAITER
        mov     ecx, [current_slot]
        mov     [esp + MUTEX_WAITER.task], ecx
        lea     edx, [ebp+PIPE.wlist]

        list_add_tail esp, edx                  ;esp= new waiter, edx= list head

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_unlock

        mov     [ecx + APPDATA.state], TSTATE_RUN_SUSPENDED
        call    change_task

        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_lock

        list_del esp
        add     esp, sizeof.MUTEX_WAITER
        popfd
        jmp     .again

.epipe:
        lea     ecx, [ebp + PIPE.pipe_lock]
        call    mutex_unlock

        add     esp, 4
        mov     [esp + SYSCALL_STACK.eax], -EPIPE
        ret

align 4
pipe_close:
        mov     [esp + SYSCALL_STACK.eax], -EBADF
        ret
