Closed tayl0r closed 2 years ago
I spent some time researching this bug, wasn't able to solve it, but here are my findings:
You can change what ThreadingPatcher
inherits from, and add a new method to make the code compile again:
public class ThreadingPatcher : IPostBuildPlayerScriptDLLs
{
public void OnPostBuildPlayerScriptDLLs(BuildReport report) {
this.OnBeforeConvertRun(report, report.summary.platform, Path.Combine(Application.dataPath, "../Temp/StagingArea/Data/Managed"));
}
...
But, it still fails at build time due to .net being upgraded from version 4 to version 5. This has changed some of the classes that this code is trying to patch, so it fails doing the patching.
Thanks for the feedback! yeah, this code is pretty sensitive to changes in BCL. Looks like 2021.2 had is slightly different ThreadPool (easy to fix) and big changes in Timer.Scheduler (harder to fix). I have some work in progress that will fix it but it's not ready. Hope will have free time soon to finish it
Hi. Any status for this? Being able to use Task in our game is crucial, as we have several non-Unity libraries using it, and we can't easily recompile them all with non-async alternatives. This plugin looks like a gold nugget for me, because, if you'd look on Unity forums, it's a highly requested future.
You could use this script in the meantime: https://github.com/transformsai/UnityWebGLThreadingFix/blob/main/Runtime/WebGLThreadPoolPumper.cs
@MHDante That script works really well, given its simplicity.
EDIT: my bad - reported crash was a bug on my side
I have made changes to the plugin. now it will not use IIl2CppProcessor. also, some minor codegen changes to support newer Unity versions
Hey. Sorry for bothering again. Thanks for looking into it, but have you tried with 2021.3.4f1, 2021.3.5f1, 2022.1.7f1, 2022.2.0a17? I get "System.ArgumentException: Library/Bee/artifacts/csharpactions/DragonShift.framework.js.unityweb_pb96.info has already been registered with different file contents" when building with thread support for webgl.
I see you mention Unity 2021.2.0, so would you recommend downgrading to it to make this work? I'm currently using 2021.3.5
Also, would this work with UniTask (particularly the SwitchToThreadPool/SwitchToMainThread) and regular Task? If you could check this code sample and confirm, I'd be very grateful 🙏 : example-unitask-async.txt
Update: I used 2021.2.19f1 and still can't get past the compilation part. Were you able to compile with 2021.2.x after you added the timer patch? I'm just looking to find any version that could build the project.
I get "System.ArgumentException: Library/Bee/artifacts/csharpactions/DragonShift.framework.js.unityweb_pb96.info has already been registered with different file contents" when building with thread support for webgl.
it's interesting issue. I will try to reproduce it locally
I see you mention Unity 2021.2.0, so would you recommend downgrading to it to make this work? I'm currently using 2021.3.5
I mentioned 2021.2 just becouse it was version from issue title. but in fact I tested on latest 2021.2 and 2021.3. so I don't think that downgrade will help
Thanks. Looking forward to it!
At this point, I downgraded all the way to 2020.1.0f1 (ofc, it's not a solution, because downgrading doesn't work in general, with the prefabs and all that). Compilation passes, but then I get this. Just to make sure I understand, your library and MHDante's require us to set "PlayerSettings.WebGL.threadsSupport = true", right?
Both my solution and Volodymyr's (use one, not both) are meant to allow short tasks that have been added to the thread pool to execute on the main thread. This is primarily because there are many c# libraries that assume the easy availability of a thread pool (like grpc). Neither solution allows for true concurrency on webgl.
The ,PlayerSettings.WebGL.threadsSupport
flag is meant to activate emscripten's multithreaded mode. This allows you to use threads in c++ plugins.
Due to a bevy of issues involving memory sharing across threads, the emscripten memory model, the .Net threading/memory model and Il2cpp, it will be a while until unity can make use of emscripten threads to allow C# threads to function.
Due to this, unless you are writing c++ plugins and have a thorough understanding of emscripten's peculiarities. I would recommend leaving PlayerSettings.WebGL.threadsSupport = false
Oh, that's so good to know! Then I don't have to worry about downgrading. Thanks a lot for clarifying it with such great detail! 🙏
What puzzles me is, with any of your 2 approaches (Unity 2021.3.5f1), the execution path just stops at this code:
public async Task<(TConfig, string)> DownloadAsync()
{
string err;
try
{
using (var wc = new WebClient())
{
var yaml = await wc.DownloadStringTaskAsync(ComposeURL());
var cfg = TryRead(yaml, out err);
if (cfg != null)
return (cfg, null);
}
}
catch (Exception e)
{
err = e.ToString();
}
return (null, err);
}
The way I'm calling it is via:
static async UniTask<string> PopulateDefaultEndpointsInternalAsync(EnvConfig newConfig)
{
(ServicesConfig svs, string err) = await ServicesConfig.DownloadAsync(newConfig.Stage);
LogV($"SwitchToMainThread"); // not called
...
}
// The actual call
await PopulateDefaultEndpointsInternalAsync(...)
I'm using Tasks (and UniTask on Unity side) mainly because some libraries only offer Async APIS, including a library that uses .net Web requests and web clients, and some Google Auth/Storage libs (although on WebGL the Google APIs are not essential)
the problem is not threading but WebClient
itself.
WebClient
internally uses sockets API, which is not available on WebGL. You need to change your code to use UnityWebRequest. Currently, it is the only available method to make network request from WebGL.
if you want to use UnityWebRequest in async/await manner, then you can add code like this to your project
public struct AsyncOperationAwaiter : INotifyCompletion
{
private AsyncOperation _asyncOperation;
public bool IsCompleted => _asyncOperation.isDone;
public AsyncOperationAwaiter(AsyncOperation asyncOperation) => _asyncOperation = asyncOperation;
public void GetResult() { }
public void OnCompleted(Action continuation)
{
_asyncOperation.completed += _ => continuation();
}
}
public static class AsyncOperationAwaitable
{
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation operation)
{
return new AsyncOperationAwaiter(operation);
}
}
it will allow you to write await request.SendWebRequest
instead of using coroutines
Oh, good, there's some light here. So after all threads would probably work fine, and I have to just hunt these points in the app that use WebClient/WebRequest. Still kind of a blocker for my project. Basically, I have to adapt my own libraries so that I pass them a "Downloader" interface that they'll use to download stuff, and for 3rd party libraries that I don't have control over - not sure if I can do something about it. Anyway, I'll post my progress here in the following days, as I'm sure other users might encounter this. And thanks again for support!
This is out of the scope of this ticket, but I wrote an HTTPHandler for WebGL you can find here:
https://github.com/transformsai/UnityWebGLHttpHandler
It's undocumented and based on MonoWASM's Http web handler. It uses fetch for web requests. I would recommend using UnityWebRequest if you can, however.
Packages\WebGLThreadingPatcher\ThreadingPatcher\Editor\WebGLThreadingPatcher.cs(13,37): error CS0619: 'IIl2CppProcessor' is obsolete: 'The IIl2CppProcessor interface has been removed from Unity. Use IPostBuildPlayerScriptDLLs if you need to access player assemblies before il2cpp runs.'