Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: replace usage of ThreadManagerSubsystem with reactor Scheduler #4799

Merged
merged 46 commits into from
Aug 27, 2021

Conversation

pollend
Copy link
Member

@pollend pollend commented Jun 27, 2021

This replaces the ThreadManagerSubsystem with Scheduler provided by rxjava. pretty straightforward since this subsystem is only ever used in the core engine and not to an extent that would make sense. the threads allocated are mostly unused and sit idle.

toTest:

  • verify that game previews are saved
  • verify that a screen shot could be taken with F12
  • game startup is dependent on some of this threading.

Contributes to #4798

@github-actions github-actions bot added the Type: Refactoring Request for or implementation of pure and automatic refactorings, e.g. renaming, to improve clarity label Jun 27, 2021
Comment on lines 36 to 47
public static Scheduler io() {
return AccessController.doPrivileged((PrivilegedAction<Scheduler>) Schedulers::io);
}

public static Scheduler newThread() {
return AccessController.doPrivileged((PrivilegedAction<Scheduler>) Schedulers::newThread);
}

public static Scheduler computation() {
return AccessController.doPrivileged((PrivilegedAction<Scheduler>) Schedulers::computation);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to have these in a privileged scope.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do they need to be in privileged scope? Let's document this here for the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a lot of thread actions are guarded by the sandbox.

@lgtm-com
Copy link

lgtm-com bot commented Jun 27, 2021

This pull request introduces 1 alert when merging a01066a into 614315e - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

@pollend pollend marked this pull request as draft June 27, 2021 04:17
@lgtm-com
Copy link

lgtm-com bot commented Jun 27, 2021

This pull request introduces 1 alert when merging c9b8f11 into 7409510 - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

@pollend pollend marked this pull request as ready for review June 27, 2021 21:19
@lgtm-com
Copy link

lgtm-com bot commented Jun 27, 2021

This pull request introduces 1 alert when merging eb44e11 into 7409510 - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

@lgtm-com
Copy link

lgtm-com bot commented Jun 27, 2021

This pull request introduces 1 alert when merging 11a257a into fbeed19 - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

@lgtm-com
Copy link

lgtm-com bot commented Jun 29, 2021

This pull request introduces 1 alert when merging 23b174e into fbeed19 - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

Copy link
Member

@skaldarnar skaldarnar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also have a look at the alert reported by LGTM

@@ -18,14 +23,33 @@
* </ul>
*
*/
@API
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did the GameThread become public API?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a separate API?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 36 to 47
public static Scheduler io() {
return AccessController.doPrivileged((PrivilegedAction<Scheduler>) Schedulers::io);
}

public static Scheduler newThread() {
return AccessController.doPrivileged((PrivilegedAction<Scheduler>) Schedulers::newThread);
}

public static Scheduler computation() {
return AccessController.doPrivileged((PrivilegedAction<Scheduler>) Schedulers::computation);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do they need to be in privileged scope? Let's document this here for the future.

Comment on lines 112 to 126
.add("io.reactivex.rxjava3.annotations")
.add("io.reactivex.rxjava3.core")
.add("io.reactivex.rxjava3.disposables")
.add("io.reactivex.rxjava3.exceptions")
.add("io.reactivex.rxjava3.flowables")
.add("io.reactivex.rxjava3.functions")
.add("io.reactivex.rxjava3.internal")
.add("io.reactivex.rxjava3.observables")
.add("io.reactivex.rxjava3.observers")
.add("io.reactivex.rxjava3.parallel")
.add("io.reactivex.rxjava3.plugins")
.add("io.reactivex.rxjava3.processors")
.add("io.reactivex.rxjava3.schedulers")
.add("io.reactivex.rxjava3.subjects")
.add("io.reactivex.rxjava3.subscribers")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you decide what to add here, and what is not added (assuming that there must be something we leave out, or otherwise we could have used a single entry io.reactivex.rxjava3).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This particular diff has been changed to replace the rxjava packages with reactor packages, but the same question still applies.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or otherwise we could have used a single entry io.reactivex.rxjava3

I took a look at the places that use this list (which is both in this repo and nui-reflect), and it seemed like it was all .equals without any wildcard or prefix matching.

@@ -153,16 +154,16 @@ public static void main(String[] args) {
engine.run(new StateHeadlessSetup());
} else if (loadLastGame) {
engine.initialize(); //initialize the managers first
engine.getFromEngineContext(ThreadManager.class).submitTask("loadGame", () -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like with ThreadManager we could give the tasks a name such that they are identifiable later, e.g., in logs. Is there a similar concept in RxJava?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there is a subscriber Context that is useful for things like this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#4839 added a GameScheduler.scheduleParallel method that takes a name which it uses in the same way as ThreadManager.submitTask did.

It doesn't address future use cases where we take advantage of Flux's model for pipelines, but it suffices for one-shot Runnables like this.

Copy link
Member Author

@pollend pollend Aug 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be fine for our use cases. i'm not too happy but it should be good for our use cases at this point. not really a ThreadMonitor though :?

….com:MovingBlocks/Terasology into refactor/remove-usage-ThreadManagerSubsystem
@lgtm-com
Copy link

lgtm-com bot commented Jul 4, 2021

This pull request introduces 1 alert when merging cc44241 into acb3181 - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

@lgtm-com
Copy link

lgtm-com bot commented Jul 4, 2021

This pull request introduces 1 alert when merging 7cf9a36 into acb3181 - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

@lgtm-com
Copy link

lgtm-com bot commented Jul 4, 2021

This pull request introduces 1 alert when merging 875b4d3 into d3d4a8c - view on LGTM.com

new alerts:

  • 1 for Dereferenced variable may be null

@pollend pollend changed the title refactor: replace usage of ThreadManagerSubsystem with RxJava Scheduler refactor: replace usage of ThreadManagerSubsystem with reactor Scheduler Jul 12, 2021
@keturn
Copy link
Member

keturn commented Aug 14, 2021

GameScheduler is marked @API. gameMain() provides access to scheduling on GameThread, which is not @API.

I brought this up in today's meeting, and it wasn't clear to any of us if/when modules need access to this.

skaldarnar's proposal was to leave this implemented, but at a permission level modules wouldn't be able to actually use, with a comment along the lines of "change this if we have a documented use case for it" or something like that.

(I don't think we can implement it exactly as proposed, because I don't think the @API system gives us per-method control, but we can make it package-private or something.)

@pollend
Copy link
Member Author

pollend commented Aug 15, 2021

GameScheduler is marked @API. gameMain() provides access to scheduling on GameThread, which is not @API.

I brought this up in today's meeting, and it wasn't clear to any of us if/when modules need access to this.

skaldarnar's proposal was to leave this implemented, but at a permission level modules wouldn't be able to actually use, with a comment along the lines of "change this if we have a documented use case for it" or something like that.

(I don't think we can implement it exactly as proposed, because I don't think the @API system gives us per-method control, but we can make it package-private or something.)

how do you propose letting a module schedule something back on the main thread?

@DarkWeird
Copy link
Contributor

Try to take screenshot:

17:48:07.881 [main] ERROR o.t.engine.core.TerasologyEngine - Uncaught exception, attempting clean game shutdown
java.lang.ExceptionInInitializerError: null
	at reactor.core.scheduler.Schedulers.newParallel(Schedulers.java:545)
	at reactor.core.scheduler.Schedulers.lambda$static$4(Schedulers.java:1135)
	at reactor.core.scheduler.Schedulers.cache(Schedulers.java:1168)
	at reactor.core.scheduler.Schedulers.parallel(Schedulers.java:238)
	at org.terasology.engine.core.GameScheduler.scheduleParallel(GameScheduler.java:69)
	at org.terasology.engine.rendering.opengl.ScreenGrabber.saveScreenshot(ScreenGrabber.java:112)
	at org.terasology.corerendering.rendering.dag.nodes.FinalPostProcessingNode.process(FinalPostProcessingNode.java:173)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
	at org.terasology.engine.rendering.world.WorldRendererImpl.render(WorldRendererImpl.java:350)
	at org.terasology.engine.core.modes.StateIngame.render(StateIngame.java:214)
	at org.terasology.engine.core.subsystem.lwjgl.LwjglGraphics.postUpdate(LwjglGraphics.java:76)
	at org.terasology.engine.core.TerasologyEngine.tick(TerasologyEngine.java:509)
	at org.terasology.engine.core.TerasologyEngine.mainLoop(TerasologyEngine.java:460)
	at org.terasology.engine.core.TerasologyEngine.runMain(TerasologyEngine.java:436)
	at org.terasology.engine.core.TerasologyEngine.run(TerasologyEngine.java:402)
	at org.terasology.engine.Terasology.call(Terasology.java:183)
	at org.terasology.engine.Terasology.call(Terasology.java:67)
	at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
	at picocli.CommandLine.access$1200(CommandLine.java:145)
	at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
	at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
	at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
	at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
	at picocli.CommandLine.execute(CommandLine.java:2058)
	at org.terasology.engine.Terasology.main(Terasology.java:115)
Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "modifyThread")
	at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
	at java.base/java.security.AccessController.checkPermission(AccessController.java:897)
	at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:322)
	at org.terasology.gestalt.module.sandbox.ModuleSecurityManager.checkPermission(ModuleSecurityManager.java:48)
	at java.base/java.util.concurrent.ThreadPoolExecutor.checkShutdownAccess(ThreadPoolExecutor.java:748)
	at java.base/java.util.concurrent.ThreadPoolExecutor.shutdownNow(ThreadPoolExecutor.java:1405)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor.shutdownNow(ScheduledThreadPoolExecutor.java:870)
	at java.base/java.util.concurrent.Executors$DelegatedExecutorService.shutdownNow(Executors.java:693)
	at reactor.core.scheduler.ParallelScheduler.<clinit>(ParallelScheduler.java:57)
	... 25 common frames omitted

Need to initialize schedulers eager. Or add warp this place with doPrivilegeAction
This is first call to scheduler and it fail because called from CoreRendering module

@keturn
Copy link
Member

keturn commented Aug 24, 2021

GameScheduler is marked @API. gameMain() provides access to scheduling on GameThread, which is not @API.

I brought this up in today's meeting, and it wasn't clear to any of us if/when modules need access to this.

Some discussion convinced me we do have a use case that requires this:

We want to allow modules to make OpenGL calls, so we must give them a way to run things on the thread that owns the OpenGL Context. (OpenGL works by pushing things on to a command queue, and you don't want to be mixing that up by doing it from multiple threads.)

While we don't currently have module code that makes use of GameThread for OpenGL this way, it's not much of a stretch for me to see that we might. Especially as we use Reactor to move more work off the main thread.

@DarkWeird
Copy link
Contributor

@keturn
OpenGL thread at my way currently.

CoreRendering is using opengl from modules.
It runs at main thread currently.

@keturn
Copy link
Member

keturn commented Aug 24, 2021

Try to take screenshot:

Oh, this is my fault! When I wrote the method that takes the task name, I didn't run it through the other method with the AccessController wrapping.

I'll try the eager initialization.

@DarkWeird
Copy link
Contributor

Try to take screenshot:

Oh, this is my fault! When I wrote the method that takes the task name, I didn't run it through the other method with the AccessController wrapping.

I'll try the eager initialization.

Eager works nice.
In graphics scheduler PR - parallel using for interval flux. For splashscreen render loop.

Privileges are only required for initialization; this way we don't need `doPrivileged` in the public method.
@lgtm-com
Copy link

lgtm-com bot commented Aug 27, 2021

This pull request introduces 1 alert when merging fd32176 into 66264a8 - view on LGTM.com

new alerts:

  • 1 for Useless null check

@keturn keturn force-pushed the refactor/remove-usage-ThreadManagerSubsystem branch from fd32176 to c80f299 Compare August 27, 2021 22:34
@lgtm-com
Copy link

lgtm-com bot commented Aug 27, 2021

This pull request introduces 1 alert when merging c80f299 into 66264a8 - view on LGTM.com

new alerts:

  • 1 for Useless null check

@keturn
Copy link
Member

keturn commented Aug 27, 2021

If you make changes to the suppression of alerts in a pull request, please note that theses changes will only be effective once that pull request is merged.

eh? that's weird, LGTM-bot.

Copy link
Member

@keturn keturn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I think the permission-modifying code is isolated enough and adequately commented now.

As for the matter of why we never had to add PermissionProperties before, I think it's a combination:

  1. The old ThreadManager seems to have been running code outside the security context, so it's possible things were sneaking by, but mostly
  2. There aren't that many things called from the module sandbox that require reading system properties.

I'm totally stumped on why wildcards don't work for PermissionProperties, but I am not worried enough about the maintenance cost of that list to put more importance on it for the time being.

@keturn keturn merged commit 80d0983 into develop Aug 27, 2021
@keturn keturn deleted the refactor/remove-usage-ThreadManagerSubsystem branch August 27, 2021 23:43
@keturn keturn added this to the v5.2.0 milestone Sep 5, 2021
@pollend
Copy link
Member Author

pollend commented Nov 5, 2021

Okay, I think the permission-modifying code is isolated enough and adequately commented now.

As for the matter of why we never had to add PermissionProperties before, I think it's a combination:

1. The old ThreadManager seems to have been running code outside the security context, so it's possible things were sneaking by, but mostly

2. There aren't that many things called from the module sandbox that require reading system properties.

I'm totally stumped on why wildcards don't work for PermissionProperties, but I am not worried enough about the maintenance cost of that list to put more importance on it for the time being.

I already tried wildcards :P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Topic: Concurrency Requests, issues, and changes relating to threading, concurrency, parallel execution, etc. Type: Refactoring Request for or implementation of pure and automatic refactorings, e.g. renaming, to improve clarity
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants