Search in sources :

Example 1 with GlobalMangerProgressListener

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();
    }
}
Also used : GlobalMangerProgressListener(com.biglybt.core.global.GlobalMangerProgressListener) AllTrackers(com.biglybt.core.tracker.AllTrackersManager.AllTrackers) LogEvent(com.biglybt.core.logging.LogEvent) AtomicLong(java.util.concurrent.atomic.AtomicLong) AllTrackersManager(com.biglybt.core.tracker.AllTrackersManager)

Aggregations

GlobalMangerProgressListener (com.biglybt.core.global.GlobalMangerProgressListener)1 LogEvent (com.biglybt.core.logging.LogEvent)1 AllTrackersManager (com.biglybt.core.tracker.AllTrackersManager)1 AllTrackers (com.biglybt.core.tracker.AllTrackersManager.AllTrackers)1 AtomicLong (java.util.concurrent.atomic.AtomicLong)1