fileClosed vs projectClosed order

Hi, I understand the fact that sometimes the fileClosed event is not called but that projectClosed always is when the IDE shuts down. Is there an order that is guaranteed though if fileClosed is invoked? I'm running into a situation where one of my projects becomes disposed but I still have a pointer to it. I suspect that it was from one of my listeners that was getting notified from the system and contacting a stale project and so on. So, I tried to uninstall all of my listeners during project closing right away:

 public void projectClosed() {
    LOG.info("projectClosed " + project.getName());
     uninstallListeners();
    project = null;                     // killing this field
...
}

but I'm getting an exception from an editor/file closing (see enclosed below); it's clearly trying to access a null project field:

 LOG.info("editorFileClosedEvent "+ grammarFileName+" "+project.getName()); // LINE 282

It looks like these might be going on concurrently or that the fileClosed is happening after the project closing. Can someone comment on the expected order of operations? I need to dispose of everything during project closing but I can't do it until I know I won't get any more events that use my ProjectComponent fields, such as fileClosed.

Thanks!
Ter
PS I'm getting some amazingly cool stuff built!

--------------------
2014-04-27 19:17:35,664 [ 631455]   INFO -  ANTLR ANTLRv4PluginController - projectClosed testplugin <-------- closes before the fileClosed?
2014-04-27 19:17:35,677 [ 631468]  ERROR - .impl.MessageBusConnectionImpl - null
java.lang.NullPointerException
 at org.antlr.intellij.plugin.ANTLRv4PluginController.editorFileClosedEvent(ANTLRv4PluginController.java:282)
 at org.antlr.intellij.plugin.ANTLRv4PluginController$MyFileEditorManagerAdapter.fileClosed(ANTLRv4PluginController.java:586) <-- FILE CLOSED
 at sun.reflect.GeneratedMethodAccessor145.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:601)
 at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:120)
 at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:228)
 at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:234)
 at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:219)
 at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:216)
 at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:209)
 at com.intellij.util.messages.impl.MessageBusImpl.access$000(MessageBusImpl.java:43)
 at com.intellij.util.messages.impl.MessageBusImpl$1.invoke(MessageBusImpl.java:131)
 at com.sun.proxy.$Proxy70.fileClosed(Unknown Source)
 at com.intellij.openapi.fileEditor.impl.EditorWindow$2$2.run(EditorWindow.java:345)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl$14$1.run(FileEditorManagerImpl.java:919)
 at com.intellij.openapi.wm.impl.FocusManagerImpl.flushRequest(FocusManagerImpl.java:613)
 at com.intellij.openapi.wm.impl.FocusManagerImpl.flushNow(FocusManagerImpl.java:597)
 at com.intellij.openapi.wm.impl.FocusManagerImpl.flushIdleRequests(FocusManagerImpl.java:567)
 at com.intellij.openapi.wm.impl.FocusManagerImpl.access$200(FocusManagerImpl.java:55)
 at com.intellij.openapi.wm.impl.FocusManagerImpl$7.run(FocusManagerImpl.java:465)
 at com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(UIUtil.java:1971)
 at com.intellij.openapi.wm.impl.FocusManagerImpl.doWhenFocusSettlesDown(FocusManagerImpl.java:443)
 at com.intellij.openapi.wm.impl.FocusManagerImpl.doWhenFocusSettlesDown(FocusManagerImpl.java:439)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl$14.run(FileEditorManagerImpl.java:916)
 at com.intellij.openapi.util.BusyObject$Impl$Simple.execute(BusyObject.java:121)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.notifyPublisher(FileEditorManagerImpl.java:913)
 at com.intellij.openapi.fileEditor.impl.EditorWindow$2.run(EditorWindow.java:338)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.runChange(FileEditorManagerImpl.java:1525)
 at com.intellij.openapi.fileEditor.impl.EditorWindow.closeFile(EditorWindow.java:292)
 at com.intellij.openapi.fileEditor.impl.EditorsSplitters.closeFile(EditorsSplitters.java:624)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl$8.run(FileEditorManagerImpl.java:585)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.runChange(FileEditorManagerImpl.java:1525)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.closeFileImpl(FileEditorManagerImpl.java:583)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.access$600(FileEditorManagerImpl.java:97)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl$7.run(FileEditorManagerImpl.java:574)
 at com.intellij.openapi.command.impl.CommandProcessorImpl.executeCommand(CommandProcessorImpl.java:117)
 at com.intellij.openapi.command.impl.CommandProcessorImpl.executeCommand(CommandProcessorImpl.java:99)
 at com.intellij.openapi.command.impl.CommandProcessorImpl.executeCommand(CommandProcessorImpl.java:85)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.closeFile(FileEditorManagerImpl.java:572)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.closeFile(FileEditorManagerImpl.java:561)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.closeAllFiles(FileEditorManagerImpl.java:1773)
 at com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl.projectClosed(FileEditorManagerImpl.java:1350)
 at com.intellij.openapi.project.impl.ProjectImpl.projectClosed(ProjectImpl.java:431)
 at com.intellij.openapi.project.impl.ProjectImpl.access$300(ProjectImpl.java:76)
 at com.intellij.openapi.project.impl.ProjectImpl$MyProjectManagerListener.projectClosed(ProjectImpl.java:460)
 at com.intellij.openapi.project.impl.ProjectManagerImpl$2.projectClosed(ProjectManagerImpl.java:163)
 at com.intellij.openapi.project.impl.ProjectManagerImpl.fireProjectClosed(ProjectManagerImpl.java:1056)
 at com.intellij.openapi.project.impl.ProjectManagerImpl.access$1300(ProjectManagerImpl.java:82)
 at com.intellij.openapi.project.impl.ProjectManagerImpl$16.run(ProjectManagerImpl.java:955)
 at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:1013)
 at com.intellij.openapi.project.impl.ProjectManagerImpl.closeProject(ProjectManagerImpl.java:944)
 at com.intellij.openapi.application.impl.ApplicationImpl$6.run(ApplicationImpl.java:322)
 at com.intellij.openapi.command.impl.CommandProcessorImpl.executeCommand(CommandProcessorImpl.java:124)
 at com.intellij.openapi.command.impl.CommandProcessorImpl.executeCommand(CommandProcessorImpl.java:99)
 at com.intellij.openapi.command.impl.CommandProcessorImpl.executeCommand(CommandProcessorImpl.java:85)
 at com.intellij.openapi.application.impl.ApplicationImpl.disposeSelf(ApplicationImpl.java:318)
 at com.intellij.openapi.application.impl.ApplicationImpl.doExit(ApplicationImpl.java:846)
 at com.intellij.openapi.application.impl.ApplicationImpl.access$800(ApplicationImpl.java:85)
 at com.intellij.openapi.application.impl.ApplicationImpl$11.run(ApplicationImpl.java:825)
 at com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:835)
 at com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:797)
 at com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:792)
 at com.intellij.ide.actions.ExitAction.actionPerformed(ExitAction.java:31)
 at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher$3.performAction(IdeKeyEventDispatcher.java:564)
 at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processAction(IdeKeyEventDispatcher.java:611)
 at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.inInitState(IdeKeyEventDispatcher.java:463)
 at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.dispatchKeyEvent(IdeKeyEventDispatcher.java:206)
 at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:494)
 at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:348)
 at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
 at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
 at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
 at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
