This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
EveryOS's profile picture

Inconsistent? error

Started by EveryOS, 10 May 2018 - 02:20 PM
EveryOS #1
Posted 10 May 2018 - 04:20 PM
So I have a table called socket (don't ask where it came from) and an API to wrap it. However, strangely, there is a function that, when I call it outside the wrapper, it works just fine, but when called from inside the wrapper, it just freezes up for a few seconds.

The wrapper looks like this:

local nativeSocket = socket
lookup = nativeSocket.lookup
checkHost = nativeSocket.checkHost
local function wrapSocket(sock)
	local nativeSock = sock
	sock.write = function(sText)
  local ok, isClosed = nativeSock.isClosed()
		if isClosed then
			return false, "Attempt to interact with closed socket"
		end
		return nativeSock.write(sText)
	end
	sock.read = function(nAmount)
  local ok, isClosed = nativeSock.isClosed()
		if isClosed then
			return false, "Attempt to interact with closed socket"
		end
		if (type(nAmount) ~= "number") or ((nAmount%1)~=0) or nAmount<1 then
			return false, "expected positive integer"
		end
		return nativeSock.read(nAmount)
	end
	sock.close = function()
  local ok, isClosed = nativeSock.isClosed()
		if isClosed then
			return false, "Attempt to interact with closed socket"
		end
		return nativeSock.close()
	end
	return sock
end
open = function(host, port)
	if not socket.checkHost("http://"..host) then
		return false, "Bad or blacklisted URL"
	end
	local ok, sock = nativeSocket.open(host, port)
	if not ok then return ok, sock end
	return true, wrapSocket(sock)
end

The code that I am using to test it looks like this:

os.loadAPI("socket.lua")
local ok, host, port = socket.lookup("https://google.com")
local ok, sock = socket.open(host, port)
if ok then
  --First call, no lag
  print(sock.isClosed())
  --Second call, just to be sure
  print(sock.isClosed())
  --The next function calls the above functions
  print(sock.close())
else
  print(sock)
end
The log looks something like this:

true false
true false
socket.lua: 25: too long without yielding
There is a long delay between the second time it prints "true false" and the too long without yielding error

Line 25 of socket.lua is this one:

local ok, isClosed = nativeSock.isClosed()

I've also removed the second call to socket.isClosed() from the test program, just to see if it has anything to do with "function memory", but it still lags and errors

Anybody know why? Am I calling a function within itself without noticing or something?
Edited on 10 May 2018 - 02:34 PM
EveryOS #2
Posted 10 May 2018 - 04:26 PM
Manually wrote in the os.loadAPI for reference, put it in the wrong code block, ignore that

I've fixed it (as in the manually written in os.loadAPI, not the actual error)

Full code available here, if needed (Except for the test program, that is)

Fixed manual re-type typo in test program
Edited on 10 May 2018 - 02:34 PM
SquidDev #3
Posted 10 May 2018 - 04:33 PM
So the issue is in your wrapping function:

local function wrapSocket(sock)
    local nativeSock = sock
    sock.write = function(sText)
      return nativeSock.write(sText)
    end
end
The issue is that nativeSock and sock are the same object, and so when you overwrite sock.write, you're also overwriting nativeSock.write.

The solution is just to change the first two lines as follows:

local function wrapSocket(nativeSock) 
    local sock = { isClosed = nativeSock.isClosed }
    --# ... as before
EveryOS #4
Posted 10 May 2018 - 04:35 PM
Ok, thanks, I'll try it when I get a chance (Around 3:08 PST, right now it is 11:50 PST)

EDIT: Wait, I thought LuaJ generated a copy of the object if you attempt to edit it?

Though if this works, it would also explain similar issues I've had about yielding and ArrayOutOfBounds exceptions
EDIT: Tested it yesterday 3:12 PST or so, it works
Edited on 11 May 2018 - 10:28 AM
Bomb Bloke #5
Posted 11 May 2018 - 05:32 AM
EDIT: Wait, I thought LuaJ generated a copy of the object if you attempt to edit it?

Not if you attempt to "edit" one, no, but if you copy the contents of one variable to another it's indeed true that you'll always get a copy of the original contents. Likewise, function arguments are always passed as values, not references.

The trick is that when you attempt to assign a table (or function, or coroutine…) to a variable, what you're actually doing is assigning a pointer - which works like a shortcut, or an alias. So "sock" contains a pointer to your table, and "nativesock" ends up containing a copy of that pointer, leading into the same table!