Przeglądaj źródła

incorporated the enhancements of the generic version,
Generic.Unix.UnixFiles.Mod is no longer needed

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@6990 8c9fc860-2736-0410-a75d-ab315db34111

eth.guenter 8 lat temu
rodzic
commit
6cac49a084
1 zmienionych plików z 234 dodań i 137 usunięć
  1. 234 137
      source/Unix.UnixFiles.Mod

+ 234 - 137
source/Unix.UnixFiles.Mod

@@ -4,25 +4,28 @@ MODULE UnixFiles;   (** AUTHOR "gf"; PURPOSE "Unix file systems" *)
 
 (*  derived fron (SPARCOberon) Files.Mod by J. Templ 1.12. 89/14.05.93 *)
 
-IMPORT S := SYSTEM, Unix, Machine, Heaps, Objects, Kernel, Modules, Log := KernelLog, Files;
+IMPORT S := SYSTEM, Unix, Kernel, Modules, Log := KernelLog, Files;
 
 
 CONST
-	NBufs = 4;  Bufsize = 4096;  FileTabSize = 1024;  ResFiles = 128;  NoDesc = -1;
+	NBufs = 4;  Bufsize = 4096;  NoDesc = -1;
 
 	Open = 0;  Create = 1;  Closed = 2;	(* file states *)
 	
 	NoKey = -1;
+	CreateFlags = Unix.rdwr + Unix.creat + Unix.trunc;
 	
+	TraceCollection = 0;
+	Trace = {};
 VAR
-	fileTab: ARRAY FileTabSize OF RECORD  f {UNTRACED}: File  END;
 	tempno: INTEGER;
-	openfiles: INTEGER;
+	openfiles: LONGINT;
 	
 	searchPath: ARRAY 1024 OF CHAR;
 	cwd: ARRAY 256 OF CHAR;
 	
-	unixFS: UnixFileSystem;
+	unixFS: UnixFileSystem; (* must be unique *)
+	collection: Collection; (* must be unique *)
 
 TYPE	
 	Filename = ARRAY 256 OF CHAR;
@@ -112,7 +115,101 @@ TYPE
 			END RemoveDirectory0;
 
 	END AliasFileSystem;
+	
+	
+	FinalizeFiles = OBJECT
+
+		PROCEDURE EnumFile( f: ANY;  VAR cont: BOOLEAN );
+		VAR F: File;
+		BEGIN
+			F := f( File );  F.Finalize();  cont := TRUE
+		END EnumFile;
+
+	END FinalizeFiles;
+	
+
+	SearchByFstat = OBJECT
+	VAR 
+		found: File;
+		stat: Unix.Status;
 		
+		PROCEDURE Init( s: Unix.Status );
+		BEGIN
+			found := NIL;  
+			stat := s;
+		END Init;
+
+		PROCEDURE EnumFile( f: ANY;  VAR cont: BOOLEAN );
+		BEGIN
+			WITH f: File DO
+				IF (stat.ino = f.ino) & (stat.dev = f.dev) THEN
+					(* possible different name but same file! *)
+					ResetBuffers( f, stat );
+					found := f; cont := FALSE;
+				END;
+			END;
+		END EnumFile;
+
+	END SearchByFstat;
+
+
+	
+	
+	Collection = OBJECT  (* methods in Collection shared by objects Filesystem and File *)
+	VAR oldFiles, newFiles: Kernel.FinalizedCollection;
+		ssearch: SearchByFstat;
+		fileKey: LONGINT;
+
+		PROCEDURE & Init*;
+		BEGIN
+			NEW( oldFiles );  NEW( newFiles );   NEW(ssearch); fileKey := -1;
+		END Init;
+
+		PROCEDURE AddNew( F: File );
+		BEGIN {EXCLUSIVE}
+			IF TraceCollection IN Trace THEN Log.String( "Collections.AddNew: " );  Log.String( F.workName );  Log.Ln;  END;
+			newFiles.Add( F, FinalizeFile );
+		END AddNew;
+
+		PROCEDURE AddOld( F: File );
+		BEGIN {EXCLUSIVE}
+			IF TraceCollection IN Trace THEN Log.String( "Collections.AddOld: " );  Log.String( F.workName );  Log.Ln;  END;
+			oldFiles.Add( F, FinalizeFile );
+		END AddOld;
+
+		PROCEDURE ByStat(CONST stat: Unix.Status): File;
+		BEGIN{EXCLUSIVE}
+			ssearch.Init(stat); 
+			oldFiles.Enumerate(ssearch.EnumFile); 
+			IF ssearch.found = NIL THEN
+				newFiles.Enumerate(ssearch.EnumFile)
+			END;
+			
+			IF TraceCollection IN Trace THEN 
+				Log.String( "Collections.ByStatus: " );  Log.Ln;  
+				IF ssearch.found = NIL THEN Log.String("not found") ELSE Log.String("found") END;
+			END;
+			
+			RETURN ssearch.found;
+		END ByStat;
+		
+		PROCEDURE Finalize;
+		VAR fin: FinalizeFiles;
+		BEGIN {EXCLUSIVE}
+			IF TraceCollection IN Trace THEN Log.String( "Collections.Finalize " );  Log.Ln;  END;
+			NEW( fin );  newFiles.Enumerate( fin.EnumFile );  newFiles.Clear();  oldFiles.Enumerate( fin.EnumFile );  oldFiles.Clear();
+		END Finalize;
+
+		PROCEDURE FinalizeFile( obj: ANY );
+		VAR F: File;
+		BEGIN
+			F := obj( File );
+			IF TraceCollection IN Trace THEN Log.String( "Collections.FinalizeFile " );  Log.String( F.workName );  Log.Ln;  END;
+			F.Finalize()
+		END FinalizeFile;
+
+	END Collection;
+
 
 	UnixFileSystem* = OBJECT (Files.FileSystem)
 
@@ -125,7 +222,7 @@ TYPE
 				PROCEDURE New0*( name: ARRAY OF CHAR ): Files.File;
 				VAR f: File;
 				BEGIN {EXCLUSIVE}
-					AwaitFinalizingDone;
+					(*AwaitFinalizingDone;*)
 					NEW( f, SELF );
 					f.workName := "";  COPY( name, f.registerName );
 					f.fd := NoDesc;  f.state := Create;  f.fsize := 0;  f.fpos := 0;
@@ -145,7 +242,7 @@ 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; 
+					oflags: SET;  nextdir, path: Filename;  fo: LONGINT;
 				BEGIN  {EXCLUSIVE}
 					IF name = "" THEN  RETURN NIL  END;
 					
@@ -155,29 +252,27 @@ TYPE
 						pos := 0;  ScanPath( pos, nextdir );  MakePath( nextdir, name, path );
 						ScanPath( pos, nextdir )
 					END;
-					
-					IF (FileTabSize - openfiles) < ResFiles THEN  GC   END;
-						
+											
 					LOOP
 						r := Unix.access( ADDRESSOF( path ), Unix.R_OK );
 						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})) OR (fd >= FileTabSize) THEN
-								IF fd > 0 THEN  r := Unix.close( fd )  END;
-								GC ;
-								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;
-
+							
 							IF fd >= 0 THEN
 								r := Unix.fstat( fd, stat );
-								f := FindCachedEntry( stat );
+								f := collection.ByStat(stat);
 								IF f # NIL THEN
 									(* use the file already cached *)  r := Unix.close( fd );  EXIT
-								ELSIF fd < FileTabSize THEN
-									AwaitFinalizingDone;
+								ELSE
 									NEW( f, SELF );
 									f.fd := fd;  f.dev := stat.dev;  f.ino := stat.ino;  
 									f.mtime := stat.mtime.sec;  f.fsize := stat.size;  f.fpos := 0;  
@@ -190,12 +285,9 @@ TYPE
 										f.flags := {Files.ReadOnly}
 									END;
 									f.key := NoKey;  f.fs := SELF;
-									fileTab[fd].f := f;  (* cache file *)
-									INC( openfiles );  RegisterFinalizer( f, Cleanup );
+									IncOpenFiles();
+									collection.AddOld(f);
 									EXIT
-								ELSE  
-									r := Unix.close( fd );  
-									Halt( f, FALSE, "UnixFiles.File.Old0: too many files open" );
 								END
 							END
 						ELSIF nextdir # "" THEN
@@ -227,52 +319,66 @@ TYPE
 					END;
 					key := 0;
 				END Delete0;
-
-
-				PROCEDURE Rename0*( old, new: ARRAY OF CHAR;  f: Files.File;  VAR res: LONGINT );
+				
+				(* return remaining old file, if any *)
+				PROCEDURE TryRename*( old, new: ARRAY OF CHAR;  f: Files.File;  VAR res: LONGINT ): LONGINT;
 				CONST Bufsize = 4096;
