[squeak-dev] The Inbox: Files-monty.172.mcz

commits at source.squeak.org commits at source.squeak.org
Fri Jun 9 05:33:00 UTC 2017


A new version of Files was added to project The Inbox:
http://source.squeak.org/inbox/Files-monty.172.mcz

==================== Summary ====================

Name: Files-monty.172
Author: monty
Time: 9 June 2017, 1:32:52.379576 am
UUID: 13aeeea1-d30e-4948-b1fb-a2b126694e13
Ancestors: Files-eem.171

Requires a VM built with CogVM sources of at least per VMMaker.oscog-eem.2237:

Added #primOpenNew: and a corresponding #openNew: to StandardFileStream, and changed #newFileNamed: to use it.

Moved the post-open initialization code from #open:forWrite: to the new #opened:forWrite:, which is also used by #openNew: and is meant to be overridden instead of #open:forWrite: in subclasses like MultiByteFileStream and CrLfFileStream.

Fixed #primFlush: to not simlulate a flush after a failure, because fflush() can fail for the same reasons write() can, so its failure can't be ignored.

Made #sync just invoke its primitive instead of also sending #flush, to keep the two operations distinct like their primitives and to be consistent with Pharo.

Replaced #retryWithGC:until:forFileNamed: with a simpler two-argument version that retries when the first block evaluates to nil, since all senders of the three arugment version test for that condition anyway.

Changed FileDirectory #deleteFileNamed:ifAbsent: and #rename:toBe: to not use #retryWithGC:... because their primitives don't need new file descriptors that could be freed by a GC.

=============== Diff against Files-eem.171 ===============

Item was changed:
  ----- Method: FileDirectory>>deleteFileNamed:ifAbsent: (in category 'file operations') -----
  deleteFileNamed: localFileName ifAbsent: failBlock
+ 	"Delete the file of the given name if it exists, else evaluate failBlock."
- 	"Delete the file of the given name if it exists, else evaluate failBlock.
- 	If the first deletion attempt fails do a GC to force finalization of any lost references. ar 3/21/98 17:53"
  	| fullName |
  	fullName := self fullNameFor: localFileName.
+ 	"no #retryWithGC:... here because the delete primitive doesn't need
+ 	new file descriptors that could be freed by a GC"
+ 	(self primDeleteFileNamed: (self fullNameFor: localFileName) asVmPathName)
+ 		ifNil: [^ failBlock value].
- 	(StandardFileStream 
- 		retryWithGC:[self primDeleteFileNamed: (self fullNameFor: localFileName) asVmPathName]
- 		until:[:result| result notNil]
- 		forFileNamed: fullName) == nil
- 			ifTrue: [^failBlock value].
  !

Item was changed:
  ----- Method: FileDirectory>>rename:toBe: (in category 'file operations') -----
  rename: oldFileName toBe: newFileName 
  	"Rename the file of the given name to the new name. Fail if there is no file of the old name or if there is an existing file with the new name."
- 	"Modified for retry after GC ar 3/21/98 18:09"
  	| replaceIt oldName newName |
  	oldName := self fullNameFor: oldFileName.
  	newName := self fullNameFor: newFileName.
  	((self fileExists: oldFileName) or: [ (self directoryExists: oldFileName) ]) ifFalse: [ ^ self error: 'Attempt to rename a non-existent file or directory.' ].
  	(self fileExists: newFileName) ifTrue:
  		[replaceIt := (ReplaceExistingFileException fileName: newFileName) signal.
  		replaceIt ifTrue: [ self deleteFileNamed: newFileName ]	ifFalse: [ ^ self ]].
  	(self directoryExists: newFileName) ifTrue: [ FileExistsException signal: newFileName, ' already exists.' ].
+ 	"no #retryWithGC:... here because the rename primitive doesn't need
+ 	new file descriptors that could be freed by a GC"
+ 	(self
+ 		primRename: oldName asVmPathName
+ 		to: newName asVmPathName)
+ 		ifNil: [^ self error: 'Failed to rename file']!
- 	(StandardFileStream
- 		retryWithGC:
- 			[ self
- 				primRename: oldName asVmPathName
- 				to: newName asVmPathName ]
- 		until: [ : result | result notNil ]
- 		forFileNamed: oldName) ~~ nil ifTrue: [ ^ self ].
- 	^ self error: 'Failed to rename file'!

Item was changed:
  ----- Method: StandardFileStream class>>newFileNamed: (in category 'file creation') -----
+ newFileNamed: fileName 
+ 	"Create a new file with the given name, and answer a stream opened
+ 	for writing on that file. If the file already exists, ask the user what to
+ 	do. "
+ 	^ self new openNew: (self fullName: fileName)!
- newFileNamed: fileName
-  	"Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, ask the user what to do."
- 
- 	| fullName |
- 	fullName := self fullName: fileName.
- 
- 	^(self isAFileNamed: fullName)
- 		ifTrue: ["file already exists:"
- 			(FileExistsException fileName: fullName fileClass: self) signal]
- 		ifFalse: [self new open: fullName forWrite: true]
- 
- !

Item was removed:
- ----- Method: StandardFileStream class>>retryWithGC:until:forFileNamed: (in category 'registry') -----
- retryWithGC: execBlock until: testBlock forFileNamed: fullName
- 	"Re-implemented to only force GC if a file with the given name exists"
- 	| blockValue foundIt |
- 	blockValue := execBlock value.
- 	(testBlock value: blockValue) ifTrue:[^blockValue].
- 	"See if we have a file with the given name"
- 	foundIt := Registry keys "hold on strongly for now" 
- 		anySatisfy:[:file| file name sameAs: fullName].
- 	foundIt ifFalse:[^blockValue].
- 	Smalltalk garbageCollectMost.
- 	blockValue := execBlock value.
- 	(testBlock value: blockValue) ifTrue:[^blockValue].
- 	Smalltalk garbageCollect.
- 	^execBlock value.!

Item was added:
+ ----- Method: StandardFileStream class>>retryWithGC:whileNilForFileNamed: (in category 'registry') -----
+ retryWithGC: execBlock whileNilForFileNamed: fullName 
+ 	"Re-implemented to only force GC if a file with the given name exists"
+ 	| foundIt |
+ 	execBlock value
+ 		ifNotNil: [:result | ^ result].
+ 	"See if we have a file with the given name"
+ 	foundIt := Registry keys
+ 				anySatisfy: [:file | "hold on strongly for now"
+ 					file name sameAs: fullName].
+ 	foundIt
+ 		ifFalse: [^ nil].
+ 	Smalltalk garbageCollectMost.
+ 	execBlock value
+ 		ifNotNil: [:result | ^ result].
+ 	Smalltalk garbageCollect.
+ 	^ execBlock value!

Item was changed:
  ----- Method: StandardFileStream>>flush (in category 'read, write, position') -----
  flush
+ 	"When writing, this flushes the write buffer the stream uses to reduce
+ 	the number of write() system calls it makes. This should generally be
+ 	used before #sync, but on Windows they do the same thing."
+ 
+ 	^ self primFlush: fileID!
- 	"Flush pending changes"
- 	^self primFlush: fileID!

Item was changed:
  ----- Method: StandardFileStream>>open:forWrite: (in category 'open/close') -----
  open: fileName forWrite: writeMode 
+ 	"Open the file with the given name. If writeMode is true, allow writing,
+ 	otherwise open the file in read-only mode."
- 	"Open the file with the given name. If writeMode is true, allow writing, otherwise open the file in read-only mode."
  	"Changed to do a GC and retry before failing ar 3/21/98 17:25"
  	| f |
  	f := fileName asVmPathName.
+ 	fileID := self class
+ 				retryWithGC: [self primOpen: f writable: writeMode]
+ 				whileNilForFileNamed: fileName.
+ 	"nil return allows sender to detect failure"
+ 	^ fileID
+ 		ifNotNil: [self opened: fileName forWrite: writeMode]!
- 
- 	fileID := StandardFileStream retryWithGC:[self primOpen: f writable: writeMode] 
- 					until:[:id| id notNil] 
- 					forFileNamed: fileName.
- 	fileID ifNil: [^ nil].  "allows sender to detect failure"
- 	name := fileName.
- 	self register.
- 	rwmode := writeMode.
- 	buffer1 := String new: 1.
- 	self enableReadBuffering
- 	!

Item was added:
+ ----- Method: StandardFileStream>>openNew: (in category 'open/close') -----
+ openNew: fileName 
+ 	"Open the new file with the given name for writing.
+ 	Raises a FileAlreadyException if the file already exists, and returns nil
+ 	for all other errors."
+ 	| f |
+ 	f := fileName asVmPathName.
+ 	"the FileExistsException isn't caught here or in
+ 	#retryWithGC:whileNilForFileNamed:, because if the file exists, GC'ing
+ 	unreferenced file streams won't delete it"
+ 	fileID := self class
+ 				retryWithGC: [self primOpenNew: f]
+ 				whileNilForFileNamed: fileName.
+ 	"nil return allows sender to detect failure"
+ 	^ fileID
+ 		ifNotNil: [self opened: fileName forWrite: true]!

Item was added:
+ ----- Method: StandardFileStream>>opened:forWrite: (in category 'private') -----
+ opened: fileName forWrite: writeMode 
+ 	name := fileName.
+ 	self register.
+ 	rwmode := writeMode.
+ 	buffer1 := String new: 1.
+ 	self enableReadBuffering!

Item was changed:
  ----- Method: StandardFileStream>>primFlush: (in category 'primitives') -----
  primFlush: id
+ 	"On Unix, the FilePlugin uses stdio FILE* structs which maintain their
+ 	own internal buffer to minimize write() syscalls. This flushes that buffer.
+ 	On Windows this and primSync: do the same thing."
+ 
- 	"Flush pending changes to the disk"
- 	| p |
  	<primitive: 'primitiveFileFlush' module: 'FilePlugin'>
+ 	
+ 	"We can't ignore fflush() failing, because it can fail for any of the
+ 	reasons write() can."
+ 	self primitiveFailed!
- 	"In some OS's seeking to 0 and back will do a flush"
- 	p := self position.
- 	self position: 0; position: p!

Item was added:
+ ----- Method: StandardFileStream>>primOpenNew: (in category 'primitives') -----
+ primOpenNew: fileName 
+ 	"Open a new file of the given name for writing and return the file ID
+ 	obtained.
+ 	If the file already exists, raise a FileExistsException.
+ 	For other errors, return nil."
+ 	<primitive: 'primitiveFileOpenNew' module: 'FilePlugin' error: errorCode>
+ 	errorCode == #'inappropriate operation'
+ 		ifTrue: [(FileExistsException fileName: fileName fileClass: self class) signal].
+ 	^ nil!

Item was changed:
  ----- Method: StandardFileStream>>primSync: (in category 'primitives') -----
  primSync: id
+ 	"On Unix, this syncs any written or flushed data still in the kernel file
+ 	system buffers to disk. On Windows this and primFlush: do the same thing"
+ 
- 	"Call fsync to really, really, flush pending changes to the disk"
- 	| p |
  	<primitive: 'primitiveFileSync' module: 'FilePlugin'>
+ 
+ 	"fsync() failing cannot be ignored"
+ 	self primitiveFailed!
- 	"In some OS's seeking to 0 and back will do a flush. Maybe that will help if we dont have the primitives"
- 	p := self position.
- 	self position: 0; position: p!

Item was changed:
  ----- Method: StandardFileStream>>sync (in category 'read, write, position') -----
  sync
+ 	"When writing, this syncs any written/flushed data still in the kernel
+ 	file system buffers to disk. This should generally be used after #flush,
+ 	but on Windows they do the same thing."
+ 
+ 	^ self primSync: fileID!
- 	"Really, really, flush pending changes"
- 	^self flush; primSync: fileID!



More information about the Squeak-dev mailing list