2014-04-27 19:17:35,679 [ 631470]  ERROR - .impl.MessageBusConnectionImpl - IntelliJ IDEA 12.1.6  Build #IC-129.1359
2014-04-27 19:17:35,679 [ 631470]  ERROR - .impl.MessageBusConnectionImpl - JDK: 1.7.0_21
2014-04-27 19:17:35,679 [ 631470]  ERROR - .impl.MessageBusConnectionImpl - VM: Java HotSpot(TM) 64-Bit Server VM
2014-04-27 19:17:35,679 [ 631470]  ERROR - .impl.MessageBusConnectionImpl - Vendor: Oracle Corporation
2014-04-27 19:17:35,680 [ 631471]  ERROR - .impl.MessageBusConnectionImpl - OS: Mac OS X
2014-04-27 19:17:35,680 [ 631471]  ERROR - .impl.MessageBusConnectionImpl - Last Action: Exit
2014-04-27 19:17:35,680 [ 631471]  ERROR - .impl.MessageBusConnectionImpl - Current Command: Exit
2014-04-27 19:17:35,766 [ 631557]   INFO -    #org.jetbrains.io.WebServer - web server stopped
2014-04-27 19:17:35,900 [ 631691]   INFO - ins.android.sdk.AndroidSdkData - DDMLib terminated
2014-04-27 19:17:35,900 [ 631691]   INFO - il.indexing.FileBasedIndexImpl - START INDEX SHUTDOWN
2014-04-27 19:17:35,921 [ 631712]   INFO - il.indexing.FileBasedIndexImpl - END INDEX SHUTDOWN
2014-04-27 19:17:35,921 [ 631712]   INFO - stubs.SerializationManagerImpl - START StubSerializationManager SHUTDOWN
2014-04-27 19:17:35,921 [ 631712]   INFO - stubs.SerializationManagerImpl - END StubSerializationManager SHUTDOWN
2014-04-27 19:17:35,922 [ 631713]   INFO - .history.utils.LocalHistoryLog - Purging local history...
2014-04-27 19:17:35,930 [ 631721]   INFO - .history.utils.LocalHistoryLog - Local history storage successfully closed.
2014-04-27 19:17:35,942 [ 631733]   WARN - api.vfs.impl.local.FileWatcher - Watcher terminated.
2014-04-27 19:17:36,011 [ 631802]   WARN - ution.process.OSProcessHandler - Cannot kill process tree. Trying to destroy process using Java API. Cmdline:
null
2014-04-27 19:17:36,011 [ 631802]   INFO - newvfs.persistent.PersistentFS - VFS dispose started
2014-04-27 19:17:36,019 [ 631810]   INFO - newvfs.persistent.PersistentFS - VFS dispose completed
2014-04-27 19:17:36,023 [ 631814]   INFO -        #com.intellij.idea.Main - ------------------------------------------------------ IDE SHUTDOWN ------------------------------------------------------

