Add RefCount.h The struct that needs to benefit of reference counting should have an "OVS_REF_COUNT" field as the first field in that struct.
What it does: * It helps shortening the time a spin lock / rw lock is held (the spin lock & rw lock block the cpu). * It allows deferred object destruction / deletion. How it works: Say we have a list of OVS_FLOWs, where OVS_FLOW uses OVS_REF_COUNT. * The OVS_REF_COUNT must be initialized to 0 at object creation. * Assign a destruction function to the func ptr "Destroy" of OVS_REF_COUNT. * When you need to retrieve a ptr to an OVS_FLOW, from the list, you must do the following: *** lock the OVS_FLOW list for read (obviously) *** do the lookup: a ptr to an existing OVS_FLOW, pFlow, is found. *** call "OVS_REFCOUNT_REFERENCE(pFlow);" - by doing so, you deny destruction of pFlow, while the object is referenced, ensuring that you keep a valid pointer to a flow. *** unlock the list *** return pFlow. * When you need to modify fields in the pFlow, use the rw lock / spinlock field of pFlow. * When you no longer need the ptr, use "OVS_REFCOUNT_DEREFERENCE(pFlow);" * When you need to do deferred deletion, call "OVS_REFCOUNT_DESTROY(pFlow);": this will mark the pFlow for deletion. If the object is not referenced, it will be destroyed immediately. Otherwise, it will be destroyed when the refCount = 0. Signed-off-by: Samuel Ghinet <sghi...@cloudbasesolutions.com> --- datapath-windows/ovsext/Core/RefCount.h | 274 +++++++++++++++++++++++++ datapath-windows/ovsext/ovsext.vcxproj | 1 + datapath-windows/ovsext/ovsext.vcxproj.filters | 3 + 3 files changed, 278 insertions(+) create mode 100644 datapath-windows/ovsext/Core/RefCount.h diff --git a/datapath-windows/ovsext/Core/RefCount.h b/datapath-windows/ovsext/Core/RefCount.h new file mode 100644 index 0000000..3694e37 --- /dev/null +++ b/datapath-windows/ovsext/Core/RefCount.h @@ -0,0 +1,274 @@ +/* +Copyright 2014 Cloudbase Solutions Srl + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http ://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include "precomp.h" + +#ifdef DBG +#define OVS_USE_REFCOUNT_CALL_STACK 1 +#else +#define OVS_USE_REFCOUNT_CALL_STACK 0 +#endif + +#if OVS_USE_REFCOUNT_CALL_STACK +#define OVS_REFCOUNT_MAX_THREAD_COUNT 16 +#define OVS_REFCOUNT_MAX_FUNC_COUNT 10 + +C_ASSERT(OVS_REFCOUNT_MAX_THREAD_COUNT <= 64); +#endif + +//NOTE: must be the first field of any struct using the OVS_REF_COUNT +typedef struct _OVS_REF_COUNT +{ +#if OVS_USE_REFCOUNT_CALL_STACK + //for debugging purposes: when a refCount is increased, it is first added to countsPerThread[currentThreadNumber] + //then the func name (__FUNCTION__) will be placed at funcs[curThreadNumber][refCountPerCurrentThread] + //if there is not enough room to store the func name in the stack (i.e. refCount >= OVS_REFCOUNT_MAX_FUNC_COUNT) => we consider it a bug + const char* funcs[OVS_REFCOUNT_MAX_THREAD_COUNT][OVS_REFCOUNT_MAX_FUNC_COUNT]; + ULONG refCountsPerThread[OVS_REFCOUNT_MAX_THREAD_COUNT]; + PKTHREAD threads[OVS_REFCOUNT_MAX_THREAD_COUNT]; + ULONG noOfThreads; +#endif + + //if refCount > 0, the object deletion will be postponed + //it is used so that we can keep a pointer to the object, while allowing it to be modified (but not deleted) by other threads. + volatile ULONG refCount; + //if the object should be destroyed, but some thread currently holds a reference to it, it will be marked for deletion instead. + //when the refCount reaches 0, if deletetionPending is set, the dereference function will destroy the object. + volatile BOOLEAN deletionPending; + + VOID (*Destroy)(VOID*); +}OVS_REF_COUNT; + +extern NDIS_RW_LOCK_EX* g_pRefRwLock; + +/**************************************/ + +static __inline VOID RefCount_DereferenceOnly(VOID* pObj) +{ + LOCK_STATE_EX lockState; + OVS_REF_COUNT* pRefCount = pObj; + + if (!pObj) + { + return; + } + + NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0); + + OVS_CHECK(pRefCount->refCount > 0); +#if OVS_USE_REFCOUNT_CALL_STACK + { + ULONG threadNumber = MAXULONG; + ULONG curThreadRefCount = 0; + PKTHREAD pThread = NULL; + + OVS_CHECK(pRefCount->refCount < OVS_REFCOUNT_MAX_FUNC_COUNT); + pThread = KeGetCurrentThread(); + + for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i) + { + if (pRefCount->threads[i] == pThread) + { + threadNumber = i; + break; + } + } + + OVS_CHECK(threadNumber != MAXULONG); + + OVS_CHECK(pRefCount->refCountsPerThread[threadNumber] > 0); + pRefCount->refCountsPerThread[threadNumber]--; + curThreadRefCount = pRefCount->refCountsPerThread[threadNumber]; + pRefCount->funcs[threadNumber][curThreadRefCount] = NULL; + + if (curThreadRefCount == 0) + { + pRefCount->threads[threadNumber] = NULL; + pRefCount->noOfThreads--; + } + } +#endif + --pRefCount->refCount; + + NdisReleaseRWLock(g_pRefRwLock, &lockState); +} + +#define OVS_REFCOUNT_DEREFERENCE_ONLY(pObj) RefCount_DereferenceOnly(pObj) + +static __inline VOID RefCount_Dereference(VOID* pObj) +{ + LOCK_STATE_EX lockState; + OVS_REF_COUNT* pRefCount = pObj; + + if (!pObj) + { + return; + } + + OVS_CHECK(pRefCount->Destroy); + + NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0); + + OVS_CHECK(pRefCount->refCount > 0); +#if OVS_USE_REFCOUNT_CALL_STACK + { + ULONG threadNumber = MAXULONG; + ULONG curThreadRefCount = 0; + PKTHREAD pThread = NULL; + + OVS_CHECK(pRefCount->refCount < OVS_REFCOUNT_MAX_FUNC_COUNT); + pThread = KeGetCurrentThread(); + + for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i) + { + if (pRefCount->threads[i] == pThread) + { + threadNumber = i; + break; + } + } + + OVS_CHECK(threadNumber != MAXULONG); + + OVS_CHECK(pRefCount->refCountsPerThread[threadNumber] > 0); + pRefCount->refCountsPerThread[threadNumber]--; + curThreadRefCount = pRefCount->refCountsPerThread[threadNumber]; + pRefCount->funcs[threadNumber][curThreadRefCount] = NULL; + + if (curThreadRefCount == 0) + { + pRefCount->threads[threadNumber] = NULL; + pRefCount->noOfThreads--; + } + } +#endif + --pRefCount->refCount; + + if (pRefCount->refCount == 0 && pRefCount->deletionPending) + { + pRefCount->Destroy(pObj); + } + + NdisReleaseRWLock(g_pRefRwLock, &lockState); +} + +#define OVS_REFCOUNT_DEREFERENCE(pObj) {RefCount_Dereference(pObj); pObj = NULL; } + +static __inline VOID* RefCount_Reference(VOID* pObj, const char* funcName) +{ + LOCK_STATE_EX lockState; + OVS_REF_COUNT* pRefCount = pObj; + + if (!pObj) + { + return NULL; + } + + NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0); + + if (pRefCount->deletionPending) + { + pObj = NULL; + } + else + { +#if OVS_USE_REFCOUNT_CALL_STACK + ULONG threadNumber = MAXULONG; + ULONG curThreadRefCount = 0; + PKTHREAD pThread = NULL; + + OVS_CHECK(pRefCount->noOfThreads < OVS_REFCOUNT_MAX_THREAD_COUNT); + + pThread = KeGetCurrentThread(); + + for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i) + { + if (pRefCount->threads[i] == pThread) + { + threadNumber = i; + break; + } + } + + if (threadNumber == MAXULONG) + { + OVS_CHECK(pRefCount->noOfThreads + 1 < OVS_REFCOUNT_MAX_THREAD_COUNT); + + for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i) + { + if (pRefCount->threads[i] == NULL) + { + pRefCount->threads[i] = pThread; + pRefCount->noOfThreads++; + + threadNumber = i; + break; + } + } + } + + OVS_CHECK(threadNumber != MAXULONG); + + curThreadRefCount = pRefCount->refCountsPerThread[threadNumber]; + OVS_CHECK(curThreadRefCount + 1 < OVS_REFCOUNT_MAX_FUNC_COUNT); + + pRefCount->funcs[threadNumber][curThreadRefCount] = funcName; + pRefCount->refCountsPerThread[threadNumber]++; +#else + UNREFERENCED_PARAMETER(funcName); +#endif + + ++pRefCount->refCount; + } + + NdisReleaseRWLock(g_pRefRwLock, &lockState); + + return pObj; +} + +#define OVS_REFCOUNT_REFERENCE(pObj) RefCount_Reference(pObj, __FUNCTION__); + +static __inline VOID RefCount_Destroy(VOID* pObj) +{ + LOCK_STATE_EX lockState; + OVS_REF_COUNT* pRefCount = pObj; + + if (!pObj) + { + return; + } + + OVS_CHECK(pRefCount->Destroy); + + NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0); + + if (pRefCount->refCount > 0) + { + pRefCount->deletionPending = TRUE; + } + else + { + pRefCount->Destroy(pObj); + } + + NdisReleaseRWLock(g_pRefRwLock, &lockState); +} + +#define OVS_REFCOUNT_DESTROY(pObj) { RefCount_Destroy(pObj); pObj = NULL; } + +#define OVS_REFCOUNT_DEREF_AND_DESTROY(pObj) { OVS_REFCOUNT_DEREFERENCE_ONLY(pObj); OVS_REFCOUNT_DESTROY(pObj); } \ No newline at end of file diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj index cce9df0..99aa6f6 100644 --- a/datapath-windows/ovsext/ovsext.vcxproj +++ b/datapath-windows/ovsext/ovsext.vcxproj @@ -75,6 +75,7 @@ <ClInclude Include="Core\IpHelper.h" /> <ClInclude Include="Core\Jhash.h" /> <ClInclude Include="Core\List.h" /> + <ClInclude Include="Core\RefCount.h" /> <ClInclude Include="Core\SpookyHash.h" /> <ClInclude Include="Core\Types.h" /> <ClInclude Include="Core\Util.h" /> diff --git a/datapath-windows/ovsext/ovsext.vcxproj.filters b/datapath-windows/ovsext/ovsext.vcxproj.filters index c23ca42..fc3ba3b 100644 --- a/datapath-windows/ovsext/ovsext.vcxproj.filters +++ b/datapath-windows/ovsext/ovsext.vcxproj.filters @@ -83,6 +83,9 @@ <ClInclude Include="Core\SpookyHash.h"> <Filter>Core</Filter> </ClInclude> + <ClInclude Include="Core\RefCount.h"> + <Filter>Core</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="ovsext.rc" /> -- 1.8.3.msysgit.0 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev