Skip to content

Commit

Permalink
Merge pull request #4799 from MovingBlocks/refactor/remove-usage-Thre…
Browse files Browse the repository at this point in the history
…adManagerSubsystem
  • Loading branch information
keturn authored Aug 27, 2021
2 parents 66264a8 + c80f299 commit 80d0983
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 123 deletions.
1 change: 1 addition & 0 deletions .idea/dictionaries/kevint.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion engine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ dependencies {
implementation "org.lwjgl:lwjgl-openal"
api "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"


api group: 'io.projectreactor', name: 'reactor-core', version: '3.4.7'
api group: 'org.joml', name: 'joml', version: '1.10.0'
api group: 'org.terasology.joml-ext', name: 'joml-geometry', version: '0.1.0'

Expand Down
79 changes: 79 additions & 0 deletions engine/src/main/java/org/terasology/engine/core/GameScheduler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0

package org.terasology.engine.core;

import org.terasology.engine.monitoring.ThreadActivity;
import org.terasology.engine.monitoring.ThreadMonitor;
import org.terasology.gestalt.module.sandbox.API;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;

import java.security.AccessController;
import java.security.PrivilegedAction;

/** Schedulers to asynchronously run tasks on other threads. */
@API
public final class GameScheduler {

private static final Scheduler MAIN;

static {
MAIN = Schedulers.fromExecutor(GameThread::asynch);

// Calling Reactor scheduler once to make sure it's initialized.
// doPriviledged in case this class isn't initialized before the security policy is installed.
AccessController.doPrivileged((PrivilegedAction<Scheduler>) Schedulers::parallel);
}

private GameScheduler() {
}

/**
* A Scheduler to run tasks on the main thread.
* <p>
* <b>⚠</b> Use this only when necessary, as anything executed on the main thread will delay the core game loop.
*/
public static Scheduler gameMain() {
return MAIN;
}

/**
* A Scheduler to run tasks off the main thread.
* <p>
* You can use this {@link Scheduler} with a
* <ul>
* <li>{@link Runnable}, to run a function with no return value.
* <li>{@link Mono}, to run an operation one time, providing a future result.
* <li>{@link Flux}, to asynchronously generate a stream of events over time.
* </ul>
* <p>
* You can expect this to always return the <em>same</em> scheduler; it does not create a new scheduler instance or thread
* on every call.
*
* @return (singleton)
* @see <a href="https://projectreactor.io/docs/core/release/reference/#core-features">Reactor Core Features</a>
*/
public static Scheduler parallel() {
return Schedulers.parallel();
}

/**
* Run a task asynchronously, named for monitoring.
* <p>
* The task will be run on the {@link #parallel()} scheduler.
*/
@SuppressWarnings("UnusedReturnValue")
public static Disposable scheduleParallel(String name, Runnable task) {
Mono<?> mono = Mono.using(
() -> ThreadMonitor.startThreadActivity(name),
activity -> Mono.fromRunnable(task),
ThreadActivity::close
)
.subscribeOn(Schedulers.parallel());
return mono.subscribe();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.terasology.engine.core.subsystem.common.NetworkSubsystem;
import org.terasology.engine.core.subsystem.common.PhysicsSubsystem;
import org.terasology.engine.core.subsystem.common.TelemetrySubSystem;
import org.terasology.engine.core.subsystem.common.ThreadManagerSubsystem;
import org.terasology.engine.core.subsystem.common.TimeSubsystem;
import org.terasology.engine.core.subsystem.common.WorldGenerationSubsystem;
import org.terasology.engine.entitySystem.prefab.Prefab;
Expand Down Expand Up @@ -165,7 +164,6 @@ public TerasologyEngine(TimeSubsystem timeSubsystem, Collection<EngineSubsystem>
this.allSubsystems.add(new ConfigurationSubsystem());
this.allSubsystems.add(timeSubsystem);
this.allSubsystems.addAll(subsystems);
this.allSubsystems.add(new ThreadManagerSubsystem());
this.allSubsystems.add(new MonitoringSubsystem());
this.allSubsystems.add(new PhysicsSubsystem());
this.allSubsystems.add(new CommandSubsystem());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public void dispose(boolean shuttingDown) {
assetTypeManager.getAssetType(Prefab.class).ifPresent(AssetType::disposeAll);

boolean save = networkSystem.getMode().isAuthority();
if (save) {
if (save && storageManager != null) {
storageManager.waitForCompletionOfPreviousSaveAndStartSaving();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ public final class ExternalApiWhitelist {
.add("org.lwjgl")
.add("org.terasology.jnlua")
.add("org.joml")
.add("reactor.core")
.add("reactor.core.publisher")
.add("reactor.core.scheduler")
.add("reactor.util")
.add("reactor.util.annotation")
.add("reactor.util.concurrent")
.add("reactor.util.context")
.add("reactor.util.function")
.add("reactor.util.retry")
.add("reactor.adapter")
.add("java.time")
.build();

public static final Set<Class<?>> CLASSES = new ImmutableSet.Builder<Class<?>>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.terasology.gestalt.module.sandbox.ModuleSecurityPolicy;
import org.terasology.gestalt.module.sandbox.PermissionProvider;
import org.terasology.gestalt.module.sandbox.PermissionProviderFactory;
import org.terasology.gestalt.module.sandbox.PermissionSet;
import org.terasology.gestalt.module.sandbox.StandardPermissionProviderFactory;
import org.terasology.gestalt.module.sandbox.WarnOnlyProviderFactory;
import org.terasology.gestalt.naming.Name;
Expand All @@ -49,6 +50,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.PropertyPermission;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -98,7 +100,7 @@ public ModuleManager(Config config, List<Class<?>> classesOnClasspathsToAddToEng

protected static boolean isLoadingClasspathModules() {
return Boolean.getBoolean(LOAD_CLASSPATH_MODULES_PROPERTY);
};
}

/** Create a ModuleFactory configured for Terasology modules. */
private static ModuleFactory newModuleFactory(ModuleMetadataJsonAdapter metadataReader) {
Expand Down Expand Up @@ -147,8 +149,8 @@ private void loadModulesFromApplicationPath(PathManager pathManager) {
}

private void loadModulesFromClassPath() {
ClasspathCompromisingModuleFactory moduleFactory = (ClasspathCompromisingModuleFactory) this.moduleFactory;
for (String metadataName : moduleFactory.getModuleMetadataLoaderMap().keySet()) {
ClasspathCompromisingModuleFactory ccModuleFactory = (ClasspathCompromisingModuleFactory) this.moduleFactory;
for (String metadataName : ccModuleFactory.getModuleMetadataLoaderMap().keySet()) {
Enumeration<URL> urls;
try {
urls = ClassLoader.getSystemResources(metadataName);
Expand All @@ -158,10 +160,10 @@ private void loadModulesFromClassPath() {
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
logger.debug("Probably a module in U:{}", url);
Path path = moduleFactory.canonicalModuleLocation(metadataName, url);
Path path = ccModuleFactory.canonicalModuleLocation(metadataName, url);
Module module;
try {
module = moduleFactory.createModule(path.toFile());
module = ccModuleFactory.createModule(path.toFile());
} catch (IOException e) {
logger.warn("Failed to create module from {}", path, e);
continue;
Expand Down Expand Up @@ -241,16 +243,29 @@ private static ModuleMetadataJsonAdapter newMetadataReader() {
}

private void setupSandbox() {
ExternalApiWhitelist.CLASSES.stream().forEach(clazz ->
permissionProviderFactory.getBasePermissionSet().addAPIClass(clazz));
ExternalApiWhitelist.PACKAGES.stream().forEach(packagee ->
permissionProviderFactory.getBasePermissionSet().addAPIPackage(packagee));
PermissionSet permissionSet = permissionProviderFactory.getBasePermissionSet();
ExternalApiWhitelist.CLASSES.forEach(permissionSet::addAPIClass);
ExternalApiWhitelist.PACKAGES.forEach(permissionSet::addAPIPackage);

APIScanner apiScanner = new APIScanner(permissionProviderFactory);
registry.stream().map(Module::getModuleManifest).forEach(apiScanner::scan);

permissionProviderFactory.getBasePermissionSet().grantPermission("com.google.gson", ReflectPermission.class);
permissionProviderFactory.getBasePermissionSet().grantPermission("com.google.gson.internal", ReflectPermission.class);
permissionSet.grantPermission("com.google.gson", ReflectPermission.class);
permissionSet.grantPermission("com.google.gson.internal", ReflectPermission.class);

//noinspection ConstantConditions - this reference is to help find this if this method gets separated from the reactor dependency
if (reactor.core.scheduler.Scheduler.class != null) { //lgtm [java/useless-null-check]
// In theory, PropertyPermission has wildcard matching and "reactor.*" should be sufficient to grant read access to all
// reactor configuration properties.
permissionSet.grantPermission(new PropertyPermission("reactor.*", "read"));
// In practice, the permission checks fail unless these are each named explicitly.
permissionSet.grantPermission(new PropertyPermission("reactor.bufferSize.x", "read"));
permissionSet.grantPermission(new PropertyPermission("reactor.bufferSize.small", "read"));
permissionSet.grantPermission(new PropertyPermission("reactor.trace.operatorStacktrace", "read"));
permissionSet.grantPermission(new PropertyPermission("reactor.schedulers.defaultPoolSize", "read"));
permissionSet.grantPermission(new PropertyPermission("reactor.schedulers.defaultBoundedElasticSize", "read"));
permissionSet.grantPermission(new PropertyPermission("reactor.schedulers.defaultBoundedElasticQueueSize", "read"));
}

Policy.setPolicy(new ModuleSecurityPolicy());
System.setSecurityManager(new ModuleSecurityManager());
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

final class RunningThreadsMode extends MetricsMode {

RunningThreadsMode() {
RunningThreadsMode() {
super("\n- Running Threads -");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import org.terasology.engine.config.Config;
import org.terasology.engine.config.RenderingConfig;
import org.terasology.engine.context.Context;
import org.terasology.engine.core.GameScheduler;
import org.terasology.engine.core.PathManager;
import org.terasology.engine.core.subsystem.common.ThreadManager;
import org.terasology.engine.persistence.internal.GamePreviewImageProvider;
import org.terasology.engine.registry.CoreRegistry;
import org.terasology.engine.rendering.opengl.fbms.DisplayResolutionDependentFbo;
Expand All @@ -21,6 +21,8 @@
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.SimpleDateFormat;
import java.util.Date;

Expand All @@ -33,7 +35,6 @@ public class ScreenGrabber {
private static final String SCREENSHOT_FILENAME_PATTERN = "Terasology-%s-%dx%d.%s";

private RenderingConfig renderingConfig;
private ThreadManager threadManager;
private float currentExposure;
private boolean isTakingScreenshot;
private DisplayResolutionDependentFbo displayResolutionDependentFBOs;
Expand All @@ -44,7 +45,6 @@ public class ScreenGrabber {
* @param context
*/
public ScreenGrabber(Context context) {
threadManager = CoreRegistry.get(ThreadManager.class);
renderingConfig = context.get(Config.class).getRendering();
}

Expand Down Expand Up @@ -109,7 +109,7 @@ public void saveScreenshot() {
task = () -> saveScreenshotTask(buffer, width, height);
}

threadManager.submitTask("Write screenshot", task);
GameScheduler.scheduleParallel("Write screenshot", task);
isTakingScreenshot = false;
}

Expand All @@ -136,12 +136,16 @@ private void saveGamePreviewTask(ByteBuffer buffer, int width, int height) {
}

private void writeImageToFile(BufferedImage image, Path path, String format) {
try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(path))) {
ImageIO.write(image, format, out);
logger.info("Screenshot saved to {}! ", path);
} catch (IOException e) {
logger.warn("Failed to save screenshot!", e);
}
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(path))) {
ImageIO.write(image, format, out);
logger.info("Screenshot saved to {}! ", path);
} catch (IOException e) {
logger.warn("Failed to save screenshot!", e);
}
return null;
});

}

/**
Expand Down
Loading

0 comments on commit 80d0983

Please sign in to comment.