Add VRChat and GitHub packages to Unity project
This commit is contained in:
1 parent
0334a52b21
commit
85538aaccc
3297 files changed
+847877
No files matched your search
@@ -0,0 +1,247 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="Cache.cs" company="Sirenix IVS">
|
||||
// Copyright (c) 2018 Sirenix IVS
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// 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.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an easy way of claiming and freeing cached values of any non-abstract reference type with a public parameterless constructor.
|
||||
/// <para />
|
||||
/// Cached types which implement the <see cref="ICacheNotificationReceiver"/> interface will receive notifications when they are claimed and freed.
|
||||
/// <para />
|
||||
/// Only one thread should be holding a given cache instance at a time if <see cref="ICacheNotificationReceiver"/> is implemented, since the invocation of
|
||||
/// <see cref="ICacheNotificationReceiver.OnFreed()"/> is not thread safe, IE, weird stuff might happen if multiple different threads are trying to free
|
||||
/// the same cache instance at the same time. This will practically never happen unless you're doing really strange stuff, but the case is documented here.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type which is cached.</typeparam>
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
public sealed class Cache<T> : IDisposable where T : class, new()
|
||||
{
|
||||
private static readonly bool IsNotificationReceiver = typeof(ICacheNotificationReceiver).IsAssignableFrom(typeof(T));
|
||||
private static object[] FreeValues = new object[4];
|
||||
|
||||
private bool isFree;
|
||||
|
||||
private static volatile int THREAD_LOCK_TOKEN = 0;
|
||||
|
||||
private static int maxCacheSize = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum size of the cache. This value can never go beneath 1.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The maximum size of the cache.
|
||||
/// </value>
|
||||
public static int MaxCacheSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return Cache<T>.maxCacheSize;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Cache<T>.maxCacheSize = Math.Max(1, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Cache()
|
||||
{
|
||||
this.Value = new T();
|
||||
this.isFree = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The cached value.
|
||||
/// </summary>
|
||||
public T Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this cached value is free.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this cached value is free; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsFree { get { return this.isFree; } }
|
||||
|
||||
/// <summary>
|
||||
/// Claims a cached value of type <see cref="T"/>.
|
||||
/// </summary>
|
||||
/// <returns>A cached value of type <see cref="T"/>.</returns>
|
||||
public static Cache<T> Claim()
|
||||
{
|
||||
Cache<T> result = null;
|
||||
|
||||
// Very, very simple spinlock implementation
|
||||
// this lock will almost never be contested
|
||||
// and it will never be held for more than
|
||||
// an instant; therefore, we want to avoid paying
|
||||
// the lock(object) statement's semaphore
|
||||
// overhead.
|
||||
while (true)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref THREAD_LOCK_TOKEN, 1, 0) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We now hold the lock
|
||||
var freeValues = FreeValues;
|
||||
var length = freeValues.Length;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
result = (Cache<T>)freeValues[i];
|
||||
if (!object.ReferenceEquals(result, null))
|
||||
{
|
||||
freeValues[i] = null;
|
||||
result.isFree = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Release the lock
|
||||
THREAD_LOCK_TOKEN = 0;
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = new Cache<T>();
|
||||
}
|
||||
|
||||
if (IsNotificationReceiver)
|
||||
{
|
||||
(result.Value as ICacheNotificationReceiver).OnClaimed();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases a cached value.
|
||||
/// </summary>
|
||||
/// <param name="cache">The cached value to release.</param>
|
||||
/// <exception cref="System.ArgumentNullException">The cached value to release is null.</exception>
|
||||
public static void Release(Cache<T> cache)
|
||||
{
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentNullException("cache");
|
||||
}
|
||||
|
||||
if (cache.isFree) return;
|
||||
|
||||
// No need to call this method inside the lock, which might do heavy work
|
||||
// there is a thread safety hole here, actually - if several different threads
|
||||
// are trying to free the same cache instance, OnFreed might be called several
|
||||
// times concurrently for the same cached value.
|
||||
if (IsNotificationReceiver)
|
||||
{
|
||||
(cache.Value as ICacheNotificationReceiver).OnFreed();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref THREAD_LOCK_TOKEN, 1, 0) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We now hold the lock
|
||||
|
||||
if (cache.isFree)
|
||||
{
|
||||
// Release the lock and leave - job's done already
|
||||
THREAD_LOCK_TOKEN = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
cache.isFree = true;
|
||||
|
||||
var freeValues = FreeValues;
|
||||
var length = freeValues.Length;
|
||||
|
||||
bool added = false;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (object.ReferenceEquals(freeValues[i], null))
|
||||
{
|
||||
freeValues[i] = cache;
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!added && length < MaxCacheSize)
|
||||
{
|
||||
var newArr = new object[length * 2];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
newArr[i] = freeValues[i];
|
||||
}
|
||||
|
||||
newArr[length] = cache;
|
||||
|
||||
FreeValues = newArr;
|
||||
}
|
||||
|
||||
// Release the lock
|
||||
THREAD_LOCK_TOKEN = 0;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from <see cref="Cache{T}"/> to <see cref="T"/>.
|
||||
/// </summary>
|
||||
/// <param name="cache">The cache to convert.</param>
|
||||
/// <returns>
|
||||
/// The result of the conversion.
|
||||
/// </returns>
|
||||
public static implicit operator T(Cache<T> cache)
|
||||
{
|
||||
if (cache == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return cache.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases this cached value.
|
||||
/// </summary>
|
||||
public void Release()
|
||||
{
|
||||
Release(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases this cached value.
|
||||
/// </summary>
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Cache<T>.Release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user