ID:2928951
 
Resolved
Proc calls from Byondapi libraries were incorrectly incrementing the refcount of the proc result.
BYOND Version:515
Operating System:Windows 10 Home 64-bit
Web Browser:Firefox 115.0
Applies to:Dream Daemon
Status: Resolved (515.1640)

This issue has been resolved.
Descriptive Problem Summary:

It seems that any refs returned by a proc called from byondapi are left hanging for some possibly indefinite amount of time.

Numbered Steps to Reproduce Problem:

1. Define a byondapi function that calls a proc (global or object)
2. Define a proc that returns a ref-counted object
3. Call_ext the byondapi function to call the proc with the appropriate arguments

Code Snippet (if applicable) to Reproduce Problem:

C++ code:
extern "C" BYOND_EXPORT CByondValue call_global(int n, CByondValue v[]) {
CByondValue result;
ByondValue_Clear(&result);
if (n < 1 || !ByondValue_IsStr(&v[0])) {
return result;
};
char* procname;
u4c char_count;
Byond_ToString(&v[0], nullptr, &char_count);
procname = new char[char_count];
Byond_ToString(&v[0], procname, &char_count);
const CByondValue* args = n > 1 ? &v[1] : nullptr;
if(!Byond_CallGlobalProc(procname, args, n-1, &result)) {
ByondValue_Clear(&result);
}

return result;
}


DM code:
/proc/_new(type, ...)
if(args.len > 1)
return new type(arglist(args.Copy(2)))
else
return new type()

/datum/New() //This is just to show that our proc call did create the object
world.log << "New: [\ref(src)]"

/datum/Del()
world.log << "Del: [\ref(src)]"

/proc/case()
call_ext(LIB, "byond:call_global")("_new", "/datum")

world/New()
case()


Expected Results:
The world.log << "Del: [\ref(src)]" line should execute after a short while (tgstation waits 5 minutes for refs held by byond to automatically clear themselves before assuming they are left hanging and need to be hard-deleted with del())

Actual Results:
So far, I haven't seen the aforementioned line execute after even 5 minutes.

Does the problem occur:
Every time? Or how often?
It seems to happen with any byondapi proc call that returns a ref-counted object.
In other games?
N/A
In other user accounts?
N/A
On other computers?
Untested

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.)
Untested

Workarounds:
While calling Byond_DecRef after any byondapi proc call will definitely remove whatever erroneous ref is created, this also runs the risk of decrementing proper ref counts, causing erroneous deletions.
Calling Byond_DecRef() shouldn't risk an erroneous decref because it only works on refs that Byondapi is known to hold.

For the Del() call, don't forget that ..() needs to be called as well.

I was going to say that you should be able to produce a simpler demo case by calling Byond_New() instead of a global proc. But in testing I've discovered that the global call does indeed hold a ref too long, so I'm digging in to find out why. Calling Byond_New() however works exactly as expected.

This is my simplified test case:

highlander
New()
world.log << "Created \ref[src]: [src]"
Del()
world.log << "Deleting \ref[src]: [src]"
..()

proc/NewHighlander()
world.log << "NewHighlander called"
return new/highlander()

mob/verb/HighlanderTest()
world.log << "calling Highlander"
call_ext(DLL, "byond:Highlander")()
world.log << "done"


// bug report 2928951
extern "C" BYOND_EXPORT CByondValue Highlander(u4c argc, ByondValue argv[]) {
ByondValue result, object, type;
type = "/highlander";
//object = Byond_New(type, NULL, 0);
object = Byond_CallGlobalProc("NewHighlander", NULL, 0);
return result;
}
Interesting. This applied to non-global procs as well.
Lummox JR resolved issue with message:
Proc calls from Byondapi libraries were incorrectly incrementing the refcount of the proc result.