ThreadDispatcher.cs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // Copyright (c) 2024 Vuplex Inc. All rights reserved.
  2. //
  3. // Licensed under the Vuplex Commercial Software Library License, you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // https://vuplex.com/commercial-library-license
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Threading;
  17. using UnityEngine;
  18. namespace Vuplex.WebView.Internal {
  19. /// <summary>
  20. /// Internal class for running code on the main Unity thread.
  21. /// </summary>
  22. public class ThreadDispatcher : MonoBehaviour {
  23. public static bool CurrentlyOnMainThread {
  24. get {
  25. if (_mainThreadId == 0) {
  26. // This happens if CurrentlyOnMainThread is accessed from a method annotated with `RuntimeInitializeOnLoadMethod()`
  27. // (for example: method that AndroidWebPlugin.cs calls on startup on Meta Quest).
  28. return true;
  29. }
  30. return Thread.CurrentThread.ManagedThreadId == _mainThreadId;
  31. }
  32. }
  33. public static void RunOnMainThread(Action action) {
  34. if (CurrentlyOnMainThread) {
  35. action();
  36. return;
  37. }
  38. lock(_backlog) {
  39. _backlog.Add(action);
  40. _queued = true;
  41. }
  42. }
  43. // It's necessary to create the instance at startup instead of dynamically through
  44. // an Instance property (like 3D WebView's other singleton classes do) because it's used
  45. // from other threads, and Unity APIs (like `new GameObject()`) can't be used from other threads.
  46. // Note: BeforeSceneLoad is used because earlier callbacks (e.g. SubsystemRegistration, BeforeSplashScreen)
  47. // prevent it from working correctly.
  48. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  49. static void _initialize() {
  50. if (_instance == null) {
  51. _mainThreadId = Thread.CurrentThread.ManagedThreadId;
  52. _instance = new GameObject("WebView Thread Dispatcher").AddComponent<ThreadDispatcher>();
  53. DontDestroyOnLoad(_instance.gameObject);
  54. }
  55. }
  56. void Update() {
  57. if (_queued) {
  58. lock(_backlog) {
  59. var tmp = _actions;
  60. _actions = _backlog;
  61. _backlog = tmp;
  62. _queued = false;
  63. }
  64. foreach (var action in _actions) {
  65. try {
  66. action();
  67. } catch (Exception e) {
  68. WebViewLogger.LogError("An exception occurred while dispatching an action on the main thread: " + e);
  69. }
  70. }
  71. _actions.Clear();
  72. }
  73. }
  74. static List<Action> _actions = new List<Action>(8);
  75. static List<Action> _backlog = new List<Action>(8);
  76. static ThreadDispatcher _instance;
  77. static int _mainThreadId;
  78. static volatile bool _queued = false;
  79. }
  80. }