//#define HEAP_PROCESS
//#define HEAP_PRIVATE_STATIC
//#define HEAP_PRIVATE
//#define GLOBAL_ALLOC
#define VIRTUAL_ALLOC
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using miew.Concurrency;
using miew.Debugging;
using miew.Tokenization;
using miew.Enumerable;
#pragma warning disable 0649
using Va = miew.Memory.Virtual;
namespace agree
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// ParseControl : each instance of this class coordinates all aspects of parsing a single sentence:
/// - morphology phase (note: but not tokenization)
/// - coverage and chart dependencies
/// - construct parse chart
/// - main parse/packing phase
/// - derivations/unpacking phase
/// - statistics management
/// - task management, concurrency, system options
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public sealed partial class ParseControl : Allocator, ISysObj, IDisposable
{
public ParseControl(Grammar g)
: base(g.tm)
{
this.config = g.config;
this.parser_config = config.parser;
this.g = g;
this.tm = g.tm;
this.lexicon = g.lex;
this.stats = new Stats(this);
tf = new TaskFactory(
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskContinuationOptions.AttachedToParent,
TaskScheduler.Default);
}
public ParseControl(Grammar g, TokenSet ts_input)
: this(g)
{
this.ts_input = ts_input;
}
/// <summary>
/// Invariants
/// </summary>
public readonly Config config;
public readonly Config.Parser parser_config;
public readonly Grammar g;
public readonly Lexicon lexicon;
/// <summary>
/// Pertaining to this parse
/// </summary>
public TokenSet ts_input;
public String InputText { get { return ts_input.Source.Text; } }
public IEnumerable<IParseObj> chart_tokens;
public LexicalAnalysis la;
public ParseChart chart;
/// <summary>
/// Statistics for this parse
/// </summary>
readonly public Stats stats;
readonly Stopwatch timer = new Stopwatch();
/// <summary>
/// Task management
/// </summary>
readonly TaskFactory tf;
int c_tasks = 1;
int cancel = 0;
public bool f_parse_phase_complete = false;
public bool AnyPacking { get { return parser_config.packing != Config.Parser.PackingOpts.None; } }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Guarantee a parent task context so that nested tasks will be attached as child tasks.
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Task<ParseControl> StartRootTask(Func<Task> root_func)
{
timer.Start();
if (parser_config.c_tasks_max == -1)
{
root_func();
AfterChart();
return Tasks.FromResult(this);
}
//return Task.Factory.StartNew(
// () => root_func().Wait(),
// CancellationToken.None,
// TaskCreationOptions.None,
// TaskScheduler.Default)
return root_func()
.ContinueWith<ParseControl>(t =>
{
AggregateException aex = t.Exception;
if (aex != null)
{
aex = aex.Flatten();
var rgex = aex.InnerExceptions.OfType<ParseException>().ToArray();
if (rgex.Length == 1)
throw rgex[0];
throw aex;
}
return AfterChart();
}, TaskContinuationOptions.ExecuteSynchronously);
}
ParseControl AfterChart()
{
stats.Parsing.Chart.SetElapsed(timer.ElapsedMilliseconds);
f_parse_phase_complete = true;
chart.ReleaseChart(); // doesn't seem to help
chart.ClearSubscribers();
if (parser_config.packing == Config.Parser.PackingOpts.None)
stats.Parsing.Unpacking.c_derivations = stats.Parsing.Chart.c_root_edges;
return this;
}
public Task<ParseControl> Parse(TokenSet ts_input)
{
this.ts_input = ts_input;
Task<ParseControl> tpc = StartRootTask(() =>
{
/// continue from the morphology job ... to the post morphology job
/// this returns immediately, but the root task won't be signaled until all child tasks complete.
return SynchronousContinuation(MorphologyJob(ts_input), PostMorphologyJob);
});
/// non-parse activities go here; they will not delay the delivery of the parse chart
if (parser_config.f_autotune)
tpc.ContinueWith(t => g.tm.Autotune());
return tpc;
}
public bool TryEnterMultithread()
{
int ctm = parser_config.c_tasks_max;
if (ctm < int.MaxValue)
{
if (!config.system.MultiThreading || c_tasks >= ctm)
return false;
Interlocked.Increment(ref c_tasks);
}
return true;
}
public void LeaveMultithread()
{
if (parser_config.c_tasks_max < int.MaxValue)
Interlocked.Decrement(ref c_tasks);
}
public
#if NODE_INDEX
new
#endif
void Dispose()
{
stats.Totals.ms_time = timer.ElapsedMilliseconds;
#if NODE_INDEX
base.Dispose();
#endif
stats.Disconnect();
}
#if false
public Task<Y> SynchronousContinuation<X, Y>(Task<X> t_prev, Func<X, Y> f)
{
if (cfg.c_tasks_max == -1)
return Tasks.FromResult(f(t_prev.Result));
return t_prev.ContinueWith<Y>(t =>
{
return f(t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
}
public Task<X> SynchronousContinuation<X>(Task t_prev, Func<X> f)
{
if (cfg.c_tasks_max == -1)
return Tasks.FromResult(f());
return t_prev.ContinueWith<X>(t =>
{
return f();
}, TaskContinuationOptions.ExecuteSynchronously);
}
#endif
public Task SynchronousContinuation(Task t_prev, Action a)
{
if (parser_config.c_tasks_max == -1)
{
a();
return Tasks.CompletedTask;
}
else
return t_prev.ContinueWith(t => a(), TaskContinuationOptions.ExecuteSynchronously);
}
static void _nop(Task[] tsk) { }
public Task WhenAll(Action[] rga)
{
if (parser_config.c_tasks_max == int.MaxValue)
{
Task[] rgt = new Task[rga.Length];
for (int i = 0; i < rga.Length; i++)
rgt[i] = tf.StartNew(rga[i]);
rga = null;
return tf.ContinueWhenAll(rgt, _nop);
}
for (int i = 0; i < rga.Length; i++)
rga[i]();
return Tasks.CompletedTask;
}
[DebuggerHidden()]
internal Task StartAttachedChildTask(Action a)
{
if (parser_config.c_tasks_max == int.MaxValue)
return tf.StartNew(a);
if (c_tasks >= parser_config.c_tasks_max)
{
a();
return Tasks.CompletedTask;
}
Interlocked.Increment(ref c_tasks);
return tf.StartNew(() =>
{
a();
Interlocked.Decrement(ref c_tasks);
});
}
[DebuggerHidden()]
internal Task<X> StartAttachedChildTask<X>(Func<X> f)
{
if (parser_config.c_tasks_max == int.MaxValue)
return tf.StartNew(f);
if (c_tasks >= parser_config.c_tasks_max)
{
return Tasks.FromResult(f());
}
Interlocked.Increment(ref c_tasks);
return tf.StartNew<X>(() =>
{
X x = f();
Interlocked.Decrement(ref c_tasks);
return x;
});
}
[DebuggerHidden()]
internal Task StartAttachedChildTask<X>(Action<X> ax, X x)
{
if (parser_config.c_tasks_max == int.MaxValue)
return tf.StartNew(() => ax(x));
if (c_tasks >= parser_config.c_tasks_max)
{
ax(x);
return Tasks.CompletedTask;
}
Interlocked.Increment(ref c_tasks);
return tf.StartNew(() =>
{
ax(x);
Interlocked.Decrement(ref c_tasks);
});
}
//internal void ParallelLoop<T>(IEnumerable<T> ie, Action<T> a)
//{
// if (parser_config.c_tasks_max == int.MaxValue)
// Parallel.ForEach(ie, a);
// else
// foreach (T t in ie)
// a(t);
//}
public bool IsParseCancelled
{
get
{
if (cancel > 0)
return true;
if (timer.Elapsed.TotalSeconds <= parser_config.ItemTimeoutSeconds)
return false;
if (Interlocked.CompareExchange(ref cancel, 1, 0) == 0)
{
timer.Stop();
double ms = timer.ElapsedMilliseconds / 1000.0;
throw new ParseTimeoutException("({0:0.00} sec.)", ms);
}
return true;
}
}
public string SysObjName
{
get { return String.Format("pc-{0:X}", this.GetHashCode()); }
}
public string SysObjDescription
{
get { return String.Format("{0}", ts_input.Source.Text); }
}
public miew.ReadOnly.IReadOnlyDictionary<string, ISysObj> SysObjChildren
{
get { return chart.SysObjChildren; }
}
public ISysObj SysObjParent
{
get { return g; }
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public unsafe class Allocator
#if NODE_INDEX
: IDisposable
#endif
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr HeapCreate(uint flOptions, UIntPtr dwInitialSize, UIntPtr dwMaximumSize);
[DllImport("kernel32.dll", SetLastError = false)]
static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, UIntPtr dwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool HeapDestroy(IntPtr hHeap);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);
[DllImport("kernel32.dll")]
static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
[DllImport("kernel32.dll")]
static extern IntPtr GlobalFree(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern UInt32 GetLargePageMinimum();
#if HEAP_PRIVATE_STATIC
static Allocator()
{
heap = HeapCreate(0, new UIntPtr(1024 * 1024 * 20), UIntPtr.Zero);
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public bool UnifyCheck(Tfs tfs0, Tfs tfs1)
{
Config.Parser.UnifierType ut = tm.config.parser.checker;
if (ut == Config.Parser.UnifierType.n_way)
return NWayCheck.Check(tfs0, tfs1);
else if (ut == Config.Parser.UnifierType.qd)
return new UnificationQd(tm).Check(tfs0, tfs1);
else
return Unification.Check(tfs0, tfs1);
}
public Allocator(TypeMgr tm)
{
this.tm = tm;
#if NODE_INDEX
#if HEAP_PRIVATE
uint flOptions = 0;
if (!tm.config.system.MultiThreading)
flOptions |= 1;
heap = HeapCreate(flOptions, new UIntPtr(1024 * 1024 * 1), UIntPtr.Zero);
#elif VIRTUAL_ALLOC
heap = Va.VirtualAlloc(IntPtr.Zero, new UIntPtr(CB_VA_HEAP), Va.AllocationType.RESERVE, Va.MemoryProtection.NOACCESS);
if (heap.ToInt64() == 0)
throw new Exception();
#else
this.m_sl = new SpinLock(false);
this.hglobals = new List<IntPtr>();
#endif
#endif
}
public TypeMgr tm;
#if NODE_INDEX
#if HEAP_PRIVATE_STATIC
static
#endif
#if HEAP_PRIVATE_STATIC || HEAP_PRIVATE || VIRTUAL_ALLOC
IntPtr heap;
#endif
#if HEAP_PROCESS || HEAP_PRIVATE_STATIC || GLOBAL_ALLOC
List<IntPtr> hglobals;
SpinLock m_sl;
#endif
#if VIRTUAL_ALLOC
const long CB_VA_COMMIT_CHUNK = 0x8000;
const long CB_VA_HEAP = (long)2 * (long)1024 * (long)1024 * (long)1024;
long o_alloc = 0;
long o_commit = 0;
#endif
int cb_alloc = 0;
#endif
public void* Alloc(int cb)
{
#if NODE_INDEX
if (cb <= 0)
throw new Exception();
#if HEAP_PROCESS
IntPtr ip = Marshal.AllocHGlobal(cb);
#elif GLOBAL_ALLOC
IntPtr ip = GlobalAlloc(0, new UIntPtr((uint)cb));
#elif VIRTUAL_ALLOC
long o_end = Interlocked.Add(ref o_alloc, cb);
if (o_end >= CB_VA_HEAP)
throw new Exception();
long _tmp = o_commit;
if (o_end >= _tmp)
{
long q = (((o_end - _tmp) + CB_VA_COMMIT_CHUNK) & ~(CB_VA_COMMIT_CHUNK - 1));
Va.VirtualAlloc(new IntPtr(heap.ToInt64() + _tmp), new UIntPtr((uint)q), Va.AllocationType.COMMIT, Va.MemoryProtection.READWRITE);
Interlocked.CompareExchange(ref o_commit, _tmp + q, _tmp);
}
IntPtr ip = new IntPtr(heap.ToInt64() + o_end - cb);
#else
IntPtr ip = HeapAlloc(heap, 0, new UIntPtr((uint)cb));
#endif
#if HEAP_PROCESS || HEAP_PRIVATE_STATIC || GLOBAL_ALLOC
bool entered = false;
m_sl.Enter(ref entered);
cb_alloc += cb;
hglobals.Add(ip);
m_sl.Exit(false);
#else
cb_alloc += cb;
#endif
return ip.ToPointer();
#else
return null;
#endif
}
#if NODE_INDEX
public void Dispose()
{
#if DEBUG
if (this.GetType() == typeof(Allocator))
throw new Exception();
#endif
#if HEAP_PRIVATE || VIRTUAL_ALLOC
IntPtr _tmp = heap;
if (_tmp != IntPtr.Zero && Interlocked.CompareExchange(ref heap, IntPtr.Zero, _tmp) == _tmp)
{
#if VIRTUAL_ALLOC
if (!Va.VirtualFree(_tmp, UIntPtr.Zero, 0x8000))
throw new Exception();
#else
HeapDestroy(_tmp);
#endif
//Task.Factory.StartNew(() => HeapDestroy(_tmp));
}
#else
List<IntPtr> _tmp = hglobals;
if (_tmp != null && Interlocked.CompareExchange(ref hglobals, null, _tmp) == _tmp)
{
Task.Factory.StartNew(() =>
{
for (int i = 0; i < _tmp.Count; i++)
#if HEAP_PROCESS
Marshal.FreeHGlobal(_tmp[i]);
#elif GLOBAL_ALLOC
GlobalFree(_tmp[i]);
#else
HeapFree(heap, 0, _tmp[i]);
#endif
});
}
#endif
}
#endif
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class ParseException : Exception
{
public ParseException(String fmt, params Object[] args)
: base(fmt == null ? String.Empty : String.Format(fmt, args))
{
}
public ParseException()
: this(null)
{
}
};
public class ParseTimeoutException : ParseException
{
public ParseTimeoutException()
{
}
public ParseTimeoutException(String fmt, params Object[] args)
: base(String.Format(fmt, args))
{
}
};
public class LexicalAnalysisException : ParseException
{
public LexicalAnalysisException(String fmt, params Object[] args)
: base(fmt == null ? String.Empty : String.Format(fmt, args))
{
}
};
public class LexicalCoverageException : ParseException
{
public LexicalCoverageException(String fmt, params Object[] args)
: base(fmt == null ? String.Empty : String.Format(fmt, args))
{
}
};
}