-				VAR fdold, fdnew, n, r: LONGINT;  ostat, nstat: Unix.Status;
+				VAR fdold, fdnew, fo, n, r: LONGINT;  ostat, nstat: Unix.Status;
 					buf: ARRAY Bufsize OF CHAR;
 				BEGIN {EXCLUSIVE}
+					fo := NoDesc;
 					r:= Unix.stat( ADDRESSOF( old ), ostat );
+
 					IF r >= 0 THEN
 						r := Unix.stat( ADDRESSOF( new ), nstat );
-						IF (r >= 0) & (ostat.dev # nstat.dev) OR (ostat.ino # nstat.ino) THEN
-							 r := Unix.unlink( ADDRESSOF( new ) )  (* work around stale nfs handles *)
+						IF (r >= 0) & ((ostat.dev # nstat.dev) OR (ostat.ino # nstat.ino)) THEN
+							 r := Unix.unlink( ADDRESSOF( new ) )  (* work around stale nfs handles *);
 						END;
 						r := Unix.rename( ADDRESSOF( old ), ADDRESSOF( new ) );
-						IF r < 0 THEN
+						IF r < 0 THEN (* could not rename, try copy *)
 							res := Unix.errno( );
-							IF res = Unix.EXDEV THEN  (* cross device link, move the file *)
+							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, {} );
+								fo := fdold;
 								IF fdold < 0 THEN    
-									res := Unix.errno( );  RETURN
+									res := Unix.errno( );  
+									RETURN NoDesc;
 								END;
 								fdnew := Unix.open( ADDRESSOF( new ), Unix.rdwr + Unix.creat + Unix.trunc, Unix.rwrwr );
 								IF fdnew < 0 THEN    
-									res := Unix.errno( );  RETURN
+									res := Unix.errno( );  
+									RETURN NoDesc;
 								END;
 								n := Unix.read( fdold, ADDRESSOF( buf ), Bufsize );
 								WHILE n > 0 DO
 									r := Unix.write( fdnew, ADDRESSOF( buf ), n );
 									IF r < 0 THEN
 										res := Unix.errno();  
-										r := Unix.close( fdold );  r := Unix.close( fdnew );
-										RETURN
+										r := Unix.close( fdold );  
+										r := Unix.close( fdnew );
+										RETURN NoDesc;
 									END;
 									n := Unix.read( fdold, ADDRESSOF( buf ), Bufsize )
 								END;
-								r := Unix.unlink( ADDRESSOF( old ) );  
-								r := Unix.close( fdold );  r := Unix.close( fdnew );  
+								r := Unix.unlink( ADDRESSOF( old ) );
+								r := Unix.close( fdold );
+								r := Unix.close( fdnew );  
 								res := Files.Ok
-							ELSE  
-								RETURN  (* res is Unix.rename return code *)
+							ELSE
+								RETURN NoDesc (* res is Unix.rename return code *)
 							END
 						END;
 						res := Files.Ok
 					ELSE  
-						res := Unix.errno()
-					END
+						res := Unix.errno();
+					END;
+					RETURN fo;
+				END TryRename;
+
+				PROCEDURE Rename0*( old, new: ARRAY OF CHAR;  f: Files.File;  VAR res: LONGINT );
+				VAR of: LONGINT;
+				BEGIN
+					of := TryRename(old, new, f, res);
 				END Rename0;
 				
 
@@ -368,7 +474,7 @@ TYPE
 					checkSet := NIL;
 				END Enumerate0;
 
-			END UnixFileSystem;
+	END UnixFileSystem;
 	
 	
 	Buffer =	POINTER TO RECORD (Files.Hint)
@@ -392,45 +498,40 @@ TYPE
 
 				PROCEDURE & Init( fs: Files.FileSystem );
 				BEGIN
-					SELF.fs := fs;  flags := {};  
+					SELF.fs := fs;  flags := {};
 				END Init;
 
-				
 				PROCEDURE CreateUnixFile;
-				CONST 
-					CreateFlags = Unix.rdwr + Unix.creat + Unix.trunc;
 				VAR 
-					stat: Unix.Status;  done: BOOLEAN;  r: LONGINT;
+					stat: Unix.Status;  done: BOOLEAN;  errno,fo: LONGINT;
 				BEGIN
 					IF state = Create THEN  
 						GetTempName( registerName, workName );  tempFile := TRUE
-					ELSIF state = Closed THEN  
-						workName := registerName;  registerName := "";  tempFile := FALSE
+					ELSIF state = Closed THEN
+						IF registerName # "" THEN
+							workName := registerName;  registerName := "";  tempFile := FALSE;
+						ELSE
+							RETURN;
+						END;
 					END;
-					r := Unix.unlink( ADDRESSOF( workName ) );
+					errno := Unix.unlink( ADDRESSOF( workName ) );
 					(*unlink first to avoid stale NFS handles and to avoid reuse of inodes*)
-					
-					IF (FileTabSize - openfiles) < ResFiles THEN  GC  END;
-						
+											
 					fd := Unix.open( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr );
-					done := fd >= 0;  r := Unix.errno();
-					IF (~done & (r IN {Unix.ENFILE, Unix.EMFILE})) OR (done & (fd >= FileTabSize)) THEN
-						IF done THEN  r := Unix.close( fd )  END;
-						GC ;
-						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
-						IF fd >= FileTabSize THEN  
-							r := Unix.close( fd );  
-							Halt( SELF, FALSE, "UnixFiles.File.Create: too many files open" )
-						ELSE
-							r := Unix.fstat( fd, stat );  
-							dev := stat.dev;  ino := stat.ino;  mtime := stat.mtime.sec;
-							state := Open;  fpos := 0;
-							fileTab[fd].f := SELF;
-							INC( openfiles );  RegisterFinalizer( SELF, Cleanup );
-						END
+						errno := Unix.fstat( fd, stat );  
+						dev := stat.dev;  ino := stat.ino;  mtime := stat.mtime.sec;
+						state := Open;  fpos := 0;
+						IncOpenFiles();
+						collection.AddNew(SELF);
 					ELSE  
 						Halt( SELF, TRUE, "UnixFiles.File.Create: open failed" );
 					END
@@ -634,18 +735,29 @@ TYPE
 
 
 				PROCEDURE Register0*( VAR res: LONGINT );
+				VAR fo: LONGINT;
 				BEGIN {EXCLUSIVE}
 					IF (state = Create) & (registerName # "") THEN  
-						state := Closed (* shortcut renaming *)   
+						state := Closed (* shortcut renaming *)   ;
 					END;
+					
 					FlushBuffers;
+					
 					IF registerName # "" THEN
-						fs.Rename0( workName, registerName, SELF, res );
+						fo := unixFS.TryRename( workName, registerName, SELF, res );
 						IF res # Files.Ok THEN  
 							Halt( SELF, FALSE, "UnixFiles.File.Register: rename failed" )  
 						END;
-						workName := registerName;  registerName := "";  tempFile := FALSE
+
+						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 );
+						END;
+
+						workName := registerName;  registerName := "";  tempFile := FALSE;
 					END;
+					(*collection.Register(SELF);*)
 				END Register0;
 				
 
@@ -665,19 +777,29 @@ TYPE
 				END FlushBuffers;
 				
 				
-				PROCEDURE Close*;
+				PROCEDURE Finalize*;
 				VAR r: LONGINT;
 				BEGIN {EXCLUSIVE}
-					IF fileTab[fd].f # NIL THEN
-						IF tempFile THEN  r := Unix.unlink( ADDRESSOF( workName ) )  
-						ELSE  FlushBuffers;
+					IF tempFile THEN  
+						IF fd # NoDesc THEN r := Unix.close(fd) END;
+						r := Unix.unlink( ADDRESSOF( registerName ) );
+						fd := NoDesc;
+					ELSE  
+						FlushBuffers;
+						IF fd # NoDesc THEN
+							r := Unix.close( fd );
+							fd := NoDesc;
 						END;
-						fileTab[fd].f := NIL;
-						r := Unix.close( fd );
-						DEC( openfiles );  state := Closed
 					END;
-				END Close;
+					DecOpenFiles();
+					state := Closed;
+				END Finalize;
 				
+				PROCEDURE Close;
+				BEGIN
+					Finalize;
+					collection.oldFiles.Remove(SELF);
+				END Close;
 				
 				PROCEDURE GetName*( VAR name: ARRAY OF CHAR );
 				BEGIN {EXCLUSIVE}
@@ -844,23 +966,6 @@ TYPE
 		HALT( 99 )
 	END Halt;
 
-	PROCEDURE RegisterFinalizer( obj: ANY;  fin: Heaps.Finalizer );
-	VAR n: Heaps.FinalizerNode;
-	BEGIN
-		NEW( n ); n.finalizer := fin;  Heaps.AddFinalizer( obj, n );
-	END RegisterFinalizer;
-	
-	PROCEDURE GC;
-	BEGIN
-		Kernel.GC;
-		AwaitFinalizingDone
-	END GC;
-	
-	PROCEDURE AwaitFinalizingDone;
-	BEGIN
-		(* wait until finalizers have finished! (Cleanup)*)
-		WHILE Machine.GCacquired DO  Objects.Sleep( 10 )  END
-	END AwaitFinalizingDone;
 
 	PROCEDURE ResetBuffers( f: File;  VAR stat: Unix.Status );
 	VAR i: INTEGER;
@@ -874,20 +979,6 @@ TYPE
 		END
 	END ResetBuffers;
 
-	PROCEDURE FindCachedEntry( VAR stat: Unix.Status ): File;
-	VAR f: File;  i: INTEGER;
-	BEGIN
-		FOR i := 0 TO FileTabSize - 1 DO
-			f := fileTab[i].f;
-			IF (f # NIL ) & (stat.ino = f.ino) & (stat.dev = f.dev) THEN
-				(* possible different name but same file! *)
-				ResetBuffers( f, stat );
-				RETURN f
-			END;
-		END;
-		RETURN NIL
-	END FindCachedEntry;
-	
 
 	PROCEDURE MakePath( CONST dir, name: ARRAY OF CHAR;  VAR dest: ARRAY OF CHAR );
 	VAR i, j: INTEGER;
@@ -929,32 +1020,11 @@ TYPE
 		AppendInt( tempName, pid )
 	END GetTempName;
 
-	
-
-	
-	PROCEDURE Cleanup( obj: ANY );
-	VAR f: File;
-	BEGIN
-		f := S.VAL( File, obj );  f.Close
-	END Cleanup;
-
-
-	PROCEDURE CloseFiles;
-	VAR i: LONGINT;  f: File;
-	BEGIN
-		i := 0;
-		WHILE i < FileTabSize DO
-			f := fileTab[i].f;
-			IF f # NIL THEN  f.Close  END;
-			INC( i )
-		END;
-	END CloseFiles;
-
-
 
 	PROCEDURE Install;
 	VAR aliasFS: AliasFileSystem;
 	BEGIN
+		NEW(collection);
 		NEW( unixFS );  (*  Files.Add( unixFS, "" );	*)
 		NEW( aliasFS, unixFS );  Files.Add( aliasFS, "searcher" )
 	END Install;
@@ -982,7 +1052,6 @@ TYPE
 		a := Unix.getenv( ADDRESSOF( "AOSPATH" ) );  i := 0;
 		IF a = 0 THEN
 			Log.String( "UnixFiles.Initialize: environment variable AOSPATH not defined" );  Log.Ln;
-			(* Unix.exit( 1 ) *)
 		ELSE
 			REPEAT
 				S.GET( a, ch );  INC( a );
@@ -990,12 +1059,40 @@ TYPE
 				searchPath[i] := ch;  INC( i )
 			UNTIL ch = 0X;
 		END;
-		i := 0;
-		WHILE i < FileTabSize DO  fileTab[i].f := NIL;  INC( i )  END;
 		tempno := 1;  openfiles := 0;  
-		Modules.InstallTermHandler( CloseFiles )	
+		Modules.InstallTermHandler( Finalization )	
 	END Initialize;
 
+	PROCEDURE Finalization;
+	VAR ft: Files.FileSystemTable;  i: LONGINT;
+	BEGIN
+		Files.GetList( ft );
+		IF ft # NIL THEN
+			FOR i := 0 TO LEN( ft^ ) - 1 DO
+				IF ft[i] IS AliasFileSystem THEN Files.Remove( ft[i] ) END
+			END
+		END;
+		collection.Finalize;
+		unixFS.Finalize;
+	END Finalization;
+	
+	PROCEDURE DecOpenFiles;
+	BEGIN{EXCLUSIVE}
+		DEC(openfiles);
+	END DecOpenFiles;
+
+	PROCEDURE IncOpenFiles;
+	BEGIN{EXCLUSIVE}
+		INC(openfiles);
+	END IncOpenFiles;
+	
+	
+	PROCEDURE WaitClose(no: LONGINT);
+	BEGIN{EXCLUSIVE}
+		AWAIT(openfiles < no);
+	END WaitClose;
+	
+
 BEGIN
 	Initialize;
 	Install