All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.utils.Watchdog Maven / Gradle / Ivy

Go to download

A Minecraft Bedrock Edition server software implementation made in Java from scratch which supports all new features.

There is a newer version: 1.6.0.1-PN
Show newest version
package cn.nukkit.utils;

import cn.nukkit.Server;
import lombok.extern.log4j.Log4j2;

import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;

@Log4j2
public class Watchdog extends Thread {

    private final Server server;
    private final long time;
    public volatile boolean running;
    private boolean responding = true;
    private Thread forcedFinalizer;
    private boolean warnedAboutFinalizer;

    public Watchdog(Server server, long time) {
        this.server = server;
        this.time = time;
        this.running = true;
        this.setName("Watchdog");
        this.setDaemon(true);
    }

    public void kill() {
        running = false;
        interrupt();
    }
    
    private void checkFinalizer() {
        if (forcedFinalizer != null && forcedFinalizer.isAlive()) {
            log.fatal("--------- The finalizer thread didn't complete in time! ---------");
            log.fatal("This detection means that the finalizer thread may be stuck and");
            log.fatal("RAM memory might be leaking!");
            log.fatal(" - https://github.com/PowerNukkit/PowerNukkit/issues/new");
            log.fatal("---------------- ForcedFinalizer ----------------");
            dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(forcedFinalizer.getId(), Integer.MAX_VALUE));
            log.fatal("-------------------------------------------------");
            warnedAboutFinalizer = true;
        } else {
            if (warnedAboutFinalizer) {
                log.warn("The ForcedFinalizer has finished");
                warnedAboutFinalizer = false;
            }
            forcedFinalizer = new Thread(()-> {
                log.trace("Forcing finalization");
                System.runFinalization();
                log.trace("Forced finalization completed");
            });
            forcedFinalizer.setName("ForcedFinalizer");
            forcedFinalizer.setDaemon(true);
            forcedFinalizer.start();
        }
    }

    @Override
    public void run() {
        while (this.running) {
            checkFinalizer();
            long current = server.getNextTick();
            if (current != 0) {
                long diff = System.currentTimeMillis() - current;
                if (!responding && diff > time * 2) {
                    System.exit(1); // Kill the server if it gets stuck on shutdown
                }
                
                if (diff <= time) {
                    responding = true;
                } else if (responding) {
                    log.fatal("--------- Server stopped responding --------- (" + Math.round(diff / 1000d) + "s)");
                    log.fatal("Please report this to PowerNukkit:");
                    log.fatal(" - https://github.com/PowerNukkit/PowerNukkit/issues/new");
                    log.fatal("---------------- Main thread ----------------");

                    dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(this.server.getPrimaryThread().getId(), Integer.MAX_VALUE));

                    log.fatal("---------------- All threads ----------------");
                    ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
                    for (int i = 0; i < threads.length; i++) {
                        if (i != 0) log.fatal("------------------------------");
                        dumpThread(threads[i]);
                    }
                    log.fatal("---------------------------------------------");
                    responding = false;
                    this.server.forceShutdown();
                }
            }
            try {
                sleep(Math.max(time / 4, 1000));
            } catch (InterruptedException interruption) {
                server.getLogger().emergency("The Watchdog Thread has been interrupted and is no longer monitoring the server state");
                running = false;
                return;
            }
        }
        server.getLogger().warning("Watchdog was stopped");
    }

    private static void dumpThread(ThreadInfo thread) {
        if (thread == null) {
            log.fatal("Attempted to dump a null thread!");
            return;
        }
        log.fatal("Current Thread: " + thread.getThreadName());
        log.fatal("\tPID: " + thread.getThreadId() + " | Suspended: " + thread.isSuspended() + " | Native: " + thread.isInNative() + " | State: " + thread.getThreadState());
        // Monitors
        if (thread.getLockedMonitors().length != 0) {
            log.fatal("\tThread is waiting on monitor(s):");
            for (MonitorInfo monitor : thread.getLockedMonitors()) {
                log.fatal("\t\tLocked on:" + monitor.getLockedStackFrame());
            }
        }

        log.fatal("\tStack:");
        for (StackTraceElement stack : thread.getStackTrace()) {
            log.fatal("\t\t" + stack);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy