2 // System.Runtime.InteropServices.SafeHandle
4 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 // This code is only API complete, but it lacks the runtime support
27 // for CriticalFinalizerObject and any P/Invoke wrapping that might
31 // http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx
33 // CER-like behavior is implemented for Close and DangerousAddRef
34 // via the try/finally uninterruptible pattern in case of async
35 // exceptions like ThreadAbortException.
37 // On implementing SafeHandles:
38 // http://blogs.msdn.com/bclteam/archive/2005/03/15/396335.aspx
42 // TODO: Although DangerousAddRef has been implemented, I need to
43 // find out whether the runtime performs the P/Invoke if the
44 // handle has been disposed already.
48 // Copyright (c) Microsoft. All rights reserved.
49 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
52 // - mscorlib/system/runtime/interopservices/safehandle.cs
56 using System
.Runtime
.InteropServices
;
57 using System
.Runtime
.ConstrainedExecution
;
58 using System
.Runtime
.CompilerServices
;
59 using System
.Threading
;
61 namespace System
.Runtime
.InteropServices
63 [StructLayout (LayoutKind
.Sequential
)]
64 public abstract partial class SafeHandle
66 const int RefCount_Mask
= 0x7ffffffc;
67 const int RefCount_One
= 0x4;
71 Disposed
= 0x00000002,
75 * This should only be called for cases when you know for a fact that
76 * your handle is invalid and you want to record that information.
77 * An example is calling a syscall and getting back ERROR_INVALID_HANDLE.
78 * This method will normally leak handles!
80 [ReliabilityContract (Consistency
.WillNotCorruptState
, Cer
.Success
)]
81 public void SetHandleAsInvalid ()
85 int old_state
, new_state
;
89 new_state
= old_state
| (int) State
.Closed
;
90 } while (Interlocked
.CompareExchange (ref _state
, new_state
, old_state
) != old_state
);
92 GC
.SuppressFinalize (this);
97 * Add a reason why this handle should not be relinquished (i.e. have
98 * ReleaseHandle called on it). This method has dangerous in the name since
99 * it must always be used carefully (e.g. called within a CER) to avoid
100 * leakage of the handle. It returns a boolean indicating whether the
101 * increment was actually performed to make it easy for program logic to
102 * back out in failure cases (i.e. is a call to DangerousRelease needed).
103 * It is passed back via a ref parameter rather than as a direct return so
104 * that callers need not worry about the atomicity of calling the routine
105 * and assigning the return value to a variable (the variable should be
106 * explicitly set to false prior to the call). The only failure cases are
107 * when the method is interrupted prior to processing by a thread abort or
108 * when the handle has already been (or is in the process of being)
111 [ReliabilityContract (Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
112 public void DangerousAddRef (ref bool success
)
116 if (!_fullyInitialized
)
117 throw new InvalidOperationException ();
119 int old_state
, new_state
;
124 if ((old_state
& (int) State
.Closed
) != 0)
125 throw new ObjectDisposedException (null, "Safe handle has been closed");
127 new_state
= old_state
+ RefCount_One
;
128 } while (Interlocked
.CompareExchange (ref _state
, new_state
, old_state
) != old_state
);
135 * Partner to DangerousAddRef. This should always be successful when used in
136 * a correct manner (i.e. matching a successful DangerousAddRef and called
137 * from a region such as a CER where a thread abort cannot interrupt
138 * processing). In the same way that unbalanced DangerousAddRef calls can
139 * cause resource leakage, unbalanced DangerousRelease calls may cause
140 * invalid handle states to become visible to other threads. This
141 * constitutes a potential security hole (via handle recycling) as well as a
142 * correctness problem -- so don't ever expose Dangerous* calls out to
145 [ReliabilityContract (Consistency
.WillNotCorruptState
, Cer
.Success
)]
146 public void DangerousRelease ()
148 DangerousReleaseInternal (false);
151 void InternalDispose ()
153 if (!_fullyInitialized
)
154 throw new InvalidOperationException ();
156 DangerousReleaseInternal (true);
157 GC
.SuppressFinalize (this);
160 void InternalFinalize ()
162 if (_fullyInitialized
)
163 DangerousReleaseInternal (true);
166 void DangerousReleaseInternal (bool dispose
)
170 if (!_fullyInitialized
)
171 throw new InvalidOperationException ();
173 int old_state
, new_state
;
175 /* See AddRef above for the design of the synchronization here. Basically we
176 * will try to decrement the current ref count and, if that would take us to
177 * zero refs, set the closed state on the handle as well. */
178 bool perform_release
= false;
183 /* If this is a Dispose operation we have additional requirements (to
184 * ensure that Dispose happens at most once as the comments in AddRef
185 * detail). We must check that the dispose bit is not set in the old
186 * state and, in the case of successful state update, leave the disposed
187 * bit set. Silently do nothing if Dispose has already been called
188 * (because we advertise that as a semantic of Dispose). */
189 if (dispose
&& (old_state
& (int) State
.Disposed
) != 0) {
190 /* we cannot use `return` in a finally block, so we have to ensure
191 * that we are not releasing the handle */
192 perform_release
= false;
196 /* We should never see a ref count of zero (that would imply we have
197 * unbalanced AddRef and Releases). (We might see a closed state before
198 * hitting zero though -- that can happen if SetHandleAsInvalid is
200 if ((old_state
& RefCount_Mask
) == 0)
201 throw new ObjectDisposedException (null, "Safe handle has been closed");
203 if ((old_state
& RefCount_Mask
) != RefCount_One
)
204 perform_release
= false;
205 else if ((old_state
& (int) State
.Closed
) != 0)
206 perform_release
= false;
207 else if (!_ownsHandle
)
208 perform_release
= false;
210 perform_release
= false;
212 perform_release
= true;
214 // Not closed, let's propose an update (to the ref count, just add
215 // StateBits.RefCountOne to the state to effectively add 1 to the ref count).
216 // Continue doing this until the update succeeds (because nobody
217 // modifies the state field between the read and write operations) or
218 // the state moves to closed.
219 new_state
= old_state
- RefCount_One
;
220 if ((old_state
& RefCount_Mask
) == RefCount_One
)
221 new_state
|= (int) State
.Closed
;
223 new_state
|= (int) State
.Disposed
;
224 } while (Interlocked
.CompareExchange (ref _state
, new_state
, old_state
) != old_state
);