mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-07-11 03:03:10 +00:00
Add the package ManagedEsent (microsoft/ManagedEsent@d358c07), and WelsonJS.Esent
338 lines
12 KiB
C#
338 lines
12 KiB
C#
//-----------------------------------------------------------------------
|
|
// <copyright file="Instance.cs" company="Microsoft Corporation">
|
|
// Copyright (c) Microsoft Corporation.
|
|
// </copyright>
|
|
//-----------------------------------------------------------------------
|
|
|
|
namespace Microsoft.Isam.Esent.Interop
|
|
{
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Security.Permissions;
|
|
|
|
using Microsoft.Isam.Esent.Interop.Vista;
|
|
using Microsoft.Win32.SafeHandles;
|
|
|
|
/// <summary>
|
|
/// A class that encapsulates a <see cref="JET_INSTANCE"/> in a disposable object. The
|
|
/// instance must be closed last and closing the instance releases all other
|
|
/// resources for the instance.
|
|
/// </summary>
|
|
public class Instance : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
/// <summary>
|
|
/// Parameters for the instance.
|
|
/// </summary>
|
|
private readonly InstanceParameters parameters;
|
|
|
|
/// <summary>
|
|
/// The name of the instance.
|
|
/// </summary>
|
|
private readonly string name;
|
|
|
|
/// <summary>
|
|
/// The display name of the instance.
|
|
/// </summary>
|
|
private readonly string displayName;
|
|
|
|
/// <summary>
|
|
/// The TermGrbit to be used at JetTerm time.
|
|
/// </summary>
|
|
private TermGrbit termGrbit;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the Instance class. The underlying
|
|
/// JET_INSTANCE is allocated, but not initialized.
|
|
/// </summary>
|
|
/// <param name="name">
|
|
/// The name of the instance. This string must be unique within a
|
|
/// given process hosting the database engine.
|
|
/// </param>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public Instance(string name) : this(name, name, TermGrbit.None)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the Instance class. The underlying
|
|
/// JET_INSTANCE is allocated, but not initialized.
|
|
/// </summary>
|
|
/// <param name="name">
|
|
/// The name of the instance. This string must be unique within a
|
|
/// given process hosting the database engine.
|
|
/// </param>
|
|
/// <param name="displayName">
|
|
/// A display name for the instance. This will be used in eventlog
|
|
/// entries.
|
|
/// </param>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public Instance(string name, string displayName) : this(name, displayName, TermGrbit.None)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the Instance class. The underlying
|
|
/// JET_INSTANCE is allocated, but not initialized.
|
|
/// </summary>
|
|
/// <param name="name">
|
|
/// The name of the instance. This string must be unique within a
|
|
/// given process hosting the database engine.
|
|
/// </param>
|
|
/// <param name="displayName">
|
|
/// A display name for the instance. This will be used in eventlog
|
|
/// entries.
|
|
/// </param>
|
|
/// <param name="termGrbit">
|
|
/// The TermGrbit to be used at JetTerm time.
|
|
/// </param>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public Instance(string name, string displayName, TermGrbit termGrbit) : base(true)
|
|
{
|
|
this.name = name;
|
|
this.displayName = displayName;
|
|
this.termGrbit = termGrbit;
|
|
|
|
JET_INSTANCE instance;
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
this.SetHandle(JET_INSTANCE.Nil.Value);
|
|
}
|
|
finally
|
|
{
|
|
// This is the code that we want in a constrained execution region.
|
|
// We need to avoid the situation where JetCreateInstance2 is called
|
|
// but the handle isn't set, so the instance is never terminated.
|
|
// This would happen, for example, if there was a ThreadAbortException
|
|
// between the call to JetCreateInstance2 and the call to SetHandle.
|
|
//
|
|
// If an Esent exception is generated we do not want to call SetHandle
|
|
// because the instance isn't valid. On the other hand if a different
|
|
// exception (out of memory or thread abort) is generated we still need
|
|
// to set the handle to avoid losing track of the instance. The call to
|
|
// JetCreateInstance2 is in the CER to make sure that the only exceptions
|
|
// which can be generated are from ESENT failures.
|
|
Api.JetCreateInstance2(out instance, this.name, this.displayName, CreateInstanceGrbit.None);
|
|
this.SetHandle(instance.Value);
|
|
}
|
|
|
|
this.parameters = new InstanceParameters(instance);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the JET_INSTANCE that this instance contains.
|
|
/// </summary>
|
|
public JET_INSTANCE JetInstance
|
|
{
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
get
|
|
{
|
|
this.CheckObjectIsNotDisposed();
|
|
return this.CreateInstanceFromHandle();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the InstanceParameters for this instance.
|
|
/// </summary>
|
|
public InstanceParameters Parameters
|
|
{
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
get
|
|
{
|
|
this.CheckObjectIsNotDisposed();
|
|
return this.parameters;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the TermGrbit for this instance.
|
|
/// </summary>
|
|
public TermGrbit TermGrbit
|
|
{
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
get
|
|
{
|
|
this.CheckObjectIsNotDisposed();
|
|
return this.termGrbit;
|
|
}
|
|
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
set
|
|
{
|
|
this.CheckObjectIsNotDisposed();
|
|
this.termGrbit = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provide implicit conversion of an Instance object to a JET_INSTANCE
|
|
/// structure. This is done so that an Instance can be used anywhere a
|
|
/// JET_INSTANCE is required.
|
|
/// </summary>
|
|
/// <param name="instance">The instance to convert.</param>
|
|
/// <returns>The JET_INSTANCE wrapped by the instance.</returns>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public static implicit operator JET_INSTANCE(Instance instance)
|
|
{
|
|
return instance.JetInstance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="Instance"/>.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="T:System.String"/> that represents the current <see cref="Instance"/>.
|
|
/// </returns>
|
|
public override string ToString()
|
|
{
|
|
return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", this.displayName, this.name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize the JET_INSTANCE.
|
|
/// </summary>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public void Init()
|
|
{
|
|
this.Init(InitGrbit.None);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize the JET_INSTANCE.
|
|
/// </summary>
|
|
/// <param name="grbit">
|
|
/// Initialization options.
|
|
/// </param>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public void Init(InitGrbit grbit)
|
|
{
|
|
this.CheckObjectIsNotDisposed();
|
|
JET_INSTANCE instance = this.JetInstance;
|
|
|
|
// Use a constrained region so that the handle is
|
|
// always set after JetInit2 is called.
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
// Remember that a failure in JetInit can zero the handle
|
|
// and that JetTerm should not be called in that case.
|
|
Api.JetInit2(ref instance, grbit);
|
|
}
|
|
finally
|
|
{
|
|
this.SetHandle(instance.Value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize the JET_INSTANCE. This API requires at least the
|
|
/// Vista version of ESENT.
|
|
/// </summary>
|
|
/// <param name="recoveryOptions">
|
|
/// Additional recovery parameters for remapping databases during
|
|
/// recovery, position where to stop recovery at, or recovery status.
|
|
/// </param>
|
|
/// <param name="grbit">
|
|
/// Initialization options.
|
|
/// </param>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public void Init(JET_RSTINFO recoveryOptions, InitGrbit grbit)
|
|
{
|
|
this.CheckObjectIsNotDisposed();
|
|
JET_INSTANCE instance = this.JetInstance;
|
|
|
|
// Use a constrained region so that the handle is
|
|
// always set after JetInit3 is called.
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
// Remember that a failure in JetInit can zero the handle
|
|
// and that JetTerm should not be called in that case.
|
|
VistaApi.JetInit3(ref instance, recoveryOptions, grbit);
|
|
}
|
|
finally
|
|
{
|
|
this.SetHandle(instance.Value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Terminate the JET_INSTANCE.
|
|
/// </summary>
|
|
[SuppressMessage(
|
|
"Microsoft.StyleCop.CSharp.MaintainabilityRules",
|
|
"SA1409:RemoveUnnecessaryCode",
|
|
Justification = "CER code belongs in the finally block, so the try clause is empty")]
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
public void Term()
|
|
{
|
|
// Use a constrained region so that the handle is
|
|
// always set as invalid after JetTerm is called.
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
// This try block deliberately left blank.
|
|
}
|
|
finally
|
|
{
|
|
// This is the code that we want in a constrained execution region.
|
|
// We need to avoid the situation where JetTerm is called
|
|
// but the handle isn't invalidated, so the instance is terminated again.
|
|
// This would happen, for example, if there was a ThreadAbortException
|
|
// between the call to JetTerm and the call to SetHandle.
|
|
//
|
|
// If an Esent exception is generated we do not want to invalidate the handle
|
|
// because the instance isn't necessarily terminated. On the other hand if a
|
|
// different exception (out of memory or thread abort) is generated we still need
|
|
// to invalidate the handle.
|
|
try
|
|
{
|
|
Api.JetTerm2(this.JetInstance, this.termGrbit);
|
|
}
|
|
catch (EsentDirtyShutdownException)
|
|
{
|
|
this.SetHandleAsInvalid();
|
|
throw;
|
|
}
|
|
|
|
this.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release the handle for this instance.
|
|
/// </summary>
|
|
/// <returns>True if the handle could be released.</returns>
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
// The object is already marked as invalid so don't check
|
|
var instance = this.CreateInstanceFromHandle();
|
|
return (int)JET_err.Success == Api.Impl.JetTerm2(instance, this.termGrbit);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a JET_INSTANCE from the internal handle value.
|
|
/// </summary>
|
|
/// <returns>A JET_INSTANCE containing the internal handle.</returns>
|
|
private JET_INSTANCE CreateInstanceFromHandle()
|
|
{
|
|
return new JET_INSTANCE { Value = this.handle };
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if this instance is invalid or closed.
|
|
/// </summary>
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
|
|
private void CheckObjectIsNotDisposed()
|
|
{
|
|
if (this.IsInvalid || this.IsClosed)
|
|
{
|
|
throw new ObjectDisposedException("Instance");
|
|
}
|
|
}
|
|
}
|
|
}
|