This Bugzilla instance is deprecated, and most Eclipse projects now use GitHub or Eclipse GitLab. Please see the deprecation plan for details.
Bug 390346 - Listener methods on part still called after closing the part
Summary: Listener methods on part still called after closing the part
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.2   Edit
Hardware: PC Windows 7
: P3 major (vote)
Target Milestone: 4.2.2   Edit
Assignee: Oleg Besedin CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 389863
  Show dependency tree
 
Reported: 2012-09-25 11:03 EDT by Thomas M??der CLA
Modified: 2012-11-23 13:30 EST (History)
8 users (show)

See Also:


Attachments
A project illustrating the problem (14.09 KB, application/zip)
2012-09-25 11:03 EDT, Thomas M??der CLA
no flags Details
Runtime modification (4.58 KB, patch)
2012-09-25 17:42 EDT, Thomas Schindl CLA
no flags Details | Diff
UI Patch (2.84 KB, patch)
2012-09-25 17:44 EDT, Thomas Schindl CLA
no flags Details | Diff
revised runtime patch (5.88 KB, patch)
2012-09-26 05:52 EDT, Thomas Schindl CLA
no flags Details | Diff
revised ui patch (2.70 KB, patch)
2012-09-26 05:52 EDT, Thomas Schindl CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas M??der CLA 2012-09-25 11:03:49 EDT
Created attachment 221473 [details]
A project illustrating the problem

I have a part with which has a listener method on the contribution class. When I send a matching event through the event broker, a message is printed out. When I repeatedly close and reopen the view, the message is printed out multiple times (grows with the number of close/open cycles)
Comment 1 Thomas M??der CLA 2012-09-25 11:07:29 EDT
To reproduct:

1) import the attached project
2) start the application
3) select "Send Event 1" from the file menu
4) Observe: you get a single message "event 1 received"
5) repeatedly close the view and reopen it from the file menu ("Open View", 5 times does the trick)
6) Select "Send Event 1" from the file menu
7) Observe: you get multiple messages saying "event 1 received"
Comment 2 Thomas Schindl CLA 2012-09-25 12:30:56 EDT
An interesting observation is that after the first close no events are received anymore, on the next activation there's once more only one event but when close that part once more it still receives the event!
Comment 3 Thomas Schindl CLA 2012-09-25 13:07:07 EDT
The problem is that we are caching the extended object supplier in the ProviderHelper, there we execute an

---------8<---------
IInjector injector = InjectorFactory.getDefault();
injector.inject(supplier, objectSupplier);
---------8<---------

so we bind the injector instance to the first requestors IEclipseContext (the simply case here the first views IEclipseContext) hence when we destroy the view for the first time we appropriately call @PreDestroy in our Supplier and unregister the listeners.

Subsequent calls now get an instance of our supplier which is not attached to a context at all.
Comment 4 Thomas Schindl CLA 2012-09-25 13:25:54 EDT
well the cache explains why on the first close we unsubscribe - the real problem looks like is that:
a) UIEventObjectSupplier.UIEventHandler misses the check if the requestor is 
   valid (compare that to the DIEventHandler!)
