/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.openapi.util;

import com.intellij.openapi.diagnostic.Logger;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ShutDownTracker implements Runnable {
  private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.ShutDownTracker");
  private static ShutDownTracker ourInstance;
  private final List<Thread> myThreads = new ArrayList<Thread>();
  private final LinkedList<Thread> myShutdownThreads = new LinkedList<Thread>();
  private final LinkedList<Runnable> myShutdownTasks = new LinkedList<Runnable>();
  private volatile boolean myIsShutdownHookRunning = false;

  private ShutDownTracker() {
    //noinspection HardCodedStringLiteral
    Runtime.getRuntime().addShutdownHook(new Thread(this, "Shutdown tracker"));
  }

  public static synchronized ShutDownTracker getInstance() {
    if (ourInstance == null) {
      ourInstance = new ShutDownTracker();
    }
    return ourInstance;
  }

  public static boolean isShutdownHookRunning() {
    return getInstance().myIsShutdownHookRunning;
  }

  public void run() {
    myIsShutdownHookRunning = true;

    Thread[] threads = getStopperThreads();
    while (threads.length > 0) {
      Thread thread = threads[0];
      if (!thread.isAlive()) {
        if (isRegistered(thread)) {
          LOG.error("Thread '" + thread.getName() + "' did not unregister itself from ShutDownTracker.");
          unregisterStopperThread(thread);
        }
      }
      else {
        try {
          thread.join(100);
        }
        catch (InterruptedException e) {
        }
      }
      threads = getStopperThreads();
    }
    
    while (!myShutdownTasks.isEmpty()) {
      //  task can change myShutdownTasks
      final Runnable task = myShutdownTasks.removeLast();
      try {
        task.run();
      }
      catch (Throwable e) {
        LOG.error(e);
      }
    }
    
    
    for (Thread thread = myShutdownThreads.removeLast(); thread != null; thread = myShutdownThreads.removeLast()) {
      thread.start();
      try {
        thread.join();
      }
      catch (InterruptedException ignored) {
      }
    }
  }

  private synchronized boolean isRegistered(Thread thread) {
    return myThreads.contains(thread);
  }

  private synchronized Thread[] getStopperThreads() {
    return myThreads.toArray(new Thread[myThreads.size()]);
  }

  public synchronized void registerStopperThread(Thread thread) {
    myThreads.add(thread);
  }

  public synchronized void unregisterStopperThread(Thread thread) {
    myThreads.remove(thread);
  }

  public void registerShutdownThread(final Thread thread) {
    myShutdownThreads.addLast(thread);
  }

  public void registerShutdownThread(int index, final Thread thread) {
    myShutdownThreads.add(index, thread);
  }

  public void registerShutdownTask(Runnable task) {
    myShutdownTasks.addLast(task);
  }

  public void unregisterShutdownTask(Runnable task) {
    myShutdownTasks.remove(task);
  }
}

@@ -2506,18 +2506,51 @@
   %7D%0A
+%0A
     
-%0A  
+if (!myShutdownThreads.isEmpty()) %7B%0A
   
-%0A
     
@@ -2663,24 +2663,26 @@
 ()) %7B%0A      
+  
 thread.start
@@ -2683,38 +2683,42 @@
 .start();%0A      
+  
 try %7B%0A
+  
         thread.j
@@ -2722,34 +2722,38 @@
 d.join();%0A      
-%7D%0A
+  %7D%0A  
       catch (Int
@@ -2777,24 +2777,34 @@
  ignored) %7B%0A
+        %7D%0A
       %7D%0A    

fix NSEE