feat: implement async file I/O with open/close/readBytes and scheduler error handling

Add foreign class File with static open, read, size methods and instance methods for close, size, readBytes. Implement async file operations using libuv callbacks that resume fibers on completion. Introduce schedulerResumeError and resumeError_ to propagate I/O errors as fiber errors. Add setExitCode to CLI vm for runtime error exit handling. Bump MAX_METHODS_PER_CLASS from 2 to 9 to accommodate new File methods.
This commit is contained in:
Bob Nystrom
2015-10-01 04:13:36 +00:00
parent af90291f6f
commit 8fd9449196
32 changed files with 475 additions and 44 deletions
+19
View File
@@ -0,0 +1,19 @@
import "io" for File
import "scheduler" for Scheduler
// See also: is_open.wren.
var file = File.open("test/io/file/close.wren")
System.print(file.close()) // expect: null
// Can call multiple times.
file.close()
// If already closed, returns synchronously.
Scheduler.add {
System.print("does not print")
}
file.close()
System.print("sync") // expect: sync
+11
View File
@@ -0,0 +1,11 @@
import "io" for File
var file = File.open("test/io/file/descriptor.wren")
// We can't test for a specific value since it's up to the OS, but it should be
// a positive number.
System.print(file.descriptor is Num) // expect: true
System.print(file.descriptor > 0) // expect: true
file.close()
System.print(file.descriptor) // expect: -1
+1
View File
@@ -0,0 +1 @@
this is a text file
+6
View File
@@ -0,0 +1,6 @@
import "io" for File
var file = File.open("test/io/file/is_open.wren")
System.print(file.isOpen) // expect: true
file.close()
System.print(file.isOpen) // expect: false
+5
View File
@@ -0,0 +1,5 @@
import "io" for File
var file = File.open("test/io/file/open.wren")
System.print(file is File) // expect: true
System.print(file.isOpen) // expect: true
+16
View File
@@ -0,0 +1,16 @@
import "io" for File
var stash
File.open("test/io/file/open_block.wren") {|file|
System.print(file is File) // expect: true
System.print(file.isOpen) // expect: true
stash = file
}
// Closes after block.
System.print(stash.isOpen) // expect: false
// Returns null.
System.print(File.open("test/io/file/open_block.wren") {|file|}) // expect: null
// TODO: Test a fiber aborting inside the block.
+3
View File
@@ -0,0 +1,3 @@
import "io" for File
File.open("nonexistent") {|file|} // expect runtime error: no such file or directory
@@ -0,0 +1,4 @@
import "io" for File
File.open("test/io/file/open_block_wrong_block_type.wren",
"no callable") // expect runtime error: String does not implement 'call(_)'.
@@ -0,0 +1,3 @@
import "io" for File
File.open(123) {|file|} // expect runtime error: Path must be a string.
+3
View File
@@ -0,0 +1,3 @@
import "io" for File
File.open("nonexistent") // expect runtime error: no such file or directory
+3
View File
@@ -0,0 +1,3 @@
import "io" for File
File.open(123) // expect runtime error: Path must be a string.
+8
View File
@@ -0,0 +1,8 @@
import "io" for File
var text = File.read("test/io/file/file.txt")
System.print(text) // expect: this is a text file
System.print(text.count) // expect: 19
// TODO: A file containing line endings.
// TODO: A file containing null bytes.
+13
View File
@@ -0,0 +1,13 @@
import "io" for File
var file = File.open("test/io/file/file.txt")
System.print(file.readBytes(3)) // expect: thi
// Always reads from the beginning.
System.print(file.readBytes(7)) // expect: this is
// Allows zero.
System.print(file.readBytes(0).bytes.count) // expect: 0
file.close()
+6
View File
@@ -0,0 +1,6 @@
import "io" for File
var file = File.open("test/io/file/file.txt")
file.close()
file.readBytes(3) // expect runtime error: File is not open.
@@ -0,0 +1,4 @@
import "io" for File
var file = File.open("test/io/file/file.txt")
file.readBytes("not num") // expect runtime error: Count must be an integer.
+4
View File
@@ -0,0 +1,4 @@
import "io" for File
var file = File.open("test/io/file/file.txt")
file.readBytes(-1) // expect runtime error: Count cannot be negative.
+4
View File
@@ -0,0 +1,4 @@
import "io" for File
var file = File.open("test/io/file/file.txt")
file.readBytes(1.2) // expect runtime error: Count must be an integer.
+3
View File
@@ -0,0 +1,3 @@
import "io" for File
File.read("nonexistent") // expect runtime error: no such file or directory
+3
View File
@@ -0,0 +1,3 @@
import "io" for File
File.read(123) // expect runtime error: Path must be a string.
+2 -7
View File
@@ -1,7 +1,7 @@
import "io" for File
import "scheduler" for Scheduler
System.print(File.size("test/io/file/size.wren")) // expect: 401
System.print(File.size("test/io/file/size.wren")) // expect: 270
// Runs asynchronously.
Scheduler.add {
@@ -10,9 +10,4 @@ Scheduler.add {
System.print(File.size("test/io/file/size.wren"))
// expect: async
// expect: 401
var error = Fiber.new {
System.print(File.size("nonexistent"))
}.try()
System.print(error) // expect: no such file or directory
// expect: 270
+6
View File
@@ -0,0 +1,6 @@
import "io" for File
var file = File.open("test/io/file/file.txt")
file.close()
file.size // expect runtime error: File is not open.
+3
View File
@@ -0,0 +1,3 @@
import "io" for File
File.size("nonexistent") // expect runtime error: no such file or directory