Tab lifecycle is occassionally notified out of order

I have a script that behaves strangely. After some debugging effort I noticed that the events for opening, activating and closing a tab are occassionally not in the expected sequence:

Script Output:

Viewer Pane Follows Focus: open tab
Viewer Pane Follows Focus: activate tab
Viewer Pane Follows Focus: close tab
Viewer Pane Follows Focus: open tab
Viewer Pane Follows Focus: activate tab
Viewer Pane Follows Focus: close tab
Viewer Pane Follows Focus: activate tab
Viewer Pane Follows Focus: open tab
Viewer Pane Follows Focus: close tab

Viewer Pane Follows Focus: open tab
Viewer Pane Follows Focus: activate tab
Viewer Pane Follows Focus: close tab

function OnOpenTab(data) {
	DOpus.output("open tab");
}

function OnActivateTab(data){
	DOpus.output("activate tab");
	updateViewerPane(data.newtab);
}

function OnCloseTab(data) {
	DOpus.output("close tab");
}

I guess the output from the dopus script console does not necessarily reflect the actual order the events are triggered and executed. I'd at least put some microseconds-timestamp to each line printed to see which one was the first being executed.

This was only my test script used for debugging. The real script does something more meaningful (variable and state handling) in the OnOpenTab method and occassionally the variables were missing when OnActivateTab was called (but magically appeared when OnCloseTab was called). Everything looked as if the methods were called in the wrong order. The textual output finally verified this assumption.

This jscript can reproduce the issue more reliably. After installing the script you simply have to perform multiple Go {sourcepath$} NEWTAB TABPOS=+1 in a sequence to trigger the "invalid lifecycle..." output.

function sleep(millis) {
	var start= new Date();
	var now = null;
	do { 
		now= new Date(); 
	} while(now - start< millis);
}

function OnOpenTab(data) {
	sleep(100);
	var vars = data.tab.vars;
	vars.Set("OPENED", true);
}

function OnActivateTab(data){
	var vars = data.newtab.vars;
	if (!vars.Exists("OPENED")) {
		DOpus.output("invalid lifecycle: " + data.tab);
	}
}

function OnCloseTab(data){
	var vars = data.tab.vars;
	vars.Delete("OPENED");
}

Leo, can this be synchronized in any way? Or is there any other workaround for these out-of-order notifications? Thanks in advance for any help.

I don't think the order of open and activate events is guaranteed at the moment, but it should be possible to write the script so it doesn't matter which is called first. If you see activate first, you can infer the tab exists and (if needed) ignore the subsequent open.

Actually I could solve this by delegating the 'activate' and 'open' methods to a new method that first tries to evaluate if an 'open' is really an 'open' or maybe yet an 'activate'. :wink: Unfortunately this does not work if the 'close' comes too early. Thus I assume introducing a guaranteed order for lifecycle events is a candidate for the list as well.

Can the close come before the open/activate events? That wasn't in any of your examples.

Actually if I press and hold a command button for creating a new tab but closing all other tabs the events are getting heavily out of order. If I remember correctly I have never seen a 'close' before an 'open' but if you programmatically change the tab and close the active one I pretty sure that I already have seen a 'close' before an 'activate'.

For the next days I won't have DOpus in front of me. But if you like I can try to write a script that reproduces this effect if it would be helpful for you to analyse this (as soon as I will be back in my office).

By command button you mean hotkey? As in the hotkey is held down, running repeatedly and at once, one hotkey invocation opening tabs that the other ones will close?

(Toolbar buttons don't do anything if held down, until released.)

I'd have to see it happen to know if there was a bug there, as it sounds like something that would easily confuse script code anyway, as each invocation of the script is fighting another. Maybe not holding down that hotkey is the solution to that problem.

Obviously I only held it down when I tried to reproduce / analyse how the out of order events affects my scripts. Holding down buttons is not part of my daily business. :grin:

I don't think the "close" event could ever come out of order. The only thing that does is that "activate" will come before "open" if the new tab is being activated. We might change this in the future but at the moment it should be relatively easy to adjust for as Leo suggested.

Ok, good to know! Than I don't need to make further tests regarding the "close" behaviour anymore. By using a tab variable that reflects the initialization state it is fairly easy to catch these "activate" events poping up before an "open" event.

I just have some spare time here, thus I took my DOpus portable stick and started trying to make the out-of-order event detection more reliable in my scripts.

Unfortunately I am not able to reliably detect which event is notified first because it looks as each event is fired simultaneously (to each other) by an own thread. In other words they may not even appear out of order but they may appear also at the same time. Since there is no thread synchronization in JavaScript both event handling methods (activate and open) occassionally think they are first and they both have to execute the initialization part (which certainly should only be done once).

I wrote a test script to verify this and it's actually looks as there is more than one event thread notifying the listeners addins. Since the command (shown in the example below) is called once but the variable is cleared three times (shown in the output) it looks as the events are even notified in parallel by multiple event threads simultaneously (which makes it hard to catch the "first").

If this assumption is correct and if the events needs to be notified asynchronously to the command thread, I would propose to at least synchronize the events that they can not appear in parallel. :slight_smile:

How to reproduce:

Here is the addin:

[code]function OnOpenTab(data) {
var vars = data.tab.lister.vars;
DOpus.output("OnOpenTab: " + data.tab.path + " : " + vars.Exists("foo"));
vars.Delete("foo")
}

function OnActivateTab(data){
var vars = data.newtab.lister.vars;
DOpus.output("OnActivateTab: " + data.newtab.path + " : " + vars.Exists("foo"));
vars.Delete("foo")
}

function OnSourceDestChange(data){
var vars = data.tab.lister.vars;
DOpus.output("OnSourceDestChange: " + data.tab.path + " : " + vars.Exists("foo"));
vars.Delete("foo")
}
[/code]

Here is the command (in my test-case enabling a previously closed dual lister and restoring 13 tabs):

@set lst:foo=bar
Set DUAL=on,remember,Vert

Here is the output:

EventTest: OnSourceDestChange: D:\Development\Subversion\asteria : [b]true[/b]
EventTest: OnOpenTab:          D:\Pictures : [b]true[/b]
EventTest: OnSourceDestChange: D:\Development\Subversion\asteria : [b]true[/b]
EventTest: OnOpenTab:          U:\Work\SPP : false
EventTest: OnOpenTab:          H:Data : false
EventTest: OnOpenTab:          H:Data : false
EventTest: OnOpenTab:           : false
EventTest: OnOpenTab:          H:Data : false
EventTest: OnOpenTab:          H:Data : false
EventTest: OnOpenTab:          H:Data : false
EventTest: OnOpenTab:           : false
EventTest: OnOpenTab:          T:\ : false
EventTest: OnActivateTab:      H:Data : false

I tried to highlight the first three lines by manually inserting a bold tag, but certainly this doesn't work inside a code block. :slight_smile:

Events for different tabs (or different windows) may run in parallel. Does that explain everything, or is there something more unexpected going on?