Skip to content

Commit

Permalink
Fixed issues with concurrent usage. (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
DJGosnell authored Jan 29, 2025
1 parent 4cfb9f5 commit f0e0d39
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 71 deletions.
2 changes: 1 addition & 1 deletion src/DtronixPdf.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Company>Dtronix</Company>
<Product>Dtronix PDF</Product>
<Copyright>Copyright © Dtronix 2024</Copyright>
<Copyright>Copyright © Dtronix 2025</Copyright>
<Authors>DJGosnell</Authors>
<PackageProjectUrl>https://github.com/Dtronix/DtronixPdf</PackageProjectUrl>
<RepositoryUrl>https://github.com/Dtronix/DtronixPdf</RepositoryUrl>
Expand Down
4 changes: 2 additions & 2 deletions src/DtronixPdf/DtronixPdf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Import Project="..\DtronixPdf.props" />
<PropertyGroup>
<Description>Tool to view and perform common modifications to PDFs. Based on PDFium.</Description>
<Version>1.2.1.0</Version>
<Version>1.3.0.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
Expand All @@ -16,6 +16,6 @@
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<PackageReference Include="PDFiumCore" Version="130.0.6693" />
<PackageReference Include="PDFiumCore" Version="134.0.6982" />
</ItemGroup>
</Project>
39 changes: 21 additions & 18 deletions src/DtronixPdf/PDFiumManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@ namespace DtronixPdf

public class PdfiumManager
{
private static bool IsInitialized;
private static bool _isInitialized;

private static PdfiumManager _managerDefaultInstance;
public static PdfiumManager Default => _managerDefaultInstance ??= new PdfiumManager();

private readonly PdfActionSynchronizer _synchronizer;
/// <summary>
/// Gets the <see cref="PdfActionSynchronizer"/> instance used to synchronize actions in a PDF document.
/// </summary>
public PdfActionSynchronizer Synchronizer { get; }

private readonly List<PdfDocument> LoadedDocuments = new ();
private readonly ConcurrentDictionary<PdfDocument, PdfDocument> _loadedDocuments = new ();

private static readonly ConcurrentBag<PdfiumManager> LoadedManagers = new ();
private static readonly ConcurrentBag<PdfiumManager> _loadedManagers = new ();

private PdfiumManager()
{
LoadedManagers.Add(this);
_loadedManagers.Add(this);

_synchronizer = new PdfActionSynchronizer();
Synchronizer = new PdfActionSynchronizer();
}

/// <summary>
Expand All @@ -32,38 +35,38 @@ private PdfiumManager()
/// <returns></returns>
internal static void Initialize()
{
if (IsInitialized)
if (_isInitialized)
return;

IsInitialized = true;
_isInitialized = true;
// Initialize the library.
Default._synchronizer.SyncExec(fpdfview.FPDF_InitLibrary);
Default.Synchronizer.SyncExec(fpdfview.FPDF_InitLibrary);
}

public static void Unload()
{
if (!IsInitialized)
if (!_isInitialized)
return;

foreach (var pdfiumCoreManager in LoadedManagers)
foreach (var pdfiumCoreManager in _loadedManagers)
{
if (pdfiumCoreManager.LoadedDocuments.Count > 0)
if (pdfiumCoreManager._loadedDocuments.Count > 0)
throw new InvalidOperationException("Can't destroy loaded library since it is still in use by PdfDocument(s)");
}

IsInitialized = false;
_isInitialized = false;

Default._synchronizer.SyncExec(fpdfview.FPDF_DestroyLibrary);
Default.Synchronizer.SyncExec(fpdfview.FPDF_DestroyLibrary);
}

internal void AddDocument(PdfDocument document)
{
if (document == null)
throw new ArgumentNullException(nameof(document));

lock (LoadedDocuments)
lock (_loadedDocuments)
{
LoadedDocuments.Add(document);
_loadedDocuments.TryAdd(document, document);
}
}

Expand All @@ -72,9 +75,9 @@ internal void RemoveDocument(PdfDocument document)
if (document == null)
throw new ArgumentNullException(nameof(document));

lock (LoadedDocuments)
lock (_loadedDocuments)
{
LoadedDocuments.Remove(document);
_loadedDocuments.Remove(document, out _);
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions src/DtronixPdf/PdfActionSynchronizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,41 @@ namespace DtronixPdf
public class PdfActionSynchronizer

Check warning on line 6 in src/DtronixPdf/PdfActionSynchronizer.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'PdfActionSynchronizer'
{
public readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1);

Check warning on line 8 in src/DtronixPdf/PdfActionSynchronizer.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'PdfActionSynchronizer.Semaphore'
private const int Timeout = 60_000;

public void SyncExec(Action action)

Check warning on line 11 in src/DtronixPdf/PdfActionSynchronizer.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'PdfActionSynchronizer.SyncExec(Action)'
{
bool acquired = false;
try
{
Semaphore.Wait();
Semaphore.Wait(Timeout);
acquired = true;
action();
Semaphore.Release();
}
catch
{
Semaphore.Release();
if(acquired)
Semaphore.Release();
throw;
}
}

public T SyncExec<T>(Func<T> function)

Check warning on line 29 in src/DtronixPdf/PdfActionSynchronizer.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'PdfActionSynchronizer.SyncExec<T>(Func<T>)'
{
bool acquired = false;
try
{
Semaphore.Wait();
Semaphore.Wait(Timeout);
acquired = true;
var result = function();
Semaphore.Release();
return result;
}
catch
{
Semaphore.Release();
if (acquired)
Semaphore.Release();
throw;
}
}
Expand Down
7 changes: 1 addition & 6 deletions src/DtronixPdf/PdfBitmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ public class PdfBitmap : IDisposable
{
private readonly FpdfBitmapT _pdfBitmap;

private readonly PdfActionSynchronizer _synchronizer;

public float Scale { get; }

Check warning on line 11 in src/DtronixPdf/PdfBitmap.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'PdfBitmap.Scale'

/// <summary>
Expand All @@ -31,12 +29,10 @@ public class PdfBitmap : IDisposable
/// Only call within the synchronizer since dll calls are made.
/// </summary>
/// <param name="pdfBitmap"></param>
/// <param name="synchronizer"></param>
/// <param name="scale"></param>
/// <param name="viewport">MinX, MinY, MaxX, MaxY</param>
internal PdfBitmap(
FpdfBitmapT pdfBitmap,
PdfActionSynchronizer synchronizer,
float scale,
Vector128<float> viewport)
{
Expand All @@ -45,7 +41,6 @@ internal PdfBitmap(
Width = (int)viewport.GetWidth();
Height = (int)viewport.GetHeight();
Pointer = fpdfview.FPDFBitmapGetBuffer(_pdfBitmap);
_synchronizer = synchronizer;
Scale = scale;
Viewport = viewport;
}
Expand All @@ -57,7 +52,7 @@ public void Dispose()

IsDisposed = true;

_synchronizer.SyncExec(() => fpdfview.FPDFBitmapDestroy(_pdfBitmap));
PdfiumManager.Default.Synchronizer.SyncExec(() => fpdfview.FPDFBitmapDestroy(_pdfBitmap));
}
}
}
37 changes: 17 additions & 20 deletions src/DtronixPdf/PdfDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,38 @@ public class PdfDocument : IDisposable
{
internal readonly FpdfDocumentT Instance;

internal readonly PdfActionSynchronizer Synchronizer;

private bool _isDisposed = false;
private IntPtr? _documentPointer;

//private static PdfActionSynchronizer _synchronizer = new PdfActionSynchronizer();

public int Pages { get; private set; }

private PdfDocument(PdfActionSynchronizer synchronizer, FpdfDocumentT instance)
private PdfDocument(FpdfDocumentT instance)
{
Synchronizer = synchronizer;
Instance = instance;
PdfiumManager.Default.AddDocument(this);
}

public static PdfDocument Load(string path, string password)
{
PdfiumManager.Initialize();

var synchronizer = new PdfActionSynchronizer();

var document = synchronizer.SyncExec(() => fpdfview.FPDF_LoadDocument(path, password));
var pages = synchronizer.SyncExec(() => fpdfview.FPDF_GetPageCount(document));

var document = PdfiumManager.Default.Synchronizer.SyncExec(() => fpdfview.FPDF_LoadDocument(path, password));
var pages = PdfiumManager.Default.Synchronizer.SyncExec(() => fpdfview.FPDF_GetPageCount(document));

if (document == null)
return null;

var pdfDocument = new PdfDocument(synchronizer, document) { Pages = pages, };
var pdfDocument = new PdfDocument(document) { Pages = pages, };

return pdfDocument;

}

public static unsafe PdfDocument Load(Stream stream, string password)
{
var synchronizer = new PdfActionSynchronizer();
//var synchronizer = new PdfActionSynchronizer();

var length = (int)stream.Length;

Expand All @@ -61,7 +58,7 @@ public static unsafe PdfDocument Load(Stream stream, string password)
PdfiumManager.Initialize();

int pages = -1;
var result = synchronizer.SyncExec(() =>
var result = PdfiumManager.Default.Synchronizer.SyncExec(() =>
{
var document = fpdfview.FPDF_LoadMemDocument(pointer, length, password);
pages = fpdfview.FPDF_GetPageCount(document);
Expand All @@ -71,7 +68,7 @@ public static unsafe PdfDocument Load(Stream stream, string password)
if (result == null)
return null;

var pdfDocument = new PdfDocument(synchronizer, result)
var pdfDocument = new PdfDocument(result)
{
Pages = pages,
_documentPointer = pointer
Expand All @@ -82,14 +79,14 @@ public static unsafe PdfDocument Load(Stream stream, string password)

public static PdfDocument Create()
{
var synchronizer = new PdfActionSynchronizer();
//var synchronizer = new PdfActionSynchronizer();

var result = synchronizer.SyncExec(fpdf_edit.FPDF_CreateNewDocument);
var result = PdfiumManager.Default.Synchronizer.SyncExec(fpdf_edit.FPDF_CreateNewDocument);

if (result == null)
return null;

return new PdfDocument(synchronizer, result);
return new PdfDocument(result);
}

public PdfPage GetPage(int pageIndex)
Expand All @@ -108,7 +105,7 @@ public PdfPage GetPage(int pageIndex)
/// <returns>True on success, false on failure.</returns>
public bool ImportPages(PdfDocument document, string pageRange, int insertIndex)
{
return Synchronizer.SyncExec(() =>
return PdfiumManager.Default.Synchronizer.SyncExec(() =>
fpdf_ppo.FPDF_ImportPages(Instance, document.Instance, pageRange, insertIndex) == 1);
}

Expand All @@ -132,7 +129,7 @@ public PdfDocument ExtractPages(string pageRange)
/// <returns>True on success, false on failure.</returns>
public void DeletePage(int pageIndex)
{
Synchronizer.SyncExec(() => fpdf_edit.FPDFPageDelete(Instance, pageIndex));
PdfiumManager.Default.Synchronizer.SyncExec(() => fpdf_edit.FPDFPageDelete(Instance, pageIndex));
}


Expand Down Expand Up @@ -162,7 +159,7 @@ public bool Save(Stream stream)
#define FPDF_REMOVE_SECURITY 3
*/

var result = Synchronizer.SyncExec(() => fpdf_save.FPDF_SaveAsCopy(Instance, writer, 1));
var result = PdfiumManager.Default.Synchronizer.SyncExec(() => fpdf_save.FPDF_SaveAsCopy(Instance, writer, 1));

return result == 1;
}
Expand All @@ -174,7 +171,7 @@ public unsafe void Dispose()

_isDisposed = true;

Synchronizer.SyncExec(() => fpdfview.FPDF_CloseDocument(Instance));
PdfiumManager.Default.Synchronizer.SyncExec(() => fpdfview.FPDF_CloseDocument(Instance));

PdfiumManager.Default.RemoveDocument(this);

Expand Down
16 changes: 5 additions & 11 deletions src/DtronixPdf/PdfPage.Edit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public partial class PdfPage
/// <returns></returns>
public void SetRotation(int rotation)
{
Document.Synchronizer.SyncExec(() => fpdf_edit.FPDFPageSetRotation(PageInstance, rotation));
PdfiumManager.Default.Synchronizer.SyncExec(() => fpdf_edit.FPDFPageSetRotation(PageInstance, rotation));
}

/// <summary>
Expand All @@ -31,22 +31,16 @@ public void SetRotation(int rotation)
/// </returns>
public int GetRotation()
{
return Document.Synchronizer.SyncExec(() => fpdf_edit.FPDFPageGetRotation(PageInstance));
return PdfiumManager.Default.Synchronizer.SyncExec(() => fpdf_edit.FPDFPageGetRotation(PageInstance));
}


/// <summary>
/// Rotates the page
/// Deletes the page.
/// </summary>
/// <param name="rotation">
/// <para>0 - No rotation.</para>
/// <para>1 - Rotated 90 degrees clockwise.</para>
/// <para>2 - Rotated 180 degrees clockwise.</para>
/// <para>3 - Rotated 270 degrees clockwise.</para>
/// </param>
/// <returns></returns>
public void Delete()
{
Document.Synchronizer.SyncExec(() => fpdf_edit.FPDFPageDelete(Document.Instance, InitialIndex));
PdfiumManager.Default.Synchronizer.SyncExec(() => fpdf_edit.FPDFPageDelete(Document.Instance, InitialIndex));
}
}
}
Loading

0 comments on commit f0e0d39

Please sign in to comment.