diff options
Diffstat (limited to 'src/inc/holder.h')
-rw-r--r-- | src/inc/holder.h | 1498 |
1 files changed, 1498 insertions, 0 deletions
diff --git a/src/inc/holder.h b/src/inc/holder.h new file mode 100644 index 0000000000..7e01bfa5a3 --- /dev/null +++ b/src/inc/holder.h @@ -0,0 +1,1498 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#ifndef __HOLDER_H_ +#define __HOLDER_H_ + +#include <wincrypt.h> +#include "cor.h" +#include "genericstackprobe.h" +#include "staticcontract.h" +#include "volatile.h" +#include "palclr.h" +#include "clr_std/utility" +#include "clr_std/type_traits" + +#if defined(FEATURE_COMINTEROP) && !defined(STRIKE) +#include <Activation.h> +#include <Inspectable.h> +#endif + +// Note: you can't use CONTRACT's in this file. You can't use dynamic contracts because the impl of dynamic +// contracts depends on holders. You can't use static contracts because they include the function name as a string, +// and the template names in this file are just too long, so you get a compiler error. +// +// All the functions in this file are pretty basic, so the lack of CONTRACT's isn't a big loss. + +#ifdef _MSC_VER +// Make sure we can recurse deep enough for FORCEINLINE +#pragma inline_recursion(on) +#pragma inline_depth(16) +#pragma warning(disable:4714) +#endif // _MSC_VER + +//------------------------------------------------------------------------------------------------ +// Declare but do not define methods that would normally be automatically generated by the +// compiler. This will produce a link-time error if they are used. This is involved in object +// initialization and operator eliding; see +// http://groups.google.com/group/comp.lang.c++/msg/3cd673ab749bed83?dmode=source +// for the intricate details. + +#ifdef __GNUC__ +// GCC checks accessibility of the copy ctor before performing elision optimization, so +// it must be declared as public. VC does not, so we can declare it private to give an +// earlier error message. +#define HIDE_GENERATED_METHODS(_NAME) \ + public: \ + _NAME(_NAME const &); \ + private: \ + _NAME & operator=(_NAME const &); +#else +#define HIDE_GENERATED_METHODS(_NAME) \ + private: \ + _NAME(_NAME const &); \ + _NAME & operator=(_NAME const &); +#endif + + +#ifdef _DEBUG + +#ifdef FEATURE_FUSION +namespace NATIVE_BINDER_SPACE +{ + class NativeAssembly; +} +#endif //FEATURE_FUSION + +//------------------------------------------------------------------------------------------------ +// This is used to make Visual Studio autoexp.dat work sensibly with holders again. +// The problem is that certain codebases (particulary Fusion) implement key data structures +// using a class but refer to it using a holder to an interface type. This combination prevents +// autoexp rules from working as desired. +// +// Example: Take this useful autoexp rule for CAssemblyName. +// +// CAssemblyName=<_rProp._rProp[3].asStr> +// +// To get the same rule to fire when your assemblyname is wrapped in a ReleaseHolder, +// add these companion rules. +// +// HolderBase<CAssemblyName *>=<m_value->_rProp._rProp[3].asStr> +// HolderBase<IAssemblyName *>=<m_pAutoExpVisibleValue->_asCAssemblyName->_rProp._rProp[3].asStr> +// +//------------------------------------------------------------------------------------------------ +struct AutoExpVisibleValue +{ + private: + union + { + // Only include a class name here if it is customarily referred to through an abstract interface. +#ifdef FEATURE_FUSION + const class CAssemblyName *_asCAssemblyName; + const class CAssembly *_asCAssembly; + const class CAssemblyManifestImport *_asCAssemblyManifestImport; + const class CAssemblyModuleImport *_asCAssemblyModuleImport; + const class CHostAssembly *_asCHostAssembly; + const class CHostAssemblyModuleImport *_asCHostAssemblyModuleImport; + const class BindResult *_asBindResult; + const class BindContext *_asBindContext; + const class NATIVE_BINDER_SPACE::NativeAssembly *_asNativeAssembly; + const class AssemblyLocation *_asAssemblyLocation; +#endif //FEATURE_FUSION + +#if defined(FEATURE_HOSTED_BINDER) && defined(FEATURE_APPX) + const class AppXBindResultImpl *_asAppXBindResultImpl; +#endif + +#ifndef FEATURE_CORECLR + const class PEFingerprint *_asPEFingerprint; +#endif //!FEATURE_CORECLR + const void *_pPreventEmptyUnion; + }; +}; +#endif //_DEBUG + +//----------------------------------------------------------------------------- +// Holder is the base class of all holder objects. Any backout object should derive from it. +// (Eventually some additional bookeeping and exception handling code will be placed in this +// base class.) +// +// There are several ways to use this class: +// 1. Derive from HolderBase, and instantiate a Holder or Wrapper around your base. This is necessary +// if you need to add more state to your holder. +// 2. Instantiate the Holder template with your type and functions. +// 3. Instantiate the Wrapper template with your type and functions. The Wrapper adds some additional +// operator overloads to provide "smart pointer" like behavior +// 4. Use a prebaked Holder. This is ALWAYS the preferable strategy. It is expected that +// the general design patter is that Holders will be provided as part of a typical data abstraction. +// (See Crst for an example of this.) +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// HolderBase defines the base holder functionality. You can subtype and plug in +// a different base if you need to add more members for access during +// acquire & release +//----------------------------------------------------------------------------- +template <typename TYPE> +class HolderBase +{ + protected: + TYPE m_value; + + HolderBase(TYPE value) + : m_value(value) + { + // TODO: Find a way to enable this check. + // We can have a holder in SO tolerant, then probe, then acquire a value. This works + // because the dtor is guaranteed to run with enough stack. + // EnsureSOIntolerantOK(__FUNCTION__, __FILE__, __LINE__); + +#ifdef _DEBUG + m_pAutoExpVisibleValue = (const AutoExpVisibleValue *)(&m_value); +#endif //_DEBUG + } + + void DoAcquire() + { + // Insert any global or thread bookeeping here + } + + void DoRelease() + { + // Insert any global or thread bookeeping here + } + +#ifdef _DEBUG + private: + // NOT DEAD CODE: This field is not referenced in the source code but it is not a dead field. See comments above "class AutoExpVisibleValue" for why this field exists. + // Note: What we really want is for m_value and m_pAutoExpVisibleValue to be members of a union. But the compiler won't let you build that + // since it can't prove that "TYPE" doesn't have a non-trivial constructor. + const AutoExpVisibleValue *m_pAutoExpVisibleValue; // This field is to be referenced from individual autoexp.dat files, NOT from the CLR source code. +#endif //_DEBUG + +}; // class HolderBase<> + +#ifndef _PREFAST_ // Work around an ICE error in EspX.dll + +template <typename TYPE> +BOOL CompareDefault(TYPE value, TYPE defaultValue) +{ + STATIC_CONTRACT_SUPPORTS_DAC; + return value == defaultValue; +} + +#else + +template <typename TYPE> +BOOL CompareDefault(TYPE value, TYPE defaultValue) +{ + return FALSE; +} + +#endif + + +template <typename TYPE> +BOOL NoNull(TYPE value, TYPE defaultValue) +{ + return FALSE; +} + +// Used e.g. for retail version of code:SyncAccessHolder +template <typename TYPE> +class NoOpBaseHolder +{ + public: + FORCEINLINE NoOpBaseHolder() + { + } + FORCEINLINE NoOpBaseHolder(TYPE value, BOOL take = TRUE) + { + } + FORCEINLINE ~NoOpBaseHolder() + { + } + FORCEINLINE void Assign(TYPE value, BOOL fTake = TRUE) + { + } + FORCEINLINE void Acquire() + { + } + FORCEINLINE void Release() + { + } + FORCEINLINE void Clear() + { + } + FORCEINLINE void SuppressRelease() + { + } + FORCEINLINE TYPE Extract() + { + } + FORCEINLINE TYPE GetValue() + { + } + FORCEINLINE BOOL IsNull() const + { + return FALSE; + } + + private: + NoOpBaseHolder& operator=(NoOpBaseHolder const &); + NoOpBaseHolder(NoOpBaseHolder const &); +}; // NoOpBaseHolder<> + + +template + < + typename TYPE, + typename BASE, + UINT_PTR DEFAULTVALUE = 0, + BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>, + HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq + > +class BaseHolder : protected BASE +{ + protected: + BOOL m_acquired; // Have we acquired the resource? + + static_assert(!std::is_pointer<TYPE>::value || DEFAULTVALUE == 0 || DEFAULTVALUE == UINT_PTR(-1 /*INVALID_HANDLE_VALUE*/), + "DEFAULTVALUE must be NULL for pointer holders and wrappers."); + + public: + FORCEINLINE BaseHolder() + : BASE(TYPE(DEFAULTVALUE)), + m_acquired(FALSE) + { + } + FORCEINLINE BaseHolder(TYPE value) + : BASE(value), + m_acquired(FALSE) + { + if (!IsNull()) + Acquire(); + } + FORCEINLINE BaseHolder(TYPE value, BOOL takeOwnership) + : BASE(value), + m_acquired(FALSE) + { + if (takeOwnership) + Acquire(); + } + FORCEINLINE ~BaseHolder() + { + Release(); + } + // Sets the value to 'value'. Doesn't call Acquire if value is DEFAULTVALUE. + FORCEINLINE void Assign(TYPE value) + { + Assign(value, !IS_NULL(value, TYPE(DEFAULTVALUE))); + } + // Sets the value to 'value'. Doesn't call Acquire if fTake is FALSE. + FORCEINLINE void Assign(TYPE value, BOOL takeOwnership) + { + Release(); + this->m_value = value; + if (takeOwnership) + Acquire(); + } + FORCEINLINE void Acquire() + { + STATIC_CONTRACT_WRAPPER; + _ASSERTE(!m_acquired); + + if (!IsNull()) + { + this->DoAcquire(); + m_acquired = TRUE; + } + } + FORCEINLINE void Release() + { + STATIC_CONTRACT_WRAPPER; + if (m_acquired) + { + _ASSERTE(!IsNull()); + + if (VALIDATION_TYPE != HSV_NoValidation) + { + VALIDATE_HOLDER_STACK_CONSUMPTION_FOR_TYPE(VALIDATION_TYPE); + this->DoRelease(); + } + else + { + this->DoRelease(); + } + m_acquired = FALSE; + } + } + + FORCEINLINE void Clear() + { + STATIC_CONTRACT_WRAPPER; + Assign(TYPE(DEFAULTVALUE), FALSE); + } + FORCEINLINE void SuppressRelease() + { + STATIC_CONTRACT_LEAF; + m_acquired = FALSE; + } + FORCEINLINE TYPE Extract() + { + STATIC_CONTRACT_WRAPPER; + SuppressRelease(); + return GetValue(); + } + FORCEINLINE TYPE GetValue() + { + STATIC_CONTRACT_LEAF; + return this->m_value; + } + FORCEINLINE BOOL IsNull() const + { + STATIC_CONTRACT_WRAPPER; + return IS_NULL(this->m_value, TYPE(DEFAULTVALUE)); + } + + HIDE_GENERATED_METHODS(BaseHolder) +}; // BaseHolder<> + +template <void (*ACQUIRE)(), void (*RELEASEF)(), HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq> +class StateHolder +{ + private: + BOOL m_acquired; // Have we acquired the state? + + public: + FORCEINLINE StateHolder(BOOL take = TRUE) + : m_acquired(FALSE) + { + STATIC_CONTRACT_WRAPPER; + if (take) + Acquire(); + } + FORCEINLINE ~StateHolder() + { + STATIC_CONTRACT_WRAPPER; + Release(); + } + FORCEINLINE void Acquire() + { + STATIC_CONTRACT_WRAPPER; + // Insert any global or thread bookeeping here + + _ASSERTE(!m_acquired); + + ACQUIRE(); + m_acquired = TRUE; + } + FORCEINLINE void Release() + { + STATIC_CONTRACT_WRAPPER; + // Insert any global or thread bookeeping here + + if (m_acquired) + { + if (VALIDATION_TYPE != HSV_NoValidation) + { + VALIDATE_HOLDER_STACK_CONSUMPTION_FOR_TYPE(VALIDATION_TYPE); + RELEASEF(); + } + else + { + RELEASEF(); + } + m_acquired = FALSE; + } + } + FORCEINLINE void Clear() + { + STATIC_CONTRACT_WRAPPER; + if (m_acquired) + { + RELEASEF(); + } + + m_acquired = FALSE; + } + FORCEINLINE void SuppressRelease() + { + STATIC_CONTRACT_LEAF; + m_acquired = FALSE; + } + + HIDE_GENERATED_METHODS(StateHolder) +}; // class StateHolder<> + +// Holder for the case where the acquire function can fail. +template <typename VALUE, BOOL (*ACQUIRE)(VALUE value), void (*RELEASEF)(VALUE value), HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq> +class ConditionalStateHolder +{ + private: + VALUE m_value; + BOOL m_acquired; // Have we acquired the state? + + public: + FORCEINLINE ConditionalStateHolder(VALUE value, BOOL take = TRUE) + : m_value(value), m_acquired(FALSE) + { + STATIC_CONTRACT_WRAPPER; + if (take) + Acquire(); + } + FORCEINLINE ~ConditionalStateHolder() + { + STATIC_CONTRACT_WRAPPER; + Release(); + } + FORCEINLINE BOOL Acquire() + { + STATIC_CONTRACT_WRAPPER; + // Insert any global or thread bookeeping here + + _ASSERTE(!m_acquired); + + m_acquired = ACQUIRE(m_value); + + return m_acquired; + } + FORCEINLINE void Release() + { + STATIC_CONTRACT_WRAPPER; + // Insert any global or thread bookeeping here + + if (m_acquired) + { + if (VALIDATION_TYPE != HSV_NoValidation) + { + VALIDATE_HOLDER_STACK_CONSUMPTION_FOR_TYPE(VALIDATION_TYPE); + RELEASEF(m_value); + } + else + { + RELEASEF(m_value); + } + m_acquired = FALSE; + } + } + FORCEINLINE void Clear() + { + STATIC_CONTRACT_WRAPPER; + if (m_acquired) + { + RELEASEF(m_value); + } + + m_acquired = FALSE; + } + FORCEINLINE void SuppressRelease() + { + STATIC_CONTRACT_LEAF; + m_acquired = FALSE; + } + FORCEINLINE BOOL Acquired() + { + STATIC_CONTRACT_LEAF; + + return m_acquired; + } + + HIDE_GENERATED_METHODS(ConditionalStateHolder) +}; // class ConditionalStateHolder<> + + +// Making the copy constructor private produces a warning about "can't generate copy +// constructor" on all holders (duh, that's the point.) +#ifdef _MSC_VER +#pragma warning(disable:4511) +#endif // _MSC_VER + +//----------------------------------------------------------------------------- +// BaseWrapper is just Base like a Holder, but it "transparently" proxies the type it contains, +// using operator overloads. Use this when you want a holder to expose the functionality of +// the value it contains. +//----------------------------------------------------------------------------- +template <typename TYPE, typename BASE, + UINT_PTR DEFAULTVALUE = 0, BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>, HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq> +class BaseWrapper : public BaseHolder<TYPE, BASE, DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> +{ + typedef BaseHolder<TYPE, BASE, DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> BaseT; + + +#ifdef __GNUC__ +//#pragma GCC visibility push(hidden) +#endif // __GNUC__ + // This temporary object takes care of the case where we are initializing + // a holder's contents by passing it as an out parameter. The object is + // guaranteed to live longer than the call it is passed to, hence we should + // properly acquire the object on return + friend class AddressInitHolder; + class AddressInitHolder + { + protected: + BaseWrapper<TYPE,BASE,DEFAULTVALUE,IS_NULL> &m_holder; + + public: + FORCEINLINE AddressInitHolder(BaseWrapper<TYPE,BASE,DEFAULTVALUE,IS_NULL> &holder) + : m_holder(holder) + { + // + // We must clear the value, to avoid the following scenario: + // + // ReleaseHolder<MyType> pMyType; + // hr = MyFunction(&pMyType, ...); + // <do something with pMyType> + // hr = MyFunction(&pMyType, ...); <- calls Release before call and Acquire on return. + // + // If the second call to MyFunction were to fail and return without updating the + // out parameter, then ~AddressInitHolder will all Acquire, which is a no-op on + // the underlying pointer but sets m_acquired to TRUE. Thus, when pMyType goes + // out of scope, ReleaseHolder will cause a double-release of pMyType. + // + // By calling Clear, then the call to Acquire in the dtor will not set m_acquired + // to true since IsNull(m_holder.m_value) will return true. + // + m_holder.Clear(); + } + FORCEINLINE ~AddressInitHolder() + { + m_holder.Acquire(); + } + + // It's not optimal to have to declare these casting operators. But if we don't, + // people cannot cast the result of &holder to these values. (The C++ compiler won't + // automatically use the TYPE * cast as an intermediate value.) So we put them here, + // rather than forcing callers to double cast in these common cases. + + FORCEINLINE operator IUnknown **() + { + IUnknown *unknown; + // Typesafe check. This will fail at compile time if + // m_holder.m_value can't be converted to an IUnknown *. + unknown = static_cast<IUnknown*>(m_holder.m_value); + // do the cast with an unsafe cast + return (IUnknown **)(&m_holder.m_value); + } + +#if defined(FEATURE_COMINTEROP) && !defined(STRIKE) + FORCEINLINE operator IInspectable **() + { + IInspectable *inspectable; + // Typesafe check. This will fail at compile time if + // m_holder.m_value can't be converted to an IInspectable *. + inspectable = static_cast<IInspectable *>(m_holder.m_value); + return (IInspectable **)(&m_holder.m_value); + + } +#endif // FEATURE_COMINTEROP + + FORCEINLINE operator void **() + { + return (void **)(&m_holder.m_value); + } + FORCEINLINE operator void *() + { + return (void *)(&m_holder.m_value); + } + }; + + // Separate out method with TYPE * cast operator, since it may clash with IUnknown ** or + // void ** cast operator. + friend class TypedAddressInitHolder; + class TypedAddressInitHolder : public AddressInitHolder + { + public: + FORCEINLINE TypedAddressInitHolder(BaseWrapper & holder) + : AddressInitHolder(holder) + { + } + + FORCEINLINE operator TYPE *() + { + return static_cast<TYPE *>(&this->m_holder.m_value); + } + }; +#ifdef __GNUC__ +//#pragma GCC visibility pop +#endif // __GNUC__ + + public: + FORCEINLINE BaseWrapper() + : BaseT(TYPE(DEFAULTVALUE), FALSE) + { + } + FORCEINLINE BaseWrapper(TYPE value) + : BaseT(value) + { + } + FORCEINLINE BaseWrapper(TYPE value, BOOL take) + : BaseT(value, take) + { + } + FORCEINLINE BaseWrapper& operator=(TYPE value) + { + BaseT::Assign(value); + return *this; + } + FORCEINLINE operator TYPE() const + { + return this->m_value; + } + FORCEINLINE TypedAddressInitHolder operator&() + { + return TypedAddressInitHolder(*this); + } + template <typename T> + FORCEINLINE bool operator==(T const & value) const + { + return !!(this->m_value == TYPE(value)); + } + template <typename T> + FORCEINLINE bool operator!=(T const & value) const + { + return !!(this->m_value != TYPE(value)); + } + FORCEINLINE const TYPE &operator->() const + { + return this->m_value; + } + FORCEINLINE int operator!() const + { + return this->IsNull(); + } + + private: + HIDE_GENERATED_METHODS(BaseWrapper) +}; // class BaseWrapper<> + +//----------------------------------------------------------------------------- +// Generic templates to use to wrap up acquire/release functionality for Holder +//----------------------------------------------------------------------------- + +template <typename TYPE> +FORCEINLINE void DoNothing(TYPE value) +{ + // @TODO: Due to prefast template problems, implementations of the DoNothing macro have been changed + // Search by prefast, and remove them when prefast is ready +} + +FORCEINLINE void DoNothing() +{ +} + +// Prefast stuff.We should have DoNothing<type*> in the holder declaration, but currently +// prefast doesnt support, it, so im stuffing all these here so if we need to change the template you can change +// everything here. When prefast works, remove the following functions +struct ConnectionCookie; +FORCEINLINE void ConnectionCookieDoNothing(ConnectionCookie* p) +{ +} + +class ComCallWrapper; +FORCEINLINE void CCWHolderDoNothing(ComCallWrapper* p) +{ +} + + +FORCEINLINE void DispParamHolderDoNothing(VARIANT* p) +{ +} + +FORCEINLINE void VariantPtrDoNothing(VARIANT* p) +{ +} + +FORCEINLINE void VariantDoNothing(VARIANT) +{ +} + +FORCEINLINE void ZeroDoNothing(VOID* p) +{ +} + +class CtxEntry; +FORCEINLINE void CtxEntryDoNothing(CtxEntry* p) +{ +} + +struct RCW; +FORCEINLINE void NewRCWHolderDoNothing(RCW*) +{ +} + +// Prefast stuff.We should have DoNothing<SafeArray*> in the holder declaration +FORCEINLINE void SafeArrayDoNothing(SAFEARRAY* p) +{ +} + + + + +//----------------------------------------------------------------------------- +// Holder/Wrapper are the simplest way to define holders - they synthesizes a base class out of +// function pointers +//----------------------------------------------------------------------------- + +template <typename TYPE, void (*ACQUIREF)(TYPE), void (*RELEASEF)(TYPE), HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq> +class FunctionBase : protected HolderBase<TYPE> +{ + protected: + + FORCEINLINE FunctionBase(TYPE value) + : HolderBase<TYPE>(value) + { + } + + FORCEINLINE void DoAcquire() + { + ACQUIREF(this->m_value); + } + + void DoRelease() + { + // <TODO> Consider removing this stack validation since it is redundant with the + // one that is already being done in BaseHolder & BaseWrapper. </TODO> + if (VALIDATION_TYPE != HSV_NoValidation) + { + VALIDATE_HOLDER_STACK_CONSUMPTION_FOR_TYPE(VALIDATION_TYPE); + RELEASEF(this->m_value); + } + else + { + RELEASEF(this->m_value); + } + } +}; // class Function<> + +template + < + typename TYPE, + void (*ACQUIREF)(TYPE), + void (*RELEASEF)(TYPE), + UINT_PTR DEFAULTVALUE = 0, + BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>, + HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq, + // For legacy compat (see EEJitManager::WriterLockHolder), where default ctor + // causes ACQUIREF(DEFAULTVALUE), but ACQUIREF ignores the argument and + // operates on static or global value instead. + bool DEFAULT_CTOR_ACQUIRE = true + > +class Holder : public BaseHolder<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF, VALIDATION_TYPE>, + DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> +{ + typedef BaseHolder<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF, VALIDATION_TYPE>, + DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> BaseT; + + public: + FORCEINLINE Holder() + : BaseT(TYPE(DEFAULTVALUE), DEFAULT_CTOR_ACQUIRE) + { + STATIC_CONTRACT_WRAPPER; + } + + FORCEINLINE Holder(TYPE value) + : BaseT(value) + { + STATIC_CONTRACT_WRAPPER; + } + + FORCEINLINE Holder(TYPE value, BOOL takeOwnership) + : BaseT(value, takeOwnership) + { + STATIC_CONTRACT_WRAPPER; + } + + FORCEINLINE Holder& operator=(TYPE p) + { + STATIC_CONTRACT_WRAPPER; + BaseT::operator=(p); + return *this; + } + + HIDE_GENERATED_METHODS(Holder) +}; + +//--------------------------------------------------------------------------------------- +// +template + < + typename TYPE, + void (*ACQUIREF)(TYPE), + void (*RELEASEF)(TYPE), + UINT_PTR DEFAULTVALUE = 0, + BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>, + HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq, + // For legacy compat (see EEJitManager::WriterLockHolder), where default ctor + // causes ACQUIREF(DEFAULTVALUE), but ACQUIREF ignores the argument and + // operates on static or global value instead. + bool DEFAULT_CTOR_ACQUIRE = true + > +class Wrapper : public BaseWrapper<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF, VALIDATION_TYPE>, + DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> +{ + typedef BaseWrapper<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF, VALIDATION_TYPE>, + DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> BaseT; + + public: + FORCEINLINE Wrapper() + : BaseT(TYPE(DEFAULTVALUE), DEFAULT_CTOR_ACQUIRE) + { + STATIC_CONTRACT_WRAPPER; + } + + FORCEINLINE Wrapper(TYPE value) + : BaseT(value) + { + STATIC_CONTRACT_WRAPPER; + } + + FORCEINLINE Wrapper(TYPE value, BOOL takeOwnership) + : BaseT(value, takeOwnership) + { + STATIC_CONTRACT_WRAPPER; + } + + FORCEINLINE Wrapper& operator=(TYPE const & value) + { + STATIC_CONTRACT_WRAPPER; + BaseT::operator=(value); + return *this; + } + + HIDE_GENERATED_METHODS(Wrapper) +}; // Wrapper<> + +//--------------------------------------------------------------------------------------- +// - Cannot use the standard INDEBUG macro: holder.h is used in places where INDEBUG is defined in the nonstandard way +#if defined(_DEBUG) +#define INDEBUG_AND_WINDOWS_FOR_HOLDERS(x) x +#else +#define INDEBUG_AND_WINDOWS_FOR_HOLDERS(x) +#endif + +//--------------------------------------------------------------------------------------- +// +// New template wrapper type macros. These save some effort when specializing +// existing holder templates. (We would rather use a construct like: +// +// template <P1> +// typedef Holder<...> NewHolder; +// +// But this construct doesn't exist in C++. These macros ease some of the cruft necessary +// to get similar functionality out of class templates. +//----------------------------------------------------------------------------- + +// Dev10 VC++ has some of the new C++0x language extensions. Of particular interest here: +// rvalue references, which enables differentiation between named (lvalue) and +// temporary (rvalue) object references, enabling move semantics and perfect forwarding. +// See http://msdn.microsoft.com/en-us/library/dd293668.aspx for more information. + +// Enable copy construction and assignment from temporary objects. This permits Wrapper objects +// to be returned from methods, and for move assignment. +#define NEW_WRAPPER_TEMPLATE_RVALREF_METHODS(_NAME) \ + public: \ + FORCEINLINE _NAME(_NAME && other) \ + : BaseT(NULL, FALSE) \ + { \ + STATIC_CONTRACT_WRAPPER; \ + INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;) \ + *this = std::move(other); \ + } \ + FORCEINLINE _NAME& operator=(_NAME && other) \ + { \ + std::swap(BaseT::m_value, other.BaseT::m_value); \ + std::swap(BaseT::m_acquired, other.BaseT::m_acquired); \ + return *this; \ + } + +#define NEW_WRAPPER_TEMPLATE1(_NAME, _RELEASEF) \ + template <typename _TYPE> \ + class _NAME : public Wrapper<_TYPE*, DoNothing<_TYPE*>, _RELEASEF, NULL> \ + { \ + typedef Wrapper<_TYPE*, DoNothing<_TYPE*>, _RELEASEF, NULL> BaseT; \ + public: \ + FORCEINLINE _NAME() : BaseT(NULL, FALSE) \ + { \ + STATIC_CONTRACT_WRAPPER; \ + INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;) \ + } \ + FORCEINLINE _NAME(_TYPE* value) : BaseT(value) \ + { \ + STATIC_CONTRACT_WRAPPER; \ + INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;) \ + } \ + FORCEINLINE _NAME(_TYPE* value, BOOL takeOwnership) : BaseT(value, takeOwnership) \ + { \ + STATIC_CONTRACT_WRAPPER; \ + INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;) \ + } \ + FORCEINLINE ~_NAME() \ + { \ + } \ + FORCEINLINE _NAME& operator=(_TYPE * value) \ + { \ + STATIC_CONTRACT_WRAPPER; \ + BaseT::operator=(value); \ + return *this; \ + } \ + /* Since operator& is overloaded we need a way to get a type safe this pointer. */ \ + FORCEINLINE _NAME* GetAddr() \ + { \ + STATIC_CONTRACT_LEAF; \ + return this; \ + } \ + NEW_WRAPPER_TEMPLATE_RVALREF_METHODS(_NAME) \ + HIDE_GENERATED_METHODS(_NAME) \ + private: \ + /* m_ppValue: Do not use from source code: Only for convenient use from debugger */ \ + /* watch windows - saves five mouseclicks when inspecting holders. */ \ + INDEBUG_AND_WINDOWS_FOR_HOLDERS(_TYPE ** m_pvalue;) \ + }; + +//----------------------------------------------------------------------------- +// NOTE: THIS IS UNSAFE TO USE IN THE VM for interop COM objects!! +// WE DO NOT CORRECTLY CHANGE TO PREEMPTIVE MODE BEFORE CALLING RELEASE!! +// USE SafeComHolder +// +// ReleaseHolder : COM Interface holder for use outside the VM (or on well known instances +// which do not need preemptive Relesae) +// +// Usage example: +// +// { +// ReleaseHolder<IFoo> foo; +// hr = FunctionToGetRefOfFoo(&foo); +// // Note ComHolder doesn't call AddRef - it assumes you already have a ref (if non-0). +// } // foo->Release() on out of scope (WITHOUT RESPECT FOR GC MODE!!) +// +//----------------------------------------------------------------------------- + +template <typename TYPE> +FORCEINLINE void DoTheRelease(TYPE *value) +{ + if (value) + { + VALIDATE_HOLDER_STACK_CONSUMPTION_FOR_TYPE(HSV_ValidateNormalStackReq); + value->Release(); + } +} + +NEW_WRAPPER_TEMPLATE1(DoNothingHolder, DoNothing<_TYPE*>); + +NEW_WRAPPER_TEMPLATE1(ReleaseHolder, DoTheRelease<_TYPE>); + +NEW_WRAPPER_TEMPLATE1(NonVMComHolder, DoTheRelease<_TYPE>); + + +//----------------------------------------------------------------------------- +// StubHolder : holder for stubs +// +// Usage example: +// +// { +// StubHolder<Stub> foo; +// foo = new Stub(); +// foo->AddRef(); +// // Note StubHolder doesn't call AddRef for you. +// } // foo->DecRef() on out of scope +// +//----------------------------------------------------------------------------- +template <typename TYPE> +FORCEINLINE void StubRelease(TYPE* value) +{ + if (value) + value->DecRef(); +} + +NEW_WRAPPER_TEMPLATE1(StubHolder, StubRelease<_TYPE>); + +//----------------------------------------------------------------------------- +// CoTaskMemHolder : CoTaskMemAlloc allocated memory holder +// +// { +// CoTaskMemHolder<Foo> foo = (Foo*) CoTaskMemAlloc(sizeof(Foo)); +// } // delete foo on out of scope +//----------------------------------------------------------------------------- + +template <typename TYPE> +FORCEINLINE void DeleteCoTaskMem(TYPE *value) +{ + if (value) + CoTaskMemFree(value); +} + +NEW_WRAPPER_TEMPLATE1(CoTaskMemHolder, DeleteCoTaskMem<_TYPE>); + +//----------------------------------------------------------------------------- +// NewHolder : New'ed memory holder +// +// { +// NewHolder<Foo> foo = new Foo (); +// } // delete foo on out of scope +//----------------------------------------------------------------------------- + +template <typename TYPE> +FORCEINLINE void Delete(TYPE *value) +{ + STATIC_CONTRACT_LEAF; + + static_assert(!std::is_same<typename std::remove_cv<TYPE>::type, WCHAR>::value, + "Must use NewArrayHolder (not NewHolder) for strings."); + static_assert(!std::is_same<typename std::remove_cv<TYPE>::type, CHAR>::value, + "Must use NewArrayHolder (not NewHolder) for strings."); + + delete value; +} + +NEW_WRAPPER_TEMPLATE1(NewHolder, Delete<_TYPE>); + + //----------------------------------------------------------------------------- +// NewExecutableHolder : New'ed memory holder for executable memory. +// +// { +// NewExecutableHolder<Foo> foo = (Foo*) new (executable) Byte[num]; +// } // delete foo on out of scope +//----------------------------------------------------------------------------- +// IJW +template<class T> void DeleteExecutable(T *p); + +NEW_WRAPPER_TEMPLATE1(NewExecutableHolder, DeleteExecutable<_TYPE>); + +//----------------------------------------------------------------------------- +// NewArrayHolder : New []'ed pointer holder +// { +// NewArrayHolder<Foo> foo = new Foo [30]; +// } // delete [] foo on out of scope +//----------------------------------------------------------------------------- + +template <typename TYPE> +FORCEINLINE void DeleteArray(TYPE *value) +{ + STATIC_CONTRACT_WRAPPER; + delete [] value; + value = NULL; +} + +NEW_WRAPPER_TEMPLATE1(NewArrayHolder, DeleteArray<_TYPE>); +typedef NewArrayHolder<CHAR> AStringHolder; +typedef NewArrayHolder<WCHAR> WStringHolder; + +//----------------------------------------------------------------------------- +// A special array holder that expects its contents are interface pointers, +// and will call Release() on them. +// +// NOTE: You may ONLY use this if you've determined that it is SAFE to call +// Release() on the contained interface pointers (e.g., as opposed to SafeRelease) +// +template <typename INTERFACE> +class NewInterfaceArrayHolder : public NewArrayHolder<INTERFACE *> +{ +public: + NewInterfaceArrayHolder() : + NewArrayHolder<INTERFACE *>(), + m_cElements(0) + { + STATIC_CONTRACT_WRAPPER; + } + + NewInterfaceArrayHolder& operator=(INTERFACE ** value) + { + STATIC_CONTRACT_WRAPPER; + NewArrayHolder<INTERFACE *>::operator=(value); + return *this; + } + + void SetElementCount(ULONG32 cElements) + { + STATIC_CONTRACT_LEAF; + m_cElements = cElements; + } + + ~NewInterfaceArrayHolder() + { + STATIC_CONTRACT_LEAF; + for (ULONG32 i=0; i < m_cElements; i++) + { + if (this->m_value[i] != NULL) + this->m_value[i]->Release(); + } + } + +protected: + ULONG32 m_cElements; +}; + + +//----------------------------------------------------------------------------- +// ResetPointerHolder : pointer which needs to be set to NULL +// { +// ResetPointerHolder<Foo> holder = &pFoo; +// } // "*pFoo=NULL" on out of scope +//----------------------------------------------------------------------------- +#ifdef __GNUC__ +// With -fvisibility-inlines-hidden, the Invoke methods below +// get hidden, which causes warnings when visible classes expose them. +#define VISIBLE __attribute__ ((visibility("default"))) +#else +#define VISIBLE +#endif // __GNUC__ + +namespace detail +{ + template <typename T> + struct ZeroMem + { + static VISIBLE void Invoke(T * pVal) + { + ZeroMemory(pVal, sizeof(T)); + } + }; + + template <typename T> + struct ZeroMem<T*> + { + static VISIBLE void Invoke(T ** pVal) + { + *pVal = NULL; + } + }; + +} +#undef VISIBLE + +NEW_WRAPPER_TEMPLATE1(ResetPointerHolder, detail::ZeroMem<_TYPE>::Invoke); +NEW_WRAPPER_TEMPLATE1(FieldNuller, detail::ZeroMem<_TYPE>::Invoke); + +//----------------------------------------------------------------------------- +// Wrap win32 functions using HANDLE +//----------------------------------------------------------------------------- + +FORCEINLINE void VoidCloseHandle(HANDLE h) { if (h != NULL) CloseHandle(h); } +// (UINT_PTR) -1 is INVALID_HANDLE_VALUE +FORCEINLINE void VoidCloseFileHandle(HANDLE h) { if (h != ((HANDLE)((LONG_PTR) -1))) CloseHandle(h); } +FORCEINLINE void VoidFindClose(HANDLE h) { FindClose(h); } +FORCEINLINE void VoidUnmapViewOfFile(void *ptr) { UnmapViewOfFile(ptr); } + +template <typename TYPE> +FORCEINLINE void TypeUnmapViewOfFile(TYPE *ptr) { UnmapViewOfFile(ptr); } + +// (UINT_PTR) -1 is INVALID_HANDLE_VALUE +//@TODO: Dangerous default value. Some Win32 functions return INVALID_HANDLE_VALUE, some return NULL (such as CreatEvent). +typedef Wrapper<HANDLE, DoNothing<HANDLE>, VoidCloseHandle, (UINT_PTR) -1> HandleHolder; +typedef Wrapper<HANDLE, DoNothing<HANDLE>, VoidCloseFileHandle, (UINT_PTR) -1> FileHandleHolder; +typedef Wrapper<HANDLE, DoNothing<HANDLE>, VoidFindClose, (UINT_PTR) -1> FindHandleHolder; + +typedef Wrapper<void *, DoNothing, VoidUnmapViewOfFile> MapViewHolder; + +#ifdef WszDeleteFile +// Deletes a file with the specified path. Do not use if you care about failures +// deleting the file, as failures are ignored by VoidDeleteFile. +FORCEINLINE void VoidDeleteFile(LPCWSTR wszFilePath) { WszDeleteFile(wszFilePath); } +typedef Wrapper<LPCWSTR, DoNothing<LPCWSTR>, VoidDeleteFile, NULL> DeleteFileHolder; +#endif // WszDeleteFile + +#if !defined(FEATURE_CORECLR) || defined(FEATURE_CRYPTO) +// Crypto holders +FORCEINLINE void VoidCryptReleaseContext(HCRYPTPROV h) { CryptReleaseContext(h, 0); } +FORCEINLINE void VoidCryptDestroyHash(HCRYPTHASH h) { CryptDestroyHash(h); } +FORCEINLINE void VoidCryptDestroyKey(HCRYPTKEY h) { CryptDestroyKey(h); } + +typedef Wrapper<HCRYPTPROV, DoNothing, VoidCryptReleaseContext, 0> HandleCSPHolder; +typedef Wrapper<HCRYPTHASH, DoNothing, VoidCryptDestroyHash, 0> HandleHashHolder; +typedef Wrapper<HCRYPTKEY, DoNothing, VoidCryptDestroyKey, 0> HandleKeyHolder; +#endif // !FEATURE_CORECLR || FEATURE_CRYPTO + +//----------------------------------------------------------------------------- +// Misc holders +//----------------------------------------------------------------------------- + +// A holder for HMODULE. +FORCEINLINE void HolderFreeLibrary(HMODULE h) { FreeLibrary(h); } + +typedef Wrapper<HMODULE, DoNothing<HMODULE>, HolderFreeLibrary, NULL> HModuleHolder; + +template <typename T> FORCEINLINE +void DoLocalFree(T* pMem) { (LocalFree)((HLOCAL)pMem); } + +NEW_WRAPPER_TEMPLATE1(LocalAllocHolder, DoLocalFree<_TYPE>); + +inline void BoolSet( _Out_ bool * val ) { *val = true; } +inline void BoolUnset( _Out_ bool * val ) { *val = false; } + +typedef Wrapper< bool *, BoolSet, BoolUnset > BoolFlagStateHolder; + +// +// We need the following methods to have volatile arguments, so that they can accept +// raw pointers in addition to the results of the & operator on Volatile<T>. +// + +FORCEINLINE void CounterIncrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedIncrement(p);}; +FORCEINLINE void CounterDecrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedDecrement(p);}; + +typedef Wrapper<RAW_KEYWORD(volatile) LONG*, CounterIncrease, CounterDecrease, (UINT_PTR)0, CompareDefault<RAW_KEYWORD(volatile) LONG*>, HSV_NoValidation> CounterHolder; + + +#ifndef FEATURE_PAL +FORCEINLINE void RegKeyRelease(HKEY k) {RegCloseKey(k);}; +typedef Wrapper<HKEY,DoNothing,RegKeyRelease> RegKeyHolder; +#endif // !FEATURE_PAL + +class ErrorModeHolder +{ + UINT m_oldMode; +public: + ErrorModeHolder(UINT newMode){m_oldMode=SetErrorMode(newMode);}; + ~ErrorModeHolder(){SetErrorMode(m_oldMode);}; + UINT OldMode() {return m_oldMode;}; +}; + +#ifndef FEATURE_PAL +//----------------------------------------------------------------------------- +// HKEYHolder : HKEY holder, Calls RegCloseKey on scope exit. +// +// { +// HKEYHolder hFoo = NULL; +// WszRegOpenKeyEx(HKEY_CLASSES_ROOT, L"Interface",0, KEY_READ, hFoo); +// +// } // close key on out of scope via RegCloseKey. +//----------------------------------------------------------------------------- + +class HKEYHolder +{ +public: + HKEYHolder() + { + STATIC_CONTRACT_LEAF; + m_value = 0; + } + + ~HKEYHolder() + { + STATIC_CONTRACT_WRAPPER; + if (m_value != NULL) + ::RegCloseKey(m_value); + } + + FORCEINLINE void operator=(HKEY p) + { + STATIC_CONTRACT_LEAF; + if (p != 0) + m_value = p; + } + + FORCEINLINE operator HKEY() + { + STATIC_CONTRACT_LEAF; + return m_value; + } + + FORCEINLINE operator HKEY*() + { + STATIC_CONTRACT_LEAF; + return &m_value; + } + + FORCEINLINE HKEY* operator&() + { + STATIC_CONTRACT_LEAF; + return &m_value; + } + +private: + HKEY m_value; +}; +#endif // !FEATURE_PAL + +//----------------------------------------------------------------------------- +// Wrapper to suppress auto-destructor (UNDER CONSTRUCTION) +// Usage: +// +// BEGIN_MANUAL_HOLDER(NewArrayHolder<Foo>, foo); +// ... use foo via -> +// END_MANUAL_HOLDER(foo); +// +//----------------------------------------------------------------------------- + +template <typename TYPE, SIZE_T SIZE = sizeof(TYPE)> +class NoAuto__DONTUSEDIRECTLY +{ + private: + BYTE hiddeninstance[SIZE]; + + public: + // Unfortunately, you can only use the default constructor + NoAuto__DONTUSEDIRECTLY() + { + new (hiddeninstance) TYPE (); + } + + operator TYPE& () { return *(TYPE *)hiddeninstance; } + TYPE& operator->() { return *(TYPE *)hiddeninstance; } + TYPE& operator*() { return *(TYPE *)hiddeninstance; } + + void Destructor() { (*(TYPE*)hiddeninstance)->TYPE::~TYPE(); } +}; + +#define BEGIN_MANUAL_HOLDER(_TYPE, _NAME) \ + { \ + NoAuto__DONTUSEDIRECTLY<_TYPE> _NAME; \ + __try \ + { + +#define END_MANUAL_HOLDER(_NAME) \ + } \ + __finally \ + { \ + _NAME.Destructor(); \ + } \ + } + +//---------------------------------------------------------------------------- +// +// External data access does not want certain holder implementations +// to be active as locks should not be taken and so on. Provide +// a no-op in that case. +// +//---------------------------------------------------------------------------- + +#ifndef DACCESS_COMPILE + +#define DacHolder Holder + +#else + +template <typename TYPE, void (*ACQUIRE)(TYPE), void (*RELEASEF)(TYPE), UINT_PTR DEFAULTVALUE = 0, BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>, BOOL VALIDATE_BACKOUT_STACK = TRUE> +class DacHolder +{ + protected: + TYPE m_value; + + private: + BOOL m_acquired; // Have we acquired the resource? + + public: + FORCEINLINE DacHolder() + : m_value(TYPE(DEFAULTVALUE)), + m_acquired(FALSE) + { + STATIC_CONTRACT_SUPPORTS_DAC; + } + + // construct a new instance of DacHolder + // Arguments: + // input: value - the resource held + // take - indicates whether the lock should be taken--the default is true. See Notes: + // Note: In DAC builds, the Acquire function does not actually take the lock, instead + // it determines whether the lock is held (by the LS). If it is, the locked data + // is assumed to be inconsistent and the Acquire function will throw. + FORCEINLINE DacHolder(TYPE value, BOOL take = TRUE) + : m_value(value), + m_acquired(FALSE) + { + STATIC_CONTRACT_SUPPORTS_DAC; + if (take) + Acquire(); + + } + FORCEINLINE ~DacHolder() + { + STATIC_CONTRACT_SUPPORTS_DAC; + } + // Sets the value to 'value'. Doesn't call Acquire/Release if fTake is FALSE. + FORCEINLINE void Assign(TYPE value, BOOL fTake = TRUE) + { + m_value = value; + } + FORCEINLINE void Acquire() + { + STATIC_CONTRACT_SUPPORTS_DAC; + + if (!IsNull()) + { + m_acquired = TRUE; + // because ACQUIRE is a template argument, if the line m_acquired = TRUE is placed after the call + // where it logically belongs, the compiler flags it as "unreachable code." + ACQUIRE(this->m_value); + } + } + FORCEINLINE void Release() + { + // Insert any global or thread bookeeping here + + if (m_acquired) + { + m_acquired = FALSE; + } + } + FORCEINLINE void Clear() + { + m_value = TYPE(DEFAULTVALUE); + m_acquired = FALSE; + } + FORCEINLINE void SuppressRelease() + { + m_acquired = FALSE; + } + FORCEINLINE TYPE GetValue() + { + return m_value; + } + FORCEINLINE BOOL IsNull() const + { + return IS_NULL(m_value, TYPE(DEFAULTVALUE)); + } + + private: + FORCEINLINE DacHolder& operator=(const Holder<TYPE, ACQUIRE, RELEASEF> &holder) + { + } + + FORCEINLINE DacHolder(const Holder<TYPE, ACQUIRE, RELEASEF> &holder) + { + } +}; + +#endif // #ifndef DACCESS_COMPILE + +// Holder-specific clr::SafeAddRef and clr::SafeRelease helper functions. +namespace clr +{ + template < typename ItfT > __checkReturn + ItfT * + SafeAddRef(ReleaseHolder<ItfT> & pItf) + { + STATIC_CONTRACT_LIMITED_METHOD; + //@TODO: Would be good to add runtime validation that the return value is used. + return SafeAddRef(pItf.GetValue()); + } + + namespace detail + { + template <typename T> + char IsHolderHelper(HolderBase<T>*); + int IsHolderHelper(...); + + template <typename T> + struct IsHolder : public std::conditional< + sizeof(IsHolderHelper(reinterpret_cast<T*>(NULL))) == sizeof(char), + std::true_type, + std::false_type>::type + {}; + } + + template < typename T > + typename std::enable_if<detail::IsHolder<T>::value, ULONG>::type + SafeRelease(T& arg) + { + STATIC_CONTRACT_LIMITED_METHOD; + return arg.Release(); + } +} + +#endif // __HOLDER_H_ |