Forums > Max For Live

How to destroy a LiveAPI object instantiated in JS ?

January 25, 2012 | 3:55 pm

Everything in the title :p

Putting objects in a table, I tried to :
- put null in the array slots
- put a new object invoked with "id 0" (which of course, on the JS Live API side gives an error)

any tips for me ?


February 14, 2012 | 5:22 am

If you allow a variable to go out of scope, it will automatically be destroyed by Javascript’s garbage collector.

So I assume you still have references to it somewhere. Is there a callback associated with it? I’m not sure how to remove a callback…

Javascript does have a delete operator – try that and see if it works…


February 14, 2012 | 9:00 am

I found a workaround for this particular case (another request/callback architecture in my script)
but yes the garbage collector works… in "pure" javascript.
I guess there was something else and the callback weren’t destroyed correctly.


February 15, 2012 | 8:30 pm

I spent a little time tracking this down, just for my own satisfaction.

In fact, if you create a LiveAPI instance and a callback, it’s unclear how exactly those two objects could ever get garbage collected!

For example, suppose you have the following code:

{
  var live;
  function callback() {
    post(live.get('name')[0], 'n');  // Called whenever the track name changes, forever.
  };

  live = new LiveAPI(callback, 'this_device canonical_parent');
  live.property = 'name';
}

The function "callback" is called immediately with the current track name and continues to be called every time the track name changes, even though both "live" and "callback" have gone out of scope. Clearly there’s some global list of "LiveAPI instances with callbacks that need to be updated" which keeps "live" from being garbage collected, and which in its turn keeps "callback" from being garbage collected – but there’s no way to get "live" off the callback update list, or at least none in the documentation – Why don’t a tags work?.

Now, it turns out that "live", the LiveAPI object, has an undocumented field called "cb_private" which contains the pointer to the callback. Adding the code delete live.cb_private; to your callback stops further callbacks:

{
  var live;
  function callback() {
    post(live.get('name')[0], 'n');    // Only called once, because the next line
    delete live.cb_private;             // prevents any further callbacks.
  };

  live = new LiveAPI(callback, 'this_device canonical_parent');
  live.property = 'name';
}

But does that actually free the LiveAPI instance? It’s unclear.

It’d have to be removed from my hypothetical callback update list. Perhaps whatever code maintains that checks each time to see if cb_private is null, and if so removes the LiveAPI from the update list – perhaps it only does this check when it adds the LiveAPI to the callback update list – perhaps this the "callback list" is accomplished with some other mechanism I haven’t even thought of!

It’s hard to see how to test this directly, but you could test indirectly by creating a lot of these LiveAPIs in a tight loop and trying to free them in the callback. If you weren’t actually freeing them, then eventually you’d run out of memory… might take a long time though if these are quite small.

If there’s interest, I’ll run the test… :-)


February 15, 2012 | 8:39 pm

nice shot !
as soon as I’ll need that (means quite soon), I’ll test it.
I hope someone from C74 will help us here :)


February 16, 2012 | 3:42 am

(btw, sorry for the weird link label – for whatever reason, I was unable to get a link to appear at the time, but now I refresh the page I see it is as link… strange!


March 20, 2014 | 7:57 pm

Hi,

Im wondering how cb_private works if you want to delete observers and then create new ones?

For instance, if I run some JS and set up a bunch of observers, when I run the js next time (to delete the old observers and set up some new ones), how can I delete the observers? Do they get deleted on each run anyway?

The usage shown in the example seems to delete observers after the first callback. How can they be deleted "on demand", or on run of next execution?

My concern is that each time the script is run, new observers are being set up, are the old ones deleted/garbage collected?



Lee
March 21, 2014 | 2:53 am

You need to set the property back to empty (null) – this will remove the observer from the internal lists.

Nothing is done automatically in js for the observers – you have to manage them all explicitly.


March 21, 2014 | 3:13 am

maybe can we use "freepeer" method for it . it works for Dict’s , Polybuffers so maybe for other constructors or instances also . im just guessing



Lee
March 21, 2014 | 3:56 am

?? don’t understand – setting the monitored property back to null clears the observation of that property, what more needs to be done?


March 21, 2014 | 4:02 am

just asking Lee . i had in mind just the instance of an API not observers . a bit off here , sorry


March 21, 2014 | 4:04 am

(so happy this thread has initiated progressively a deep discussion!)

I can understand that C74′s dudes don’t reveal too much, but we’d need that, actually ;)