b) But even when patching it requestor.isValid() returns true although the 
   context and object got removed (but most likely not gc'ed)

I can proof this by adding a System.gc(); before the Send1Handler is calling its code and then isValid returns false! There most be something more explicit to mark the requestor invalid
Comment 5 Thomas Schindl CLA 2012-09-25 14:43:46 EDT
Please also note haveing the @PreDestroy in the supplier is also not really a good idea because in that case we'll remove listeners registered by other injected instances as well!
Comment 6 Thomas Schindl CLA 2012-09-25 16:55:19 EDT
To me it looks like the ExtendedObjectSupplier as it is implemented today is completely broken, because we are using DI to inject stuff.

The problem is that the ExtendedObjectSupplier is a OSGi-Service hence there's only one instance of a supplier and we are filling it with the first requestors PrimaryObjectSupplier, if the first supplier is something like a part whose context is coming and going together with the corresponding IEclipseContext we are uninjecting the values (e.g. the logger in the UIEventObjectSupplier) and invoking the @PreDestroy (e.g. EventObjectSupplier).

I think we need to support DI for suppliers (e.g. to get access to the UISynchronize) so we can't directly contribute ExtendedObjectSupplier instances instead probably have to register ExtendedObjectSupplier-Factory and create object suppliers for every ObjectSupplier.
Comment 7 Thomas Schindl CLA 2012-09-25 17:42:44 EDT
Created attachment 221492 [details]
Runtime modification

I've added a method to IRequestor to allow the requestors PrimaryObjectSupplier to inject values so that handlers are now created through DI to get access to context informations.

I'm not sure this is a good thing or there's something one can do instead certainly adding a dependency on IEclipseContext is not the correct one so I modified the IRequestor-Interface.
Comment 8 Thomas Schindl CLA 2012-09-25 17:44:53 EDT
Created attachment 221493 [details]
UI Patch

here you see DI in action to inject the logger and UISync stuff
Comment 9 Thomas M??der CLA 2012-09-26 03:22:02 EDT
Might be related to https://bugs.eclipse.org/bugs/show_bug.cgi?id=389863
Comment 10 Thomas Schindl CLA 2012-09-26 03:33:09 EDT
right it is what i expressed in comment 5
Comment 11 Thomas Schindl CLA 2012-09-26 05:52:02 EDT
Created attachment 221504 [details]
revised runtime patch

I've modified the IRequestor to now expose the IInjector and PrimaryObjectSupplier. I think this is in general a good thing because e.g. the tools.services is currently using internal APIs to create an instance through DI inside a supplier
Comment 12 Thomas Schindl CLA 2012-09-26 05:52:27 EDT
Created attachment 221505 [details]
revised ui patch
Comment 13 Thomas Schindl CLA 2012-09-26 06:11:53 EDT
> b) But even when patching it requestor.isValid() returns true although the 
>    context and object got removed (but most likely not gc'ed)
> 
> I can proof this by adding a System.gc(); before the Send1Handler is calling
> its code and then isValid returns false! There most be something more
> explicit to mark the requestor invalid

I filed bug 390424 for the problem with isValid() if we manage to fix this we can probably make an intermediate fix in 4.2.2.
Comment 14 Thomas M??der CLA 2012-09-26 06:17:21 EDT
Are you talking about a fix for this bug or for #390424 in the 4.2.2 timeframe?
Comment 15 Thomas Schindl CLA 2012-09-26 06:18:47 EDT
Well if the isValid() is fixed we can have an intermediate fix for both bugs - I guess
Comment 16 Thomas Schindl CLA 2012-09-26 14:13:48 EDT
Oleg could you take a look at the direction i'm heading to - if is the way to go i'll write junit tests for them
Comment 17 Oleg Besedin CLA 2012-09-26 16:24:15 EDT
(In reply to comment #16)
> Oleg could you take a look at the direction i'm heading to - if is the way
> to go i'll write junit tests for them

I just recently got back to work on Eclipse and currently looking at the memory leaks. Once that is done I'll do my best to work on the backlog of bugs.
Comment 18 Thomas Schindl CLA 2012-10-15 15:30:04 EDT
Oleg?
Comment 19 Oleg Besedin CLA 2012-10-19 14:56:31 EDT
First, couple things to improve the sample project:

- Getting javax annotations:
Instead of 
	Require-Bundle: ...  javax.annotation
please use:
	Import-Package: javax.annotation;version="1.0.0" ...

Otherwise you risk @PostConstruct and @PreDestroy annotations not getting called. This happens due to confusion between javax annotations supplied by the OSGi bundle and JVM.

- Injecting events:
Instead of 
	@Inject
	public void event1Occurred(@Optional @UIEventTopic...)
please use:
	@Inject
	@Optional 
	public void event1Occurred(@UIEventTopic...)

The difference is that second form only gets called when an event occurs while the first form gets also called on injection and uninjection.
Comment 20 Oleg Besedin CLA 2012-10-19 15:19:31 EDT
About multiple calls: note that those occur on different TestView1 objects. Each object gets one call (good), however, views that should have being deleted get an event too (bad).

The problem lies in the Injector#uninject() call (called via PartRenderingEngine#safeRemoveGui()):
						ContextInjectionFactory.uninject(client, parentContext);

That removes references from the primary object supplier (EclipseContext), but does not touch secondary object suppliers, such as the event supplier.

Secondary suppliers currently continue to send events until views get garbage collected.

The InjectorImpl has a forgetInjectedObject() method. I'd try to see if we can make similar list of objects for the secondary suppliers or make secondary suppliers refer to this list.
Comment 21 Thomas Schindl CLA 2012-10-19 15:37:58 EDT
I'm not sure I can follow your explainations. I think the real problem in this complete supplier story here is:

IInjector injector = InjectorFactory.getDefault();
injector.inject(supplier, objectSupplier);

we are attaching the supplier to the initial requestor and if this guy gets disposed we uninject things like the logger, ... .

Did you looked at my proposed patches?
Comment 22 Thomas Schindl CLA 2012-10-19 18:26:33 EDT
To repeat what I said on IRC:
a) object suppliers are OSGi-Services and we are attaching them to an arbitary 
   context and its lifecycle
   The following problem are arising from this:
   * bug #389863
   * uninjecting of informations like UISynchronize in UIEventObjectSupplier when 
     initial context is disposed
   * Multi-Instance support e.g. RAP because there any application has to have 
     its own UISynchronize but we have only 1 ObjectSupplier instance!
   
b) cleaning up of listeners (e.g. EventHandler) is not done in a synchronous 
   fashion but rely on GC running

