The test
PDOMProvidersTests.testCommonSDK occasionally fails. I‘ve
narrowed the problem to code related to
AbstractIndexerTask.LocationTask and FileVersionTask. I don’t
understand what they are trying to do. Here is a description of
what I've observed.
It is difficult to force
the failure, but I'm able to trigger the race condition with the
proper application of breakpoints.
The start of this test
case has two lines:
ICProject cproject= CProjectHelper.createCCProject("foo" + System.currentTimeMillis(), null, IPDOMManager.ID_FAST_INDEXER);
TestSourceReader.createFile(cproject.getProject(), new Path("/this.h"), "class A {};\n\n");
This will always trigger
a PDOMRebuildTask (since a new project is being added) but it
will also sometimes trigger an extra PDOMIndexerTask. The
behaviour depends on whether the core.resources plugin has time
to send a ResourceDelta between these calls. If only one delta
is sent then CDT only uses a PDOMRebuildTask. If two deltas are
sent, then processing of the second delta creates a second
PDOMIndexerTask.
In the problem case, the
second task arrives (PDOMManager.enqueue) as the PDOMRebuildTask
is still running. In this case that update is handled by the
PDOMRebuildTask as an “urgent task”.
PDOMRebuildTask
processes it's own file updates and creates a LocationTask for
the updated file “header.hh”. When that file is processed,
LocationTask.fStoredAVersion is set to true.
Next it processes files
from the urgent task, which creates a new FileVersionTask in
that same LocationTask. This new instance of FileVersionTask is
initialized with fOutdated set to true.
Then #parseLinkage has
three sections that try to parse the file.
1) “// First parse the
required sources”: does nothing because the fKind is
ONE_LINKAGE_HEADER.
2) “// Files with
context”: calls #parseVersionInContext which calls
#findContextFile which returns null because
ctxFile.getParsedInContext() is null.
3) “// Files without
context”: does nothing because locTask.needsVersion() returns
false (since LocationTask.fStoredAVersion is true).
This means that the
final part of the method runs and deletes the header.hh file
from the index.
This is bad. It removes
header.hh (which was added by the original PDOMRebuildTask
handling) and also ignores the header.hh that was passed in by
this urgent task.
I have different ideas
for fixes:
[A]
LocationTask.addVersionTask could clear the fStoredAVersion flag
if it modifies the fVersionTasks list. However, that flag seems
to be intentionally named to not change in this way.
[B]
#parseVersionInContext could call FileVersionTask#setUpdated if
the file is not going to be parsed. However, I'm not sure what
will happen in cases where the file really is out of date and
does need to be removed.
[C] PDOMManager.enqueue
could be changed to stop offering new tasks to the currently
running task. This would avoid cases where tasks are accepting
new work in the middle of existing work.
-Andrew