Abstract:
This document searches for improvements in threading
model of the openide.text module under issue
33165.
It focuses purely on threading
and various functionality descriptions, terminology etc.
may be somewhat inaccurate.
Current threading of openide.text does not use
Document.render()
to read-lock the document during document-related operations.
Because of that number of threading problems may arise.
Issue
33040
is likely the most noticeable and unpleasant one.
Main functionality building blocks of the openide.text include
CloneableEditorSupport.getDocument()
- returns valid document instance if the document was loaded.
Returns null if none has asked to open the document yet
or if the document was closed previously. In case the document is being
loaded it waits till the loading is finished and returns
the loaded instance.
EditorCookie
claimed
that getDocument()
does *no* waiting but certain issues arised
with this approach which resulted in the current implementation
that actually does wait in case the document is being loaded.
CloneableEditorSupport.openDocument()
- get document instance and wait until it's loaded and then return it.
Document.render()
.
There is already a use case of the code first locking the document atomically by NbDocument.runAtomic() and manipulating the positions inside the executed Runnable. To avoid deadlocks caused by counter locking the added read-locking must be done prior to synchronization of PositionRef.Manager.
Note: Making changes related to this was not planned
originally.
I was forced to do them because after
I have performed the read-locking changes certain threading
issues started to occur. Concretely once I have created
a java class in a wizard from a template with some methods overriden
then in certain amount of cases
(usually at least once from 10 invocations) I've got
the java source file garbled. The overriden methods
were generated in the middle of the javadoc for constructor.
Originally I thought that my changes were the cause
of the problem but later I"ve found out that the same
errorneous behavior can be achieved
by putting a debugging System.out.println()
at certain place in PositionRef
without doing any other changes.
Still I was not convinced whether the openide.text
is the culprit or whether the problem is somewhere
else (e.g. in inproper synchronization of java metadata
updating in java module) but after I've made
the modifications to openide.text these problems
went away.
Document.render()
.
PositionRef.Manager
this is done *after* the read-lock on document is done.
Note: To avoid creation of many anonymous inner classes
there is just one DocumentRenderer
inner class
having several different constructors and opCode
to determine the purpose it was constructed for.
Although it works fine the code is not too nice to read.
We can either live with that or we could possibly in the future
require all the document implementations being used to extend
AbstractDocument
(e.g. all swing document implementations do so)
where there are readLock()
and readUnlock()
public methods.
CloneableEditorSupport.getLock()
.
Object.wait()
mechanism until loading
of the document is finished.
JavaEditor.processAnnotations()
).
There are still architectural issues to think about.
For example in the existing openide.text design
if someone calls openDocument()
and e.g. write-locks the returned document instance
and then starts to modify it
then it should also attach the change listener
to the CloneableEditorSupport
to be notified that the document being changed was closed
by another thread and that it is no longer
valid. In case the notification comes
the work done in the document should be abandoned
and likely redone in the new document.
Otherwise it may happen that the modifications
are done to the document no longer being valid.
The problem is that the notification
about closing of the document
can currently come anytime so theoretically
the document could be closed but more
importantly saved with just partial
changes (because of the notifyModify()
gets called during closing).
We could try to improve the situation
by running the closeDocument() under write-lock
on the document being closed. However to prevent
deadlocks we should first write-lock the document
obtained by previous call to getDocument()
and then use CES.getLock()
and do the document
closing.
Even with that it's still possible that the document
is closed between the moment when thread obtains the document
instance by openDocument()
and the moment
when thread write-locks the document for modification.
However to fix this case we could set a property
in the document once it closed e.g.
doc.putProperty("closed", Boolean.TRUE)
.
The property would only have to be checked
once right after the write-lock (or read-lock)
was obtained on the document
returned from openDocument()
.