Lee
March 21, 2014 | 4:13 am

dw… no problem mate, just wondered if there was something I was missing… :)


March 21, 2014 | 5:38 am

Thanks Lee,

Im wondering how would to set the properties to Null?

In the following code, would I just run through the same IDs/Paths again, to setting "obsids[i].property" to null instead of value?

for (i = 0; i < leng; i++) {
obsids[i] = new LiveAPI(callback, "id "+arguments[i]);
obsids[i].is_observing = false;
obsids[i].property = "value";
obsids[i].is_observing = true;

}
}
function callback(args)
{
outlet(0, args[0] + " " + args[1] + " " + this.id + " " + this.is_observing);
}
}



Lee
March 21, 2014 | 5:54 am

yes, just do:

obsids[i].property = "";

for each one (I checked my code and I actually set it to the empty string rather than null – I’m guessing I did this for a reason as normally I would just null something like that out)


March 21, 2014 | 10:45 am

Works perfectly. Thanks Lee :)


March 29, 2014 | 7:41 am

Ive been testing this thoroughly over the last few days. Im coming to the conclusion that setting them to "" empty or null doesnt stop these observers still working away in the background (although they do stop outputting values). Im trying to put a patch together to demonstrate this, but its hard because you only notice it when you are observing lots of IDs at once and the only thing to notice is that observers are slowing down.

If I create say 200 observers for an Operator, then use hot-swap in live to switch the parameters. It takes around 3 seconds to hot-swap. If I then delete the observers and re-create them, it takes 5 seconds to hot swap. Each time I delete/recreate the observers there is a few seconds added to hot-swap, which demonstrates that although the observers may be deleted and not outputting any values, they are still working away in the background.

The reason why I suspect the observers are still working in the background and not being garbage collected is because the behaviour when they are deleted (ie resulting in slower and slower observing), is the same as when new observers are created over and over again (without deleting the old ones).

Ive tried with both "delete live.cb_private;" and with setting null or empty strings and while they both do stop the observers outputting values, clearly they are still working in the background.


March 29, 2014 | 9:04 am

Ok, so I have finally managed to make a test patch to show this in more detail.

The way this works is it measures the amount of seconds between updating the first and last parameter of an operator device. You have to put this device to the right of an operator. Then click bang, then hot-swap 2-3 times. Then keep clicking bang and hotswapping 2/3 times, each time checking the speed of hot-swap in the print window.

Here are my results (which seem conclusive):

1st Button press (creation of observers)

Hotswap 1 – 1.14s
Hotswap 2 – 1.14s
Hotswap 3 – 1.13s

2nd Button press (delete and recreate observers)

Hotswap 1 – 2.10s
Hotswap 2 – 2.09s
Hotswap 3 – 2.09s

3rd Button press (delete and recreate observers)

Hotswap 1 – 3.05s
Hotswap 2 – 3.06s
Hotswap 3 – 3.06s

This goes on until I run out of memory I guess. JS is definitely not removing those observers. However, I have a feeling that the live.observer object behaves exactly the same way (its just easier to produce a demo in JS).

Any thoughts?

Ive sent the patch into C74. Hopefully theyll come back to me with some explanation :/



Lee
March 29, 2014 | 12:28 pm

Hi. This again exists in the MxDCore code owned by Ableton. The observer is being disabled (as in the fact it is no longer a registered callback), but there is a data structure that is not being cleared down properly. Hence, over time this grows and causes the issue. These are two separate items as far as the code is concerned. Let’s try and get the Abes to look at the first issue first, then will move onto this one :) As well as proposing a solution to this I will also propose a new call is added so that we can remove all current observers for the current device. This should simplify things a lot. Currently, this only happens, and the only way of getting this to happen, is to remove the device.



Lee
March 29, 2014 | 12:31 pm

By first issue I mean the one that we are discussing in the other thread… And ultimately, fixing that will also make this kind of go away, but this should still be addressed for completeness tho…



Lee
March 31, 2014 | 2:15 am

Newtfish, are you on L8 and not L9?


March 31, 2014 | 3:44 am

L9, you think this might be to do with L9 only?



Lee
March 31, 2014 | 4:08 am

No, it’s definitely both. I have a replacement MxDCore which fixes the main issue for now but only for L9. send me your email address to admin@sigabort.co.uk and I’ll send it over tonight if you want to give it a go.


Viewing 23 posts - 1 through 23 (of 23 total)