synchronizing filesystem component and making open/read/seek/close limited direct calls, reduced default max read buffer in turn. this should give better performance when reading many small files; made fs.close direct without limit to get rid of the ugly file handle gc workaround involving a timer

This commit is contained in:
Florian Nücke 2014-02-20 01:29:42 +01:00
parent 02827f69b7
commit 83860523a7
4 changed files with 29 additions and 37 deletions

View File

@ -20,13 +20,15 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
// ----------------------------------------------------------------------- //
@Callback(direct = true)
def getLabel(context: Context, args: Arguments): Array[AnyRef] = label match {
def getLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
label match {
case value: Label => result(label.getLabel)
case _ => null
}
}
@Callback
def setLabel(context: Context, args: Arguments): Array[AnyRef] = {
def setLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
if (label == null) throw new Exception("filesystem does not support labeling")
if (args.checkAny(0) == null) label.setLabel(null)
else label.setLabel(args.checkString(0))
@ -34,12 +36,12 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback(direct = true)
def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = {
def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.isReadOnly)
}
@Callback(direct = true)
def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = {
def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val space = fileSystem.spaceTotal
if (space < 0)
Array("unlimited")
@ -48,32 +50,32 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback(direct = true)
def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = {
def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.spaceUsed)
}
@Callback(direct = true)
def exists(context: Context, args: Arguments): Array[AnyRef] = {
def exists(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.exists(clean(args.checkString(0))))
}
@Callback(direct = true)
def size(context: Context, args: Arguments): Array[AnyRef] = {
def size(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.size(clean(args.checkString(0))))
}
@Callback(direct = true)
def isDirectory(context: Context, args: Arguments): Array[AnyRef] = {
def isDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.isDirectory(clean(args.checkString(0))))
}
@Callback(direct = true)
def lastModified(context: Context, args: Arguments): Array[AnyRef] = {
def lastModified(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.lastModified(clean(args.checkString(0))))
}
@Callback
def list(context: Context, args: Arguments): Array[AnyRef] = {
def list(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
Option(fileSystem.list(clean(args.checkString(0)))) match {
case Some(list) => Array(list)
case _ => null
@ -81,26 +83,26 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback
def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = {
def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
def recurse(path: String): Boolean = !fileSystem.exists(path) && (fileSystem.makeDirectory(path) ||
(recurse(path.split("/").dropRight(1).mkString("/")) && fileSystem.makeDirectory(path)))
result(recurse(clean(args.checkString(0))))
}
@Callback
def remove(context: Context, args: Arguments): Array[AnyRef] = {
def remove(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
def recurse(parent: String): Boolean = (!fileSystem.isDirectory(parent) ||
fileSystem.list(parent).forall(child => recurse(parent + "/" + child))) && fileSystem.delete(parent)
result(recurse(clean(args.checkString(0))))
}
@Callback
def rename(context: Context, args: Arguments): Array[AnyRef] = {
def rename(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.rename(clean(args.checkString(0)), clean(args.checkString(1))))
}
@Callback
def close(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true)
def close(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
Option(fileSystem.getHandle(handle)) match {
case Some(file) =>
@ -113,8 +115,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
null
}
@Callback
def open(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true, limit = 4)
def open(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
if (owners.get(context.node.address).fold(false)(_.size >= Settings.get.maxHandles)) {
throw new IOException("too many open handles")
}
@ -127,8 +129,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
result(handle)
}
@Callback
def read(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true, limit = 4)
def read(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1)))
checkOwner(context.node.address, handle)
@ -158,8 +160,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
}
@Callback
def seek(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true, limit = 4)
def seek(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
val whence = args.checkString(1)
val offset = args.checkInteger(2)
@ -178,7 +180,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback
def write(context: Context, args: Arguments): Array[AnyRef] = {
def write(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
val value = args.checkByteArray(1)
if (!node.tryChangeBuffer(-Settings.get.hddWriteCost * value.length)) {

View File

@ -3,7 +3,7 @@ local unicode = require("unicode")
local filesystem, fileStream = {}, {}
local isAutorunEnabled = true
local mtab = {children={}, links={}}
local mtab = {name="", children={}, links={}}
local function segments(path)
path = path:gsub("\\", "/")
@ -472,18 +472,9 @@ function filesystem.open(path, mode)
local stream = {fs = node.fs, handle = handle}
-- stream:close does a syscall, which yields, and that's not possible in
-- the __gc metamethod. So we start a timer to do the yield/cleanup.
local function cleanup(self)
if not self.handle then return end
-- save non-gc'ed values as upvalues
local fs = self.fs
local handle = self.handle
local function close()
fs.close(handle)
end
-- Required locally because this is a bootstrapped file.
require("event").timer(0, close)
self.fs.close(self.handle)
end
local metatable = {__index = fileStream,
__gc = cleanup,

View File

@ -8,7 +8,6 @@ local loaded = {
["_G"] = _G,
["bit32"] = bit32,
["coroutine"] = coroutine,
["io"] = io,
["math"] = math,
["os"] = os,
["package"] = package,

View File

@ -588,7 +588,7 @@ opencomputers {
# allocations regardless of the amount of RAM installed in the computer it
# runs on. As a side effect this pretty much determines the read
# performance of file systems.
maxReadBuffer: 8192
maxReadBuffer: 2048
}
# Internet settings, security related.