备份CatanBuilding瘦身独立工程

This commit is contained in:
JSD\13999
2026-05-26 16:15:54 +08:00
commit 2d0e6a61b7
12001 changed files with 2431925 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
using System;
namespace com.fpnn.common
{
public interface ErrorRecorder
{
void RecordError(Exception e);
void RecordError(string message);
void RecordError(string message, Exception e);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3eb5149f8432d46c9b5fd967c04837cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,640 @@
using System;
using System.Text;
using System.Collections.Generic;
namespace com.fpnn.common
{
public static class Json
{
public static object Parse(string json)
{
JsonParser parser = JsonParser.Create(json);
return parser.Parse();
}
public static Dictionary<string, object> ParseObject(string json)
{
JsonParser parser = JsonParser.Create(json);
return parser.ParseObject();
}
public static string ToString(object obj)
{
JsonStringify js = new JsonStringify();
return js.Stringify(obj);
}
}
//============================[ Exception ]============================//
public class JsonException : Exception
{
public JsonException(string message) : base(message) { }
public JsonException(String message, Exception ex) : base(message, ex) { }
}
public class InvalidJsonException : JsonException
{
public InvalidJsonException(string message) : base(message) { }
public InvalidJsonException(string message, Exception ex) : base(message, ex) { }
}
public class JsonTypeException : JsonException
{
public JsonTypeException(string message) : base(message) { }
}
//============================[ Json Parser ]============================//
internal enum JsonElementType
{
Empty,
Null,
Boolean,
String,
Int64,
UInt64,
Double,
Array,
Dictionary,
}
internal delegate void JsonParseSignDelegate(JsonParser parser);
internal class JsonParser
{
class JsonElement
{
public object element;
public JsonElementType type;
}
static private readonly Dictionary<char, JsonParseSignDelegate> signDelegateMap;
static private readonly Dictionary<char, ushort> hexTable;
static private readonly HashSet<char> numericalChars;
private readonly string json;
private int idx;
private string key;
private bool wantKey;
private bool wantValue;
private bool wantComma;
private bool wantSemicolon;
private readonly Stack<JsonElement> elementStack;
private object parseResult;
private JsonElementType resultType;
static JsonParser()
{
signDelegateMap = new Dictionary<char, JsonParseSignDelegate>
{
{ '{', EnterObject },
{ '}', ExitObject },
{ '[', EnterArray },
{ ']', ExitArray },
{ ',', ProcessComma },
{ ':', ProcessSemicolon },
{ '"', ProcessString },
};
hexTable = new Dictionary<char, ushort>
{
{ '0', 0 },
{ '1', 1 },
{ '2', 2 },
{ '3', 3 },
{ '4', 4 },
{ '5', 5 },
{ '6', 6 },
{ '7', 7 },
{ '8', 8 },
{ '9', 9 },
{ 'a', 10 },
{ 'b', 11 },
{ 'c', 12 },
{ 'd', 13 },
{ 'e', 14 },
{ 'f', 15 },
{ 'A', 10 },
{ 'B', 11 },
{ 'C', 12 },
{ 'D', 13 },
{ 'E', 14 },
{ 'F', 15 },
};
numericalChars = new HashSet<char>
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'+', '-', '.', 'E', 'e'
};
}
private static void EnterObject(JsonParser parser)
{
parser.EnterObject();
}
private static void ExitObject(JsonParser parser)
{
parser.ExitObject();
}
private static void EnterArray(JsonParser parser)
{
parser.EnterArray();
}
private static void ExitArray(JsonParser parser)
{
parser.ExitArray();
}
private static void ProcessComma(JsonParser parser)
{
parser.ProcessComma();
}
private static void ProcessSemicolon(JsonParser parser)
{
parser.ProcessSemicolon();
}
private static void ProcessString(JsonParser parser)
{
parser.ProcessString();
}
public static JsonParser Create(string json)
{
if (string.IsNullOrWhiteSpace(json))
throw new InvalidJsonException("Json Parser: content error. Null or empty content.");
JsonParser parser;
try
{
string normalizedString = json;
if (!json.IsNormalized())
normalizedString = json.Normalize();
parser = new JsonParser(normalizedString);
}
catch (ArgumentException e)
{
throw new InvalidJsonException("Json Parser: content error. Include invalid Unicode.", e);
}
return parser;
}
//---------------------[ Instance Parts ]--------------------------//
private JsonParser(string jsonString)
{
json = jsonString;
idx = 0;
elementStack = new Stack<JsonElement>();
}
private void Reset()
{
key = string.Empty;
wantKey = false;
wantValue = false;
wantComma = false;
wantSemicolon = false;
elementStack.Clear();
parseResult = null;
resultType = JsonElementType.Empty;
}
private void ParseCore()
{
Reset();
while (idx < json.Length && parseResult == null)
{
if (Char.IsWhiteSpace(json[idx]))
{
idx++;
continue;
}
if (signDelegateMap.TryGetValue(json[idx], out JsonParseSignDelegate method))
method(this);
else
GeneralProcess();
}
}
public object Parse()
{
ParseCore();
return parseResult;
}
public Dictionary<string, object> ParseObject()
{
ParseCore();
if (resultType == JsonElementType.Dictionary)
return (Dictionary<string, object>)parseResult;
throw new JsonTypeException("Json Parser: want the Dictionary type, but the parsed type is " + resultType.ToString("G") + ".");
}
private void FillKey(string value)
{
key = value;
wantKey = false;
wantValue = false;
wantComma = false;
wantSemicolon = true;
}
private void FillValue(object obj, JsonElementType type)
{
Dictionary<string, object> dict = (Dictionary<string, object>)elementStack.Peek().element;
try
{
dict.Add(key, obj);
}
catch (ArgumentException e)
{
throw new InvalidJsonException("Json Parser: reduplicated key: \"" + key + "\", offset " + idx, e);
}
wantKey = false;
wantValue = false;
wantComma = true;
wantSemicolon = false;
}
private void InsertNode(object obj, JsonElementType type)
{
int stackCount = elementStack.Count;
if (stackCount > 0)
{
if (wantValue)
FillValue(obj, type);
else
{
List<object> list = (List<object>)elementStack.Peek().element;
list.Add(obj);
wantComma = true;
}
}
if (type == JsonElementType.Array || type == JsonElementType.Dictionary)
{
elementStack.Push(new JsonElement()
{
element = obj,
type = type
});
}
else if (stackCount == 0)
{
parseResult = obj;
resultType = type;
}
}
private void FinishNode()
{
JsonElement element = elementStack.Pop();
if (elementStack.Count == 0)
{
parseResult = element.element;
resultType = element.type;
}
else
{
wantKey = false;
wantValue = false;
wantComma = true;
wantSemicolon = false;
}
}
private void ProcessSlash(ref StringBuilder stringBuilder)
{
idx++;
if (idx >= json.Length)
throw new InvalidJsonException("Json Parser: content error, json truncated after '\\'.");
switch (json[idx])
{
case '"':
stringBuilder.Append('"');
break;
case '\\':
stringBuilder.Append('\\');
break;
case '/':
stringBuilder.Append('/');
break;
case 'b':
stringBuilder.Append('\b');
break;
case 'f':
stringBuilder.Append('\f');
break;
case 'n':
stringBuilder.Append('\n');
break;
case 'r':
stringBuilder.Append('\r');
break;
case 't':
stringBuilder.Append('\t');
break;
case 'u':
if (idx + 5 >= json.Length)
throw new InvalidJsonException("Json Parser: content error, json truncated after '\\u'.");
idx++;
ushort value = 0;
for (int i = 0; i < 4; i++)
{
if (hexTable.TryGetValue(json[idx+i], out ushort v))
{
value <<= 4;
value += v;
}
else
throw new InvalidJsonException("Json Parser: content error, invalid hex number for '\\u'. Offset " + idx);
}
try
{
char c = Convert.ToChar(value);
stringBuilder.Append(c);
}
catch (OverflowException e)
{
throw new InvalidJsonException("Json Parser: content error, invalid unicode value for '\\u'. Offset " + idx, e);
}
idx += 4;
return;
}
idx++;
}
private string FetchString()
{
idx++;
int startIdx = idx;
StringBuilder stringBuilder = new StringBuilder();
while (idx < json.Length)
{
if (json[idx] == '\\')
{
int count = idx - startIdx;
if (count > 0)
stringBuilder.Append(json, startIdx, count);
ProcessSlash(ref stringBuilder);
startIdx = idx;
continue;
}
if (json[idx] == '"')
{
int count = idx - startIdx;
if (count > 0)
stringBuilder.Append(json, startIdx, count);
idx++;
return stringBuilder.ToString();
}
idx++;
}
throw new InvalidJsonException("Json Parser: content error, json truncated in string value.");
}
private void EnterObject()
{
if (wantKey || wantSemicolon || wantComma)
throw new InvalidJsonException("Json Parser: content error, '{' at improper place. Offset " + idx);
InsertNode(new Dictionary<string, object>(), JsonElementType.Dictionary);
wantKey = true;
wantValue = false;
wantComma = false;
wantSemicolon = false;
idx++;
}
private void ExitObject()
{
if (wantValue || wantSemicolon || elementStack.Count == 0 || elementStack.Peek().type != JsonElementType.Dictionary)
throw new InvalidJsonException("Json Parser: content error, '}' at improper place. Offset " + idx);
idx++;
FinishNode();
}
private void EnterArray()
{
if (wantKey || wantSemicolon || wantComma)
throw new InvalidJsonException("Json Parser: content error, '[' at improper place. Offset " + idx);
InsertNode(new List<object>(), JsonElementType.Array);
wantKey = false;
wantValue = false;
wantComma = false;
wantSemicolon = false;
idx++;
}
private void ExitArray()
{
if (wantValue || wantSemicolon || elementStack.Count == 0 || elementStack.Peek().type != JsonElementType.Array)
throw new InvalidJsonException("Json Parser: content error, ']' at improper place. Offset " + idx);
idx++;
FinishNode();
}
private void ProcessComma()
{
if (!wantComma)
throw new InvalidJsonException("Json Parser: content error. ',' at error position. Offset " + idx);
wantComma = false;
if (elementStack.Peek().type == JsonElementType.Dictionary)
wantKey = true;
idx++;
}
private void ProcessSemicolon()
{
if (!wantSemicolon)
throw new InvalidJsonException("Json Parser: content error. ':' at error position. Offset " + idx);
wantSemicolon = false;
wantValue = true;
idx++;
}
private void ProcessString()
{
if (wantSemicolon || wantComma)
throw new InvalidJsonException("Json Parser: content error. Require ':' or ','. Offset " + idx);
string value = FetchString();
if (wantKey)
FillKey(value);
else
InsertNode(value, JsonElementType.String);
}
private void GeneralProcess()
{
if (wantKey || wantSemicolon || wantComma)
throw new InvalidJsonException("Json Parser: content error. Require '\"' or ':' or ','. Offset " + idx);
if (idx + 3 < json.Length)
{
string tmp = json.Substring(idx, 4);
if (string.Equals(tmp, "true", StringComparison.OrdinalIgnoreCase))
{
idx += 4;
InsertNode(true, JsonElementType.Boolean);
return;
}
if (string.Equals(tmp, "null", StringComparison.OrdinalIgnoreCase))
{
idx += 4;
InsertNode(null, JsonElementType.Null);
return;
}
}
if (idx + 4 < json.Length)
{
string tmp = json.Substring(idx, 5);
if (string.Equals(tmp, "false", StringComparison.OrdinalIgnoreCase))
{
idx += 5;
InsertNode(false, JsonElementType.Boolean);
return;
}
}
ProcessNumber();
}
private void ProcessNumber()
{
int dot = 0;
int e = 0;
int startIndex = idx;
while (idx < json.Length && numericalChars.Contains(json[idx]))
{
if (json[idx] == '.')
dot++;
else if (json[idx] == 'e' || json[idx] == 'E')
e++;
idx++;
}
if (startIndex == idx)
{
if (idx + 7 < json.Length)
{
string tmp = json.Substring(idx, 8);
if (string.Equals(tmp, "Infinity", StringComparison.OrdinalIgnoreCase))
{
idx += 8;
InsertNode(Double.PositiveInfinity, JsonElementType.Double);
return;
}
}
if (idx + 2 < json.Length)
{
string tmp = json.Substring(idx, 3);
if (string.Equals(tmp, "nan", StringComparison.OrdinalIgnoreCase))
{
idx += 3;
InsertNode(Double.NaN, JsonElementType.Double);
return;
}
if (string.Equals(tmp, "inf", StringComparison.OrdinalIgnoreCase))
{
idx += 3;
InsertNode(Double.PositiveInfinity, JsonElementType.Double);
return;
}
}
throw new InvalidJsonException("Json Parser: content error. Unparseable content. Offset " + idx);
}
if (e > 1 || dot > 1)
throw new InvalidJsonException("Json Parser: content error. Unparseable content, invalid number. Offset " + idx);
string numberString = json.Substring(startIndex, idx - startIndex);
if (e > 0 || dot > 0)
{
try
{
double value = Convert.ToDouble(numberString);
InsertNode(value, JsonElementType.Double);
return;
}
catch (Exception ex)
{
throw new InvalidJsonException("Json Parser: content error. Invalid number. Offset " + idx, ex);
}
}
try
{
try
{
long value = Convert.ToInt64(numberString);
InsertNode(value, JsonElementType.Int64);
return;
}
catch (OverflowException)
{
UInt64 value = Convert.ToUInt64(numberString);
InsertNode(value, JsonElementType.UInt64);
}
}
catch (Exception ex)
{
throw new InvalidJsonException("Json Parser: content error. Invalid number. Offset " + idx, ex);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67b78d9afdd27429c8705ad6b904740a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,322 @@
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
namespace com.fpnn.common
{
internal static class JsonStringEscaper
{
public delegate void CharEscape(StringBuilder sb, char c);
private static readonly Dictionary<char, CharEscape> _charEscapeDict;
static JsonStringEscaper()
{
_charEscapeDict = new Dictionary<char, CharEscape>
{
{ '\\', Slash },
{ '"', QuotationMarks },
{ '\b', SpecialChars },
{ '\f', SpecialChars },
{ '\n', SpecialChars },
{ '\r', SpecialChars },
{ '\t', SpecialChars },
};
}
static public void Escape(StringBuilder sb, string str)
{
for (int i = 0; i < str.Length; i++)
{
char c = str[i];
if (_charEscapeDict.TryGetValue(c, out CharEscape escapeFunc))
{
escapeFunc(sb, c);
}
else
{
ushort value = Convert.ToUInt16(c);
if (value > 0x1f && value < 0x7f) //-- ASCII visible chars
{
sb.Append(c);
}
else
{
sb.Append("\\u");
sb.Append(value.ToString("x4"));
}
}
}
}
static private void SpecialChars(StringBuilder sb, char c)
{
switch (c)
{
case '\b': sb.Append("\\b"); break;
case '\f': sb.Append("\\f"); break;
case '\n': sb.Append("\\n"); break;
case '\r': sb.Append("\\r"); break;
case '\t': sb.Append("\\t"); break;
}
}
static private void Slash(StringBuilder sb, char c)
{
sb.Append("\\\\");
}
static private void QuotationMarks(StringBuilder sb, char c)
{
sb.Append("\\\"");
}
}
//============================[ Exception ]============================//
public class UnsupportedTypeException : JsonException
{
public UnsupportedTypeException(string message) : base(message) { }
static public UnsupportedTypeException Create(Object obj)
{
string typeFullName = obj.GetType().FullName;
return new UnsupportedTypeException("FPJson unsupported type: " + typeFullName);
}
static public UnsupportedTypeException DictionaryKey(Object obj)
{
string typeFullName = obj.GetType().FullName;
return new UnsupportedTypeException("FPJson unsupported object key type: " + typeFullName);
}
}
//============================[ Json Stringify ]============================//
internal class JsonStringify
{
public delegate void Serialize(JsonStringify js, object obj);
private static readonly Dictionary<string, Serialize> _serializeDict;
static JsonStringify()
{
_serializeDict = new Dictionary<string, Serialize>
{
{ "System.Boolean", SerializeBoolean },
{ "System.Decimal", SerializeDecimal },
{ "System.Double", SerializeDouble },
{ "System.Single", SerializeFloat },
{ "System.SByte", SerializeInteger },
{ "System.Int16", SerializeInteger },
{ "System.Int32", SerializeInteger },
{ "System.Int64", SerializeInteger },
{ "System.Byte", SerializeUInteger },
{ "System.Char", SerializeUInteger },
{ "System.UInt16", SerializeUInteger },
{ "System.UInt32", SerializeUInteger },
{ "System.UInt64", SerializeUInteger },
{ "System.String", SerializeString },
{ "System.Tuple", SerializeTuple },
{ "System.DateTime", SerializeTimestamp }
};
}
//-- Instance Methods
private StringBuilder sb;
public JsonStringify()
{
sb = new StringBuilder();
}
public string Stringify(object obj)
{
if (obj == null)
{
SerializeNull(this);
return sb.ToString();
}
string typeFullName = obj.GetType().FullName;
int idx = typeFullName.IndexOf('`');
if (idx != -1)
{
typeFullName = typeFullName.Substring(0, idx);
}
if (_serializeDict.TryGetValue(typeFullName, out Serialize serializer))
{
serializer(this, obj);
}
else if (obj is IEnumerable)
{
SerializeIEnumerable(this, obj);
}
else
{
throw UnsupportedTypeException.Create(obj);
}
return sb.ToString();
}
static private void SerializeBoolean(JsonStringify js, object obj)
{
Boolean v = (bool)obj;
if (v == true)
js.sb.Append("true");
else
js.sb.Append("false");
}
static private void SerializeDecimal(JsonStringify js, object obj)
{
Decimal dec = (Decimal)obj;
js.sb.Append(dec);
}
static private void SerializeDouble(JsonStringify js, object obj)
{
double value = (double)obj;
js.sb.Append(value);
}
static private void SerializeFloat(JsonStringify js, object obj)
{
float value = (float)obj;
js.sb.Append(value);
}
static private void SerializeInteger(JsonStringify js, object obj)
{
Int64 value = (Int64)Convert.ChangeType(obj, TypeCode.Int64);
js.sb.Append(value);
}
static private void SerializeUInteger(JsonStringify js, object obj)
{
UInt64 value = (UInt64)Convert.ChangeType(obj, TypeCode.UInt64);
js.sb.Append(value);
}
static private void SerializeString(JsonStringify js, object obj)
{
string str = (string)obj;
js.sb.Append("\"");
JsonStringEscaper.Escape(js.sb, str);
js.sb.Append("\"");
}
static private void SerializeTuple(JsonStringify js, object obj)
{
Type objType = obj.GetType();
var props = objType.GetProperties();
bool isFirst = true;
js.sb.Append("[");
foreach (System.Reflection.PropertyInfo prop in props)
{
if (isFirst)
isFirst = false;
else
js.sb.Append(",");
object o = prop.GetValue(obj, null);
js.Stringify(o);
}
js.sb.Append("]");
}
static private void SerializeTimestamp(JsonStringify js, object obj)
{
DateTime userDate = (DateTime)obj;
var univDateTime = userDate.ToUniversalTime();
js.sb.Append("\"");
js.sb.Append(univDateTime.ToString("yyyy-MM-dd HH:mm:ss"));
js.sb.Append("\"");
}
static private void SerializeNull(JsonStringify js)
{
js.sb.Append("null");
}
static private void SerializeArray(JsonStringify js, object obj)
{
IEnumerable ie = (IEnumerable)obj;
IEnumerator it = ie.GetEnumerator();
it.Reset();
bool isFirst = true;
js.sb.Append("[");
while (it.MoveNext())
{
if (isFirst)
isFirst = false;
else
js.sb.Append(",");
object o = it.Current;
js.Stringify(o);
}
js.sb.Append("]");
}
static private void SerializeDictionary(JsonStringify js, object obj)
{
IEnumerable ie = (IEnumerable)obj;
IEnumerator it = ie.GetEnumerator();
it.Reset();
bool isFirst = true;
js.sb.Append("{");
IDictionaryEnumerator id = (IDictionaryEnumerator)it;
while (it.MoveNext())
{
if (isFirst)
isFirst = false;
else
js.sb.Append(",");
object k = id.Key;
if (Type.GetTypeCode(k.GetType()) != TypeCode.String)
throw UnsupportedTypeException.DictionaryKey(k);
js.Stringify(k);
js.sb.Append(":");
object v = id.Value;
js.Stringify(v);
}
js.sb.Append("}");
}
static private void SerializeIEnumerable(JsonStringify js, object obj)
{
IEnumerable ie = (IEnumerable)obj;
IEnumerator it = ie.GetEnumerator();
it.Reset();
if (it is IDictionaryEnumerator)
SerializeDictionary(js, obj);
else
SerializeArray(js, obj);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f777f23eee1ce44f8b5a9f635069c4d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,353 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace com.fpnn.common
{
/*
* Close() is not necessary when using background thread.
*
* If using ErrorRecoder, please call TaskThreadPool.SetDefaultErrorRecorder()
* before creating any instance, or call SetErrorRecorder() for each instance
* before WakeUp()s are called.
*/
public class TaskThreadPool
{
public interface ITask
{
void Run();
}
class ActionTask: ITask
{
Action action;
public ActionTask(Action act)
{
action = act;
}
public void Run()
{
action();
}
}
//----------------[ fields ]-----------------------//
private readonly int perfectCount;
private readonly int maxCount;
private readonly int maxQueueLength;
private static ErrorRecorder defaultErrorRecorder;
private class TaskThreadPoolCore
{
public bool backgroundThread;
public int normalThreadCount;
public int busyThreadCount;
public int tempThreadCount;
public bool stopped;
public Semaphore semaphore;
public Semaphore quitSemaphore;
public Queue<ITask> taskQueue;
public readonly int tempThreadLatencySeconds;
public ErrorRecorder errorRecorder;
public TaskThreadPoolCore(int tempLatencySeconds, bool usingBackgroundThread)
{
backgroundThread = usingBackgroundThread;
normalThreadCount = 0;
busyThreadCount = 0;
tempThreadCount = 0;
stopped = false;
semaphore = new Semaphore(0, Int32.MaxValue);
taskQueue = new Queue<ITask>();
tempThreadLatencySeconds = tempLatencySeconds;
errorRecorder = defaultErrorRecorder;
}
}
private readonly TaskThreadPoolCore core;
//----------------[ Constructor ]-----------------------//
public TaskThreadPool(int initThreadCount, int perfectThreadCount, int maxThreadCount, int maxQueueLengthLimitation = 0, int tempLatencySeconds = 60, bool usingBackGroundThread = true)
{
if (initThreadCount < 0)
throw new ArgumentException("Param initThreadCount is less than Zero.", nameof(initThreadCount));
if (maxThreadCount <= 0)
throw new ArgumentException("Param maxThreadCount is less than or equal to Zero.", nameof(maxThreadCount));
if (perfectThreadCount < initThreadCount)
throw new ArgumentOutOfRangeException(nameof(perfectThreadCount), "Param perfectThreadCount is less than initThreadCount");
if (maxThreadCount < perfectThreadCount)
throw new ArgumentOutOfRangeException(nameof(maxThreadCount), "Param maxThreadCount is less than perfectThreadCount");
perfectCount = perfectThreadCount;
maxCount = maxThreadCount;
maxQueueLength = maxQueueLengthLimitation;
core = new TaskThreadPoolCore(tempLatencySeconds, usingBackGroundThread);
for (int i = 0; i < initThreadCount; i++)
{
var thread = new Thread(Worker)
{
Name = "FPNN.ThreadPool.NormalWorker",
IsBackground = core.backgroundThread
};
thread.Start(core);
core.normalThreadCount++; //-- Unneed lock in there.
}
}
public static void SetDefaultErrorRecorder(ErrorRecorder er)
{
defaultErrorRecorder = er;
}
public void SetErrorRecorder(ErrorRecorder er)
{
core.errorRecorder = er;
}
private void Append(bool normalThread)
{
try
{
Thread thread;
if (normalThread)
{
thread = new Thread(Worker)
{
Name = "FPNN.ThreadPool.NormalWorker"
};
}
else
{
thread = new Thread(TempWorker)
{
Name = "FPNN.ThreadPool.TempWorker"
};
}
thread.IsBackground = core.backgroundThread;
thread.Start(core);
}
catch (Exception e)
{
lock (core)
{
if (normalThread)
core.normalThreadCount--;
else
core.tempThreadCount--;
}
core.errorRecorder?.RecordError(e);
}
}
public bool Wakeup(ITask task)
{
if (task == null)
return false;
bool needAppend = false;
bool appendNormalThread = false;
lock (core)
{
if (core.stopped)
return false;
if (maxQueueLength > 0 && core.taskQueue.Count >= maxQueueLength)
return false;
core.taskQueue.Enqueue(task);
if (core.busyThreadCount + core.taskQueue.Count >= core.normalThreadCount + core.tempThreadCount)
{
if (core.normalThreadCount < perfectCount)
{
needAppend = true;
appendNormalThread = true;
core.normalThreadCount++;
}
else if (core.normalThreadCount + core.tempThreadCount < maxCount)
{
needAppend = true;
appendNormalThread = false;
core.tempThreadCount++;
}
}
}
if (needAppend)
{
Append(appendNormalThread);
}
core.semaphore.Release();
return true;
}
public bool Wakeup(Action action)
{
ActionTask task = new ActionTask(action);
return Wakeup(task);
}
//-- Please call this function in locked status.
private static void ExitWorker(bool normal, TaskThreadPoolCore core)
{
if (normal)
core.normalThreadCount--;
else
core.tempThreadCount--;
if (core.normalThreadCount == 0 && core.tempThreadCount == 0 && core.stopped && core.backgroundThread == false)
core.quitSemaphore.Release();
}
private static void Worker(Object obj)
{
TaskThreadPoolCore core = (TaskThreadPoolCore)obj;
while (true)
{
ITask task = null;
core.semaphore.WaitOne();
lock (core)
{
if (core.taskQueue.Count > 0)
{
task = core.taskQueue.Dequeue();
core.busyThreadCount++;
}
else if (core.stopped)
{
ExitWorker(true, core);
return;
}
else
continue;
}
try
{
task.Run();
}
catch (Exception e)
{
if (core.errorRecorder != null)
core.errorRecorder.RecordError(e);
}
finally
{
lock(core)
{
core.busyThreadCount--;
}
}
}
}
private static void TempWorker(Object obj)
{
TaskThreadPoolCore core = (TaskThreadPoolCore)obj;
int latencySeconds = core.tempThreadLatencySeconds;
DateTime idleTime = DateTime.Now;
while (true)
{
ITask task = null;
core.semaphore.WaitOne(latencySeconds * 1000);
lock (core)
{
if (core.taskQueue.Count > 0)
{
task = core.taskQueue.Dequeue();
core.busyThreadCount++;
}
else if (core.stopped)
{
ExitWorker(false, core);
return;
}
else
{
TimeSpan duration = DateTime.Now - idleTime;
latencySeconds -= Convert.ToInt32(duration.TotalSeconds);
if (latencySeconds <= 0)
{
ExitWorker(false, core);
return;
}
else
continue;
}
}
try
{
task.Run();
}
catch (Exception e)
{
if (core.errorRecorder != null)
core.errorRecorder.RecordError(e);
}
finally
{
lock (core)
{
core.busyThreadCount--;
}
}
idleTime = DateTime.Now;
}
}
public void Close(bool dropAllTasks = false) //-- Synchronous method.
{
bool active = true;
lock (core)
{
if (core.stopped)
return;
if (dropAllTasks)
core.taskQueue.Clear();
if (core.backgroundThread || (core.normalThreadCount == 0 && core.tempThreadCount == 0))
active = false;
else
core.quitSemaphore = new Semaphore(0, 1);
core.stopped = true;
}
core.semaphore.Release(maxCount);
if (active)
{
core.quitSemaphore.WaitOne();
core.quitSemaphore.Close();
}
core.semaphore.Close();
}
~TaskThreadPool()
{
Close();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 66749982ee02845f4992ea52fe113376
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: