// Copyright (c) 2024 Vuplex Inc. All rights reserved.
//
// Licensed under the Vuplex Commercial Software Library License, you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// https://vuplex.com/commercial-library-license
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
namespace Vuplex.WebView.Internal {
///
/// Internal class for running code on the main Unity thread.
///
public class ThreadDispatcher : MonoBehaviour {
public static bool CurrentlyOnMainThread {
get {
if (_mainThreadId == 0) {
// This happens if CurrentlyOnMainThread is accessed from a method annotated with `RuntimeInitializeOnLoadMethod()`
// (for example: method that AndroidWebPlugin.cs calls on startup on Meta Quest).
return true;
}
return Thread.CurrentThread.ManagedThreadId == _mainThreadId;
}
}
public static void RunOnMainThread(Action action) {
if (CurrentlyOnMainThread) {
action();
return;
}
lock(_backlog) {
_backlog.Add(action);
_queued = true;
}
}
// It's necessary to create the instance at startup instead of dynamically through
// an Instance property (like 3D WebView's other singleton classes do) because it's used
// from other threads, and Unity APIs (like `new GameObject()`) can't be used from other threads.
// Note: BeforeSceneLoad is used because earlier callbacks (e.g. SubsystemRegistration, BeforeSplashScreen)
// prevent it from working correctly.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void _initialize() {
if (_instance == null) {
_mainThreadId = Thread.CurrentThread.ManagedThreadId;
_instance = new GameObject("WebView Thread Dispatcher").AddComponent();
DontDestroyOnLoad(_instance.gameObject);
}
}
void Update() {
if (_queued) {
lock(_backlog) {
var tmp = _actions;
_actions = _backlog;
_backlog = tmp;
_queued = false;
}
foreach (var action in _actions) {
try {
action();
} catch (Exception e) {
WebViewLogger.LogError("An exception occurred while dispatching an action on the main thread: " + e);
}
}
_actions.Clear();
}
}
static List _actions = new List(8);
static List _backlog = new List(8);
static ThreadDispatcher _instance;
static int _mainThreadId;
static volatile bool _queued = false;
}
}