use of com.biglybt.core.global.GlobalMangerProgressListener in project BiglyBT by BiglySoftware.
the class CoreImpl method stopSupport.
void stopSupport(final boolean for_restart, final boolean apply_updates, CoreOperationTask.ProgressCallback callback) throws CoreException {
Logger.setClosing();
AEDiagnostics.flushPendingLogs();
boolean wait_and_return = false;
try {
this_mon.enter();
if (stopped) {
// ensure config is saved as there may be pending changes to persist and we've got here
// via a shutdown hook
COConfigurationManager.save();
wait_and_return = true;
} else {
stopped = true;
if (!started) {
Logger.log(new LogEvent(LOGID, "Core not started"));
if (AEDiagnostics.isDirty()) {
AEDiagnostics.markClean();
}
stopping_sem.releaseForever();
return;
}
}
} finally {
this_mon.exit();
}
if (wait_and_return) {
Logger.log(new LogEvent(LOGID, "Waiting for stop to complete"));
stopping_sem.reserve();
return;
}
int stall_mins = Math.max(2, COConfigurationManager.getIntParameter(ConfigKeys.StartupShutdown.ICFG_STOP_FORCE_TERMINATE_AFTER));
long stall_millis = stall_mins * 60 * 1000;
AtomicLong last_progress = new AtomicLong(SystemTime.getMonotonousTime());
SimpleTimer.addEvent("ShutFail", SystemTime.getOffsetTime(60 * 1000), new TimerEventPerformer() {
boolean die_die_die;
@Override
public void perform(TimerEvent event) {
if (System.getProperty(SystemProperties.SYSPROP_LOGGING_DISABLE_STOP_ON_SLOW_CLOSE, "0").equals("0")) {
Logger.setClosingTakingTooLong();
}
while (SystemTime.getMonotonousTime() - last_progress.get() < stall_millis) {
try {
Thread.sleep(5000);
} catch (Throwable e) {
}
}
AEDiagnostics.dumpThreads();
if (die_die_die) {
Debug.out("Shutdown blocked, force exiting");
stopping_sem.releaseForever();
if (for_restart) {
ClientRestarterFactory.create(CoreImpl.this).restart(false);
} else if (apply_updates) {
if (getPluginManager().getDefaultPluginInterface().getUpdateManager().getInstallers().length > 0) {
ClientRestarterFactory.create(CoreImpl.this).restart(true);
}
}
if (ca_shutdown_computer_after_stop) {
if (apply_updates) {
// best we can do here is wait a while for updates to be applied
try {
Thread.sleep(10 * 1000);
} catch (Throwable e) {
}
}
try {
PlatformManagerFactory.getPlatformManager().shutdown(PlatformManager.SD_SHUTDOWN);
} catch (Throwable e) {
Debug.out("PlatformManager: shutdown failed", e);
}
}
SESecurityManager.exitVM(0);
}
die_die_die = true;
SimpleTimer.addEvent("ShutFail", SystemTime.getOffsetTime(30 * 1000), this);
}
});
List sync_listeners = new ArrayList();
List async_listeners = new ArrayList();
Iterator it = lifecycle_listeners.iterator();
while (it.hasNext()) {
CoreLifecycleListener l = (CoreLifecycleListener) it.next();
if (l.syncInvokeRequired()) {
sync_listeners.add(l);
} else {
async_listeners.add(l);
}
}
int progress = 125;
callback.setSubTaskName(MessageText.getString("label.starting.closedown"));
callback.setProgress(progress);
try {
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "Invoking synchronous 'stopping' listeners"));
for (int i = 0; i < sync_listeners.size(); i++) {
try {
((CoreLifecycleListener) sync_listeners.get(i)).stopping(this);
} catch (Throwable e) {
Debug.printStackTrace(e);
}
last_progress.set(SystemTime.getMonotonousTime());
}
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "Invoking asynchronous 'stopping' listeners"));
// in case something hangs during listener notification (e.g. version check server is down
// and the instance manager tries to obtain external address) we limit overall dispatch
// time to 10 seconds
ListenerManager.dispatchWithTimeout(async_listeners, new ListenerManagerDispatcher() {
@Override
public void dispatch(Object listener, int type, Object value) {
((CoreLifecycleListener) listener).stopping(CoreImpl.this);
last_progress.set(SystemTime.getMonotonousTime());
}
}, 10 * 1000);
if (Logger.isEnabled()) {
Logger.log(new LogEvent(LOGID, "Waiting for quiescence pre gm stop"));
}
NonDaemonTaskRunner.waitUntilIdle();
last_progress.set(SystemTime.getMonotonousTime());
if (Logger.isEnabled()) {
Logger.log(new LogEvent(LOGID, "Stopping global manager"));
}
progress = 250;
callback.setSubTaskName(MessageText.getString("label.stopping.downloads"));
callback.setProgress(progress);
if (global_manager != null) {
int p_start = 250;
int p_end = 699;
global_manager.stopGlobalManager(new GlobalMangerProgressListener() {
@Override
public void reportPercent(int percent) {
callback.setProgress(p_start + ((p_end - p_start) * percent) / 100);
last_progress.set(SystemTime.getMonotonousTime());
}
@Override
public void reportCurrentTask(String currentTask) {
callback.setSubTaskName(currentTask);
last_progress.set(SystemTime.getMonotonousTime());
}
});
}
last_progress.set(SystemTime.getMonotonousTime());
AllTrackers at = AllTrackersManager.getAllTrackers();
progress = 750;
int wait_secs = COConfigurationManager.getIntParameter(ConfigKeys.Tracker.ICFG_TRACKER_CLIENT_CLOSEDOWN_TIMEOUT);
callback.setSubTaskName(MessageText.getString("label.waiting.tracker.updates", new String[] { String.valueOf(wait_secs) }));
callback.setProgress(progress);
int active_req = at.getActiveRequestCount();
AllTrackersManager.AnnounceStats announce_stats = at.getAnnounceStats();
int scheduled_req = announce_stats.getPrivateScheduledCount() + announce_stats.getPublicScheduledCount();
int total_req = active_req + scheduled_req;
if (total_req > 0) {
if (Logger.isEnabled()) {
Logger.log(new LogEvent(LOGID, "Waiting for tracker updates, " + active_req + "/" + scheduled_req + " outstanding"));
}
long at_start = SystemTime.getMonotonousTime();
int current_req = total_req;
int p_start = 750;
int p_end = 899;
if (wait_secs > 0) {
while (true) {
if (SystemTime.getMonotonousTime() - at_start > wait_secs * 1000) {
break;
}
try {
Thread.sleep(500);
} catch (Throwable e) {
}
int latest_active = at.getActiveRequestCount();
announce_stats = at.getAnnounceStats();
int latest_scheduled = announce_stats.getPrivateScheduledCount() + announce_stats.getPublicScheduledCount();
int latest_req = latest_active + latest_scheduled;
if (latest_req == 0) {
break;
}
if (latest_req < current_req) {
current_req = latest_req;
int percent = ((total_req - current_req) * 100) / total_req;
callback.setProgress(p_start + ((p_end - p_start) * percent) / 100);
last_progress.set(SystemTime.getMonotonousTime());
}
}
}
}
if (Logger.isEnabled()) {
Logger.log(new LogEvent(LOGID, "Invoking synchronous 'stopped' listeners"));
}
progress = 900;
callback.setSubTaskName(MessageText.getString("label.finalising.closedown"));
callback.setProgress(progress);
for (int i = 0; i < sync_listeners.size(); i++) {
try {
((CoreLifecycleListener) sync_listeners.get(i)).stopped(this);
} catch (Throwable e) {
Debug.printStackTrace(e);
}
last_progress.set(SystemTime.getMonotonousTime());
}
if (Logger.isEnabled()) {
Logger.log(new LogEvent(LOGID, "Invoking asynchronous 'stopped' listeners"));
}
ListenerManager.dispatchWithTimeout(async_listeners, new ListenerManagerDispatcher() {
@Override
public void dispatch(Object listener, int type, Object value) {
((CoreLifecycleListener) listener).stopped(CoreImpl.this);
last_progress.set(SystemTime.getMonotonousTime());
}
}, 10 * 1000);
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "Waiting for quiescence post gm stop"));
NonDaemonTaskRunner.waitUntilIdle();
last_progress.set(SystemTime.getMonotonousTime());
// shut down diags - this marks the shutdown as tidy and saves the config
AEDiagnostics.markClean();
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "Stop operation completes"));
if (apply_updates && getPluginManager().getDefaultPluginInterface().getUpdateManager().getInstallers().length > 0) {
ClientRestarterFactory.create(this).restart(true);
}
if (System.getProperty("skip.shutdown.nondeamon.check", "0").equals("1")) {
return;
}
try {
Class c = Class.forName("sun.awt.AWTAutoShutdown");
if (c != null) {
c.getMethod("notifyToolkitThreadFree", new Class[] {}).invoke(null, new Object[] {});
}
} catch (Throwable t) {
}
if (ca_shutdown_computer_after_stop) {
if (apply_updates) {
// best we can do here is wait a while for updates to be applied
try {
Thread.sleep(10 * 1000);
} catch (Throwable e) {
}
}
try {
PlatformManagerFactory.getPlatformManager().shutdown(PlatformManager.SD_SHUTDOWN);
} catch (Throwable e) {
Debug.out("PlatformManager: shutdown failed", e);
}
}
try {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
while (tg.getParent() != null) {
tg = tg.getParent();
}
Thread[] threads = new Thread[tg.activeCount() + 1024];
tg.enumerate(threads, true);
boolean bad_found = false;
for (int i = 0; i < threads.length; i++) {
Thread t = threads[i];
if (t != null && t.isAlive() && t != Thread.currentThread() && !t.isDaemon() && !AEThread2.isOurThread(t)) {
bad_found = true;
break;
}
}
if (bad_found) {
new AEThread2("VMKiller", true) {
@Override
public void run() {
try {
int loops = 0;
while (true) {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
Thread[] threads = new Thread[tg.activeCount() + 1024];
tg.enumerate(threads, true);
List<String> bad = new ArrayList<>();
String bad_found = "";
for (int i = 0; i < threads.length; i++) {
Thread t = threads[i];
if (t != null && t.isAlive() && !t.isDaemon() && !AEThread2.isOurThread(t)) {
String details = t.getName();
bad.add(details);
StackTraceElement[] trace = t.getStackTrace();
if (trace.length > 0) {
details += "[";
for (int j = 0; j < trace.length; j++) {
details += (j == 0 ? "" : ",") + trace[j];
}
details += "]";
}
bad_found += (bad_found.length() == 0 ? "" : ", ") + details;
}
}
if (bad.size() == 1 && bad.get(0).equals("Launcher::bootstrap")) {
Debug.outNoStack("Only non-daemon bootstrap thread remaining, exiting...");
SESecurityManager.exitVM(0);
break;
}
if (loops == 10) {
Debug.out("Non-daemon thread(s) found: '" + bad_found + "' - force closing VM");
SESecurityManager.exitVM(0);
break;
}
Thread.sleep(1 * 1000);
loops++;
}
} catch (Throwable e) {
Debug.out(e);
}
}
}.start();
}
} catch (Throwable e) {
}
} finally {
stopping_sem.releaseForever();
}
}
Aggregations