Skip to content

Commit 1b403b2

Browse files
committed
oh, theres ThreadUtil
1 parent 162f1ad commit 1b403b2

File tree

8 files changed

+129
-84
lines changed

8 files changed

+129
-84
lines changed

source/funkin/backend/scripting/MultiThreadedScript.hx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package funkin.backend.scripting;
22

33
import hscript.IHScriptCustomBehaviour;
4-
import funkin.backend.utils.EngineUtil;
54

65
class MultiThreadedScript implements IFlxDestroyable implements IHScriptCustomBehaviour {
76
/**
@@ -57,7 +56,7 @@ class MultiThreadedScript implements IFlxDestroyable implements IHScriptCustomBe
5756

5857
public function call(func:String, args:Array<Dynamic>) {
5958
#if ALLOW_MULTITHREADING
60-
EngineUtil.execAsync(() -> {
59+
funkin.backend.utils.ThreadUtil.execAsync(() -> {
6160
callEnded = false;
6261
returnValue = script.call(func, args);
6362
callEnded = true;

source/funkin/backend/scripting/Script.hx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
102102
#if sys "ZipUtil" => funkin.backend.utils.ZipUtil, #end
103103
"MarkdownUtil" => funkin.backend.utils.MarkdownUtil,
104104
"EngineUtil" => funkin.backend.utils.EngineUtil,
105+
"ThreadUtil" => funkin.backend.utils.ThreadUtil,
105106
"MemoryUtil" => funkin.backend.utils.MemoryUtil,
106107
"BitmapUtil" => funkin.backend.utils.BitmapUtil,
107108

source/funkin/backend/system/Main.hx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import funkin.backend.assets.ModsFolder;
1313
import funkin.backend.system.framerate.Framerate;
1414
import funkin.backend.system.framerate.SystemInfo;
1515
import funkin.backend.system.modules.*;
16-
import funkin.backend.utils.EngineUtil;
16+
import funkin.backend.utils.ThreadUtil;
1717
import funkin.editors.SaveWarning;
1818
import funkin.options.PlayerSettings;
1919
import openfl.Assets;
@@ -57,13 +57,6 @@ class Main extends Sprite
5757

5858
// You can pretty much ignore everything from here on - your code should go in your states.
5959

60-
#if ALLOW_MULTITHREADING
61-
// DEPRECATED
62-
@:dox(hide) public static var gameThreads(get, set):Array<sys.thread.Thread>;
63-
static function get_gameThreads() return EngineUtil.gameThreads;
64-
static function set_gameThreads(v) return EngineUtil.gameThreads = v;
65-
#end
66-
6760
public static function preInit() {
6861
funkin.backend.utils.NativeAPI.registerAsDPICompatible();
6962
funkin.backend.system.CommandLineHandler.parseCommandLine(Sys.args());
@@ -100,7 +93,7 @@ class Main extends Sprite
10093
public static var startedFromSource:Bool = #if TEST_BUILD true #else false #end;
10194

10295
// DEPRECATED
103-
@:dox(hide) public static function execAsync(func:Void->Void) EngineUtil.execAsync(func);
96+
@:dox(hide) public static function execAsync(func:Void->Void) ThreadUtil.execAsync(func);
10497

10598
private static function getTimer():Int {
10699
return time = Lib.getTimer();

source/funkin/backend/system/updating/UpdateUtil.hx

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,30 @@ package funkin.backend.system.updating;
22

33
import funkin.backend.system.github.GitHub;
44
import funkin.backend.system.github.GitHubRelease;
5+
#if ALLOW_MULTITHREADING
6+
import funkin.backend.utils.ThreadUtil;
7+
#end
58

69
import lime.app.Application;
710

8-
import sys.thread.Mutex;
9-
import sys.thread.Thread;
1011
import sys.FileSystem;
1112

1213
import haxe.io.Path;
1314

15+
#if (target.threaded)
16+
import sys.thread.Thread;
17+
import sys.thread.Mutex;
18+
#end
19+
1420
using funkin.backend.system.github.GitHub;
1521

1622
class UpdateUtil {
1723
public static var lastUpdateCheck:Null<UpdateCheckCallback>;
1824

25+
#if (target.threaded)
1926
private static var __waitCallbacks:Array<UpdateCheckCallback->Void>;
2027
private static var __mutex:Mutex;
28+
#end
2129

2230
public static function init() {
2331
// deletes old bak file if it exists
@@ -26,26 +34,34 @@ class UpdateUtil {
2634
if (FileSystem.exists(bakPath)) FileSystem.deleteFile(bakPath);
2735
#end
2836

37+
#if (target.threaded)
2938
__waitCallbacks = [];
3039
__mutex = new Mutex();
31-
Thread.create(checkForUpdates.bind(true, false));
40+
41+
#if ALLOW_MULTITHREADING ThreadUtil.execAsync #else Thread.create #end(checkForUpdates.bind(true, false));
42+
#end
3243
}
3344

3445
public static function waitForUpdates(force = false, callback:UpdateCheckCallback->Void, lazy = false) {
46+
#if (target.threaded)
3547
if (__mutex.tryAcquire()) {
3648
__mutex.release();
3749
if (__shouldCheck(lazy) || force) {
3850
__waitCallbacks.push(callback);
39-
Thread.create(checkForUpdates.bind(force, false));
51+
#if ALLOW_MULTITHREADING ThreadUtil.execAsync #else Thread.create #end(checkForUpdates.bind(force, false));
4052
}
4153
else
4254
callback(lastUpdateCheck);
4355
}
4456
else
4557
__waitCallbacks.push(callback);
58+
#else
59+
callback(checkForUpdates(true, false));
60+
#end
4661
}
4762

4863
public static function checkForUpdates(force = false, lazy = false):UpdateCheckCallback {
64+
#if (target.threaded)
4965
var wasAcquired = !__mutex.tryAcquire();
5066
if (wasAcquired) __mutex.acquire();
5167

@@ -60,8 +76,19 @@ class UpdateUtil {
6076
FlxG.signals.preUpdate.addOnce(__callWaitCallbacks);
6177

6278
return lastUpdateCheck;
79+
#else
80+
if (!__shouldCheck(lazy)) return lastUpdateCheck;
81+
return lastUpdateCheck = __checkForUpdates();
82+
#end
6383
}
6484

85+
#if (target.threaded)
86+
static function __callWaitCallbacks() {
87+
for (callback in __waitCallbacks) callback(lastUpdateCheck);
88+
__waitCallbacks.resize(0);
89+
}
90+
#end
91+
6592
static function __checkForUpdates():UpdateCheckCallback {
6693
var curTag = 'v' + (Flags.VERSION == null ? Application.current.meta.get('version') : Flags.VERSION), error = false;
6794
var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, (e) -> {
@@ -82,11 +109,6 @@ class UpdateUtil {
82109
static function __shouldCheck(lazy:Bool):Bool
83110
return lastUpdateCheck == null || !lazy && (!lastUpdateCheck.newUpdate || Date.now().getTime() - lastUpdateCheck.date.getTime() > 1800000);
84111

85-
static function __callWaitCallbacks() {
86-
for (callback in __waitCallbacks) callback(lastUpdateCheck);
87-
__waitCallbacks.resize(0);
88-
}
89-
90112
static function __doReleaseFiltering(releases:Array<GitHubRelease>, currentVersionTag:String) {
91113
releases = releases.filterReleases(Options.betaUpdates, false);
92114
if (releases.length <= 0)

source/funkin/backend/utils/AudioAnalyzer.hx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ final class AudioAnalyzer {
9595
static var __twiddleImags:Array<Array<Float>> = [];
9696
static var __freqReals:Array<Array<Float>> = [];
9797
static var __freqImags:Array<Array<Float>> = [];
98+
static var __freqCalculating:Int = 0;
9899
#if (target.threaded)
99100
static var __mutex:Mutex = new Mutex();
100-
static var __freqCalculating:Int = 0;
101101
#end
102102

103103
/**
Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
package funkin.backend.utils;
22

3-
#if ALLOW_MULTITHREADING
4-
import sys.thread.Thread;
5-
#end
6-
7-
#if !macro
83
import funkin.backend.scripting.MultiThreadedScript;
94
import funkin.backend.scripting.Script;
10-
#end
115

126
final class EngineUtil {
13-
#if !macro
147
/**
158
* Starts a new multithreaded script.
169
* This script will share all the variables with the current one, which means already existing callbacks will be replaced by new ones on conflict.
@@ -19,32 +12,4 @@ final class EngineUtil {
1912
public static function startMultithreadedScript(path:String) {
2013
return new MultiThreadedScript(path, Script.curScript);
2114
}
22-
#end
23-
24-
#if ALLOW_MULTITHREADING
25-
public static var gameThreads:Array<Thread> = [];
26-
27-
private static var maxThreads:Int = 4;
28-
private static var threadCycle:Int = 0;
29-
private static var threadsInitialized:Bool = false;
30-
#else
31-
public static var gameThreads:Array<Dynamic> = [];
32-
#end
33-
34-
/**
35-
* Execute a function asynchronously using existing threads when initialized with ALLOW_MULTITHREADING.
36-
* @param func Void -> Void
37-
*/
38-
public static function execAsync(func:Void->Void) {
39-
#if ALLOW_MULTITHREADING
40-
if (!threadsInitialized) {
41-
threadsInitialized = true;
42-
for (i in 0...maxThreads) gameThreads.push(Thread.createWithEventLoop(() -> Thread.current().events.promise()));
43-
}
44-
gameThreads[threadCycle].events.run(func);
45-
if (++threadCycle >= maxThreads) threadCycle = 0;
46-
#else
47-
func();
48-
#end
49-
}
5015
}
Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,97 @@
11
package funkin.backend.utils;
22