I think the general rule here is that. It is not the supplier who has to be attached to the lifecycle but the part that does the object injection (for events this is the EventHandler). This one is created within the context of the object it does the injection for. This is exaclty what my 2 patches do.

Paul has set this bug to 4.2.2 but if you are following my suggestion this is not possible to fix this because we need new API (well we didn't release any of this as API but I'm not sure which rules apply).
Comment 23 Paul Webster CLA 2012-10-22 07:58:37 EDT
(In reply to comment #22)
> Paul has set this bug to 4.2.2 but if you are following my suggestion this
> is not possible to fix this because we need new API (well we didn't release
> any of this as API but I'm not sure which rules apply).

We have to be careful, but we have some flexibility as we didn't publish this as API

PW
Comment 24 Oleg Besedin CLA 2012-10-24 13:20:31 EDT
The "gist" of the fix is to explicitly call #unsubscribe() in the EventObjectSupplier#get() when tracking is not requested.

Git reference:

http://git.eclipse.org/c/platform/eclipse.platform.runtime.git/commit/?h=R4_2_maintenance&id=bef47180f41e3951d610fdb3dcb52eb15adce0c8

Thomas Maeder - huge thank you for providing snippets that show the issue!
Comment 25 Thomas Schindl CLA 2012-10-28 04:19:54 EDT
I'd like to repeat that I'm not happy with the fix here and bug #389863 because it breaks multi-user envs (bug #392977). I can live with it for 4.2.2. What we have here are singletons we are brining state too which violates one of the principal design goals e4. Singletons (which OSGi-Services are!) are only fine to act as factories.

I'll file a new bug now - if we could at least get the API I proposed in my patches on IRequestor I could at least fix the problem in a multi-user env by registering a higher ranked OSGi-Service.
Comment 26 Thomas Schindl CLA 2012-10-28 04:24:29 EDT
I filed bug #392990
Comment 27 Thomas Schindl CLA 2012-10-28 04:30:39 EDT
and bug #392977
Comment 28 Oleg Besedin CLA 2012-10-30 13:22:12 EDT
Verified using I20121030-0800 and M20121024-1600.
Comment 29 Nobody - feel free to take it CLA 2012-11-23 13:30:02 EST
While building support for @UIEventTopic and @EventTopic for the vaadin renderer https://github.com/semanticsoft/vaaclipse/issues/8 I got stuck in that I get events from one workbench to another if I use annotations. I managed to override the default event object suppliers and when I use one workbench it works ok but if I open another session I get the events from the first one. This is a killer for applications like this.