Переглянути джерело

fixed handling of interruped Unix system calls

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@7092 8c9fc860-2736-0410-a75d-ab315db34111
eth.guenter 8 роки тому
батько
коміт
98b9d9b154
1 змінених файлів з 60 додано та 42 видалено
  1. 60 42
      source/Unix.UnixFiles.Mod

+ 60 - 42
source/Unix.UnixFiles.Mod

@@ -258,8 +258,8 @@ TYPE
 
 
 				PROCEDURE Old0*( name: ARRAY OF CHAR ): Files.File;
-				VAR f: File;  stat: Unix.Status;  fd, r, errno, pos: LONGINT;
-					oflags: SET;  nextdir, path: Filename;  fo: LONGINT;
+				VAR f: File;  stat: Unix.Status;  fd, r, pos: LONGINT; 
+					oflags: SET;  nextdir, path: Filename; 
 				BEGIN  {EXCLUSIVE}
 					IF name = "" THEN  RETURN NIL  END;
 					
@@ -275,15 +275,7 @@ TYPE
 						IF r >= 0 THEN
 							r := Unix.access( ADDRESSOF( path ), Unix.W_OK );
 							IF r < 0 THEN  oflags := Unix.rdonly  ELSE  oflags := Unix.rdwr  END;
-						
-							fd := Unix.open( ADDRESSOF( path ), oflags, {} );  errno := Unix.errno();
-							IF ((fd < 0) & (errno IN {Unix.ENFILE, Unix.EMFILE, Unix.EINVAL}))  THEN
-								REPEAT
-									fo := openfiles;  Kernel.GC;  WaitClose( fo );
-									fd := Unix.open( ADDRESSOF( path ), oflags, {} );  errno := Unix.errno();
-								UNTIL (fd >= 0) OR ~ (errno IN  {Unix.ENFILE, Unix.EMFILE, Unix.EINVAL})
-							END;
-							
+							fd := UnixOpen( ADDRESSOF( path ), oflags, {} );
 							IF fd >= 0 THEN
 								r := Unix.fstat( fd, stat );
 								f := collection.ByStat(stat);
@@ -337,6 +329,7 @@ TYPE
 					key := 0;
 				END Delete0;
 				
+				
 				(* return remaining old file, if any *)
 				PROCEDURE TryRename*( old, new: ARRAY OF CHAR;  f: Files.File;  VAR res: LONGINT ): LONGINT;
 				CONST Bufsize = 4096;
@@ -355,24 +348,21 @@ TYPE
 						IF r < 0 THEN (* could not rename, try copy *)
 							res := Unix.errno( );
 							IF (res = Unix.EXDEV) OR (res = Unix.ETXTBSY) THEN  (* cross device link, move the file / file busy frequently happens in VirtualBox *)
-								fdold := Unix.open( ADDRESSOF( old ), Unix.rdonly, {} );
+								fdold := UnixOpen( ADDRESSOF( old ), Unix.rdonly, {} );
 								fo := fdold;
 								IF fdold < 0 THEN    
 									res := Unix.errno( );  
 									RETURN NoDesc;
 								END;
-								fdnew := Unix.open( ADDRESSOF( new ), Unix.rdwr + Unix.creat + Unix.trunc, Unix.rwrwr );
+								fdnew := UnixOpen( ADDRESSOF( new ), Unix.rdwr + Unix.creat + Unix.trunc, Unix.rwrwr );
 								IF fdnew < 0 THEN    
 									res := Unix.errno( );  
 									RETURN NoDesc;
 								END;
 								REPEAT
-									n := Unix.read( fdold, ADDRESSOF( buf ), Bufsize ); (*can return -1 with EINTR without any particular reason*)
+									n := UnixRead( fdold, ADDRESSOF( buf ), Bufsize ); 
 									IF n > 0 THEN
