public void VerifyWrapperIsCollectedWhenCallbackIsCollected() { JET_CALLBACK callback = CreateCallback(); var callbackRef = new WeakReference(callback); var callbackWrappers = new CallbackWrappers(); var wrapperRef = new WeakReference(callbackWrappers.Add(callback)); callback = null; RunFullGarbageCollection(); callbackWrappers.Collect(); RunFullGarbageCollection(); // In DEBUG test code, the objects remain alive for an indeterminate amount of time, for some reason. // Note that they do get collected if a RETAIL test code is used, even if the product code is DEBUG // so it must be something to do with assigning the local variable 'callback' to null and the effect that // it has on garbage collecting weak references to it. #if !DEBUG Assert.IsFalse(callbackRef.IsAlive); Assert.IsFalse(wrapperRef.IsAlive); #endif // Avoid premature collection of these objects GC.KeepAlive(callbackWrappers); }
public void VerifyIsWrappingReturnsFalseForNoMatch() { JET_CALLBACK callback = CreateCallback(); var wrapper = new JetCallbackWrapper(callback); Assert.IsFalse(wrapper.IsWrapping(CreateCallback())); GC.KeepAlive(callback); }
public void VerifyAddingSameCallbackReturnsSameWrapper() { JET_CALLBACK callback = CreateCallback(); var callbackWrappers = new CallbackWrappers(); var wrapper = callbackWrappers.Add(callback); Assert.AreEqual(wrapper, callbackWrappers.Add(callback)); }
public void VerifyAddingCallbackReturnsWrapper() { JET_CALLBACK callback = CreateCallback(); var callbackWrappers = new CallbackWrappers(); var wrapper = callbackWrappers.Add(callback); Assert.IsNotNull(callbackWrappers.Add(callback)); }
public void VerifyNativeCallbackIsNotGarbageCollected() { JET_CALLBACK callback = CreateCallback(); var callbackWrappers = new CallbackWrappers(); JetCallbackWrapper wrapper = callbackWrappers.Add(callback); WeakReference weakRef = new WeakReference(wrapper.NativeCallback); RunFullGarbageCollection(); Assert.IsTrue(weakRef.IsAlive); GC.KeepAlive(wrapper); }
/// <summary> /// Look in the list of callback wrappers to see if there is already an entry for /// this callback. /// </summary> /// <param name="callback">The callback to look for.</param> /// <param name="wrapper">Returns the wrapper, if found.</param> /// <returns>True if a wrapper was found, false otherwise.</returns> private bool TryFindWrapperFor(JET_CALLBACK callback, out JetCallbackWrapper wrapper) { foreach (JetCallbackWrapper w in this.callbackWrappers) { if (w.IsWrapping(callback)) { wrapper = w; return(true); } } wrapper = null; return(false); }
/// <summary> /// Wrap a callback and returns its wrapper. If the callback is /// already wrapped then the existing wrapper is returned. /// </summary> /// <param name="callback">The callback to add.</param> /// <returns>The callback wrapper for the callback.</returns> public JetCallbackWrapper Add(JET_CALLBACK callback) { lock (this.lockObject) { JetCallbackWrapper wrapper; if (!this.TryFindWrapperFor(callback, out wrapper)) { wrapper = new JetCallbackWrapper(callback); this.callbackWrappers.Add(wrapper); } return wrapper; } }
/// <summary> /// Look in the list of callback wrappers to see if there is already an entry for /// this callback. /// </summary> /// <param name="callback">The callback to look for.</param> /// <param name="wrapper">Returns the wrapper, if found.</param> /// <returns>True if a wrapper was found, false otherwise.</returns> private bool TryFindWrapperFor(JET_CALLBACK callback, out JetCallbackWrapper wrapper) { foreach (JetCallbackWrapper w in this.callbackWrappers) { if (w.IsWrapping(callback)) { wrapper = w; return true; } } wrapper = null; return false; }
/// <summary> /// Wrap a callback and returns its wrapper. If the callback is /// already wrapped then the existing wrapper is returned. /// </summary> /// <param name="callback">The callback to add.</param> /// <returns>The callback wrapper for the callback.</returns> public JetCallbackWrapper Add(JET_CALLBACK callback) { lock (this.lockObject) { JetCallbackWrapper wrapper; if (!this.TryFindWrapperFor(callback, out wrapper)) { wrapper = new JetCallbackWrapper(callback); this.callbackWrappers.Add(wrapper); } return(wrapper); } }
public void VerifyWrapperIsAliveWhenCallbackIsAlive() { JET_CALLBACK callback = CreateCallback(); var callbackWrappers = new CallbackWrappers(); var wrapperRef = new WeakReference(callbackWrappers.Add(callback)); RunFullGarbageCollection(); callbackWrappers.Collect(); RunFullGarbageCollection(); Assert.IsTrue(wrapperRef.IsAlive); // Avoid premature collection of these objects GC.KeepAlive(callback); GC.KeepAlive(callbackWrappers); }
/// <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 for which the callback is being made.</param> /// <param name="nativeDbid">The database for which the callback is being made.</param> /// <param name="nativeTableid">The cursor for which the callback is being made.</param> /// <param name="nativeCbtyp">The operation for which the callback is being made.</param> /// <param name="arg1">First callback-specific argument.</param> /// <param name="arg2">Second callback-specific argument.</param> /// <param name="nativeContext">Callback context.</param> /// <param name="unused">This parameter is not used.</param> /// <returns>An ESENT error code.</returns> private JET_err CallbackImpl( IntPtr nativeSesid, uint nativeDbid, IntPtr nativeTableid, uint nativeCbtyp, IntPtr arg1, IntPtr arg2, IntPtr nativeContext, IntPtr unused) { RuntimeHelpers.PrepareConstrainedRegions(); try { var sesid = new JET_SESID { Value = nativeSesid }; var dbid = new JET_DBID { Value = nativeDbid }; var tableid = new JET_TABLEID { Value = nativeTableid }; JET_cbtyp cbtyp = (JET_cbtyp)nativeCbtyp; Debug.Assert(this.wrappedCallback.IsAlive, "Wrapped callback has been garbage collected"); // This will throw an exception if the wrapped callback has been collected. The exception // will be handled below. JET_CALLBACK callback = (JET_CALLBACK)this.wrappedCallback.Target; return(callback(sesid, dbid, tableid, cbtyp, null, null, nativeContext, IntPtr.Zero)); } catch (Exception ex) { // Thread aborts aren't handled here. ESENT callbacks can execute on client threads or // internal ESENT threads so it isn't clear what should be done on an abort. Trace.WriteLineIf( traceSwitch.TraceWarning, String.Format(CultureInfo.InvariantCulture, "Caught Exception {0}", ex)); return(JET_err.CallbackFailed); } }
public void TestJetDefragment2Callback() { ManualResetEvent oldFinishedEvent = new ManualResetEvent(false); JET_CALLBACK callback = (sesid, dbid, tableid, cbtyp, arg1, arg2, context, unused) => { oldFinishedEvent.Set(); return(JET_err.Success); }; int passes = 1; int seconds = 1; Api.JetDefragment2(this.sesid, this.dbid, null, ref passes, ref seconds, callback, DefragGrbit.BatchStart); Assert.IsTrue( oldFinishedEvent.WaitOne(TimeSpan.FromSeconds(10)), "Online Defragmentation Callback not called"); Api.JetDefragment2(this.sesid, this.dbid, null, ref passes, ref seconds, null, DefragGrbit.BatchStop); // Don't let the callback be collected before OLD finishes. GC.KeepAlive(callback); }
public void VerifyRegisteredCallbackIsCalled() { bool callbackWasCalled = false; JET_CALLBACK callback = (s, d, t, cbtyp, arg1, arg2, context, unused) => { callbackWasCalled = true; return(JET_err.Success); }; JET_HANDLE callbackId; Api.JetRegisterCallback(this.sesid, this.tableid, JET_cbtyp.BeforeInsert, callback, IntPtr.Zero, out callbackId); using (var transaction = new Transaction(this.sesid)) using (var update = new Update(this.sesid, this.tableid, JET_prep.Insert)) { Api.SetColumn(this.sesid, this.tableid, this.columnidLongText, Any.String, Encoding.Unicode); update.Save(); transaction.Commit(CommitTransactionGrbit.None); } Assert.IsTrue(callbackWasCalled, "callback was not called"); }
/// <summary> /// Initializes a new instance of the JetCallbackWrapper class. /// </summary> /// <param name="callback"> /// The managed callback to use. /// </param> public JetCallbackWrapper(JET_CALLBACK callback) { this.wrappedCallback = new WeakReference(callback); Debug.Assert(this.wrappedCallback.IsAlive, "Callback isn't alive"); }
/// <summary> /// Determine if the callback is wrapping the specified JET_CALLBACK. /// </summary> /// <param name="callback">The callback.</param> /// <returns>True if this wrapper is wrapping the callback.</returns> public bool IsWrapping(JET_CALLBACK callback) { return(callback.Equals(this.wrappedCallback.Target)); }
/// <summary> /// Initializes a new instance of the <see cref="JetSetGetLs"/> class. /// </summary> public JetSetGetLs() { this.runtimeCallback = new JET_CALLBACK(this.RuntimeCallback); }
/// <summary> /// Sets database configuration options. /// </summary> /// <param name="instance"> /// The instance to set the option on or <see cref="JET_INSTANCE.Nil"/> /// to set the option on all instances. /// </param> /// <param name="sesid">The session to use.</param> /// <param name="paramid">The parameter to set.</param> /// <param name="paramValue">The value of the parameter to set, if the parameter is a JET_CALLBACK.</param> /// <param name="paramString">The value of the parameter to set, if the parameter is a string type.</param> /// <returns>An ESENT warning code.</returns> public static JET_wrn JetSetSystemParameter(JET_INSTANCE instance, JET_SESID sesid, JET_param paramid, JET_CALLBACK paramValue, string paramString) { return Api.Check(Impl.JetSetSystemParameter(instance, sesid, paramid, paramValue, paramString)); }
/// <summary> /// Allows the application to configure the database engine to issue /// notifications to the application for specific events. These /// notifications are associated with a specific table and remain in /// effect only until the instance containing the table is shut down /// using <see cref="JetTerm"/>. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid"> /// A cursor opened on the table that the callback should be /// registered on. /// </param> /// <param name="cbtyp"> /// The callback reasons for which the application wishes to receive notifications. /// </param> /// <param name="callback">The callback function.</param> /// <param name="context">A context that will be given to the callback.</param> /// <param name="callbackId"> /// A handle that can later be used to cancel the registration of the given /// callback function using <see cref="JetUnregisterCallback"/>. /// </param> public static void JetRegisterCallback( JET_SESID sesid, JET_TABLEID tableid, JET_cbtyp cbtyp, JET_CALLBACK callback, IntPtr context, out JET_HANDLE callbackId) { Api.Check(Impl.JetRegisterCallback(sesid, tableid, cbtyp, callback, context, out callbackId)); }
/// <summary> /// Starts and stops database defragmentation tasks that improves data /// organization within a database. /// </summary> /// <remarks> /// The callback passed to JetDefragment2 can be executed asynchronously. /// The GC doesn't know that the unmanaged code has a reference to the callback /// so it is important to make sure the callback isn't collected. /// </remarks> /// <param name="sesid">The session to use for the call.</param> /// <param name="dbid">The database to be defragmented.</param> /// <param name="tableName"> /// Unused parameter. Defragmentation is performed for the entire database described by the given database ID. /// </param> /// <param name="passes"> /// When starting an online defragmentation task, this parameter sets the maximum number of defragmentation /// passes. When stopping an online defragmentation task, this parameter is set to the number of passes /// performed. /// </param> /// <param name="seconds"> /// When starting an online defragmentation task, this parameter sets /// the maximum time for defragmentation. When stopping an online /// defragmentation task, this output buffer is set to the length of /// time used for defragmentation. /// </param> /// <param name="callback">Callback function that defrag uses to report progress.</param> /// <param name="grbit">Defragmentation options.</param> /// <returns>A warning code.</returns> public static JET_wrn JetDefragment2( JET_SESID sesid, JET_DBID dbid, string tableName, ref int passes, ref int seconds, JET_CALLBACK callback, DefragGrbit grbit) { return Api.Check(Impl.JetDefragment2(sesid, dbid, tableName, ref passes, ref seconds, callback, grbit)); }
/// <summary> /// Initializes a new instance of the JetCallbackWrapper class. /// </summary> /// <param name="callback"> /// The managed callback to use. /// </param> public JetCallbackWrapper(JET_CALLBACK callback) { this.wrappedCallback = new WeakReference(callback); this.nativeCallback = this.CallbackImpl; Debug.Assert(this.wrappedCallback.IsAlive, "Callback isn't alive"); }
/// <summary> /// Determine if the callback is wrapping the specified JET_CALLBACK. /// </summary> /// <param name="callback">The callback.</param> /// <returns>True if this wrapper is wrapping the callback.</returns> public bool IsWrapping(JET_CALLBACK callback) { return callback.Equals(this.wrappedCallback.Target); }