mirror of
				https://github.com/gnh1201/welsonjs.git
				synced 2025-10-31 04:51:17 +00:00 
			
		
		
		
	Add the package ManagedEsent (microsoft/ManagedEsent@d358c07), and WelsonJS.Esent
		
			
				
	
	
		
			191 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //-----------------------------------------------------------------------
 | |
| // <copyright file="jet_pfnstatus.cs" company="Microsoft Corporation">
 | |
| //     Copyright (c) Microsoft Corporation.
 | |
| // </copyright>
 | |
| //-----------------------------------------------------------------------
 | |
| 
 | |
| namespace Microsoft.Isam.Esent.Interop
 | |
| {
 | |
|     using System;
 | |
|     using System.Diagnostics;
 | |
|     using System.Globalization;
 | |
|     using System.Reflection;
 | |
|     using System.Runtime.CompilerServices;
 | |
|     using System.Threading;
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Receives information about the progress of long-running operations,
 | |
|     /// such as defragmentation, backup, or restore operations. During such
 | |
|     /// operations, the database engine calls this callback function to give
 | |
|     ///  an update on the progress of the operation.
 | |
|     /// </summary>
 | |
|     /// <param name="sesid">
 | |
|     /// The session with which the long running operation was called.
 | |
|     /// </param>
 | |
|     /// <param name="snp">The type of operation.</param>
 | |
|     /// <param name="snt">The status of the operation.</param>
 | |
|     /// <param name="data">Optional data. May be a <see cref="JET_SNPROG"/>.</param>
 | |
|     /// <returns>An error code.</returns>
 | |
|     public delegate JET_err JET_PFNSTATUS(JET_SESID sesid, JET_SNP snp, JET_SNT snt, object data);
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Receives information about the progress of long-running operations,
 | |
|     /// such as defragmentation, backup, or restore operations. During such
 | |
|     /// operations, the database engine calls this callback function to give
 | |
|     ///  an update on the progress of the operation.
 | |
|     /// </summary>
 | |
|     /// <remarks>
 | |
|     /// This is the internal version of the callback. The final parameter is
 | |
|     /// a void* pointer, which may point to a NATIVE_SNPROG.
 | |
|     /// </remarks>
 | |
|     /// <param name="nativeSesid">
 | |
|     /// The session with which the long running operation was called.
 | |
|     /// </param>
 | |
|     /// <param name="snp">The type of operation.</param>
 | |
|     /// <param name="snt">The status of the operation.</param>
 | |
|     /// <param name="snprog">Optional <see cref="NATIVE_SNPROG"/>.</param>
 | |
|     /// <returns>An error code.</returns>
 | |
|     internal delegate JET_err NATIVE_PFNSTATUS(IntPtr nativeSesid, uint snp, uint snt, IntPtr snprog);
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Wraps a NATIVE_PFNSTATUS callback around a JET_PFNSTATUS. This is
 | |
|     /// used to convert the snprog argument to a managed snprog.
 | |
|     /// </summary>
 | |
|     internal sealed class StatusCallbackWrapper
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// API call tracing.
 | |
|         /// </summary>
 | |
|         private static readonly TraceSwitch TraceSwitch = new TraceSwitch("ESENT StatusCallbackWrapper", "Wrapper around unmanaged ESENT status callback");
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The wrapped status callback.
 | |
|         /// </summary>
 | |
|         private readonly JET_PFNSTATUS wrappedCallback;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The native version of the callback. This will be a closure (because we are wrapping
 | |
|         /// a non-static method) so keep track of it here to make sure it isn't garbage collected.
 | |
|         /// </summary>
 | |
|         private readonly NATIVE_PFNSTATUS nativeCallback;
 | |
| 
 | |
| #if !MANAGEDESENT_ON_WSA
 | |
|         /// <summary>
 | |
|         /// Initializes static members of the <see cref="StatusCallbackWrapper"/> class. 
 | |
|         /// </summary>
 | |
|         static StatusCallbackWrapper()
 | |
|         {
 | |
|             // We don't want a JIT failure when trying to execute the callback
 | |
|             // because that would throw an exception through ESENT, corrupting it.
 | |
|             // It is fine for the wrapped callback to fail because CallbackImpl
 | |
|             // will catch the exception and deal with it.
 | |
|             RuntimeHelpers.PrepareMethod(typeof(StatusCallbackWrapper).GetMethod(
 | |
|                 "CallbackImpl",
 | |
|                 BindingFlags.NonPublic | BindingFlags.Instance).MethodHandle);
 | |
|         }
 | |
| #endif
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes a new instance of the StatusCallbackWrapper class.
 | |
|         /// </summary>
 | |
|         /// <param name="wrappedCallback">
 | |
|         /// The managed callback to use.
 | |
|         /// </param>
 | |
|         public StatusCallbackWrapper(JET_PFNSTATUS wrappedCallback)
 | |
|         {
 | |
|             this.wrappedCallback = wrappedCallback;
 | |
|             this.nativeCallback = wrappedCallback != null ? this.CallbackImpl : (NATIVE_PFNSTATUS)null;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a NATIVE_PFNSTATUS callback that wraps the managed callback.
 | |
|         /// </summary>
 | |
|         public NATIVE_PFNSTATUS NativeCallback
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return this.nativeCallback;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets or sets the saved exception. If the callback throws an exception
 | |
|         /// it is saved here and should be rethrown when the API call finishes.
 | |
|         /// </summary>
 | |
|         private Exception SavedException { get; set; }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets or sets a value indicating whether the thread was aborted during
 | |
|         /// the callback.
 | |
|         /// </summary>
 | |
|         private bool ThreadWasAborted { get; set; }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// If an exception was generated during a callback throw it.
 | |
|         /// </summary>
 | |
|         public void ThrowSavedException()
 | |
|         {
 | |
| #if !MANAGEDESENT_ON_WSA // Thread model has changed in Windows store apps.
 | |
|             if (this.ThreadWasAborted)
 | |
|             {
 | |
|                 Thread.CurrentThread.Abort();
 | |
|             }
 | |
| #endif
 | |
| 
 | |
|             if (null != this.SavedException)
 | |
|             {
 | |
|                 throw this.SavedException;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Callback function for native code. We don't want to throw an exception through
 | |
|         /// unmanaged ESENT because that will corrupt ESENT's internal state. Instead we
 | |
|         /// catch all exceptions and return an error instead. We use a CER to make catching
 | |
|         /// the exceptions as reliable as possible.
 | |
|         /// </summary>
 | |
|         /// <param name="nativeSesid">
 | |
|         /// The session with which the long running operation was called.
 | |
|         /// </param>
 | |
|         /// <param name="nativeSnp">The type of operation.</param>
 | |
|         /// <param name="nativeSnt">The status of the operation.</param>
 | |
|         /// <param name="nativeData">Optional <see cref="NATIVE_SNPROG"/>.</param>
 | |
|         /// <returns>An error code.</returns>
 | |
|         private JET_err CallbackImpl(IntPtr nativeSesid, uint nativeSnp, uint nativeSnt, IntPtr nativeData)
 | |
|         {
 | |
|             RuntimeHelpers.PrepareConstrainedRegions();
 | |
|             try
 | |
|             {
 | |
|                 var sesid = new JET_SESID { Value = nativeSesid };
 | |
|                 JET_SNP snp = (JET_SNP)nativeSnp;
 | |
|                 JET_SNT snt = (JET_SNT)nativeSnt;
 | |
|                 object data = CallbackDataConverter.GetManagedData(nativeData, snp, snt);
 | |
|                 return this.wrappedCallback(sesid, snp, snt, data);
 | |
|             }
 | |
| #if !MANAGEDESENT_ON_WSA // Thread model has changed in windows store apps.
 | |
|             catch (ThreadAbortException)
 | |
|             {
 | |
|                 Trace.WriteLineIf(TraceSwitch.TraceWarning, "Caught ThreadAbortException");
 | |
| 
 | |
|                 // Stop the thread abort and let the unmanaged ESENT code finish.
 | |
|                 // ThrowSavedException will call Thread.Abort() again.
 | |
|                 this.ThreadWasAborted = true;
 | |
|                 LibraryHelpers.ThreadResetAbort();
 | |
|                 return JET_err.CallbackFailed;
 | |
|             }
 | |
| #endif
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 Trace.WriteLineIf(
 | |
|                     TraceSwitch.TraceWarning,
 | |
|                     string.Format(CultureInfo.InvariantCulture, "Caught Exception {0}", ex));
 | |
|                 this.SavedException = ex;
 | |
|                 return JET_err.CallbackFailed;
 | |
|             }
 | |
| 
 | |
|             // What happens if the thread is aborted here, outside of the CER?
 | |
|             // We probably throw the exception through ESENT, which isn't good.
 | |
|         }
 | |
|     }
 | |
| }
 |