// // 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 #include "cor.h" #include "genericstackprobe.h" #include "staticcontract.h" #include "volatile.h" #include "palclr.h" #ifdef PAL_STDCPP_COMPAT #include #include #else #include "clr_std/utility" #include "clr_std/type_traits" #endif #if defined(FEATURE_COMINTEROP) && !defined(STRIKE) #include #include #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=_rProp._rProp[3].asStr> // HolderBase=_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 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 BOOL CompareDefault(TYPE value, TYPE defaultValue) { STATIC_CONTRACT_SUPPORTS_DAC; return value == defaultValue; } #else template BOOL CompareDefault(TYPE value, TYPE defaultValue) { return FALSE; } #endif template BOOL NoNull(TYPE value, TYPE defaultValue) { return FALSE; } // Used e.g. for retail version of code:SyncAccessHolder template 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, HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq > class BaseHolder : protected BASE { protected: BOOL m_acquired; // Have we acquired the resource? static_assert(!std::is_pointer::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 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 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 , HolderStackValidation VALIDATION_TYPE = HSV_ValidateNormalStackReq> class BaseWrapper : public BaseHolder { typedef BaseHolder 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 &m_holder; public: FORCEINLINE AddressInitHolder(BaseWrapper &holder) : m_holder(holder) { // // We must clear the value, to avoid the following scenario: // // ReleaseHolder pMyType; // hr = MyFunction(&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(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(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(&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 FORCEINLINE bool operator==(T const & value) const { return !!(this->m_value == TYPE(value)); } template FORCEINLINE bool operator!=(T const & value) const { return !!(this->m_value != TYPE(value)); } #ifdef __llvm__ // This handles the NULL value that is an int and clang // doesn't want to convert int to a pointer FORCEINLINE bool operator==(int value) const { return !!(this->m_value == TYPE((void*)(SIZE_T)value)); } FORCEINLINE bool operator!=(int value) const { return !!(this->m_value != TYPE((void*)(SIZE_T)value)); } #endif // __llvm__ 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 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 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 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 class FunctionBase : protected HolderBase { protected: FORCEINLINE FunctionBase(TYPE value) : HolderBase(value) { } FORCEINLINE void DoAcquire() { ACQUIREF(this->m_value); } void DoRelease() { // Consider removing this stack validation since it is redundant with the // one that is already being done in BaseHolder & BaseWrapper. 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, 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, DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> { typedef BaseHolder, 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, 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, DEFAULTVALUE, IS_NULL, VALIDATION_TYPE> { typedef BaseWrapper, 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 // 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 \ 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 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 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 foo; // foo = new Stub(); // foo->AddRef(); // // Note StubHolder doesn't call AddRef for you. // } // foo->DecRef() on out of scope // //----------------------------------------------------------------------------- template FORCEINLINE void StubRelease(TYPE* value) { if (value) value->DecRef(); } NEW_WRAPPER_TEMPLATE1(StubHolder, StubRelease<_TYPE>); //----------------------------------------------------------------------------- // CoTaskMemHolder : CoTaskMemAlloc allocated memory holder // // { // CoTaskMemHolder foo = (Foo*) CoTaskMemAlloc(sizeof(Foo)); // } // delete foo on out of scope //----------------------------------------------------------------------------- template FORCEINLINE void DeleteCoTaskMem(TYPE *value) { if (value) CoTaskMemFree(value); } NEW_WRAPPER_TEMPLATE1(CoTaskMemHolder, DeleteCoTaskMem<_TYPE>); //----------------------------------------------------------------------------- // NewHolder : New'ed memory holder // // { // NewHolder foo = new Foo (); // } // delete foo on out of scope //----------------------------------------------------------------------------- template FORCEINLINE void Delete(TYPE *value) { STATIC_CONTRACT_LEAF; static_assert(!std::is_same::type, WCHAR>::value, "Must use NewArrayHolder (not NewHolder) for strings."); static_assert(!std::is_same::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*) new (executable) Byte[num]; // } // delete foo on out of scope //----------------------------------------------------------------------------- // IJW template void DeleteExecutable(T *p); NEW_WRAPPER_TEMPLATE1(NewExecutableHolder, DeleteExecutable<_TYPE>); //----------------------------------------------------------------------------- // NewArrayHolder : New []'ed pointer holder // { // NewArrayHolder foo = new Foo [30]; // } // delete [] foo on out of scope //----------------------------------------------------------------------------- template FORCEINLINE void DeleteArray(TYPE *value) { STATIC_CONTRACT_WRAPPER; delete [] value; value = NULL; } NEW_WRAPPER_TEMPLATE1(NewArrayHolder, DeleteArray<_TYPE>); typedef NewArrayHolder AStringHolder; typedef NewArrayHolder 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 class NewInterfaceArrayHolder : public NewArrayHolder { public: NewInterfaceArrayHolder() : NewArrayHolder(), m_cElements(0) { STATIC_CONTRACT_WRAPPER; } NewInterfaceArrayHolder& operator=(INTERFACE ** value) { STATIC_CONTRACT_WRAPPER; NewArrayHolder::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 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 struct ZeroMem { static VISIBLE void Invoke(T * pVal) { ZeroMemory(pVal, sizeof(T)); } }; template struct ZeroMem { 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 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, VoidCloseHandle, (UINT_PTR) -1> HandleHolder; typedef Wrapper, VoidCloseFileHandle, (UINT_PTR) -1> FileHandleHolder; typedef Wrapper, VoidFindClose, (UINT_PTR) -1> FindHandleHolder; typedef Wrapper 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, 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 HandleCSPHolder; typedef Wrapper HandleHashHolder; typedef Wrapper HandleKeyHolder; #endif // !FEATURE_CORECLR || FEATURE_CRYPTO //----------------------------------------------------------------------------- // Misc holders //----------------------------------------------------------------------------- // A holder for HMODULE. FORCEINLINE void HolderFreeLibrary(HMODULE h) { FreeLibrary(h); } typedef Wrapper, HolderFreeLibrary, NULL> HModuleHolder; template 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. // FORCEINLINE void CounterIncrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedIncrement(p);}; FORCEINLINE void CounterDecrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedDecrement(p);}; typedef Wrapper, HSV_NoValidation> CounterHolder; #ifndef FEATURE_PAL FORCEINLINE void RegKeyRelease(HKEY k) {RegCloseKey(k);}; typedef Wrapper 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); // ... use foo via -> // END_MANUAL_HOLDER(foo); // //----------------------------------------------------------------------------- template 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 , 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 &holder) { } FORCEINLINE DacHolder(const Holder &holder) { } }; #endif // #ifndef DACCESS_COMPILE // Holder-specific clr::SafeAddRef and clr::SafeRelease helper functions. namespace clr { template < typename ItfT > __checkReturn ItfT * SafeAddRef(ReleaseHolder & 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 char IsHolderHelper(HolderBase*); int IsHolderHelper(...); template struct IsHolder : public std::conditional< sizeof(IsHolderHelper(reinterpret_cast(NULL))) == sizeof(char), std::true_type, std::false_type>::type {}; } template < typename T > typename std::enable_if::value, ULONG>::type SafeRelease(T& arg) { STATIC_CONTRACT_LIMITED_METHOD; return arg.Release(); } } #endif // __HOLDER_H_