Browse Source

libposix: add support for O_NONBLOCK

We keep a list of non blocking fds and use awake to
make them not block on read, write, pread and pwrite.
Giacomo Tesio 6 years ago
parent
commit
ea21d3cd2d

+ 110 - 0
qa/lib/newlib/104-files-nonblocking.c

@@ -0,0 +1,110 @@
+// C program to illustrate
+// non I/O blocking calls
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h> // library for fcntl function
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#define MSGSIZE 6
+char* msg1 ="hello";
+char* msg2 ="bye !!";
+ 
+void parent_read(int p[])
+{
+	int nread;
+	char buf[MSGSIZE];
+ 
+	// write link
+	close(p[1]);
+ 
+	while (1) {
+ 
+		// read call if return -1 then pipe is
+		// empty because of fcntl
+		nread = read(p[0], buf, MSGSIZE);
+		switch (nread) {
+		case -1:
+ 
+			// case -1 means pipe is empty and errono
+			// set EAGAIN
+			if (errno == EAGAIN) {
+				printf("(pipe empty)\n");
+				sleep(1);
+				break;
+			}
+ 
+			else {
+				perror("read");
+				exit(4);
+			}
+ 
+		// case 0 means all bytes are read and EOF(end of conv.)
+		case 0:
+			printf("End of conversation\n");
+ 
+			// read link
+			close(p[0]);
+ 
+			exit(0);
+		default:
+ 
+			// text read
+			// by default return no. of bytes
+			// which read call read at that time
+			printf("MSG = %s\n", buf);
+		}
+	}
+}
+void child_write(int p[])
+{
+	int i;
+ 
+	// read link
+	close(p[0]);
+ 
+	// write 3 times "hello" in 3 second interval
+	for (i = 0; i < 3; i++) {
+		write(p[1], msg1, MSGSIZE);
+		sleep(3);
+	}
+ 
+	// write "bye" one times
+	write(p[1], msg2, MSGSIZE);
+ 
+	// here after write all bytes then write end
+	// doesn't close so read end block but
+	// because of fcntl block doesn't happen..
+	exit(0);
+}
+int main()
+{
+	int p[2];
+ 
+	// error checking for pipe
+	if (pipe(p) < 0)
+		exit(1);
+ 
+	// error checking for fcntl
+	if (fcntl(p[0], F_SETFL, O_NONBLOCK) < 0)
+		exit(2);
+ 
+	// continued
+	switch (fork()) {
+ 
+	// error
+	case -1:
+		exit(3);
+ 
+	// 0 for child process
+	case 0:
+		child_write(p);
+		break;
+ 
+	default:
+		parent_read(p);
+		break;
+	}
+	return 0;
+}

+ 2 - 0
qa/lib/newlib/build.json

@@ -45,6 +45,7 @@
 			"101-files.c",
 			"102-files.c",
 			"103-files.c",
+			"104-files-nonblocking.c",
 			"120-fcntl.c",
 			"121-fcntl.c",
 			"200-signals.c",
@@ -112,6 +113,7 @@
 			"101-files.c",
 			"102-files.c",
 			"103-files.c",
+			"104-files-nonblocking.c",
 			"120-fcntl.c",
 			"121-fcntl.c",
 			"200-signals.c",

+ 4 - 1
sys/include/posix.h

@@ -1,7 +1,7 @@
 /*
  * This file is part of Jehanne.
  *
- * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ * Copyright (C) 2017-2018 Giacomo Tesio <giacomo@tesio.it>
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -375,6 +375,9 @@ extern int libposix_translate_error(PosixErrorTranslator translation, uintptr_t
 /* define the value of AT_FDCWD according to the library headers */
 extern int libposix_define_at_fdcwd(int AT_FDCWD);
 
+/* define the value of O_NONBLOCK according to the library headers */
+extern int libposix_define_ononblock(int O_NONBLOCK);
+
 /* define the value of a specific PosixError according to the library headers */
 extern int libposix_define_errno(PosixError e, int errno);
 

+ 5 - 2
sys/src/lib/posix/fcntl.c

