When using call()() to call an external library function, arguments passed to that call don't get their ref count decreased and live indefinitely.
Numbered Steps to Reproduce Problem:
1. Use call(library, function)(args)
2. Clear all known references to args
3. Args doesn't get garbage collected
Code Snippet (if applicable) to Reproduce Problem:
var/list/constantNumStrings
proc
BuildNumStrings(n)
var/list/temp = new(n)
for(var/i in 1 to n)
temp[i] = "[i]"
constantNumStrings = temp
GCTest1(n)
var/nStr = sha1("[n]")
var/weakref_nStr = ref(nStr)
var/refNum = text2num(copytext(weakref_nStr, 5, 12), 16)
weakref_nStr = null
return refNum
GCTest2(n)
var/nStr = sha1("[n]")
var/weakref_nStr = ref(nStr)
call("dllTest.dll", "testCall")(nStr) // memory leak, passed arguments don't have their ref counts decreased at the end of the call
var/refNum = text2num(copytext(weakref_nStr, 5, 12), 16)
weakref_nStr = null
return refNum
mob
verb
LoopGCTest1(n as num) // use with smaller numbers, strings are garbage collected between GCTest1 calls, resulting in only 1 reference number used
BuildNumStrings(n)
var/list/uniqueRefs = new()
for(var/i in 1 to n)
uniqueRefs |= GCTest1(i)
usr << "[json_encode(uniqueRefs)] - [length(uniqueRefs)] items"
LoopGCTest2(n as num) // use with smaller numbers, strings are not garbage collected between GCTest2 calls, resulting in n number of reference numbers used, calling LoopGCTest1 afterwards with the same n value shows the same result
BuildNumStrings(n)
var/list/uniqueRefs = new()
for(var/i in 1 to n)
uniqueRefs |= GCTest2(i)
usr << "[json_encode(uniqueRefs)] - [length(uniqueRefs)] items"
LoopFloodTest1(n as num) // use with large numbers, no hanging string references in memory stats
for(var/i in 1 to n)
GCTest1(i)
usr << "Done"
LoopFloodTest2(n as num) // use with large numbers, hanging string references in memory stats
for(var/i in 1 to n)
GCTest2(i)
usr << "Done"
Expected Results:
Arguments passed to external calls get properly garbage collected when all of their known references are cleared.
Actual Results:
Arguments passed to external calls live indefinitely and cause a memory leak.
Does the problem occur:
Every time? Or how often? Every time
In other games? Yes
In other user accounts? Yes
On other computers? Yes
When does the problem NOT occur?
Never.
Did the problem NOT occur in any earlier versions? If so, what was the last version that worked? (Visit http://www.byond.com/download/build to download old versions for testing.)
Tested in 512.1454 and 514.1588, bug exists in both versions.
Workarounds:
Don't overuse call()().