This Bugzilla instance is deprecated, and most Eclipse projects now use GitHub or Eclipse GitLab. Please see the deprecation plan for details.
Bug 408506 - PlatformObject.getAdapter(Class) can return null for valid adapter due to race condition in AdapterFactoryProxy
Summary: PlatformObject.getAdapter(Class) can return null for valid adapter due to rac...
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Runtime (show other bugs)
Version: 4.3   Edit
Hardware: PC Windows 7
: P3 major (vote)
Target Milestone: 4.3.1   Edit
Assignee: John Arthorne CLA
QA Contact:
URL:
Whiteboard:
Keywords: greatbug
Depends on:
Blocks: 405320 409608 419080
  Show dependency tree
 
Reported: 2013-05-20 12:58 EDT by Brian Vosburgh CLA
Modified: 2013-10-09 16:01 EDT (History)
2 users (show)

See Also:


Attachments
Proposed patch (1.08 KB, patch)
2013-05-24 12:12 EDT, Neil Hauge CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Brian Vosburgh CLA 2013-05-20 12:58:19 EDT
We (Dali) adapt the platform interface IProject to the Dali interface ProjectResourceLocator.

This Dali interface and its sole implementation, SimpleProjectResourceLocator, are in the plug-in o.e.jpt.common.core. The IAdapterFactory that implements this "adaptation" is in the same plug-in. And plugin.xml defining the corresponding extension to the o.e.core.runtime.adapters extension point is also in the same plug-in.

The problem: Intermittently, during start-up, a call to IProject.getAdapter(ProjectResourceLocator.class) will return null with the first call but not null with subsequent calls. The adapter factory is implemented in such a way that it will never return null; so, apparently, the adapter factory is not invoked by the platform adapter manager. This seems to be a bug, as the plug-in must have been loaded for the class ProjectResourceLocator to be resolved. Unfortunately, in a test workspace where this happens with every start-up, it never happens when executing under the debugger (even with no breakpoints whatsoever).

Is this expected behavior (i.e. that even though a plug-in is loaded, its adapter factories are not necessarily loaded)? If not, can anyone offer any hints as to how we can investigate, sans the debugger, why the platform adapter manager does not invoke the appropriate adapter factory?
Comment 1 John Arthorne CLA 2013-05-21 11:00:49 EDT
This is not expected behaviour to me. It sounds like some kind of race condition where the bundle is not yet active at the time the adapter request comes in, although I don't see how that is possible if you are referencing a class from the plugin providing the adapter before-hand. If suspending in debugger alters the timing, the only debugging technique I can think of is stdout messages in plugin startup, factory instantiation, and adapter lookup to start with.
Comment 2 Neil Hauge CLA 2013-05-24 12:11:47 EDT
Brian and I have been looking at this issue, and after some println debugging were able to identify the cause of the race condition.  The root of this issue is in AdapterFactoryProxy and its use of the 'factoryLoaded' flag.  The summary is that 'factoryLoaded' can be set to true and accessed by another thread as true before the actual 'factory' has been assigned in the proxy.  This results in null being returned for an adapter factory that could be successfully loaded by has not yet been loaded.

Details:

During workbench start-up, depending on the what the UI context is for a given workbench session, multiple Dali "JPA projects" may end up being built concurrently.  In this situation, multiple threads end up making a call to IProject.getAdatper(...) around the same time to retrieve a particular adapter that is needed to build a JPA project.  What ends up happening in the case where this fails (which is 50-75% of the time depending on the number and size of projects in the workspace) is:

- a call is made to IProject.getAdatper(...) on Thread A
- the AdapterFactoryProxy in question has already been pre-built, but the internal 'factory' has not yet been loaded (it is lazily loaded)
- AdapterFactoryProxy.getAdapter(...) calls loadFactory(...) to load the real AdapterFactory, as 'factoryLoaded' is set to false
- 'factoryLoaded' flag is set to true in loadFactory(...) before the 'factory' instance var has been assigned
- Thread B has also called IProject.getAdatper(...), which has gone to the proxy, where the 'factoryLoaded' check in getAdapter(...) returns true but the 'factory' itself has not yet been assigned and therefore not actually loaded
- As a result, Thread B's call to getAdapter(...) returns a factory proxy that has a null internal factory, which results in a return value of null from AdapterManager.getAdapter(...)

Proposed solution:

I have attached a patch that solves the issue by changing when the 'factoryLoaded' flag is set to true.  The intended behavior of this flag should stay the same and avoid the race condition by setting the 'factoryLoaded' flag in a finally block.  Since the 'factoryLoaded' flag is double-checked, it should prevent any redundant loading of the proxy's 'factory', given that the loadFactory(...) method is synchronized. 

Other notes:

The AdapterFactoryProxy.loadFactory(...) method is synchronized, but the AdapterFactoryProxy.getAdapter(...) method is not, so we cannot rely on the synchronized loadFactory(...) method to prevent a race condition in relation to the 'factoryLoaded' flag.
Comment 3 Neil Hauge CLA 2013-05-24 12:12:43 EDT
Created attachment 231487 [details]
Proposed patch
Comment 4 John Arthorne CLA 2013-05-27 10:25:59 EDT
Great find!
Comment 5 John Arthorne CLA 2013-07-16 11:10:55 EDT
Reviewing this now. Neil to get the formalities out of the way, can you state here that your contribution complies with the CoO:

http://www.eclipse.org/legal/CoO.php
Comment 6 Neil Hauge CLA 2013-07-16 11:51:40 EDT
(In reply to comment #5)
> Reviewing this now. Neil to get the formalities out of the way, can you
> state here that your contribution complies with the CoO:
> 
> http://www.eclipse.org/legal/CoO.php

I confirm that the contribution complies with the CoO.  In fact, I just now submitted my CLA for Eclipse.