Browse Source

[os2]: Implement pipe_has_data procedure

flysand7 11 months ago
parent
commit
dbad23385d

+ 3 - 1
core/os/os2/errors_windows.odin

@@ -55,13 +55,15 @@ _get_platform_error :: proc() -> Error {
 	case win32.ERROR_NEGATIVE_SEEK:
 	case win32.ERROR_NEGATIVE_SEEK:
 		return .Invalid_Offset
 		return .Invalid_Offset
 
 
+	case win32.ERROR_BROKEN_PIPE:
+		return .Broken_Pipe
+
 	case
 	case
 		win32.ERROR_BAD_ARGUMENTS,
 		win32.ERROR_BAD_ARGUMENTS,
 		win32.ERROR_INVALID_PARAMETER,
 		win32.ERROR_INVALID_PARAMETER,
 		win32.ERROR_NOT_ENOUGH_MEMORY,
 		win32.ERROR_NOT_ENOUGH_MEMORY,
 		win32.ERROR_NO_MORE_FILES,
 		win32.ERROR_NO_MORE_FILES,
 		win32.ERROR_LOCK_VIOLATION,
 		win32.ERROR_LOCK_VIOLATION,
-		win32.ERROR_BROKEN_PIPE,
 		win32.ERROR_CALL_NOT_IMPLEMENTED,
 		win32.ERROR_CALL_NOT_IMPLEMENTED,
 		win32.ERROR_INSUFFICIENT_BUFFER,
 		win32.ERROR_INSUFFICIENT_BUFFER,
 		win32.ERROR_INVALID_NAME,
 		win32.ERROR_INVALID_NAME,

+ 37 - 0
core/os/os2/pipe.odin

@@ -1,6 +1,43 @@
 package os2
 package os2
 
 
+/*
+Create an anonymous pipe.
+
+This procedure creates an anonymous pipe, returning two ends of the pipe, `r`
+and `w`. The file `r` is the readable end of the pipe. The file `w` is a
+writeable end of the pipe.
+
+Pipes are used as an inter-process communication mechanism, to communicate
+between a parent and a child process. The child uses one end of the pipe to
+write data, and the parent uses the other end to read from the pipe
+(or vice-versa). When a parent passes one of the ends of the pipe to the child
+process, that end of the pipe needs to be closed by the parent, before any data
+is attempted to be read.
+
+Although pipes look like files and is compatible with most file APIs in package
+os2, the way it's meant to be read is different. Due to asynchronous nature of
+the communication channel, the data may not be present at the time of a read
+request. The other scenario is when a pipe has no data because the other end
+of the pipe was closed by the child process.
+*/
 @(require_results)
 @(require_results)
 pipe :: proc() -> (r, w: ^File, err: Error) {
 pipe :: proc() -> (r, w: ^File, err: Error) {
 	return _pipe()
 	return _pipe()
 }
 }
+
+/*
+Check if the pipe has any data.
+
+This procedure checks whether a read-end of the pipe has data that can be
+read, and returns `true`, if the pipe has readable data, and `false` if the
+pipe is empty. This procedure does not block the execution of the current
+thread.
+
+**Note**: If the other end of the pipe was closed by the child process, the
+`.Broken_Pipe`
+can be returned by this procedure. Handle these errors accordingly.
+*/
+@(require_results)
+pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	return _pipe_has_data(r)
+}

+ 26 - 0
core/os/os2/pipe_linux.odin

@@ -15,3 +15,29 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 
 
 	return
 	return
 }
 }
+
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	if r == nil || r.impl == nil {
+		return false, nil
+	}
+	fd := linux.Fd((^File_Impl)(r.impl).fd)
+	poll_fds := []linux.Poll_Fd {
+		linux.Poll_Fd {
+			fd = fd,
+			events = {.IN, .HUP},
+		},
+	}
+	n, errno := linux.poll(poll_fds, 0)
+	if n != 1 || errno != nil {
+		return false, _get_platform_error(errno)
+	}
+	pipe_events := poll_fds[0].revents
+	if pipe_events >= {.IN} {
+		return true, nil
+	}
+	if pipe_events >= {.HUP} {
+		return false, .Broken_Pipe
+	}
+	return false, nil
+}

+ 25 - 0
core/os/os2/pipe_posix.odin

@@ -44,3 +44,28 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 	return
 	return
 }
 }
 
 
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	if r == nil || r.impl == nil {
+		return false, nil
+	}
+	fd := posix.FD((^File_Impl)(r.impl).fd)
+	poll_fds := []posix.pollfd {
+		posix.pollfd {
+			fd = fd,
+			events = {.IN, .HUP},
+		},
+	}
+	n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0)
+	if n != 1 {
+		return false, _get_platform_error()
+	}
+	pipe_events := poll_fds[0].revents
+	if pipe_events >= {.IN} {
+		return true, nil
+	}
+	if pipe_events >= {.HUP} {
+		return false, .Broken_Pipe
+	}
+	return false, nil
+}

+ 12 - 0
core/os/os2/pipe_windows.odin

@@ -15,3 +15,15 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 	return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
 	return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
 }
 }
 
 
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	if r == nil || r.impl == nil {
+		return false, nil
+	}
+	handle := win32.HANDLE((^File_Impl)(r.impl).fd)
+	bytes_available: u32
+    if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) {
+        return false, _get_platform_error()
+    }
+    return bytes_available > 0, nil
+}

+ 8 - 0
core/sys/windows/kernel32.odin

@@ -381,6 +381,14 @@ foreign kernel32 {
 		nDefaultTimeOut: DWORD,
 		nDefaultTimeOut: DWORD,
 		lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
 		lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
 	) -> HANDLE ---
 	) -> HANDLE ---
+	PeekNamedPipe :: proc(
+        hNamedPipe: HANDLE,
+        lpBuffer: rawptr,
+        nBufferSize: u32,
+        lpBytesRead: ^u32,
+        lpTotalBytesAvail: ^u32,
+        lpBytesLeftThisMessage: ^u32,
+    ) -> BOOL ---
 	CancelIo :: proc(handle: HANDLE) -> BOOL ---
 	CancelIo :: proc(handle: HANDLE) -> BOOL ---
 	GetOverlappedResult :: proc(
 	GetOverlappedResult :: proc(
 		hFile: HANDLE,
 		hFile: HANDLE,