Feat: incompatible process

This commit is contained in:
Sheepit Renderfarm
2024-06-08 03:21:33 +00:00
committed by Laurent Clouet
parent b295467ca7
commit 83cd92b903
10 changed files with 163 additions and 7 deletions

View File

@@ -187,14 +187,24 @@ import okhttp3.HttpUrl;
Thread threadSender = new Thread(runnableSender); Thread threadSender = new Thread(runnableSender);
threadSender.start(); threadSender.start();
IncompatibleProcessChecker incompatibleProcessChecker = new IncompatibleProcessChecker(this);
Timer incompatibleProcessCheckerTimer = new Timer();
incompatibleProcessCheckerTimer.schedule(incompatibleProcessChecker, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1));
incompatibleProcessChecker.run(); // before the first request, check if it should be stopped
do { do {
while (this.running) { while (this.running) {
this.renderingJob = null; this.renderingJob = null;
synchronized (this) { synchronized (this) {
if (this.suspended) { if (this.suspended) {
this.gui.status("Client paused", true); if (incompatibleProcessChecker.isSuspendedDueToOtherProcess()) {
this.log.debug("Client paused"); this.gui.status("Client paused due to 'incompatible process' feature", true);
}
else {
this.gui.status("Client paused", true);
this.log.debug("Client paused");
}
} }
while (this.suspended && !this.shuttingdown) { while (this.suspended && !this.shuttingdown) {
wait(); wait();
@@ -215,8 +225,14 @@ import okhttp3.HttpUrl;
} }
this.sleep(wait); this.sleep(wait);
} }
this.gui.status("Requesting Job"); if (incompatibleProcessChecker.isRunningCompatibleProcess() == false) {
this.renderingJob = this.server.requestJob(); this.gui.status("Requesting Job");
this.renderingJob = this.server.requestJob();
}
else {
this.gui.status("Wait until compatible process is stopped");
this.sleep(30 * 1000);
}
} }
catch (SheepItExceptionNoRightToRender e) { catch (SheepItExceptionNoRightToRender e) {
this.gui.error("User does not have enough right to render project"); this.gui.error("User does not have enough right to render project");

View File

@@ -82,6 +82,7 @@ import lombok.Data;
private String hostname; private String hostname;
private String theme; private String theme;
private boolean disableLargeDownloads; private boolean disableLargeDownloads;
private String incompatibleProcess;
public Configuration(File cache_dir_, String login_, String password_) { public Configuration(File cache_dir_, String login_, String password_) {
this.configFilePath = null; this.configFilePath = null;
@@ -116,6 +117,7 @@ import lombok.Data;
this.UIType = null; this.UIType = null;
this.theme = null; this.theme = null;
this.disableLargeDownloads = false; this.disableLargeDownloads = false;
this.incompatibleProcess = null;
} }
/** /**
@@ -155,6 +157,7 @@ import lombok.Data;
c + "UIType: " + UIType + n + c + "UIType: " + UIType + n +
c + "hostname: " + hostname + n + c + "hostname: " + hostname + n +
c + "theme: " + theme + n + c + "theme: " + theme + n +
c + "incompatibleProcess: " + incompatibleProcess + n +
c + "disableLargeDownloads: " + disableLargeDownloads; c + "disableLargeDownloads: " + disableLargeDownloads;
} }

View File

@@ -44,6 +44,7 @@ public class Error {
RENDERER_KILLED_BY_USER(20), RENDERER_KILLED_BY_USER(20),
RENDERER_KILLED_BY_USER_OVER_TIME(23), RENDERER_KILLED_BY_USER_OVER_TIME(23),
RENDERER_KILLED_BY_SERVER(22), RENDERER_KILLED_BY_SERVER(22),
RENDERER_KILLED_BY_USER_INCOMPATIBLE_PROCESS(34),
RENDERER_MISSING_LIBRARIES(15), RENDERER_MISSING_LIBRARIES(15),
FAILED_TO_EXECUTE(16), FAILED_TO_EXECUTE(16),
OS_NOT_SUPPORTED(17), OS_NOT_SUPPORTED(17),
@@ -202,6 +203,8 @@ public class Error {
return "Render canceled because you've blocked the project."; return "Render canceled because you've blocked the project.";
case RENDERER_KILLED_BY_SERVER: case RENDERER_KILLED_BY_SERVER:
return "Render canceled because the project has been stopped by the server. Usually because the project will take too much time or it's been paused."; return "Render canceled because the project has been stopped by the server. Usually because the project will take too much time or it's been paused.";
case RENDERER_KILLED_BY_USER_INCOMPATIBLE_PROCESS:
return "Stopped rendering: The incompatible user-specified process is running.";
case SESSION_DISABLED: case SESSION_DISABLED:
return "The server has disabled your session. Your client may have generated a broken frame (GPU not compatible, not enough RAM/VRAM, etc)."; return "The server has disabled your session. Your client may have generated a broken frame (GPU not compatible, not enough RAM/VRAM, etc).";
case RENDERER_NOT_AVAILABLE: case RENDERER_NOT_AVAILABLE:

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2024 Laurent CLOUET
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sheepit.client;
import java.util.TimerTask;
import com.sheepit.client.os.OS;
import lombok.Getter;
import oshi.software.os.OSProcess;
public class IncompatibleProcessChecker extends TimerTask {
private final Client client;
@Getter
private boolean suspendedDueToOtherProcess;
public IncompatibleProcessChecker(Client client_) {
this.client = client_;
this.suspendedDueToOtherProcess = false;
}
@Override public void run() {
String search = this.client.getConfiguration().getIncompatibleProcess();
if (search == null || search.isEmpty()) { // to nothing
return;
}
search = search.toLowerCase();
if (isSearchProcessRunning(search)) {
if (this.client.getRenderingJob() != null && this.client.getRenderingJob().getProcessRender().getProcess() != null) {
this.client.getRenderingJob().incompatibleProcessBlock();
}
this.client.suspend();
this.client.getGui().status("Client paused due to 'incompatible process' feature", true);
this.suspendedDueToOtherProcess = true;
}
else {
if (this.client.isSuspended() && this.suspendedDueToOtherProcess) {
// restart the client since the other process has been shutdown
this.client.resume();
}
}
}
public boolean isRunningCompatibleProcess() {
String search = this.client.getConfiguration().getIncompatibleProcess();
if (search == null || search.isEmpty()) { // to nothing
return false;
}
return isSearchProcessRunning(search.toLowerCase());
}
private boolean isSearchProcessRunning(String search) {
for (OSProcess processInfo : OS.getOS().getProcesses()) {
String name = processInfo.getName();
if (name == null || name.isEmpty()) {
continue;
}
if (name.toLowerCase().contains(search)) {
this.client.getLog().debug("IncompatibleProcessChecker(" + search + ") found " + processInfo.getName());
return true;
}
}
return false;
}
}

View File

@@ -86,6 +86,7 @@ import java.util.regex.Pattern;
private boolean askForRendererKill; private boolean askForRendererKill;
private boolean userBlockJob; private boolean userBlockJob;
private boolean serverBlockJob; private boolean serverBlockJob;
private boolean incompatibleProcessKillJob;
private Gui gui; private Gui gui;
private Configuration configuration; private Configuration configuration;
private Log log; private Log log;
@@ -113,6 +114,7 @@ import java.util.regex.Pattern;
askForRendererKill = false; askForRendererKill = false;
userBlockJob = false; userBlockJob = false;
serverBlockJob = false; serverBlockJob = false;
incompatibleProcessKillJob = false;
log = log_; log = log_;
render = new RenderProcess(log_); render = new RenderProcess(log_);
blenderShortVersion = null; blenderShortVersion = null;
@@ -129,6 +131,15 @@ import java.util.regex.Pattern;
} }
} }
public void incompatibleProcessBlock() {
setAskForRendererKill(true);
setIncompatibleProcessKillJob(true);
RenderProcess process = getProcessRender();
if (process != null) {
process.kill();
}
}
public RenderProcess getProcessRender() { public RenderProcess getProcessRender() {
return render; return render;
} }
@@ -596,6 +607,9 @@ import java.util.regex.Pattern;
if (isUserBlockJob()) { if (isUserBlockJob()) {
return Error.Type.RENDERER_KILLED_BY_USER; return Error.Type.RENDERER_KILLED_BY_USER;
} }
if (isIncompatibleProcessKillJob()) {
return Error.Type.RENDERER_KILLED_BY_USER_INCOMPATIBLE_PROCESS;
}
return Error.Type.RENDERER_KILLED; return Error.Type.RENDERER_KILLED;
} }

View File

@@ -66,6 +66,7 @@ public class SettingsLoader {
THEME("theme"), THEME("theme"),
LOG_DIR("log-dir"), LOG_DIR("log-dir"),
DEBUG("debug"), DEBUG("debug"),
INCOMPATIBLE_PROCESS("incompatible-process"),
DISABLE_LARGE_DOWNLOADS("disable-large-downloads"); DISABLE_LARGE_DOWNLOADS("disable-large-downloads");
String propertyName; String propertyName;
@@ -110,6 +111,7 @@ public class SettingsLoader {
public static final String ARG_HOSTNAME = "-hostname"; public static final String ARG_HOSTNAME = "-hostname";
public static final String ARG_HEADLESS = "--headless"; public static final String ARG_HEADLESS = "--headless";
public static final String ARG_DISABLE_LARGE_DOWNLOADS = "--disable-large-downloads"; public static final String ARG_DISABLE_LARGE_DOWNLOADS = "--disable-large-downloads";
public static final String ARG_INCOMPATIBLE_PROCESS = "-incompatible-process";
private String path; private String path;
@@ -135,6 +137,7 @@ public class SettingsLoader {
private Option<String> theme; private Option<String> theme;
private Option<Integer> priority; private Option<Integer> priority;
private Option<String> disableLargeDownloads; private Option<String> disableLargeDownloads;
private Option<String> incompatibleProcess;
public SettingsLoader(String path_) { public SettingsLoader(String path_) {
if (path_ == null) { if (path_ == null) {
@@ -148,7 +151,7 @@ public class SettingsLoader {
public void setSettings(String path_, String login_, String password_, String proxy_, String hostname_, public void setSettings(String path_, String login_, String password_, String proxy_, String hostname_,
ComputeType computeMethod_, GPUDevice gpu_, Integer cores_, Long maxRam_, ComputeType computeMethod_, GPUDevice gpu_, Integer cores_, Long maxRam_,
Integer maxRenderTime_, String cacheDir_, String sharedZip_, Boolean autoSignIn_, Boolean useSysTray_, Integer maxRenderTime_, String cacheDir_, String sharedZip_, Boolean autoSignIn_, Boolean useSysTray_,
Boolean isHeadless, String ui_, String theme_, Integer priority_, Boolean disableLargeDownloads_, Boolean debug_) { Boolean isHeadless, String ui_, String theme_, Integer priority_, Boolean disableLargeDownloads_, Boolean debug_, String incompatibleProcess_) {
if (path_ == null) { if (path_ == null) {
path = OS.getOS().getDefaultConfigFilePath(); path = OS.getOS().getDefaultConfigFilePath();
} }
@@ -169,6 +172,7 @@ public class SettingsLoader {
theme = setValue(theme_, theme, ARG_THEME); theme = setValue(theme_, theme, ARG_THEME);
disableLargeDownloads = setValue(disableLargeDownloads_.toString(), disableLargeDownloads, ARG_DISABLE_LARGE_DOWNLOADS); disableLargeDownloads = setValue(disableLargeDownloads_.toString(), disableLargeDownloads, ARG_DISABLE_LARGE_DOWNLOADS);
debug = setValue(debug_.toString(), debug, ARG_VERBOSE); debug = setValue(debug_.toString(), debug, ARG_VERBOSE);
incompatibleProcess = setValue(incompatibleProcess_, incompatibleProcess, ARG_INCOMPATIBLE_PROCESS);
if (cores_ > 0) { if (cores_ > 0) {
cores = setValue(cores_.toString(), cores, ARG_CORES); cores = setValue(cores_.toString(), cores, ARG_CORES);
@@ -290,6 +294,7 @@ public class SettingsLoader {
setProperty(prop, configFileProp, PropertyNames.DISABLE_LARGE_DOWNLOADS, disableLargeDownloads); setProperty(prop, configFileProp, PropertyNames.DISABLE_LARGE_DOWNLOADS, disableLargeDownloads);
setProperty(prop, configFileProp, PropertyNames.LOG_DIR, logDir); setProperty(prop, configFileProp, PropertyNames.LOG_DIR, logDir);
setProperty(prop, configFileProp, PropertyNames.DEBUG, debug); setProperty(prop, configFileProp, PropertyNames.DEBUG, debug);
setProperty(prop, configFileProp, PropertyNames.INCOMPATIBLE_PROCESS, incompatibleProcess);
prop.store(output, null); prop.store(output, null);
} }
catch (IOException io) { catch (IOException io) {
@@ -395,6 +400,8 @@ public class SettingsLoader {
disableLargeDownloads = loadConfigOption(prop, PropertyNames.DISABLE_LARGE_DOWNLOADS, disableLargeDownloads, ARG_DISABLE_LARGE_DOWNLOADS); disableLargeDownloads = loadConfigOption(prop, PropertyNames.DISABLE_LARGE_DOWNLOADS, disableLargeDownloads, ARG_DISABLE_LARGE_DOWNLOADS);
logDir = loadConfigOption(prop, PropertyNames.LOG_DIR, logDir, ARG_LOG_DIRECTORY); logDir = loadConfigOption(prop, PropertyNames.LOG_DIR, logDir, ARG_LOG_DIRECTORY);
incompatibleProcess = loadConfigOption(prop, PropertyNames.INCOMPATIBLE_PROCESS, incompatibleProcess, "");
debug = loadConfigOption(prop, PropertyNames.DEBUG, debug, ARG_VERBOSE); debug = loadConfigOption(prop, PropertyNames.DEBUG, debug, ARG_VERBOSE);
@@ -470,6 +477,11 @@ public class SettingsLoader {
if (config.getPriority() == 19) { // 19 is default value if (config.getPriority() == 19) { // 19 is default value
config.setPriority(priority.getValue()); config.setPriority(priority.getValue());
} }
if (incompatibleProcess != null) {
config.setIncompatibleProcess(incompatibleProcess.getValue());
}
try { try {
if (config.getComputeMethod() == null && computeMethod == null) { if (config.getComputeMethod() == null && computeMethod == null) {
config.setComputeMethod(ComputeType.CPU); config.setComputeMethod(ComputeType.CPU);
@@ -574,6 +586,7 @@ public class SettingsLoader {
this.disableLargeDownloads = new Option<>(String.valueOf(defaultConfigValues.isDisableLargeDownloads()), ARG_DISABLE_LARGE_DOWNLOADS); this.disableLargeDownloads = new Option<>(String.valueOf(defaultConfigValues.isDisableLargeDownloads()), ARG_DISABLE_LARGE_DOWNLOADS);
this.logDir = null; this.logDir = null;
this.debug = null; this.debug = null;
this.incompatibleProcess = null;
} }
@Override public String toString() { @Override public String toString() {

View File

@@ -25,6 +25,7 @@ import java.util.Map;
import oshi.SystemInfo; import oshi.SystemInfo;
import oshi.hardware.CentralProcessor; import oshi.hardware.CentralProcessor;
import oshi.software.os.OSProcess;
import oshi.software.os.OperatingSystem; import oshi.software.os.OperatingSystem;
import oshi.hardware.HardwareAbstractionLayer; import oshi.hardware.HardwareAbstractionLayer;
import com.sheepit.client.hardware.cpu.CPU; import com.sheepit.client.hardware.cpu.CPU;
@@ -219,4 +220,8 @@ public abstract class OS {
file.mkdirs(); file.mkdirs();
return file.getAbsolutePath() + File.separator + "sheepit.conf"; return file.getAbsolutePath() + File.separator + "sheepit.conf";
} }
public List<OSProcess> getProcesses() {
return operatingSystem.getProcesses();
}
} }

View File

@@ -116,6 +116,9 @@ public class Worker {
@Option(name = SettingsLoader.ARG_HEADLESS, usage = "Mark your client manually as headless to block Eevee projects", required = false) private boolean headless = java.awt.GraphicsEnvironment.isHeadless(); @Option(name = SettingsLoader.ARG_HEADLESS, usage = "Mark your client manually as headless to block Eevee projects", required = false) private boolean headless = java.awt.GraphicsEnvironment.isHeadless();
@Option(name = SettingsLoader.ARG_DISABLE_LARGE_DOWNLOADS, usage = "Disable download of larger projects to preserve internet traffic", required = false) private boolean disableLargeDownloads = false; @Option(name = SettingsLoader.ARG_DISABLE_LARGE_DOWNLOADS, usage = "Disable download of larger projects to preserve internet traffic", required = false) private boolean disableLargeDownloads = false;
@Option(name = SettingsLoader.ARG_INCOMPATIBLE_PROCESS, usage = "Specify a process to stop the current render job and pause while the said process is running. For example, if we take Firefox the formatting is firefox.exe on Windows and firefox on Linux.", required = false) private String incompatibleProcess = null;
public static void main(String[] args) { public static void main(String[] args) {
if (OS.getOS() == null) { if (OS.getOS() == null) {
System.err.println(Error.humanString(Error.Type.OS_NOT_SUPPORTED)); System.err.println(Error.humanString(Error.Type.OS_NOT_SUPPORTED));
@@ -144,6 +147,8 @@ public class Worker {
config.setPrintLog(print_log); config.setPrintLog(print_log);
config.setPriority(priority); config.setPriority(priority);
config.setDetectGPUs(!no_gpu_detection); config.setDetectGPUs(!no_gpu_detection);
config.setIncompatibleProcess(incompatibleProcess);
if (sharedDownloadsDir != null) { if (sharedDownloadsDir != null) {
File dir = new File(sharedDownloadsDir); File dir = new File(sharedDownloadsDir);

View File

@@ -22,6 +22,8 @@ public enum SwingTooltips {
COMPUTER_NAME("What this machine will be displayed as on your Sheepit profile page. Only you and admins can see this."), COMPUTER_NAME("What this machine will be displayed as on your Sheepit profile page. Only you and admins can see this."),
INCOMPATIBLE_PROCESS("If your specified process starts or is running, SheepIt will stop the current render job and pause. For example, if we take Firefox the formatting is firefox.exe on Windows and firefox on Linux."),
MAX_TIME_PER_FRAME("How much time a frame should take at most. Sheepit will try to assign jobs to your machine that take less time to compute."), MAX_TIME_PER_FRAME("How much time a frame should take at most. Sheepit will try to assign jobs to your machine that take less time to compute."),
DISABLE_LARGE_DOWNLOADS("Limits the client to smaller projects <= 750 MB. Consider this option if you are on a metered connection"); DISABLE_LARGE_DOWNLOADS("Limits the client to smaller projects <= 750 MB. Consider this option if you are on a metered connection");

View File

@@ -91,6 +91,7 @@ public class Settings implements Activity {
private JTextField proxy; private JTextField proxy;
private JTextField hostname; private JTextField hostname;
private JCheckBox disableLargeDownloads; private JCheckBox disableLargeDownloads;
private JTextField incompatibleProcess;
private ButtonGroup themeOptionsGroup; private ButtonGroup themeOptionsGroup;
private JRadioButton lightMode; private JRadioButton lightMode;
@@ -442,7 +443,7 @@ public class Settings implements Activity {
parent.getContentPanel().add(compute_devices_panel, constraints); parent.getContentPanel().add(compute_devices_panel, constraints);
// other // other
CollapsibleJPanel advanced_panel = new CollapsibleJPanel(new GridLayout(6, 2), this); CollapsibleJPanel advanced_panel = new CollapsibleJPanel(new GridLayout(7, 2), this);
advanced_panel.setBorder(BorderFactory.createTitledBorder("Advanced options")); advanced_panel.setBorder(BorderFactory.createTitledBorder("Advanced options"));
JLabel useSysTrayLabel = new JLabel("Minimize to SysTray"); JLabel useSysTrayLabel = new JLabel("Minimize to SysTray");
@@ -484,6 +485,14 @@ public class Settings implements Activity {
advanced_panel.add(hostnameLabel); advanced_panel.add(hostnameLabel);
advanced_panel.add(hostname); advanced_panel.add(hostname);
JLabel incompatibleProcessLabel = new JLabel("Incompatible process:");
incompatibleProcessLabel.setToolTipText(SwingTooltips.INCOMPATIBLE_PROCESS.getText());
incompatibleProcess = new JTextField();
incompatibleProcess.setText(parent.getConfiguration().getIncompatibleProcess());
advanced_panel.add(incompatibleProcessLabel);
advanced_panel.add(incompatibleProcess);
JLabel renderTimeLabel = new JLabel("Max time per frame (in minute):"); JLabel renderTimeLabel = new JLabel("Max time per frame (in minute):");
renderTimeLabel.setToolTipText(SwingTooltips.MAX_TIME_PER_FRAME.getText()); renderTimeLabel.setToolTipText(SwingTooltips.MAX_TIME_PER_FRAME.getText());
int val = 0; int val = 0;
@@ -789,7 +798,7 @@ public class Settings implements Activity {
.setSettings(config.getConfigFilePath(), login.getText(), new String(password.getPassword()), proxyText, hostnameText, method, .setSettings(config.getConfigFilePath(), login.getText(), new String(password.getPassword()), proxyText, hostnameText, method,
selected_gpu, cpu_cores, max_ram, max_rendertime, getCachePath(config), getSharedPath(config), autoSignIn.isSelected(), selected_gpu, cpu_cores, max_ram, max_rendertime, getCachePath(config), getSharedPath(config), autoSignIn.isSelected(),
useSysTray.isSelected(), headlessCheckbox.isSelected(), GuiSwing.type, themeOptionsGroup.getSelection().getActionCommand(), useSysTray.isSelected(), headlessCheckbox.isSelected(), GuiSwing.type, themeOptionsGroup.getSelection().getActionCommand(),
config.getPriority(), disableLargeDownloads.isSelected(), config.isDebugLevel()); config.getPriority(), disableLargeDownloads.isSelected(), config.isDebugLevel(), incompatibleProcess.getText().trim());
// wait for successful authentication (to store the public key) // wait for successful authentication (to store the public key)
// or do we already have one? // or do we already have one?