Skip to content

Commit 82cc648

Browse files
committed
Get rid of usages of __gc. Closes #1606.
Wrapping file handles with userdata that has a `dispose` closing the handle. Internet card already closed via userdata, so nothing to do there. Removed cleanup logic from term, deal with it. Added setting to re-enable it, if you really really need it. # Conflicts: # src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua # src/main/scala/li/cil/oc/server/component/FileSystem.scala
1 parent 851d869 commit 82cc648

File tree

11 files changed

+112
-63
lines changed

11 files changed

+112
-63
lines changed

src/main/resources/application.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ opencomputers {
210210
# know what you're doing.
211211
allowBytecode: false
212212

213+
# Whether to allow user defined __gc callbacks, i.e. __gc callbacks
214+
# defined *inside* the sandbox. Since garbage collection callbacks
215+
# are not sandboxed (hooks are disabled while they run), this is not
216+
# recommended.
217+
allowGC: false
218+
213219
# Whether to make the Lua 5.3 architecture available. If enabled, you
214220
# can reconfigure any CPU to use the Lua 5.3 architecture.
215221
enableLua53: true

src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ function memoryStream.new()
6565
local stream = {closed = false, buffer = "",
6666
redirect = {}, result = {}, args = {}}
6767
local metatable = {__index = memoryStream,
68-
__gc = memoryStream.close,
6968
__metatable = "memorystream"}
7069
return setmetatable(stream, metatable)
7170
end

src/main/resources/assets/opencomputers/loot/OpenOS/lib/filesystem.lua

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -509,13 +509,7 @@ function filesystem.open(path, mode)
509509
end
510510

511511
local stream = {fs = node.fs, handle = handle}
512-
513-
local function cleanup(self)
514-
if not self.handle then return end
515-
pcall(self.fs.close, self.handle)
516-
end
517512
local metatable = {__index = fileStream,
518-
__gc = cleanup,
519513
__metatable = "filestream"}
520514
return setmetatable(stream, metatable)
521515
end

src/main/resources/assets/opencomputers/loot/Plan9k/lib/internet.lua

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,7 @@ function internet.socket(address, port)
9898
end
9999

100100
local stream = {inet = inet, socket = socket}
101-
102-
-- stream:close does a syscall, which yields, and that's not possible in
103-
-- the __gc metamethod. So we start a timer to do the yield/cleanup.
104-
local function cleanup(self)
105-
if not self.socket then return end
106-
pcall(self.socket.close)
107-
end
108101
local metatable = {__index = socketStream,
109-
__gc = cleanup,
110102
__metatable = "socketstream"}
111103
return setmetatable(stream, metatable)
112104
end

src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,13 +528,7 @@ function filesystem.open(path, mode)
528528
end
529529

530530
local stream = {fs = node.fs, handle = handle}
531-
532-
local function cleanup(self)
533-
if not self.handle then return end
534-
pcall(self.fs.close, self.handle)
535-
end
536531
local metatable = {__index = fileStream,
537-
__gc = cleanup,
538532
__metatable = "filestream"}
539533
return setmetatable(stream, metatable)
540534
end

src/main/resources/assets/opencomputers/lua/component/internet/lib/internet.lua

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,7 @@ function internet.socket(address, port)
9999
end
100100

101101
local stream = {inet = inet, socket = socket}
102-
103-
-- stream:close does a syscall, which yields, and that's not possible in
104-
-- the __gc metamethod. So we start a timer to do the yield/cleanup.
105-
local function cleanup(self)
106-
if not self.socket then return end
107-
pcall(self.socket.close)
108-
end
109102
local metatable = {__index = socketStream,
110-
__gc = cleanup,
111103
__metatable = "socketstream"}
112104
return setmetatable(stream, metatable)
113105
end

src/main/resources/assets/opencomputers/lua/machine.lua

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,14 @@ sandbox = {
724724
sbmt[k] = v
725725
end
726726
sbmt.mt = mt
727-
sbmt.__gc = sgc
727+
-- Garbage collector callbacks apparently can't be sandboxed after
728+
-- all, because hooks are disabled while they're running. So we just
729+
-- disable them altogether by default.
730+
if not system.allowGC() then
731+
sbmt.__gc = nil -- Silent fail for backwards compat. TODO error in OC 1.7
732+
else
733+
sbmt.__gc = sgc
734+
end
728735
mt = sbmt
729736
end
730737
return setmetatable(t, mt)

src/main/scala/li/cil/oc/Settings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class Settings(val config: Config) {
7676

7777
// computer.lua
7878
val allowBytecode = config.getBoolean("computer.lua.allowBytecode")
79+
val allowGC = config.getBoolean("computer.lua.allowGC")
7980
val enableLua53 = config.getBoolean("computer.lua.enableLua53")
8081
val ramSizes = Array(config.getIntList("computer.lua.ramSizes"): _*) match {
8182
case Array(tier1, tier2, tier3, tier4, tier5, tier6) =>

src/main/scala/li/cil/oc/server/component/FileSystem.scala

Lines changed: 87 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import li.cil.oc.api.machine.Callback
1414
import li.cil.oc.api.machine.Context
1515
import li.cil.oc.api.network._
1616
import li.cil.oc.api.prefab
17+
import li.cil.oc.api.prefab.AbstractValue
1718
import li.cil.oc.server.{PacketSender => ServerPacketSender}
1819
import li.cil.oc.util.ExtendedNBT._
1920
import net.minecraft.nbt.NBTTagCompound
@@ -122,21 +123,13 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option
122123
result(success)
123124
}
124125

125-
@Callback(direct = true, doc = """function(handle:number) -- Closes an open file descriptor with the specified handle.""")
126+
@Callback(direct = true, doc = """function(handle:userdata) -- Closes an open file descriptor with the specified handle.""")
126127
def close(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
127-
val handle = args.checkInteger(0)
128-
Option(fileSystem.getHandle(handle)) match {
129-
case Some(file) =>
130-
owners.get(context.node.address) match {
131-
case Some(set) if set.remove(handle) => file.close()
132-
case _ => throw new IOException("bad file descriptor")
133-
}
134-
case _ => throw new IOException("bad file descriptor")
135-
}
128+
close(context, checkHandle(args, 0))
136129
null
137130
}
138131

139-
@Callback(direct = true, limit = 4, doc = """function(path:string[, mode:string='r']):number -- Opens a new file descriptor and returns its handle.""")
132+
@Callback(direct = true, limit = 4, doc = """function(path:string[, mode:string='r']):userdata -- Opens a new file descriptor and returns its handle.""")
140133
def open(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
141134
if (owners.get(context.node.address).fold(false)(_.size >= Settings.get.maxHandles)) {
142135
throw new IOException("too many open handles")
@@ -148,11 +141,11 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option
148141
owners.getOrElseUpdate(context.node.address, mutable.Set.empty[Int]) += handle
149142
}
150143
diskActivity()
151-
result(handle)
144+
result(new HandleValue(node.address, handle))
152145
}
153146

154147
def read(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
155-
val handle = args.checkInteger(0)
148+
val handle = checkHandle(args, 0)
156149
val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1)))
157150
checkOwner(context.node.address, handle)
158151
Option(fileSystem.getHandle(handle)) match {
@@ -183,7 +176,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option
183176
}
184177

185178
def seek(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
186-
val handle = args.checkInteger(0)
179+
val handle = checkHandle(args, 0)
187180
val whence = args.checkString(1)
188181
val offset = args.checkInteger(2)
189182
checkOwner(context.node.address, handle)
@@ -201,7 +194,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option
201194
}
202195

203196
def write(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
204-
val handle = args.checkInteger(0)
197+
val handle = checkHandle(args, 0)
205198
val value = args.checkByteArray(1)
206199
if (!node.tryChangeBuffer(-Settings.get.hddWriteCost * value.length)) {
207200
throw new IOException("not enough energy")
@@ -218,6 +211,28 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option
218211

219212
// ----------------------------------------------------------------------- //
220213

214+
def checkHandle(args: Arguments, index: Int) = {
215+
if (args.isInteger(index)) {
216+
args.checkInteger(index)
217+
} else args.checkAny(0) match {
218+
case handle: HandleValue => handle.handle
219+
case _ => throw new IOException("bad file descriptor")
220+
}
221+
}
222+
223+
def close(context: Context, handle: Int): Unit = {
224+
Option(fileSystem.getHandle(handle)) match {
225+
case Some(file) =>
226+
owners.get(context.node.address) match {
227+
case Some(set) if set.remove(handle) => file.close()
228+
case _ => throw new IOException("bad file descriptor")
229+
}
230+
case _ => throw new IOException("bad file descriptor")
231+
}
232+
}
233+
234+
// ----------------------------------------------------------------------- //
235+
221236
override def onMessage(message: Message) = fileSystem.synchronized {
222237
super.onMessage(message)
223238
if (message.name == "computer.stopped" || message.name == "computer.started") {
@@ -317,64 +332,103 @@ object FileSystem {
317332
// I really need to come up with a way to make the call limit dynamic...
318333
def apply(fileSystem: IFileSystem, label: Label, host: Option[EnvironmentHost], sound: Option[String], speed: Int = 1): FileSystem = speed match {
319334
case 6 => new FileSystem(fileSystem, label, host, sound) {
320-
@Callback(direct = true, limit = 15, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
335+
@Callback(direct = true, limit = 15, doc = """function(handle:userdata, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
321336
override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args)
322337

323-
@Callback(direct = true, limit = 15, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
338+
@Callback(direct = true, limit = 15, doc = """function(handle:userdata, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
324339
override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args)
325340

326-
@Callback(direct = true, limit = 6, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
341+
@Callback(direct = true, limit = 6, doc = """function(handle:userdata, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
327342
override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args)
328343
}
329344
case 5 => new FileSystem(fileSystem, label, host, sound) {
330-
@Callback(direct = true, limit = 13, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
345+
@Callback(direct = true, limit = 13, doc = """function(handle:userdata, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
331346
override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args)
332347

333-
@Callback(direct = true, limit = 13, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
348+
@Callback(direct = true, limit = 13, doc = """function(handle:userdata, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
334349
override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args)
335350

336-
@Callback(direct = true, limit = 5, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
351+
@Callback(direct = true, limit = 5, doc = """function(handle:userdata, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
337352
override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args)
338353
}
339354
case 4 => new FileSystem(fileSystem, label, host, sound) {
340-
@Callback(direct = true, limit = 10, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
355+
@Callback(direct = true, limit = 10, doc = """function(handle:userdata, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
341356
override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args)
342357

343-
@Callback(direct = true, limit = 10, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
358+
@Callback(direct = true, limit = 10, doc = """function(handle:userdata, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
344359
override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args)
345360

346-
@Callback(direct = true, limit = 4, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
361+
@Callback(direct = true, limit = 4, doc = """function(handle:userdata, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
347362
override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args)
348363
}
349364
case 3 => new FileSystem(fileSystem, label, host, sound) {
350-
@Callback(direct = true, limit = 7, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
365+
@Callback(direct = true, limit = 7, doc = """function(handle:userdata, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
351366
override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args)
352367

353-
@Callback(direct = true, limit = 7, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
368+
@Callback(direct = true, limit = 7, doc = """function(handle:userdata, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
354369
override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args)
355370

356-
@Callback(direct = true, limit = 3, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
371+
@Callback(direct = true, limit = 3, doc = """function(handle:userdata, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
357372
override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args)
358373
}
359374
case 2 => new FileSystem(fileSystem, label, host, sound) {
360-
@Callback(direct = true, limit = 4, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
375+
@Callback(direct = true, limit = 4, doc = """function(handle:userdata, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
361376
override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args)
362377

363-
@Callback(direct = true, limit = 4, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
378+
@Callback(direct = true, limit = 4, doc = """function(handle:userdata, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
364379
override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args)
365380

366-
@Callback(direct = true, limit = 2, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
381+
@Callback(direct = true, limit = 2, doc = """function(handle:userdata, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
367382
override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args)
368383
}
369384
case _ => new FileSystem(fileSystem, label, host, sound) {
370-
@Callback(direct = true, limit = 1, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
385+
@Callback(direct = true, limit = 1, doc = """function(handle:userdata, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""")
371386
override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args)
372387

373-
@Callback(direct = true, limit = 1, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
388+
@Callback(direct = true, limit = 1, doc = """function(handle:userdata, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""")
374389
override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args)
375390

376-
@Callback(direct = true, limit = 1, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
391+
@Callback(direct = true, limit = 1, doc = """function(handle:userdata, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""")
377392
override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args)
378393
}
379394
}
380395
}
396+
397+
final class HandleValue extends AbstractValue {
398+
def this(owner: String, handle: Int) = {
399+
this()
400+
this.owner = owner
401+
this.handle = handle
402+
}
403+
404+
var owner = ""
405+
var handle = 0
406+
407+
override def dispose(context: Context): Unit = {
408+
super.dispose(context)
409+
if (context.node() != null && context.node().network() != null) {
410+
val node = context.node().network().node(owner)
411+
if (node != null) {
412+
node.host() match {
413+
case fs: FileSystem => try fs.close(context, handle) catch {
414+
case _: Throwable => // Ignore, already closed.
415+
}
416+
}
417+
}
418+
}
419+
}
420+
421+
override def load(nbt: NBTTagCompound): Unit = {
422+
super.load(nbt)
423+
owner = nbt.getString("owner")
424+
handle = nbt.getInteger("handle")
425+
}
426+
427+
override def save(nbt: NBTTagCompound): Unit = {
428+
super.save(nbt)
429+
nbt.setInteger("handle", handle)
430+
nbt.setString("owner", owner)
431+
}
432+
433+
override def toString: String = handle.toString
434+
}

src/main/scala/li/cil/oc/server/machine/luac/SystemAPI.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ class SystemAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
3232
})
3333
lua.setField(-2, "allowBytecode")
3434

35+
// Whether custom __gc callbacks are allowed.
36+
lua.pushScalaFunction(lua => {
37+
lua.pushBoolean(Settings.get.allowGC)
38+
1
39+
})
40+
lua.setField(-2, "allowGC")
41+
3542
// How long programs may run without yielding before we stop them.
3643
lua.pushScalaFunction(lua => {
3744
lua.pushNumber(Settings.get.timeout)

0 commit comments

Comments
 (0)