Author Topic: ImageWindows.windows returns closed windows  (Read 3215 times)

Offline bitli

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 513
ImageWindows.windows returns closed windows
« on: 2012 April 01 07:48:37 »
Create an image with a View called TOTO

Execute the following code:
Code: [Select]
var v = View.viewById("TOTO");
v.window.close();
var c = ImageWindow.windows;
Console.writeln(c.map(function(e) {return e.mainView.id}))

TOTO is still part of the views returned by ImageWindows.windows, although it seems properly closed. Getting the list of windows in a new instance of the script returns the proper list.

This happens also if using forceClose or if adding processEvents around.

Currently the workaround I found was to execute commitPendingUpdates() on all windows returned by ImageWindows, catching the error produced on closed windows to remove them from the list. This may or may not be robust.

-- bitli

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: ImageWindows.windows returns closed windows
« Reply #1 on: 2012 April 01 17:48:18 »
This is expected behavior. The JavaScript garbage collector can only be controlled partially from a script. In your example, garbage collection does not happen and hence does not destroy the closed window before you access the ImageWindow.windows static property. In a larger script, garbage collection would eventually work as expected, but this cannot be guaranteed.

A robust way to do this is as follows:

Code: [Select]
var v = View.viewById( "TOTO" );
v.window.close();
var c = ImageWindow.windows;
Console.writeln( c.map( function( e ){ return View.viewById( e.mainView.id ).isNull ? "null" : e.mainView.id; } ) );

Note that calling gc() after closing the window does not help either, since gc() is only a hint passed to the runtime, which may or may not be honored at the calling point, for performance reasons. In JavaScript (as in other dynamic languages) you cannot take for granted that a given object will be effectively destroyed just after you destroy it programmatically.

The above snippet works because the View.viewById static method ignores child views of already closed windows. To facilitate this task, I'll add an ImageWindow.isClosed read-only property in the next version of the PI Core application.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline bitli

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 513
Re: ImageWindows.windows returns closed windows
« Reply #2 on: 2012 April 02 01:04:15 »
Hi Juan.

I do not really agree that this is the expected behavior. I understand the underlying behavior, but the script writer should not be concerned with it. For example if I use 'delete anObject.toto', the property 'toto' is not visible anymore in the object, even if the value of toto is still hanging in memory until the next GC.  It seems that we can still 'raiseToTop' a closed window and it become visible - this is like being able to read a file after closing it.

But I can work with your proposed workaround and I thank you for your prompt answer.

-- bitli

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: ImageWindows.windows returns closed windows
« Reply #3 on: 2012 April 02 04:16:37 »
Hi bitli,

Quote
this is like being able to read a file after closing it.

Exactly. That's how the ImageWindow.windows array works in PJSR. Bear in mind that image windows and views are quite complex objects, and their relations with the rest of the platform are intricate and delicate. PI is a multithreaded environment. We cannot simply destroy a window asynchronously at the point we want, because that would generate lots of problems and leave many objects unstable. For the same reason, we cannot remove a still living window from the global window container arbitrarily.

GUI objects (as image windows and views) can only be destroyed when the event processing loop gains control. This happens naturally in a running GUI application or script during idle states, but not in code like this snippet. In PCL-based modules, calling ProcessInterface::ProcessEvents() normally flushes all pending events immediately, since the PI core application has a synchronous garbage collector. In the case of PJSR, however, we have an additional problem: an object's destructor won't be invoked until the JavaScript garbage collector (asynchronous) decides to destroy it. For this reason, calling processEvents() in the above code does not work: we have two garbage collectors working in sequence: JS's asynchronous collector first, and then PI Core's synchronous collector, which requires at least a run of the main event loop to destroy a GUI object.

This is a limitation of the JavaScript runtime, along with limitations of the PixInsight Core application. However, in the next version of the core there will be more robust and efficient ways to know if a given window or view has been closed, which will facilitate these tasks.
« Last Edit: 2012 April 02 06:39:32 by Juan Conejero »
Juan Conejero
PixInsight Development Team
http://pixinsight.com/