ID:2824809
 
Resolved
Calling an external library would not decrease the refcount of the arguments that were sent, except when the call()() used an arglist.
BYOND Version:514
Operating System:Windows 10 Home 64-bit
Web Browser:Chrome 105.0.0.0
Applies to:Dream Daemon
Status: Resolved (514.1589)

This issue has been resolved.
Descriptive Problem Summary:
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()().
Lummox JR resolved issue with message:
Calling an external library would not decrease the refcount of the arguments that were sent, except when the call()() used an arglist.