@@ -1,7 +1,7 @@
 /*
  * This file is part of Jehanne.
  *
- * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ * Copyright (C) 2017-2018 Giacomo Tesio <giacomo@tesio.it>
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -21,6 +21,8 @@
 #include <posix.h>
 #include "internal.h"
 
+extern int __libposix_O_NONBLOCK;
+
 static int
 fcntl_dup(int *errnop, int fd, int minfd)
 {
@@ -150,8 +152,9 @@ POSIX_fcntl(int *errnop, int fd, PosixFDCmds cmd, uintptr_t arg)
 			return -1;
 		return flags & (~OCEXEC);
 	case PosixFDCSetFL:
+		flags = (int)arg;
+		__libposix_set_non_blocking(fd, flags & __libposix_O_NONBLOCK);
 		return 0;
-		break;
 	}
 
 	*errnop = __libposix_get_errno(PosixEINVAL);

+ 110 - 6
sys/src/lib/posix/files.c

@@ -1,7 +1,7 @@
 /*
  * This file is part of Jehanne.
  *
- * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ * Copyright (C) 2017-2018 Giacomo Tesio <giacomo@tesio.it>
  *
  * This is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -33,6 +33,11 @@ static int *__libposix_coe_fds;
 static int __libposix_coe_fds_size;
 static int __libposix_coe_fds_used;
 
+int __libposix_O_NONBLOCK;
+static int *__libposix_nb_fds;
+static int __libposix_nb_fds_size;
+static int __libposix_nb_fds_used;
+
 typedef enum SeekTypes
 {
 	SeekSet		= 0,
@@ -117,6 +122,53 @@ __libposix_should_close_on_exec(int fd)
 	return 0;
 }
 
+void
+__libposix_set_non_blocking(int fd, int enable)
+{
+	int i;
+	if(__libposix_nb_fds_size == __libposix_nb_fds_used){
+		__libposix_nb_fds_size += 8;
+		__libposix_nb_fds = realloc(__libposix_nb_fds, __libposix_nb_fds_size * sizeof(int));
+		i = __libposix_nb_fds_size;
+		while(i > __libposix_nb_fds_used)
+			__libposix_nb_fds[--i] = -1;
+	}
+	/* remove fd if already present */
+	i = 0;
+	while(i < __libposix_nb_fds_size){
+		if(__libposix_nb_fds[i] == fd){
+			__libposix_nb_fds[i] = -1;
+			--__libposix_nb_fds_used;
+			break;
+		}
+		++i;
+	}
+	if(enable){
+		/* add fd to ensure it will not block */
+		i = 0;
+		while(i < __libposix_nb_fds_size){
+			if(__libposix_nb_fds[i] == -1){
+				__libposix_nb_fds[i] = fd;
+				break;
+			}
+			++i;
+		}
+		++__libposix_nb_fds_used;
+	}
+}
+
+int
+__libposix_should_not_block(int fd)
+{
+	int i = 0;
+	while(i < __libposix_nb_fds_size){
+		if(__libposix_nb_fds[i] == fd)
+			return 1;
+		++i;
+	}
+	return 0;
+}
+
 int
 libposix_translate_open(PosixOpenTranslator translation)
 {
@@ -295,8 +347,11 @@ POSIX_open(int *errnop, const char *name, int flags, int mode)
 	} else {
 		f = ocreate(name, (unsigned int)omode, (unsigned int)cperm);
 	}
-	if(f >= 0)
+	if(f >= 0){
+		if(flags & __libposix_O_NONBLOCK)
+			__libposix_set_non_blocking(f, 1);
 		return f;
+	}
 	*errnop = __libposix_translate_errstr((uintptr_t)POSIX_open);
 	return -1;
 
