ID:2247182
 
Applies to:
Status: Open

Issue hasn't been assigned a status value.
Is there a chance we could get a way of setting how long view() and similar procs cache results -- or even better, a way disabling that feature entirely?

These procs hold onto references to all the objects detected by the call for an unnecessary long time (try hours).

Ideally, I'd like to be able to turn the caching part off completely, or set a maximum time that these procs are to keep the cache around.

Thanks.
The list is only held onto until the next call. If you're really concerned about this, create an object at a null location and just use range(0) on it.
In response to Lummox JR
That doesn't seem to be doing anything -- at least from what I can tell via the Memory Stats profile.
You could, I suppose, try putting an object on the map and doing range(0) that way. That might produce a difference.
In response to Lummox JR
No change.
What are you looking at that tells you references are being kept, anyway? Because I wouldn't rule out that you're simply measuring this with the wrong metric.
In response to Lummox JR
The "Memory Stats" profile in Dream Daemon shows the memory is never freed. Nothing is ever garbage collected either.
Memory stats isn't going to tell you anything substantive about what happens with view() or range(). How do you know nothing is getting garbage collected?
In response to Lummox JR
Memory stats isn't going to tell you anything substantive about what happens with view() or range().

Why not? Wouldn't it be expected to see the memory count go back to what it was prior to the proc calls if nothing is being held onto anymore?

How do you know nothing is getting garbage collected?

Debug messages that are never seen.
Are the objects being moved off the map? An atom with a location always has a reference. And there are lots of ways for a reference to be kept, so maybe something external is holding onto them.

The temp list used by view() and range() is always [0xf000000] I believe (you can always verify that by testing with the \ref macro), so one option you could try is using locate() to grab that list and setting its length to 0. If the references drop, that's a sure sign that the last view/range call was the only thing still holding onto them. If not, then it's something else.
In response to Lummox JR
Are there any other "special" lists like that that are exposed to us? could you list them? (Purely for curiosity's sake)
Are the objects being moved off the map? An atom with a location always has a reference. And there are lots of ways for a reference to be kept, so maybe something external is holding onto them.

The problem occurs only when view() or similar is involved. I'm 99% sure the problem is not on my end. I've avoided entire use of said procs for a long time now because this very reason -- but it's come to a point now where I kinda need it.

The temp list used by view() and range() is always [0xf000000] I believe (you can always verify that by testing with the \ref macro), so one option you could try is using locate() to grab that list and setting its length to 0. If the references drop, that's a sure sign that the last view/range call was the only thing still holding onto them. If not, then it's something else.

This isn't consistent with my test results. No list reference is ever returned -- yet, garbage collection only impedes after using view() or the like.

Edit: Here's what I've been using to test as this topic has developed:

mob/verb/test()
. = view(50)

mob/verb/test2()
var/l[] = locate("\ref[0xf000000]")
if(l) world.log << "view() cache exists"

key = null
loc = null
. = range(0)

var/x[] = locate("\ref[0xf000000]")
if(x)
world.log << "view() cache still exists; nulling"
x.len = 0


I was using separate objects from my own mob at one point; same results.
In response to FKI
FKI wrote:
Are the objects being moved off the map? An atom with a location always has a reference. And there are lots of ways for a reference to be kept, so maybe something external is holding onto them.

The problem occurs only when view() or similar is involved. I'm 99% sure the problem is not on my end. I've avoided entire use of said procs for a long time now because this very reason -- but it's come to a point now where I kinda need it.

The temp list used by view() and range() is always [0xf000000] I believe (you can always verify that by testing with the \ref macro), so one option you could try is using locate() to grab that list and setting its length to 0. If the references drop, that's a sure sign that the last view/range call was the only thing still holding onto them. If not, then it's something else.

This isn't consistent with my test results. No list reference is ever returned -- yet, garbage collection only impedes after using view() or the like.

Edit: Here's what I've been using to test as this topic has developed:

> mob/verb/test()
> . = view(50)
>
> mob/verb/test2()
> var/l[] = locate("\ref[0xf000000]")
> if(l) world.log << "view() cache exists"
>
> key = null
> loc = null
> . = range(0)
>
> var/x[] = locate("\ref[0xf000000]")
> if(x)
> world.log << "view() cache still exists; nulling"
> x.len = 0
>


I was using separate objects from my own mob at one point; same results.

you don't need \ref before the [0xf000000]. [0xf000000] is reference you are trying to locate. locate("\[0xf000000]").

\ref[view()] should evaluate to [0xf000000]
In response to Super Saiyan X
I thought that looked weird. Appreciate it.

Same result either way though.
I noticed your test code doesn't actually output the length of the temp list, or info about the references contained in it. That's important information too.

It turns out I'm actually mistaken on one point as well. view() has two instruction codes: one simply puts all the values into TMP_LIST and pushes that to the stack, whereas the other puts all the values into TMP_LIST but then pushes a copy. (Only view() and oview() work this way. For all other such lists, it's always TMP_LIST but then they get an instruction to duplicate it into a new list.) In most cases you're getting the duplicate, and the temp list is being cleared. Either way, though, the only extra references being held onto here should be in the temp list.

