-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathUnityRuntimePlatform.cs
163 lines (147 loc) · 5.35 KB
/
UnityRuntimePlatform.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Platform;
using UnityEngine;
namespace Unilonia
{
internal class UnityRuntimePlatform : IRuntimePlatform
{
private static readonly Lazy<RuntimePlatformInfo> Info = new Lazy<RuntimePlatformInfo>(() =>
{
OperatingSystemType os;
switch(Application.platform)
{
case RuntimePlatform.WindowsEditor:
case RuntimePlatform.WindowsPlayer:
os = OperatingSystemType.WinNT;
break;
case RuntimePlatform.OSXEditor:
case RuntimePlatform.OSXPlayer:
os = OperatingSystemType.OSX;
break;
case RuntimePlatform.LinuxEditor:
case RuntimePlatform.LinuxPlayer:
os = OperatingSystemType.Linux;
break;
case RuntimePlatform.IPhonePlayer:
os = OperatingSystemType.iOS;
break;
case RuntimePlatform.Android:
os = OperatingSystemType.Android;
break;
default:
os = OperatingSystemType.Unknown;
break;
}
return new RuntimePlatformInfo
{
IsMono = true,
IsDesktop = os != OperatingSystemType.Android && os != OperatingSystemType.iOS,
IsMobile = os != OperatingSystemType.Android && os != OperatingSystemType.iOS,
IsUnix = os != OperatingSystemType.WinNT && os != OperatingSystemType.Android,
OperatingSystem = os
};
});
public RuntimePlatformInfo GetRuntimeInfo() => Info.Value;
public IDisposable StartSystemTimer(TimeSpan interval, Action tick)
{
return new Timer(_ => tick(), null, interval, interval);
}
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size);
class UnmanagedBlob : IUnmanagedBlob
{
private readonly UnityRuntimePlatform _plat;
private IntPtr _address;
private readonly object _lock = new object();
#if DEBUG
private static readonly List<string> Backtraces = new List<string>();
private static Thread GCThread;
private readonly string _backtrace;
private static readonly object _btlock = new object();
class GCThreadDetector
{
~GCThreadDetector()
{
GCThread = Thread.CurrentThread;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void Spawn() => new GCThreadDetector();
static UnmanagedBlob()
{
Spawn();
GC.WaitForPendingFinalizers();
}
#endif
public UnmanagedBlob(UnityRuntimePlatform plat, int size)
{
if (size <= 0)
throw new ArgumentException("Positive number required", nameof(size));
_plat = plat;
_address = plat.Alloc(size);
GC.AddMemoryPressure(size);
Size = size;
#if DEBUG
_backtrace = Environment.StackTrace;
lock (_btlock)
Backtraces.Add(_backtrace);
#endif
}
void DoDispose()
{
lock (_lock)
{
if (!IsDisposed)
{
#if DEBUG
lock (_btlock)
Backtraces.Remove(_backtrace);
#endif
_plat?.Free(_address, Size);
GC.RemoveMemoryPressure(Size);
IsDisposed = true;
_address = IntPtr.Zero;
Size = 0;
}
}
}
public void Dispose()
{
#if DEBUG
if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId)
{
lock (_lock)
{
if (!IsDisposed)
{
Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: "
+ Environment.StackTrace
+ "\n\nBlob created by " + _backtrace);
}
}
}
#endif
DoDispose();
GC.SuppressFinalize(this);
}
~UnmanagedBlob()
{
#if DEBUG
Console.Error.WriteLine("Undisposed native blob created by " + _backtrace);
#endif
DoDispose();
}
public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address;
public int Size { get; private set; }
public bool IsDisposed { get; private set; }
}
IntPtr Alloc(int size) => Marshal.AllocHGlobal(size);
void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr);
}
}