1393 lines
45 KiB
C++
1393 lines
45 KiB
C++
/* --------------------------------------------------------------------
|
||
|
||
Microsoft OS/2 LAN Manager
|
||
Copyright(c) Microsoft Corp., 1991-2001
|
||
|
||
-------------------------------------------------------------------- */
|
||
/* --------------------------------------------------------------------
|
||
|
||
Description :
|
||
|
||
Provides RPC server side context handle management
|
||
|
||
History :
|
||
|
||
stevez 01-15-91 First bits into the bucket.
|
||
Kamen Moutafov [kamenm] Sep 2000 Threw away all of Steve's bits and
|
||
buckets, and rewrote it to fix or pave
|
||
the road for the fixing of the following
|
||
design bugs:
|
||
- non-serialized context handles are
|
||
unusable, and mixing serialized and
|
||
non-serialized doesn't work
|
||
- stealing context handles
|
||
- gradual rundown of context handles
|
||
- poor scalability of the context handle
|
||
code (high cost of individual context
|
||
handles) and contention on the context
|
||
list
|
||
|
||
-------------------------------------------------------------------- */
|
||
|
||
#include <precomp.hxx>
|
||
#include <context.hxx>
|
||
#include <SContext.hxx>
|
||
#include <HndlSvr.hxx>
|
||
#include <CtxColl.hxx>
|
||
#include <OSFPcket.hxx>
|
||
|
||
#ifdef SCONTEXT_UNIT_TESTS
|
||
#define CODE_COVERAGE_CHECK ASSERT(_NOT_COVERED_)
|
||
|
||
inline ServerContextHandle *
|
||
AllocateServerContextHandle (
|
||
IN void *CtxGuard
|
||
)
|
||
{
|
||
if ((GetRandomLong() % 9999) == 0)
|
||
return NULL;
|
||
|
||
return new ServerContextHandle(CtxGuard);
|
||
}
|
||
|
||
#ifdef GENERATE_STATUS_FAILURE
|
||
#undef GENERATE_STATUS_FAILURE
|
||
#endif // GENERATE_STATUS_FAILURE
|
||
|
||
#define GENERATE_STATUS_FAILURE(s) \
|
||
if ((GetRandomLong() % 9999) == 0) \
|
||
s = RPC_S_OUT_OF_MEMORY;
|
||
|
||
#define GENERATE_ADD_TO_COLLECTION_FAILURE(scall, ctx, status) \
|
||
if ((GetRandomLong() % 9999) == 0) \
|
||
{ \
|
||
scall->RemoveFromActiveContextHandles(ctx); \
|
||
status = RPC_S_OUT_OF_MEMORY; \
|
||
} \
|
||
|
||
#define GENERATE_LOCK_FAILURE(ctx, th, wcptr, status) \
|
||
if ((GetRandomLong() % 9999) == 0) \
|
||
{ \
|
||
ctx->Lock.Unlock(wcptr); \
|
||
th->FreeWaiterCache(wcptr); \
|
||
status = RPC_S_OUT_OF_MEMORY; \
|
||
} \
|
||
|
||
#else
|
||
#define CODE_COVERAGE_CHECK
|
||
|
||
inline ServerContextHandle *
|
||
AllocateServerContextHandle (
|
||
IN void *CtxGuard
|
||
)
|
||
{
|
||
return new ServerContextHandle(CtxGuard);
|
||
}
|
||
|
||
#define GENERATE_STATUS_FAILURE(s)
|
||
#define GENERATE_ADD_TO_COLLECTION_FAILURE(scall, ctx, status)
|
||
#define GENERATE_LOCK_FAILURE(ctx, th, wcptr, status)
|
||
|
||
#endif
|
||
|
||
WIRE_CONTEXT NullContext; // all zeros
|
||
|
||
// per process variable defining what is the synchronization mode
|
||
// for new context handles that don't have NDR level default
|
||
unsigned int DontSerializeContext = 0;
|
||
|
||
|
||
inline BOOL
|
||
DoesContextHandleNeedExclusiveLock (
|
||
IN unsigned long Flags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines if a context handle needs exclusive lock or
|
||
shared lock.
|
||
|
||
Arguments:
|
||
|
||
Flags - the flags given to the runtime by NDR.
|
||
|
||
Return Value:
|
||
non-zero if the context handle needs exclusive lock. FALSE
|
||
otherwise. There is no failure for this function.
|
||
|
||
--*/
|
||
{
|
||
// make sure exactly one flag is set
|
||
ASSERT((Flags & RPC_CONTEXT_HANDLE_FLAGS) != RPC_CONTEXT_HANDLE_FLAGS);
|
||
|
||
switch (Flags & RPC_CONTEXT_HANDLE_FLAGS)
|
||
{
|
||
case RPC_CONTEXT_HANDLE_SERIALIZE:
|
||
// serialize is Exclusive
|
||
return TRUE;
|
||
|
||
case RPC_CONTEXT_HANDLE_DONT_SERIALIZE:
|
||
// non-serialized is Shared
|
||
return FALSE;
|
||
}
|
||
|
||
return (DontSerializeContext == 0);
|
||
}
|
||
|
||
inline ContextCollection *
|
||
GetContextCollection (
|
||
IN RPC_BINDING_HANDLE BindingHandle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets the context collection from the call object and
|
||
throws exception if this fails
|
||
|
||
Arguments:
|
||
|
||
BindingHandle - the scall
|
||
|
||
Return Value:
|
||
The collection. If getting the collection fails, an
|
||
exception is thrown
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS RpcStatus;
|
||
ContextCollection *CtxCollection;
|
||
|
||
RpcStatus = ((SCALL *)BindingHandle)->GetAssociationContextCollection(&CtxCollection);
|
||
if (RpcStatus != RPC_S_OK)
|
||
{
|
||
RpcpRaiseException(RpcStatus);
|
||
}
|
||
|
||
return CtxCollection;
|
||
}
|
||
|
||
void
|
||
DestroyContextCollection (
|
||
IN ContextCollection *CtxCollection
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Destroys all context handles in the collection, regardless
|
||
of guard value. The context handles are rundown before destruction in
|
||
case they aren't used. If they are, the rundown needed flag is set
|
||
|
||
Arguments:
|
||
|
||
CtxCollection - the collection of context handles.
|
||
|
||
--*/
|
||
{
|
||
DestroyContextHandlesForGuard((PVOID)CtxCollection,
|
||
TRUE, // Rundown context handle
|
||
NULL // nuke all contexts, regardless of guard value
|
||
);
|
||
|
||
delete CtxCollection;
|
||
}
|
||
|
||
void
|
||
NDRSRundownContextHandle (
|
||
IN ServerContextHandle *ContextHandle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Runs down a context handle by calling the user's rundown routine
|
||
|
||
Arguments:
|
||
ContextHandle - the context handle
|
||
|
||
Notes:
|
||
This routine will only touch the UserRunDown and UserContext members
|
||
of Context. This allows caller to make up ServerContextHandles on the fly
|
||
and just fill in those two members.
|
||
--*/
|
||
|
||
{
|
||
// Only contexts which have a rundown and
|
||
// are valid are cleaned up.
|
||
if ((ContextHandle->UserRunDown != NULL)
|
||
&& ContextHandle->UserContext)
|
||
{
|
||
RpcTryExcept
|
||
{
|
||
(*ContextHandle->UserRunDown)(ContextHandle->UserContext);
|
||
}
|
||
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
|
||
{
|
||
#if DBG
|
||
DbgPrint("Routine %p threw an exception %lu (0x%08lX) - this is illegal\n", ContextHandle->UserRunDown, RpcExceptionCode(), RpcExceptionCode());
|
||
ASSERT(!"The rundown routine is not allowed to throw exceptions")
|
||
#endif
|
||
}
|
||
RpcEndExcept
|
||
}
|
||
}
|
||
|
||
void
|
||
DestroyContextHandlesForGuard (
|
||
IN PVOID CtxCollectionPtr,
|
||
IN BOOL RundownContextHandle,
|
||
IN void *CtxGuard OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Each context handle in this association with the specified
|
||
guard *and* a zero refcount will be cleaned up in a way
|
||
determined by RundownContextHandle (see comment for
|
||
RundownContextHandle below)
|
||
|
||
Arguments:
|
||
Context - the context for the association
|
||
RundownContextHandle - if non-zero, rundown the context handle
|
||
If zero, just cleanup the runtime part and the app will
|
||
cleanup its part
|
||
CtxGuard - the guard for which to cleanup context handles. If
|
||
NULL, all context handles will be cleaned.
|
||
|
||
Notes:
|
||
Access to a context handle with lifetime refcount only is implicitly
|
||
synchronized as this function will be called from two places -
|
||
the association rundown, and RpcServerUnregisterIfEx. In the
|
||
former case all connections are gone, and nobody can come
|
||
and start using the context handle. In the second, the interface
|
||
is unregistered, and again nobody can come and start using the
|
||
context handle. Therefore each context handle with lifetime refcount only
|
||
(and for the specified guard) is synrchronized. This is not
|
||
true however for the list itself. In the association rundown
|
||
case some context handles may be used asynchronously for parked
|
||
calls, and we need to synchronize access to the list.
|
||
If this is called from RpcServerUnregisterIfEx, then all context
|
||
handles must have zero refcount as before unregistering the
|
||
interface we must have waited for all calls to complete.
|
||
--*/
|
||
{
|
||
ContextCollection *CtxCollection = (ContextCollection *)CtxCollectionPtr;
|
||
LIST_ENTRY *NextListEntry;
|
||
ServerContextHandle *CurrentContextHandle;
|
||
|
||
// N.B. It may seem like there is a race condition here b/n the two
|
||
// callers of this function - RpcServerUnregisterIfEx & the destructor
|
||
// of the association, as they can be called independently, and start
|
||
// partying on the same list. However, the RpcServerUnregisterIfEx
|
||
// branch will either take the association mutex, or will add a
|
||
// refcount on the association, so that the association can
|
||
// never be destroyed while this function is called by the
|
||
// RpcServerUnregisterIfEx branch, and it is implicitly
|
||
// synchronized as far as destruction is concerned. We still
|
||
// need to synchronize access to the list
|
||
|
||
CtxCollection->Lock();
|
||
|
||
// for each user created context for this assoication, check
|
||
// whether it fits our criteria for destruction, and if yes,
|
||
// destroy it. This is an abnormal case, so we don't care about
|
||
// performance
|
||
NextListEntry = NULL;
|
||
while ((CurrentContextHandle = CtxCollection->GetNext(&NextListEntry)) != NULL)
|
||
{
|
||
// if we were asked to clean up for a specific context
|
||
// guard, check whether there is a match
|
||
if (CtxGuard && (CtxGuard != CurrentContextHandle->CtxGuard))
|
||
{
|
||
// there is no match - move on to the next
|
||
continue;
|
||
}
|
||
|
||
// NextListEntry is valid even after destruction of the current
|
||
// context handle - we don't need to worry about that
|
||
CtxCollection->Remove(CurrentContextHandle);
|
||
|
||
|
||
ASSERT((CurrentContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollectionMask) == 0);
|
||
CurrentContextHandle->Flags |= ServerContextHandle::ContextRemovedFromCollectionMask
|
||
| ServerContextHandle::ContextNeedsRundown;
|
||
|
||
// remove the lifetime reference. If the refcount drops to 0, we can
|
||
// do the cleanup.
|
||
if (CurrentContextHandle->RemoveReference() == 0)
|
||
{
|
||
if (RundownContextHandle)
|
||
{
|
||
NDRSRundownContextHandle(CurrentContextHandle);
|
||
}
|
||
|
||
delete CurrentContextHandle;
|
||
}
|
||
|
||
// N.B. Don't touch the CurrentContextHandle below this. We have released
|
||
// our refcount
|
||
#if DBG
|
||
// enforce it on checked
|
||
CurrentContextHandle = NULL;
|
||
#endif
|
||
}
|
||
|
||
CtxCollection->Unlock();
|
||
}
|
||
|
||
void
|
||
FinishUsingContextHandle (
|
||
IN SCALL *CallObject,
|
||
IN ServerContextHandle *ContextHandle,
|
||
IN BOOL fUserDeletedContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform functions commonly needed when execution returns
|
||
from the server manager routine - if the context is in
|
||
the list of active context handles, unlock it and remove it.
|
||
Decrease the refcount, and if 0, remove the context from
|
||
the collection, and if rundown as asked for, fire the rundown.
|
||
|
||
Arguments:
|
||
|
||
CallObject - the server-side call object (the scall)
|
||
ContextHandle - the context handle
|
||
fUserDeletedContext - non-zero if the user has deleted the context handle
|
||
(i.e. set UserContext to NULL)
|
||
--*/
|
||
{
|
||
ServerContextHandle *RemovedContextHandle;
|
||
ContextCollection *CtxCollection = NULL;
|
||
long LocalRefCount;
|
||
RPC_STATUS RpcStatus;
|
||
SWMRWaiter *WaiterCache;
|
||
THREAD *Thread;
|
||
BOOL fRemoveLifeTimeReference;
|
||
|
||
RemovedContextHandle = CallObject->RemoveFromActiveContextHandles(ContextHandle);
|
||
|
||
if (fUserDeletedContext)
|
||
{
|
||
RpcStatus = CallObject->GetAssociationContextCollection(&CtxCollection);
|
||
// the getting of the collection must succeed here, as we have already
|
||
// created it, and we're simply getting it
|
||
ASSERT(RpcStatus == RPC_S_OK);
|
||
|
||
fRemoveLifeTimeReference = FALSE;
|
||
CtxCollection->Lock();
|
||
// if the context is still in the collection, remove it and take
|
||
// down the lifetime reference
|
||
if ((ContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollectionMask) == 0)
|
||
{
|
||
ContextHandle->Flags |= ServerContextHandle::ContextRemovedFromCollectionMask;
|
||
fRemoveLifeTimeReference = TRUE;
|
||
CtxCollection->Remove(ContextHandle);
|
||
}
|
||
CtxCollection->Unlock();
|
||
|
||
// do it outside the lock
|
||
if (fRemoveLifeTimeReference)
|
||
{
|
||
LocalRefCount = ContextHandle->RemoveReference();
|
||
ASSERT(LocalRefCount);
|
||
}
|
||
}
|
||
|
||
// if we were able to extract it from the list of active context handles, it must
|
||
// have been active, and thus needs unlocking
|
||
if (RemovedContextHandle)
|
||
{
|
||
WaiterCache = NULL;
|
||
ContextHandle->Lock.Unlock(&WaiterCache);
|
||
Thread = ThreadSelf();
|
||
if (Thread)
|
||
{
|
||
Thread->FreeWaiterCache(&WaiterCache);
|
||
}
|
||
else
|
||
{
|
||
SWMRLock::FreeWaiterCache(&WaiterCache);
|
||
}
|
||
}
|
||
|
||
LocalRefCount = ContextHandle->RemoveReference();
|
||
if (LocalRefCount == 0)
|
||
{
|
||
// if we were asked to rundown by the rundown code, do it.
|
||
if (ContextHandle->Flags & ServerContextHandle::ContextNeedsRundownMask)
|
||
{
|
||
NDRSRundownContextHandle(ContextHandle);
|
||
}
|
||
|
||
ASSERT (ContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollectionMask);
|
||
|
||
delete ContextHandle;
|
||
}
|
||
}
|
||
|
||
ServerContextHandle *
|
||
FindAndAddRefContextHandle (
|
||
IN ContextCollection *CtxCollection,
|
||
IN WIRE_CONTEXT *WireContext,
|
||
IN PVOID CtxGuard,
|
||
OUT BOOL *ContextHandleNewlyCreated
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attempts to find the context handle for the given wire buffer,
|
||
and if found, add a refcount to it, and return it.
|
||
|
||
Arguments:
|
||
|
||
CtxCollection - the context handle collection.
|
||
WireContext - the on-the-wire representation of the context
|
||
CtxGuard - the context guard - if NULL, then any context handle
|
||
matches. If non-NULL, the context handle that matches the
|
||
wire context must have the same context guard in order for it
|
||
to be considered a match.
|
||
ContextHandleNewlyCreated - a pointer to a boolean variable that
|
||
will be set to non-zero if the context handle had the newly
|
||
created flag set, or to FALSE if it didn't. If the return value
|
||
is NULL, this is undefined.
|
||
|
||
Return Value:
|
||
The found context handle. NULL if no matching context handle was
|
||
found.
|
||
|
||
Notes:
|
||
The newly created flag is always taken down regardless of other
|
||
paremeters.
|
||
|
||
--*/
|
||
{
|
||
ServerContextHandle *ContextHandle;
|
||
BOOL LocalContextHandleNewlyCreated = FALSE;
|
||
|
||
CtxCollection->Lock();
|
||
|
||
ContextHandle = CtxCollection->Find(WireContext);
|
||
|
||
// if we have found a context handle, and is from the same interface, or
|
||
// we don't care from what interface it is, get it
|
||
if (ContextHandle
|
||
&& (
|
||
(ContextHandle->CtxGuard == CtxGuard)
|
||
||
|
||
(CtxGuard == NULL)
|
||
)
|
||
)
|
||
{
|
||
ASSERT(ContextHandle->ReferenceCount);
|
||
// the only two flags that can be possibly set here are ContextAllocState and/or
|
||
// ContextNewlyCreated. ContextAllocState *must* be ContextCompletedAlloc.
|
||
// ASSERT that
|
||
ASSERT(ContextHandle->Flags & ServerContextHandle::ContextAllocState);
|
||
ASSERT((ContextHandle->Flags &
|
||
~(ServerContextHandle::ContextAllocState | ServerContextHandle::ContextNewlyCreatedMask))
|
||
== 0);
|
||
ContextHandle->AddReference();
|
||
if (ContextHandle->Flags & ServerContextHandle::ContextNewlyCreatedMask)
|
||
{
|
||
LocalContextHandleNewlyCreated = TRUE;
|
||
}
|
||
// take down the ContextNewlyCreated flag. Since we know that the only other
|
||
// flag that can be set at this point is ContextNewlyCreated, a simple assignment
|
||
// is sufficient
|
||
ContextHandle->Flags = ServerContextHandle::ContextCompletedAlloc;
|
||
}
|
||
else
|
||
{
|
||
ContextHandle = NULL;
|
||
}
|
||
|
||
CtxCollection->Unlock();
|
||
|
||
*ContextHandleNewlyCreated = LocalContextHandleNewlyCreated;
|
||
return ContextHandle;
|
||
}
|
||
|
||
void
|
||
NDRSContextHandlePostDispatchProcessing (
|
||
IN SCALL *SCall,
|
||
ServerContextHandle *CtxHandle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs post dispatch processing needed for in only context
|
||
handles. If the context handle was NULL on input, just delete
|
||
it. Else, finish using it.
|
||
|
||
Arguments:
|
||
|
||
BindingHandle - the server-side binding handle (the scall)
|
||
CtxHandle - the context handle
|
||
--*/
|
||
{
|
||
if ((CtxHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextPendingAlloc)
|
||
{
|
||
CODE_COVERAGE_CHECK;
|
||
// [in] only context handle that didn't get set
|
||
delete CtxHandle;
|
||
}
|
||
else
|
||
{
|
||
FinishUsingContextHandle(SCall,
|
||
CtxHandle,
|
||
FALSE // fUserDeletedContextHandle
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
NDRSContextEmergencyCleanup (
|
||
IN RPC_BINDING_HANDLE BindingHandle,
|
||
IN OUT NDR_SCONTEXT hContext,
|
||
IN NDR_RUNDOWN UserRunDownIn,
|
||
IN PVOID UserContext,
|
||
IN BOOL ManagerRoutineException)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform emergency cleanup if the manager routine throws an exception,
|
||
or marshalling fails, or an async call is aborted. In the process,
|
||
if the context handle was actively used, it must finish using it.
|
||
|
||
Arguments:
|
||
|
||
BindingHandle - the server-side binding handle (the scall)
|
||
hContext - the hContext created during unmarshalling.
|
||
UserRunDownIn - if hContext is non-NULL, the user rundown from
|
||
there will be used. This parameter will be used only if
|
||
hContext is NULL.
|
||
UserContext - the user context returned from the user. This will be
|
||
set only in case 9 (see below). For all other cases, it will be 0
|
||
ManagerRoutineException - non-zero if the exception was thrown
|
||
from the manager routine.
|
||
|
||
Notes:
|
||
Here's the functionality matrix for this function:
|
||
|
||
NDR will not call runtime in cases 1, 4 and 8
|
||
|
||
User C Exc Handle Clea- Run- Finish Further use of
|
||
N Unm Mar From: tx(To:) ept Type hCtx nup down UsingCH context handle on the client:
|
||
-- ---- --- ----- ------- --- ------ ---- ----- ----- ------ -----------------------------
|
||
1 N NA NA NA NA NA NA N N N *As if the call was never made
|
||
2a Y N NULL NA Y NA !NULL Y N N *As if the call was never made
|
||
2b Y N !NULL NA Y NA !NULL N N Y *As if the call was never made
|
||
4 Y Y Any NULL N Any Any N N N *New context on the server
|
||
5a Y Y NULL !NULL N Any Marker Y Y N *As if the call was never made
|
||
5b Y Y !NULL !NULL N Any Marker N N N *To: value on the server
|
||
6a Y N NULL NULL N !ret !NULL Y N N *As if the call was never made
|
||
6b Y N !NULL NULL N !ret !NULL Y N Y *Invalid context from the server
|
||
7a Y N NULL !NULL N !ret !NULL Y Y N *As if the call was never made
|
||
7b Y N !NULL !NULL N !ret !NULL N N Y To: value on the server
|
||
8 Y N NA NULL N ret NULL N N N *NA (i.e. no retval)
|
||
9 Y N NA !NULL N ret NULL N Y N *NA (i.e. no retval)
|
||
|
||
N.B. This routine throws exceptions on failure. Only datagram context handles have failure
|
||
paths (aside from claiming critical section failures)
|
||
--*/
|
||
{
|
||
ServerContextHandle *ContextHandle = (ServerContextHandle *)hContext;
|
||
ContextCollection *CtxCollection;
|
||
SCALL *SCall = (SCALL *)BindingHandle;
|
||
BOOL ContextHandleNewlyCreated;
|
||
DictionaryCursor cursor;
|
||
PVOID Buffer;
|
||
|
||
ASSERT(SCall->Type(SCALL_TYPE));
|
||
|
||
LogEvent(SU_EXCEPT, EV_DELETE, ContextHandle, UserContext, ManagerRoutineException, 1, 0);
|
||
|
||
// N.B. The following code doesn't make sense unless you have gone
|
||
// through the notes in the comments. Please, read the notes before you
|
||
// read this code
|
||
if (ManagerRoutineException)
|
||
{
|
||
ASSERT(ContextHandle != NULL);
|
||
|
||
// Cases 2a, 2b
|
||
// Detect case 2a and cleanup runtime stuff for it
|
||
if ((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextPendingAlloc)
|
||
{
|
||
// case 2a started with NULL context handle - no need to call
|
||
// FinishUsingContextHandle
|
||
delete ContextHandle;
|
||
}
|
||
else
|
||
{
|
||
// case 2b - we started with a non-NULL context handle - we need to finish
|
||
// using it
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
FALSE // fUserDeletedContext
|
||
);
|
||
}
|
||
}
|
||
else if (ContextHandle == NULL)
|
||
{
|
||
ServerContextHandle TempItem(NULL);
|
||
|
||
// Case 9
|
||
// This must be a return value context handle, which the user has set to !NULL,
|
||
// but we encountered marshalling problems before marshalling it. In this
|
||
// case, simply rundown the user context.
|
||
|
||
CODE_COVERAGE_CHECK;
|
||
ASSERT(UserRunDownIn);
|
||
ASSERT(UserContext);
|
||
|
||
// create a temp context we can use for rundowns
|
||
TempItem.UserRunDown = UserRunDownIn;
|
||
TempItem.UserContext = UserContext;
|
||
|
||
NDRSRundownContextHandle(&TempItem);
|
||
}
|
||
else if (ContextHandle == CONTEXT_HANDLE_AFTER_MARSHAL_MARKER)
|
||
{
|
||
// Cases 5a, 5b.
|
||
// The context handle has been marshalled. Since we have released
|
||
// all reference to the context handle, we cannot touch it. We need
|
||
// to go back and search for the context handle again. It may have
|
||
// been deleted either through a rundown, or by an attacker guessing
|
||
// the context handle. Either way we want to handle it gracefully
|
||
// Once we find the context handle (and get a lock on it), we need
|
||
// to check if it has been used in the meantime, and if not, we
|
||
// can proceed with the cleanup. If yes, just ignore it.
|
||
|
||
CtxCollection = GetContextCollection(BindingHandle);
|
||
// this must succeed as we have already obtained the collection once
|
||
// during umarshalling
|
||
ASSERT(CtxCollection);
|
||
|
||
// in case 5b, we won't find anything, since we don't put buffers in
|
||
// the collection. In this case, the loop will exit, and we'll be fine
|
||
SCall->ActiveContextHandles.Reset(cursor);
|
||
while ((Buffer = SCall->ActiveContextHandles.Next(cursor)) != 0)
|
||
{
|
||
// if this is not a buffer
|
||
if (((ULONG_PTR)Buffer & SCALL::DictionaryEntryIsBuffer) == 0)
|
||
{
|
||
CODE_COVERAGE_CHECK;
|
||
continue;
|
||
}
|
||
|
||
Buffer = (PVOID)((ULONG_PTR)Buffer & (~(SCALL::DictionaryEntryIsBuffer)));
|
||
|
||
ContextHandle = FindAndAddRefContextHandle(CtxCollection,
|
||
(WIRE_CONTEXT *)Buffer,
|
||
NULL, // CtxGuard
|
||
&ContextHandleNewlyCreated
|
||
);
|
||
|
||
if (ContextHandle)
|
||
{
|
||
if (ContextHandleNewlyCreated)
|
||
{
|
||
// Case 5a
|
||
// this context handle was newly created - it cannot be used
|
||
// by anybody, and it cannot be in the active calls collection
|
||
// Therefore, it is safe to set the flag without holding the
|
||
// lock and to call FinishUsingContextHandle, which will decrement
|
||
// the ref count and will rundown & cleanup the context handle
|
||
ContextHandle->Flags |= ServerContextHandle::ContextNeedsRundown;
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
TRUE // fUserDeletedContext
|
||
);
|
||
}
|
||
else
|
||
{
|
||
CODE_COVERAGE_CHECK;
|
||
// somebody managed to use the context handle - just finish off using it
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
FALSE // fUserDeletedContext
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if ((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextPendingAlloc)
|
||
{
|
||
// Cases 6a, 7a
|
||
|
||
UserContext = ContextHandle->UserContext;
|
||
|
||
if (UserContext)
|
||
{
|
||
// if we're in case 7a
|
||
NDRSRundownContextHandle(ContextHandle);
|
||
}
|
||
|
||
// cases 6a, 7a
|
||
delete ContextHandle;
|
||
}
|
||
else if (UserContext == NULL)
|
||
{
|
||
// Case 6b
|
||
// this is the case where we have transition from !NULL to NULL
|
||
// and marshalling hasn't passed yet
|
||
ASSERT((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextCompletedAlloc);
|
||
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
TRUE // fUserDeletedContext
|
||
);
|
||
}
|
||
else
|
||
{
|
||
UserContext = ContextHandle->UserContext;
|
||
|
||
// Cases 7b
|
||
ASSERT(UserContext != NULL);
|
||
ASSERT((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextCompletedAlloc);
|
||
|
||
// the context handle was actively used - finish using it
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
FALSE // fUserDeletedContext
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
ByteSwapWireContext(
|
||
IN WIRE_CONTEXT *WireContext,
|
||
IN unsigned char *DataRepresentation
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If necessary, the wire context will be byte swapped in place.
|
||
|
||
Arguments:
|
||
|
||
WireContext - Supplies the wire context be byte swapped and returns the
|
||
resulting byte swapped context.
|
||
|
||
DataRepresentation - Supplies the data representation of the supplied wire
|
||
context.
|
||
|
||
Notes:
|
||
The wire context is guaranteed only 4 byte alignment.
|
||
--*/
|
||
{
|
||
if ( ( DataConvertEndian(DataRepresentation) != 0 )
|
||
&& ( WireContext != 0 ) )
|
||
{
|
||
WireContext->ContextType = RpcpByteSwapLong(WireContext->ContextType);
|
||
ByteSwapUuid((class RPC_UUID *)&WireContext->ContextUuid);
|
||
}
|
||
}
|
||
|
||
|
||
NDR_SCONTEXT RPC_ENTRY
|
||
NDRSContextUnmarshall (
|
||
IN void *pBuff,
|
||
IN unsigned long DataRepresentation
|
||
)
|
||
{
|
||
return(NDRSContextUnmarshall2(I_RpcGetCurrentCallHandle(),
|
||
pBuff,
|
||
DataRepresentation,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_FLAGS));
|
||
}
|
||
|
||
NDR_SCONTEXT RPC_ENTRY
|
||
NDRSContextUnmarshallEx(
|
||
IN RPC_BINDING_HANDLE BindingHandle,
|
||
IN void *pBuff,
|
||
IN unsigned long DataRepresentation
|
||
)
|
||
{
|
||
return(NDRSContextUnmarshall2(BindingHandle,
|
||
pBuff,
|
||
DataRepresentation,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_FLAGS));
|
||
}
|
||
|
||
// make sure the public structure and our private ones agree on where is the user context
|
||
C_ASSERT(FIELD_OFFSET(ServerContextHandle, UserContext) == ((LONG)(LONG_PTR)&(((NDR_SCONTEXT)0)->userContext)));
|
||
|
||
|
||
|
||
NDR_SCONTEXT RPC_ENTRY
|
||
NDRSContextUnmarshall2 (
|
||
IN RPC_BINDING_HANDLE BindingHandle,
|
||
IN void *pBuff,
|
||
IN unsigned long DataRepresentation,
|
||
IN void *CtxGuard,
|
||
IN unsigned long Flags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Translate a NDR context to a handle
|
||
The stub calls this routine to lookup a NDR wire format context into
|
||
a context handle that can be used with the other context functions
|
||
provided for the stubs use.
|
||
|
||
Arguments:
|
||
|
||
BindingHandle - the server side binding handle (scall)
|
||
pBuff - pointer to the on-the-wire represenation of the context handle
|
||
DataRepresentation - specifies the NDR data representation
|
||
CtxGuard - non-NULL and interface unique id for strict context handles. NULL
|
||
for non-strict context handles
|
||
Flags - the flags for this operation.
|
||
|
||
Return Value:
|
||
A handle usable by NDR. Failures are reported by throwing exceptions.
|
||
--*/
|
||
|
||
{
|
||
ServerContextHandle *ContextHandle;
|
||
ServerContextHandle *TempContextHandle;
|
||
ContextCollection *CtxCollection;
|
||
WIRE_CONTEXT *WireContext;
|
||
THREAD * Thread;
|
||
RPC_STATUS RpcStatus;
|
||
BOOL fFound;
|
||
SCALL *SCall;
|
||
SWMRWaiter *WaiterCache;
|
||
BOOL Ignore;
|
||
|
||
ByteSwapWireContext((WIRE_CONTEXT *) pBuff,
|
||
(unsigned char *) &DataRepresentation);
|
||
|
||
// even if we don't put it in the collection, make sure that
|
||
// we call this function to force creating the collection
|
||
// if it isn't there. If it fails, it will throw an exception
|
||
CtxCollection = GetContextCollection(BindingHandle);
|
||
|
||
WireContext = (WIRE_CONTEXT *)pBuff;
|
||
if (!WireContext || WireContext->IsNullContext())
|
||
{
|
||
// Allocate a new context
|
||
ContextHandle = AllocateServerContextHandle(CtxGuard);
|
||
if (ContextHandle == NULL)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
RPC_S_OUT_OF_MEMORY,
|
||
EEInfoDLNDRSContextUnmarshall2_30,
|
||
sizeof(ServerContextHandle));
|
||
|
||
RpcRaiseException(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
#if DBG
|
||
if (CtxGuard == RPC_CONTEXT_HANDLE_DEFAULT_GUARD)
|
||
RpcpInterfaceForCallDoesNotUseStrict(BindingHandle);
|
||
#endif
|
||
|
||
// we don't put it in the active context handle list, because
|
||
// non of the APIs work on newly created context handles.
|
||
// We don't put it in the context collection either, allowing
|
||
// us to put it on unmarshalling only if it is non-zero.
|
||
}
|
||
else
|
||
{
|
||
ContextHandle = FindAndAddRefContextHandle(CtxCollection,
|
||
WireContext,
|
||
CtxGuard,
|
||
&Ignore // ContextHandleNewlyCreated
|
||
);
|
||
|
||
if (!ContextHandle)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
RPC_X_SS_CONTEXT_MISMATCH,
|
||
EEInfoDLNDRSContextUnmarshall2_10,
|
||
WireContext->GetDebugULongLong1(),
|
||
WireContext->GetDebugULongLong2()
|
||
);
|
||
RpcpRaiseException(RPC_X_SS_CONTEXT_MISMATCH);
|
||
}
|
||
|
||
SCall = (SCALL *)BindingHandle;
|
||
RpcStatus = SCall->AddToActiveContextHandles(ContextHandle);
|
||
GENERATE_ADD_TO_COLLECTION_FAILURE(SCall, ContextHandle, RpcStatus)
|
||
if (RpcStatus != RPC_S_OK)
|
||
{
|
||
// remove the refcount and kill if it is the last one
|
||
// Since this is not in the collection, no unlock
|
||
// attempt will be made
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
FALSE // fUserDeletedContext
|
||
);
|
||
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
RpcStatus,
|
||
EEInfoDLNDRSContextUnmarshall2_50);
|
||
|
||
RpcpRaiseException(RpcStatus);
|
||
}
|
||
|
||
Thread = RpcpGetThreadPointer();
|
||
ASSERT(Thread);
|
||
|
||
// here it must have been found. Find out what mode do we want this locked
|
||
// in.
|
||
if (DoesContextHandleNeedExclusiveLock(Flags))
|
||
{
|
||
Thread->GetWaiterCache(&WaiterCache, SCall, swmrwtWriter);
|
||
RpcStatus = ContextHandle->Lock.LockExclusive(&WaiterCache);
|
||
}
|
||
else
|
||
{
|
||
Thread->GetWaiterCache(&WaiterCache, SCall, swmrwtReader);
|
||
RpcStatus = ContextHandle->Lock.LockShared(&WaiterCache);
|
||
}
|
||
|
||
// in rare cases the lock operation may yield a cached waiter.
|
||
// Make sure we handle it
|
||
Thread->FreeWaiterCache(&WaiterCache);
|
||
|
||
GENERATE_LOCK_FAILURE(ContextHandle, Thread, &WaiterCache, RpcStatus)
|
||
|
||
if (RpcStatus != RPC_S_OK)
|
||
{
|
||
// first, we need to remove the context handle from the active calls
|
||
// collection. This is necessary so that when we finish using it, it
|
||
// doesn't attempt to unlock the handle (which it will attempt if the
|
||
// handle is in the active contexts collection).
|
||
TempContextHandle = SCall->RemoveFromActiveContextHandles(ContextHandle);
|
||
ASSERT(TempContextHandle);
|
||
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
FALSE // fUserDeletedContext
|
||
);
|
||
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
RpcStatus,
|
||
EEInfoDLNDRSContextUnmarshall2_40);
|
||
|
||
RpcpRaiseException(RpcStatus);
|
||
}
|
||
|
||
// did we pick a deleted context? Since we have a refcount, it can't go away
|
||
// but it may very well have been marked deleted while we were waiting to get a lock
|
||
// on the context handle. Check, and bail out if this is the case. This can
|
||
// happen either if we encountered a rundown while waiting for the context
|
||
// handle lock, or if an exclusive user before us deleted the context handle
|
||
if (ContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollection)
|
||
{
|
||
CODE_COVERAGE_CHECK;
|
||
// since the context handle is in the active contexts collection,
|
||
// this code will unlock it
|
||
FinishUsingContextHandle(SCall,
|
||
ContextHandle,
|
||
FALSE // fUserDeletedContext
|
||
);
|
||
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
RPC_X_SS_CONTEXT_MISMATCH,
|
||
EEInfoDLNDRSContextUnmarshall2_20,
|
||
WireContext->GetDebugULongLong1(),
|
||
WireContext->GetDebugULongLong2()
|
||
);
|
||
RpcpRaiseException(RPC_X_SS_CONTEXT_MISMATCH);
|
||
}
|
||
}
|
||
|
||
return ((NDR_SCONTEXT) ContextHandle);
|
||
}
|
||
|
||
|
||
void RPC_ENTRY
|
||
NDRSContextMarshallEx (
|
||
IN RPC_BINDING_HANDLE BindingHandle,
|
||
IN OUT NDR_SCONTEXT hContext,
|
||
OUT void *pBuffer,
|
||
IN NDR_RUNDOWN userRunDownIn
|
||
)
|
||
{
|
||
NDRSContextMarshall2(BindingHandle,
|
||
hContext,
|
||
pBuffer,
|
||
userRunDownIn,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
|
||
}
|
||
|
||
void RPC_ENTRY
|
||
NDRSContextMarshall (
|
||
IN OUT NDR_SCONTEXT hContext,
|
||
OUT void *pBuff,
|
||
IN NDR_RUNDOWN userRunDownIn
|
||
)
|
||
{
|
||
NDRSContextMarshall2(I_RpcGetCurrentCallHandle(),
|
||
hContext,
|
||
pBuff,
|
||
userRunDownIn,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
|
||
RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
|
||
}
|
||
|
||
|
||
void RPC_ENTRY
|
||
NDRSContextMarshall2(
|
||
IN RPC_BINDING_HANDLE BindingHandle,
|
||
IN OUT NDR_SCONTEXT hContext,
|
||
OUT void *pBuff,
|
||
IN NDR_RUNDOWN UserRunDownIn,
|
||
IN void *CtxGuard,
|
||
IN unsigned long
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Marshall the context handle. If set to NULL, it will be destroyed.
|
||
|
||
Arguments:
|
||
|
||
BindingHandle - the server side binding handle (the scall)
|
||
hContext - the NDR handle of the context handle
|
||
pBuff - buffer to marshell to
|
||
UserRunDownIn - user function to be called when the rundown occurs
|
||
CtxGuard - the magic id used to differentiate contexts created on
|
||
different interfaces
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS RpcStatus;
|
||
ServerContextHandle *ContextHandle = (ServerContextHandle *)hContext;
|
||
ContextCollection *CtxCollection;
|
||
SCALL *SCall;
|
||
BOOL fUserDeletedContextHandle;
|
||
WIRE_CONTEXT *WireContext;
|
||
|
||
SCall = (SCALL *)BindingHandle;
|
||
|
||
// 0 for the flags is ContextPendingAlloc. If this is a new context, it
|
||
// cannot have ContextNeedsRundown, because it's not in the collection.
|
||
// It cannot have ContextRemovedFromCollection for the same reason.
|
||
// Therefore, testing for 0 is sufficient to determine if this is a new
|
||
// context
|
||
if (ContextHandle->Flags == 0)
|
||
{
|
||
if (ContextHandle->UserContext == NULL)
|
||
{
|
||
// NULL to NULL - just delete the context handle
|
||
ContextHandle->WireContext.CopyToBuffer(pBuff);
|
||
delete ContextHandle;
|
||
}
|
||
else
|
||
{
|
||
// the context handle was just created - initialize the members that
|
||
// weren't initialized before and insert it in the list
|
||
ContextHandle->Flags = ServerContextHandle::ContextNewlyCreated
|
||
| ServerContextHandle::ContextCompletedAlloc;
|
||
// UserContext was already set by NDR
|
||
ContextHandle->UserRunDown = UserRunDownIn;
|
||
ASSERT(CtxGuard == ContextHandle->CtxGuard);
|
||
|
||
// create the UUID
|
||
RpcStatus = UuidCreateSequential((UUID *)&ContextHandle->WireContext.ContextUuid);
|
||
|
||
GENERATE_STATUS_FAILURE(RpcStatus);
|
||
|
||
if (RpcStatus == RPC_S_OK)
|
||
{
|
||
RpcStatus = SCall->AddToActiveContextHandles(
|
||
(ServerContextHandle *) ((ULONG_PTR) pBuff | SCALL::DictionaryEntryIsBuffer));
|
||
GENERATE_ADD_TO_COLLECTION_FAILURE(SCall, (ServerContextHandle *)((ULONG_PTR) pBuff & SCALL::DictionaryEntryIsBuffer), RpcStatus);
|
||
}
|
||
|
||
if ((RpcStatus != RPC_S_OK)
|
||
&& (RpcStatus != RPC_S_UUID_LOCAL_ONLY))
|
||
{
|
||
// run down the context handle
|
||
NDRSRundownContextHandle(ContextHandle);
|
||
// in a sense, marshalling failed
|
||
delete ContextHandle;
|
||
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
RpcStatus,
|
||
EEInfoDLNDRSContextMarshall2_10);
|
||
RpcpRaiseException(RpcStatus);
|
||
}
|
||
|
||
ContextHandle->WireContext.CopyToBuffer(pBuff);
|
||
|
||
CtxCollection = GetContextCollection(BindingHandle);
|
||
// the context collection must have been created during
|
||
// marshalling. This cannot fail here
|
||
ASSERT(CtxCollection);
|
||
CtxCollection->Lock();
|
||
CtxCollection->Add(ContextHandle);
|
||
CtxCollection->Unlock();
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
fUserDeletedContextHandle = (ContextHandle->UserContext == NULL);
|
||
|
||
if (fUserDeletedContextHandle)
|
||
{
|
||
WireContext = (WIRE_CONTEXT *)pBuff;
|
||
WireContext->SetToNull();
|
||
}
|
||
else
|
||
{
|
||
ContextHandle->WireContext.CopyToBuffer(pBuff);
|
||
}
|
||
|
||
FinishUsingContextHandle(SCall, ContextHandle, fUserDeletedContextHandle);
|
||
}
|
||
|
||
|
||
void RPC_ENTRY
|
||
I_RpcSsDontSerializeContext (
|
||
void
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
By default, context handles are serialized at the server. One customer
|
||
who doesn't like that is the spooler. They make use of a single context
|
||
handle by two threads at a time. This API is used to turn off serializing
|
||
access to context handles for the process. It has been superseded by
|
||
shared/exclusive access to the context, and must not be used anymore.
|
||
|
||
--*/
|
||
{
|
||
DontSerializeContext = 1;
|
||
}
|
||
|
||
ServerContextHandle *
|
||
NDRSConvertUserContextToContextHandle (
|
||
IN SCALL *SCall,
|
||
IN PVOID UserContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Finds the context handle corresponding to the specified
|
||
UserContext and returns it.
|
||
|
||
Arguments:
|
||
|
||
SCall - the server side call object (the SCall)
|
||
|
||
UserContext - the user context as given to the user by NDR. For
|
||
in/out parameters, this will be a pointer to the UserContext
|
||
field in the ServerContextHandle. For in parameters, this will
|
||
be a value equal to the UserContext field in the
|
||
ServerContextHandle. We don't know what type of context handle
|
||
is this, so we have to search both, giving precedence to in/out
|
||
as they are more precise.
|
||
|
||
Return Value:
|
||
|
||
NULL if the UserContext couldn't be matched to any context handle.
|
||
The context handle pointer otherwise.
|
||
|
||
--*/
|
||
{
|
||
DictionaryCursor cursor;
|
||
ServerContextHandle *CurrentCtxHandle = NULL;
|
||
ServerContextHandle *UserContextMatchingCtxHandle = NULL;
|
||
|
||
if (SCall->InvalidHandle(SCALL_TYPE))
|
||
return (NULL);
|
||
|
||
SCall->ActiveContextHandles.Reset(cursor);
|
||
while ((CurrentCtxHandle = SCall->ActiveContextHandles.Next(cursor)) != 0)
|
||
{
|
||
// make sure this is not a buffer pointer for some reason
|
||
ASSERT (((ULONG_PTR)CurrentCtxHandle & SCALL::DictionaryEntryIsBuffer) == 0);
|
||
|
||
if (&CurrentCtxHandle->UserContext == UserContext)
|
||
{
|
||
return CurrentCtxHandle;
|
||
}
|
||
|
||
if (CurrentCtxHandle->UserContext == UserContext)
|
||
{
|
||
UserContextMatchingCtxHandle = CurrentCtxHandle;
|
||
}
|
||
}
|
||
|
||
// if we didn't find anything, this will be NULL and this is what we will
|
||
// return
|
||
return UserContextMatchingCtxHandle;
|
||
}
|
||
|
||
RPCRTAPI
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
RpcSsContextLockExclusive (
|
||
IN RPC_BINDING_HANDLE ServerBindingHandle,
|
||
IN PVOID UserContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Lock the specified context for exclusive use.
|
||
|
||
Arguments:
|
||
|
||
ServerBindingHandle - the server side binding handle (the SCall)
|
||
|
||
UserContext - the user context as given to the user by NDR. For
|
||
in/out parameters, this will be a pointer to the UserContext
|
||
field in the ServerContextHandle. For in parameters, this will
|
||
be a value equal to the UserContext field in the
|
||
ServerContextHandle. We don't know what type of context handle
|
||
is this, so we have to search both, giving precedence to in/out
|
||
as they are more precise.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK, ERROR_INVALID_HANDLE if the ServerBindingHandle or the
|
||
UserContext are invalid, a Win32 error if the locking failed,
|
||
or ERROR_MORE_WRITES if two readers attempted to upgrade to Exclusive
|
||
and one was evicted from its read lock (see the comment in
|
||
SWMR::ConvertToExclusive)
|
||
|
||
--*/
|
||
{
|
||
ServerContextHandle *ContextHandle;
|
||
SCALL *SCall = (SCALL *)ServerBindingHandle;
|
||
SWMRWaiter *WaiterCache;
|
||
THREAD *ThisThread;
|
||
RPC_STATUS RpcStatus;
|
||
|
||
if (SCall == NULL)
|
||
{
|
||
SCall = (SCALL *) RpcpGetThreadContext();
|
||
// if there is still no context, it will be handled by
|
||
// NDRSConvertUserContextToContextHandle below.
|
||
}
|
||
|
||
ContextHandle = NDRSConvertUserContextToContextHandle(SCall,
|
||
UserContext);
|
||
|
||
if (ContextHandle == NULL)
|
||
return ERROR_INVALID_HANDLE;
|
||
|
||
// try to get a waiter for the locking
|
||
ThisThread = ThreadSelf();
|
||
if (ThisThread == NULL)
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
|
||
WaiterCache = NULL;
|
||
|
||
// we cannot allocate a waiter from the thread,
|
||
// because by definition we already have a lock, and
|
||
// the waiter for this lock may come from the thread.
|
||
// Since the thread tends to overwrite the previous
|
||
// waiter on recursive allocation, we don't want to
|
||
// do that.
|
||
|
||
RpcStatus = ContextHandle->Lock.ConvertToExclusive(&WaiterCache);
|
||
|
||
// if a waiter was produced, store it. Conversion
|
||
// operations can produce spurious waiters because of
|
||
// race conditions
|
||
ThisThread->FreeWaiterCache(&WaiterCache);
|
||
|
||
return RpcStatus;
|
||
}
|
||
|
||
RPCRTAPI
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
RpcSsContextLockShared (
|
||
IN RPC_BINDING_HANDLE ServerBindingHandle,
|
||
IN PVOID UserContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Lock the specified context for shared use.
|
||
|
||
Arguments:
|
||
|
||
ServerBindingHandle - the server side binding handle (the SCall)
|
||
|
||
UserContext - the user context as given to the user by NDR. For
|
||
in/out parameters, this will be a pointer to the UserContext
|
||
field in the ServerContextHandle. For in parameters, this will
|
||
be a value equal to the UserContext field in the
|
||
ServerContextHandle. We don't know what type of context handle
|
||
is this, so we have to search both, giving precedence to in/out
|
||
as they are more precise.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK, ERROR_INVALID_HANDLE if the ServerBindingHandle or the
|
||
UserContext are invalid, a Win32 error if the locking failed
|
||
|
||
--*/
|
||
{
|
||
ServerContextHandle *ContextHandle;
|
||
SCALL *SCall = (SCALL *)ServerBindingHandle;
|
||
SWMRWaiter *WaiterCache;
|
||
THREAD *ThisThread;
|
||
RPC_STATUS RpcStatus;
|
||
|
||
if (SCall == NULL)
|
||
{
|
||
SCall = (SCALL *) RpcpGetThreadContext();
|
||
// if there is still no context, it will be handled by
|
||
// NDRSConvertUserContextToContextHandle below.
|
||
}
|
||
|
||
ContextHandle = NDRSConvertUserContextToContextHandle(SCall,
|
||
UserContext);
|
||
|
||
if (ContextHandle == NULL)
|
||
return ERROR_INVALID_HANDLE;
|
||
|
||
// try to get a waiter for the locking
|
||
ThisThread = ThreadSelf();
|
||
if (ThisThread == NULL)
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
|
||
WaiterCache = NULL;
|
||
// we cannot allocate a waiter from the thread,
|
||
// because by definition we already have a lock, and
|
||
// the waiter for this lock may come from the thread.
|
||
// Since the thread tends to overwrite the previous
|
||
// waiter on recursive allocation, we don't want to
|
||
// do that.
|
||
|
||
RpcStatus = ContextHandle->Lock.ConvertToShared(&WaiterCache,
|
||
TRUE // fSyncCacheUsed
|
||
);
|
||
|
||
// if a waiter was produced, store it. Conversion
|
||
// operations can produce spurious waiters because of
|
||
// race conditions
|
||
ThisThread->FreeWaiterCache(&WaiterCache);
|
||
|
||
return RpcStatus;
|
||
}
|
||
|