7 comments

Judging by the stack trace, there's no fixed order. One of projectClosed listeners files fileClosed, so it really depends on the order of listeners. Sorry.

To dispose something on project closing, you may just listen to projectClosed. You can also look at Disposer.register(project, yourDisposable) where yourDisposable will also be called on project closing.

0

Found an easy way. use a lock:

 public final Object shutdownLock = new Object();

 public void projectClosed() {
      LOG.info("projectClosed " + project.getName());
      synchronized ( shutdownLock ) {
           projectIsClosed = true;
          uninstallListeners();
          ...
}

then gate events

 synchronized ( shutdownLock ) {
      if ( !projectIsClosed ) editorFileClosedEvent(file);
 }


seems to work.
0

It doesn't look like an easy/intuitive way. Besides, fileClosed and projectClosed should both be fired on EDT, so synchronization shouldn't be of any help here.

0

Hmm... I don't do the synchronization to get an order of operations (though, it seems like the API could reasonably be expected to indicate an order). I'm just doing thread safety there. The real value is the Boolean that prevents fileClosed events after project closing has started. Does that make sense or the still inappropriate?

0

Do you have accesses from multiple threads here? I'm under impression you don't, so synchronization is not needed.

0

I'm not sure how many threads are running here and what can change in the future, so I'm just trying to avoid the same issue when both project closing and file closing are done at the same time.  We have no known order of operation and no known thread count, unless you are indicating that each of these is called only from the single EDT.

0

They should be called from EDT only.

0

Please sign in to leave a comment.