-										REPEAT
-											r := Unix.write( fdnew, ADDRESSOF( buf ), n );
-											IF r < 0 THEN  res:= Unix.errno( )  END;
-										UNTIL (r >= 0) OR (res # Unix.EINTR); (*ignore EINTR, retry*)
+										r := UnixWrite( fdnew, ADDRESSOF( buf ), n );
 										IF r < 0 THEN
 											r := Unix.close( fdold );  
 											r := Unix.close( fdnew );
@@ -551,7 +541,7 @@ TYPE
 
 				PROCEDURE CreateUnixFile;
 				VAR 
-					stat: Unix.Status;  done: BOOLEAN;  errno,fo: LONGINT;
+					stat: Unix.Status;  errno: LONGINT;
 				BEGIN
 					IF state = Create THEN  
 						GetTempName( registerName, workName );  tempFile := TRUE
@@ -565,17 +555,8 @@ TYPE
 					errno := Unix.unlink( ADDRESSOF( workName ) );
 					(*unlink first to avoid stale NFS handles and to avoid reuse of inodes*)
 											
-					fd := Unix.open( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr );
-					done := fd >= 0;  errno := Unix.errno();
-
-					IF ( (~done) & (errno IN {Unix.ENFILE, Unix.EMFILE, Unix.EINVAL})) THEN
-						REPEAT
-							fo := openfiles;  Kernel.GC;  WaitClose( fo );
-							fd := Unix.open( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr);  errno := Unix.errno();
-						UNTIL (fd >= 0) OR ~ (errno IN  {Unix.ENFILE, Unix.EMFILE, Unix.EINVAL});
-						done := fd >= 0
-					END;
-					IF done THEN
+					fd := UnixOpen( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr );
+					IF fd >= 0 THEN
 						errno := Unix.fstat( fd, stat );  
 						dev := stat.dev;  ino := stat.ino;  mtime := stat.mtime.sec;
 						state := Open;  fpos := 0;
@@ -588,15 +569,12 @@ TYPE
 				
 						
 				PROCEDURE Flush( buf: Buffer );
-				VAR res,err: LONGINT;  stat: Unix.Status;
+				VAR res: LONGINT;  stat: Unix.Status;
 				BEGIN
 					IF buf.chg THEN
 						IF fd = NoDesc THEN  CreateUnixFile  END;
 						IF buf.org # fpos THEN  res := Unix.lseek( fd, buf.org, 0 )  END;
-						REPEAT
-							res := Unix.write( fd, ADDRESSOF( buf.data ), buf.size );
-							IF res < 0 THEN  err := Unix.errno()  END;
-						UNTIL (res >= 0) OR (err # Unix.EINTR);   (*ignore EINTR, retry*)
+						res := UnixWrite( fd, ADDRESSOF( buf.data ), buf.size );
 						IF res < 0 THEN  Halt( SELF, TRUE, "UnixFiles.File.Flush: write failed" )  END;
 						fpos := buf.org + buf.size;  buf.chg := FALSE;
 						res := Unix.fstat( fd, stat );  mtime := stat.mtime.sec
@@ -637,11 +615,7 @@ TYPE
 							IF fd = NoDesc THEN  CreateUnixFile  END;
 							IF fpos # org THEN  res := Unix.lseek( fd, org, 0 )  END;
 							IF res < 0 THEN  Halt( SELF, TRUE, "UnixFiles.File.Set: lseek failed" )  END;
-							REPEAT
-								n := Unix.read( fd, ADDRESSOF( buf.data ), Bufsize ); (*can return -1 with EINTR without warning*)
-								IF n < 0 THEN  res:= Unix.errno( )  END;
-							UNTIL (n >= 0) OR ((res # Unix.EINTR) & (res # Unix.EINVAL));
-									(* Solaris returns EINVAL instead of EINTR ! ??? *)
+							n := UnixRead( fd, ADDRESSOF( buf.data ), Bufsize ); 
 							IF n < 0 THEN  
 								IF p < fsize THEN  Halt( SELF, TRUE, "UnixFiles.File.Set: read failed" )  
 								ELSE n := 0
@@ -808,7 +782,7 @@ TYPE
 						IF fo # NoDesc THEN (* SELF still refers to old file *)
 							res := Unix.close(fd); 
 							res := Unix.unlink(ADDRESSOF(workName));  (* VirtualBox ! Can only delete file when closed. *)
-							fd := Unix.open( ADDRESSOF( registerName ), Unix.rdwr, Unix.rwrwr );
+							fd := UnixOpen( ADDRESSOF( registerName ), Unix.rdwr, Unix.rwrwr );
 						END;
 
 						workName := registerName;  registerName := "";  tempFile := FALSE;
@@ -866,7 +840,9 @@ TYPE
 				END GetName;
 
 			END File;
-
+			
+		
+	
 (*===================================================================*)
 
 	(** Get the current directory. *)
@@ -1119,6 +1095,48 @@ TYPE
 		Modules.InstallTermHandler( Finalization )	
 	END Initialize;
 	
+	
+	(*!	The system calls open, read and write return -1 when they get interrupted 
+		by receiving a signal. Possibly through Objects.SuspendActivities() (GC).
+	*)
+	PROCEDURE UnixOpen( path: ADDRESS; flags, permissions: SET ): LONGINT;
+	VAR 
+		fd, fo, error: LONGINT;  failure: BOOLEAN;
+	BEGIN
+		failure := FALSE;
+		REPEAT
+			fd := Unix.open( path, flags, permissions );
+			IF fd < 0 THEN
+				error := Unix.errno();
+				IF error IN  {Unix.ENFILE, Unix.EMFILE} THEN
+					fo := openfiles;  Kernel.GC;  WaitClose( fo )
+				ELSIF error # Unix.EINTR THEN
+					failure := TRUE
+				END
+			END
+		UNTIL (fd >= 0) OR failure;
+		RETURN fd
+	END UnixOpen;
+	
+	PROCEDURE UnixRead( fd: LONGINT; buf: ADDRESS; len: SIZE ): LONGINT;
+	VAR n: LONGINT;
+	BEGIN
+		REPEAT
+			n := Unix.read( fd, buf, len )
+		UNTIL (n >= 0) OR (Unix.errno() # Unix.EINTR);
+		RETURN n
+	END UnixRead;	
+	
+	PROCEDURE UnixWrite( fd: LONGINT; buf: ADDRESS; len: SIZE ): LONGINT;
+	VAR n: LONGINT;
+	BEGIN
+		REPEAT
+			n := Unix.write( fd, buf, len )
+		UNTIL (n >= 0) OR (Unix.errno() # Unix.EINTR);
+		RETURN n
+	END UnixWrite;	
+	
+	
 	PROCEDURE AddSearchPath*(context: Commands.Context);
 	VAR name: Files.FileName; i,j: LONGINT; ch : CHAR;
 	BEGIN