About the only way I can look into this is if you have a test case that shows references being held onto where they shouldn't be. Without seeing more code I have no idea what could be keeping those references. What I do know is that only certain things can.
I noticed your test code doesn't actually output the length of the temp list, or info about the references contained in it. That's important information too.

You never see the output anyway since the list reference is always non-true. (Before or after the range(0) call -- there is never a list.)

http://files.byondhome.com/FKI/Test%20Environment.rar

Run the world, use one of the two verbs: test_src_start() or test_obj_start(). Based on the one you picked, use the *_end() verb. Observe the world log and lack of output.
You frelled up the locate() on the temp list by not putting brackets around it. With brackets, the locate() always succeeds.

When I run test_obj_start() and then test_obj_end(), I am definitely getting the delete message from the object. That shows that the list isn't being held onto.

Furthermore, checking for tmp_obj by reference shows me exactly when the deletion is happening, which is when I expect it to be. If you leave the range() call as-is, the temp list still has tmp_obj in it until the block after locate() removes it. After the proc returns, its return value which was list(tmp_obj) is discarded, and with it the last references to tmp_obj. If you change the range() to orange(), tmp_obj is deleted as soon as you clear the var.

    verb/test_obj_end()
var/tmpref = "\ref[tmp_obj]"
world.log << "tmp_obj = \ref[tmp_obj]"
tmp_obj.loc = null
// this range() call clears the last temp list
// now there are two lists with references to tmp_obj: the new temp list and the copy in .
// if you use orange() it will be cleared completely
. = range(tmp_obj, 0)
// if you used orange() instead of range(), tmp_obj is deleted on this line:
tmp_obj = null
// otherwise, we still have two references to the object

var/l[] = locate("\[0xf000000]")
if(l)
world.log << "view() tmp list exists (len: [l.len])"
l.len = 0
// the temp list is cleared here, leaving only one reference to the obj
world.log << "tmp_obj from locate() is [locate(tmpref)]"
// the last reference to former tmp_obj is deleted after the proc returns
// and the return value is discarded

So everything is happening exactly as it should. Your object is being deleted at the appropriate times.

If you want a quick way to clear the view/range cache, you can set up a #define to do it.

var/list/_tmp_list
#define CLEAR_VIEW_CACHE if((_tmp_list = locate("\[0xf000000]")) _tmp_list.len = 0

Or in 512 you can use this much simpler syntax:

#define CLEAR_VIEW_CACHE locate("\[0xf000000]")?.len = 0
Consider me thoroughly confused. The moment you mentioned using range(0) to clear the list, I made sure to test all variations on an obj both with a loc and without. The fact that it's working now is baffling and relieving at the same time.

Appreciate your time and didn't mean to waste it.

Props to Super Saiyan X as well. I managed to overlook the "\" before the reference number. Always nice to learn something.
In response to Lummox JR
Lummox JR wrote:
TMP_LIST

I wonder what else uses this temp list?

After what operations would it be an opportune time to check the list for giggles?

Lummox JR wrote:
frelled

Farscape fan detected