// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; namespace Spludlow.Data { /// /// Keep TextTable file locked while "using" this class /// Make sure you only use it for a short time so other users don't find the file locked for too long /// AppendMode will perform much better when simpily adding rows to the end of existing table, will only read schema, then you add the rows to append /// In Append mode if empty table is passed it will be cloned and used as the table rather than reading the file /// When using append mode you can prepare a table containing the rows to add in adavance then just set the LockedTextTable's table inside the using and that's it /// When append mode is false the whole table is read and then overwrites existing when finished /// An empty table can be passed that will be used if the file does not exist /// public class LockedTextTable : IDisposable { private FileStream FileStream = null; public DataTable Table = null; private Encoding Encoding; private bool AppendMode; private DataTable EmptyTable = null; public LockedTextTable(string filename, bool appendMode) { this.Start(filename, false, Encoding.Default, appendMode, null); } public LockedTextTable(string filename, Encoding encoding, bool appendMode) { this.Start(filename, false, encoding, appendMode, null); } public LockedTextTable(string filename, bool appendMode, DataTable emptyTable) { this.Start(filename, false, Encoding.Default, appendMode, emptyTable); } public LockedTextTable(string filename, bool removeEmptyEntries, Encoding encoding, bool appendMode, DataTable emptyTable) { this.Start(filename, removeEmptyEntries, encoding, appendMode, emptyTable); } private void Start(string filename, bool removeEmptyEntries, Encoding encoding, bool appendMode, DataTable emptyTable) { this.Encoding = encoding; this.AppendMode = appendMode; this.EmptyTable = emptyTable; this.FileStream = Spludlow.Io.Files.FileStreamOpenOrCreateReadWrite(filename); if (this.FileStream.Length == 0) { if (this.EmptyTable == null) throw new ApplicationException("LockedTextTable; No existing table and empty table not supplied: " + filename); Spludlow.Data.TextTable.Write(this.FileStream, this.EmptyTable, this.Encoding, false); this.FileStream.Position = 0; } if (this.AppendMode == true && emptyTable != null) this.Table = emptyTable.Clone(); else this.Table = Spludlow.Data.TextTable.ReadStream(this.FileStream, removeEmptyEntries, this.Encoding, this.AppendMode); } public void Dispose() { if (this.FileStream != null) { if (this.Table != null) { if (this.AppendMode == false) { this.FileStream.SetLength(0); this.FileStream.Position = 0; } else { this.FileStream.Seek(0, SeekOrigin.End); } Spludlow.Data.TextTable.Write(this.FileStream, this.Table, this.Encoding, this.AppendMode); } this.FileStream.Close(); this.FileStream.Dispose(); } } public static void TestLockedTextTable(string filename, int threads, int iterations) { Spludlow.Log.Report("TestLockedTextTable Starting"); if (File.Exists(filename) == true) File.Delete(filename); DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "Thread Pass Time", "Int32 Int32 DateTime", }); Spludlow.Data.TextTable.Write(filename, table); Task[] tasks = new Task[threads]; for (int index = 0; index < threads; ++index) { int threadId = index; tasks[index] = new Task(() => TestLockedTextTableWorker(filename, iterations, threadId)); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks); } public static void TestLockedTextTableWorker(string filename, int iterations, int threadId) { Random rand = new Random(); for (int pass = 0; pass < iterations; ++pass) { using (LockedTextTable lockedTable = new LockedTextTable(filename, false)) { System.Threading.Thread.Sleep(rand.Next(100)); lockedTable.Table.Rows.Add(threadId, pass, DateTime.Now); } System.Threading.Thread.Sleep(rand.Next(500)); Spludlow.Log.Info("TestLockedTextTable: Thread:" + threadId + ", Pass:" + pass); } } } }