3-
#if ALLOW_MULTITHREADING
3+
#if (target.threaded)
4+
import sys.thread.Deque;
5+
import sys.thread.Thread;
6+
import sys.thread.Mutex;
7+
#else
8+
private typedef Thread = Dynamic;
9+
#end
10+
11+
#if !macro
12+
import funkin.backend.system.Logs;
13+
#end
14+
415
final class ThreadUtil {
16+
inline static function error(text:String) {
17+
#if macro
18+
trace(text);
19+
#else
20+
FlxG.signals.preUpdate.addOnce(Logs.error.bind(text));
21+
#end
22+
}
23+
524
/**
625
* Creates a new Thread with an error handler.
726
* @param func Function to execute
827
* @param autoRestart Whenever the thread should auto restart itself after crashing.
928
*/
10-
public static function createSafe(func:Void->Void, autoRestart:Bool = false) {
11-
if (autoRestart) {
12-
return sys.thread.Thread.create(function() {
13-
while(true) {
14-
try {
15-
func();
16-
} catch(e) {
17-
trace(e.details());
18-
}
19-
}
20-
});
21-
} else {
22-
return sys.thread.Thread.create(function() {
23-
try {
29+
public static function createSafe(func:Void->Void, autoRestart:Bool = false):Thread {
30+
#if (target.threaded)
31+
try {
32+
return if (autoRestart) Thread.create(() -> {
33+
var restart = true;
34+
while (restart) try {
2435
func();
25-
} catch(e) {
26-
trace(e.details());
36+
restart = false;
2737
}
38+
catch (e) error(e.details());
39+
})
40+
else Thread.create(() -> {
41+
try {func();}
42+
catch (e) error(e.details());
2843
});
2944
}
45+
catch (e) error("Failed to safely create a thread: " + e.details());
46+
#end
47+
return null;
48+
}
49+
50+
#if ALLOW_MULTITHREADING
51+
public static var maxThreads:Int = 4;
52+
53+
static var __threads:Array<Thread> = [];
54+
static var __pendingExecs:Deque<Void->Void> = new Deque();
55+
static var __threadMutex:Mutex = new Mutex();
56+
static var __threadUsed:Int = 0;
57+
58+
static function __threadExecAsync() {
59+
var callback:Void->Void;
60+
while ((callback = __pendingExecs.pop(true)) != null) {
61+
__threadMutex.acquire();
62+
__threadUsed++;
63+
__threadMutex.release();
64+
65+
callback();
66+
67+
__threadMutex.acquire();
68+
__threadUsed--;
69+
__threadMutex.release();
70+
}
71+
__threadMutex.acquire();
72+
__threads.remove(Thread.current());
73+
__threadMutex.release();
74+
}
75+
#end
76+
77+
public static function execAsync(func:Void->Void) {
78+
if (func == null) return;
79+
80+
#if (ALLOW_MULTITHREADING && !macro)
81+
__pendingExecs.add(func);
82+
if (__threadUsed >= __threads.length) {
83+
if (__threads.length == maxThreads) return;
84+
85+
__threadMutex.acquire();
86+
try {
87+
var thread = Thread.create(__threadExecAsync);
88+
__threads.push(thread);
89+
}
90+
catch (e) Logs.warn(e.details());
91+
__threadMutex.release();
92+
}
93+
#else
94+
func();
95+
#end
3096
}
31-
}
32-
#end
97+
}

0 commit comments

Comments
 (0)