@@ -308,40 +363,60 @@ FailWithError:
 long
 POSIX_read(int *errnop, int fd, char *buf, size_t len)
 {
-	long r;
+	long r, wkp = 0;
 
 	if(fd < 0){
 		*errnop = __libposix_get_errno(PosixEBADF);
 		return -1;
 	}
 OnIgnoredSignalInterrupt:
+	if(__libposix_should_not_block(fd))
+		wkp = awake(2);
 	r = sys_pread(fd, buf, len, -1);
 	if(r < 0){
+		if(wkp){
+			if(!awakened(wkp))
+				forgivewkp(wkp);
+			*errnop = __libposix_get_errno(PosixEAGAIN);
+			return -1;
+		}
 		if(__libposix_restart_syscall())
 			goto OnIgnoredSignalInterrupt;
 		*errnop = __libposix_translate_errstr((uintptr_t)POSIX_read);
 		return -1;
 	}
+	if(wkp)
+		forgivewkp(wkp);
 	return r;
 }
 
 long
 POSIX_write(int *errnop, int fd, const void *buf, size_t len)
 {
-	long w;
+	long w, wkp = 0;
 
 	if(fd < 0){
 		*errnop = __libposix_get_errno(PosixEBADF);
 		return -1;
 	}
 OnIgnoredSignalInterrupt:
+	if(__libposix_should_not_block(fd))
+		wkp = awake(2);
 	w = sys_pwrite(fd, buf, len, -1);
 	if(w < 0){
+		if(wkp){
+			if(!awakened(wkp))
+				forgivewkp(wkp);
+			*errnop = __libposix_get_errno(PosixEAGAIN);
+			return -1;
+		}
 		if(__libposix_restart_syscall())
 			goto OnIgnoredSignalInterrupt;
 		*errnop = __libposix_translate_errstr((uintptr_t)POSIX_write);
 		return -1;
 	}
+	if(wkp)
+		forgivewkp(wkp);
 	return w;
 }
 
@@ -370,40 +445,60 @@ POSIX_lseek(int *errnop, int fd, off_t pos, int whence)
 long
 POSIX_pread(int *errnop, int fd, char *buf, size_t len, long offset)
 {
-	long r;
+	long r, wkp = 0;
 
 	if(fd < 0){
 		*errnop = __libposix_get_errno(PosixEBADF);
 		return -1;
 	}
 OnIgnoredSignalInterrupt:
+	if(__libposix_should_not_block(fd))
+		wkp = awake(2);
 	r = sys_pread(fd, buf, len, offset);
 	if(r < 0){
+		if(wkp){
+			if(!awakened(wkp))
+				forgivewkp(wkp);
+			*errnop = __libposix_get_errno(PosixEAGAIN);
+			return -1;
+		}
 		if(__libposix_restart_syscall())
 			goto OnIgnoredSignalInterrupt;
 		*errnop = __libposix_translate_errstr((uintptr_t)POSIX_read);
 		return -1;
 	}
+	if(wkp)
+		forgivewkp(wkp);
 	return r;
 }
 
 long
 POSIX_pwrite(int *errnop, int fd, const char *buf, size_t len, long offset)
 {
-	long w;
+	long w, wkp = 0;
 
 	if(fd < 0){
 		*errnop = __libposix_get_errno(PosixEBADF);
 		return -1;
 	}
 OnIgnoredSignalInterrupt:
+	if(__libposix_should_not_block(fd))
+		wkp = awake(2);
 	w = sys_pwrite(fd, buf, len, offset);
 	if(w < 0){
+		if(wkp){
+			if(!awakened(wkp))
+				forgivewkp(wkp);
+			*errnop = __libposix_get_errno(PosixEAGAIN);
+			return -1;
+		}
 		if(__libposix_restart_syscall())
 			goto OnIgnoredSignalInterrupt;
 		*errnop = __libposix_translate_errstr((uintptr_t)POSIX_write);
 		return -1;
 	}
+	if(wkp)
+		forgivewkp(wkp);
 	return w;
 }
 
@@ -881,3 +976,12 @@ libposix_define_at_fdcwd(int AT_FDCWD)
 	__libposix_AT_FDCWD = AT_FDCWD;
 	return 0;
 }
+
+int
+libposix_define_ononblock(int O_NONBLOCK)
+{
+	if(__libposix_O_NONBLOCK != 0)
+		return -1;
+	__libposix_O_NONBLOCK = O_NONBLOCK;
+	return 0;
+}

+ 1 - 0
sys/src/lib/posix/internal.h

@@ -142,3 +142,4 @@ extern long __libposix_sighelper_set(PosixHelperCommand command, PosixSignalMask
 extern long __libposix_sighelper_signal(PosixHelperCommand command, int target, PosixSignalInfo *siginfo);
 extern long __libposix_sighelper_wait(PosixSignalMask set, PosixSignalInfo *siginfo);
 extern long __libposix_sighelper_set_pgid(int target, int group_id);
+extern void __libposix_set_non_blocking(int fd, int enable);