/*
**==============================================================================
**
** Open Management Infrastructure (OMI)
**
** Copyright (c) Microsoft Corporation
**
** 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
**
** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
** MERCHANTABLITY OR NON-INFRINGEMENT.
**
** See the Apache 2 License for the specific language governing permissions
** and limitations under the License.
**
**==============================================================================
*/
#include
#include
#include
//------------------------------------------------------------------------------------------------------------
// Debugging aid
//------------------------------------------------------------------------------------------------------------
#if defined(STRAND_ENABLE_DEBUG)
StrandBaseDebugInfo _Strand_BaseNameDebugInfo[Strand_NumMethods+5] = {
{"", '@' },
{"S", '*' },
{"CompleteOpenAsync", '$' },
{"Timer", '&' },
{"Cancel", 'N' },
{"CancelSelf", 'n' },
{"PostControl", 'T' },
{"Post", 'P' },
{"PostOther", 'p' },
{"Ack", 'A' },
{"AckOther", 'a' },
{"Close", 'C' },
{"CloseOther", 'c' },
{"", '0' },
{"", '1' },
{"", '2' },
{"", '3' },
{"", '4' },
{"ERROR!", '!' },
{"ERROR!", '!' },
{"ERROR!", '!' } };
StrandBaseDebugInfo _StrandBoth_BaseNameDebugInfo[StrandBoth_NumMethods+5] = {
{"", '@' },
{"SB", '*' },
{"CompleteOpenAsync", '$' },
{"Timer", '&' },
{"Cancel_Left", 'N' },
{"Cancel_Right", 'E' },
{"CancelSelf_Both", 'n' },
{"PostControl_Left", 'T' },
{"PostControl_Right", 'R' },
{"Post_Left", 'P' },
{"Post_Right", 'S' },
{"PostOther_Left", 'p' },
{"PostOther_Right", 's' },
{"Ack_Left", 'A' },
{"Ack_Right", 'K' },
{"AckOther_Left", 'a' },
{"AckOther_Right", 'k' },
{"Close_Left", 'C' },
{"Close_Right", 'L' },
{"CloseOther_Left", 'c' },
{"CloseOther_Right", 'l' },
{"", '0' },
{"", '1' },
{"", '2' },
{"", '3' },
{"", '4' },
{"", '5' },
{"", '6' },
{"", '7' },
{"", '8' },
{"", '9' },
{"ERROR!", '!' },
{"ERROR!", '!' },
{"ERROR!", '!' } };
StrandBaseDebugInfo _StrandMany_BaseNameDebugInfo[StrandMany_NumMethods+5] = {
{"", '@' },
{"SM", '*' },
{"CompleteOpenAsync", '$' },
{"Timer", '&' },
{"Cancel(M)", 'N' },
{"CancelSelf(M)", 'n' },
{"CancelInternal", 'E' },
{"PostControl(M)", 'T' },
{"PostControlInternal", 't' },
{"Post(M)", 'P' },
{"PostOther(M)", 'p' },
{"PostInternal", 'S' },
{"Ack(M)", 'A' },
{"AckOther(M)", 'a' },
{"AckInternal", 'K' },
{"EntryOperation", '#' },
{"Close(M)", 'C' },
{"CloseOther(M)", 'c' },
{"CloseInternal", 'L' },
{"", '0' },
{"", '1' },
{"", '2' },
{"", '3' },
{"", '4' },
{"ERROR!", '!' },
{"ERROR!", '!' },
{"ERROR!", '!' } };
char * _StrandMany_BaseEntryOperationDebugInfo[StrandMany_NumEntryOperations+3] = {
"Add",
"Cancel",
"PostControl",
"Post",
"Close",
"",
"",
"",
"",
"",
"Deleted",
"ERROR!",
"ERROR!",
"ERROR!" };
#if defined(CONFIG_OS_WINDOWS)
#define SPRINTF_FUNC sprintf_s
#define STRCPY_FUNC(orig, size, dest) strcpy_s( (orig), (size), (dest) )
#else
#define SPRINTF_FUNC snprintf
#define STRCPY_FUNC(orig, size, dest) strncpy( (orig), (dest), (size) )
#endif
static char * _StrandLogScheduledState(
_In_ ptrdiff_t state,
_In_ StrandBaseDebugInfo const * baseDebugInfo,
size_t size,
_Out_writes_(size+4) char * buffer )
{
ptrdiff_t mask = 1;
size_t pos = 0;
size_t bufferPos = 0;
buffer[bufferPos++] = baseDebugInfo[1].name[0];
if( '\0' != baseDebugInfo[1].name[1] )
{
buffer[bufferPos++] = baseDebugInfo[1].name[1];
}
buffer[bufferPos++] = ':';
while( pos < size )
{
if( ( mask & state ) != 0 )
{
buffer[bufferPos++] = baseDebugInfo[pos+1].letter;
}
mask <<= 1;
++pos;
}
buffer[bufferPos] = '\0';
return buffer;
}
#define _STRANDLOGINFOSTATE_SIZE 33 // 15 + 16 digits(pointer) + possible 0x prefix
#define _STRANDLOGINFOSTATE_MAXSIZE ((_STRANDLOGINFOSTATE_SIZE*2)+13) // + two state characters + 10 digits(parent) + terminator
MI_INLINE int _StrandLogInfoState( _In_ InteractionInfo * info, _Out_writes_z_(_STRANDLOGINFOSTATE_SIZE+1) char * buffer )
{
return SPRINTF_FUNC( buffer, _STRANDLOGINFOSTATE_SIZE, ", %c t:%c%c%co:%c%c %p",
info->opened ? 'O' : '-',
info->thisClosedOther ? 'C' : '-',
info->thisAckPending ? 'A' : '-',
info->ackPassthru ? 'P' : ' ',
info->otherClosedThis ? 'C' : '-',
info->otherAckPending ? 'A' : '-',
info->interaction.other );
}
MI_INLINE int _StrandLogStrandState( _In_ Strand* self, _Out_writes_(2) char * buffer )
{
int written = 0;
if( self->canceled )
{
buffer[written++] = 'N';
}
if( self->delayFinish )
{
buffer[written++] = 'D';
}
if( 0 == written )
{
buffer[written++] = '-';
}
return written;
}
MI_INLINE int _StrandLogStrandStateParent( _In_ StrandMany* self, _Out_writes_(10) char * buffer )
{
return SPRINTF_FUNC( buffer, 10, "%d", (unsigned int)self->numEntries );
}
MI_INLINE int _StrandLogInfoStateSingle( _In_ Strand* self, _Out_writes_z_(_STRANDLOGINFOSTATE_MAXSIZE) char * buffer )
{
int written = _StrandLogStrandState( self, buffer );
DEBUG_ASSERT( written > 0 && written <= 2 );
return _StrandLogInfoState( &self->info, buffer+written ) + written;
}
MI_INLINE int _StrandLogInfoStateBoth( _In_ StrandBoth* self, _Out_writes_z_(_STRANDLOGINFOSTATE_MAXSIZE) char * buffer )
{
int written = _StrandLogStrandState( &self->base, buffer );
DEBUG_ASSERT( written > 0 && written <= 2 );
written += _StrandLogInfoState( &self->base.info, buffer+written );
DEBUG_ASSERT( written > 1 && written <= _STRANDLOGINFOSTATE_SIZE+2 );
return _StrandLogInfoState( &self->infoRight, buffer+written ) + written;
}
MI_INLINE int _StrandLogInfoStateParent( _In_ StrandMany* self, _Out_writes_z_(_STRANDLOGINFOSTATE_MAXSIZE) char * buffer )
{
int written = _StrandLogStrandStateParent( self, buffer );
DEBUG_ASSERT( written > 0 && written <= 10 );
written += _StrandLogStrandState( &self->strand, buffer+written );
DEBUG_ASSERT( written > 1 && written <= 12 );
return _StrandLogInfoState( &self->strand.info, buffer+written ) + written;
}
MI_INLINE void _StrandLogInfoState_Store( _In_ Strand* strand, _Out_writes_z_(_STRANDLOGINFOSTATE_MAXSIZE) char * buffer )
{
if( STRAND_TYPE_MIDDLE == (strand)->strandType )
{
_StrandLogInfoStateBoth( (StrandBoth*)(strand), buffer);
}
else if( STRAND_ISTYPE_PARENT( strand ) )
{
_StrandLogInfoStateParent( FromOffset(StrandMany,strand,strand), buffer );
}
else
{
_StrandLogInfoStateSingle( strand, buffer );
}
}
void _StrandLogWithName( _In_ Strand* self, _In_ const char * operation )
{
trace_Strand_Action( self, STRAND_DEBUG_GETNAME(self), operation );
}
void _Strand_AssertOnStrand( _In_ Strand* strand )
{
ThreadID threadId = Thread_ID();
if( !Thread_Equal(&threadId,&((strand)->testThreadId)) )
{
DEBUG_ASSERT( Thread_Equal(&threadId,&((strand)->threadId)) );
STRAND_ASSERTEXECUTING( strand ); \
}
}
#endif
#if defined(STRAND_ENABLE_DEBUG)
#define STRAND_DEBUG_GETSTATE_USED \
char _buffer_STRAND_DEBUG_GETSTATE[StrandBoth_NumMethods+6]
#define STRAND_DEBUG_GETSTATE( strand, state ) \
_StrandLogScheduledState( state, STRAND_DEBUG_GETBASEINFO(strand), STRAND_DEBUG_GETINFOSIZE(strand)+1, _buffer_STRAND_DEBUG_GETSTATE )
#define STRAND_DEBUG_GETINFOSTATE_USED \
char _buffer_STRAND_DEBUG_GETINFOSTATE[_STRANDLOGINFOSTATE_MAXSIZE]
#define STRAND_DEBUG_GETINFOSTATE_STORE(strand) \
_StrandLogInfoState_Store( strand,_buffer_STRAND_DEBUG_GETINFOSTATE)
#define STRAND_DEBUG_GETINFOSTATE_STORED (_buffer_STRAND_DEBUG_GETINFOSTATE)
// Note that this uses same buffer as STRAND_DEBUG_GETSTATE
#define STRAND_DEBUG_GETNAME_STORE( strand ) \
STRCPY_FUNC( _buffer_STRAND_DEBUG_GETSTATE, StrandBoth_NumMethods+6, STRAND_DEBUG_GETNAME(strand) )
#define STRAND_DEBUG_GETNAME_STORED (_buffer_STRAND_DEBUG_GETSTATE)
MI_INLINE
void _Strand_TracePostMsg( _In_ Strand* self, _In_ Message* msg, _In_ const char * info )
{
DEBUG_ASSERT( NULL != self );
DEBUG_ASSERT( NULL != msg );
// TODO why is linux crashing when passing info into the trace function on x86?
MI_UNUSED(info);
if( PostResultMsgTag == msg->tag )
{
PostResultMsg* msgResult = (PostResultMsg*)msg;
trace_Strand_TracePostResult(self, msg, msg->tag, msg->operationId, msgResult->result);
}
else
{
trace_Strand_TracePost(self, msg, msg->tag, MessageName(msg->tag), msg->operationId);
}
}
#else
#define STRAND_DEBUG_GETSTATE_USED
#define STRAND_DEBUG_GETSTATE( strand, state ) ""
#define STRAND_DEBUG_GETINFOSTATE_USED
#define STRAND_DEBUG_GETINFOSTATE_STORE(strand)
#define STRAND_DEBUG_GETINFOSTATE_STORED ""
#define STRAND_DEBUG_GETNAME_STORE( strand )
#define STRAND_DEBUG_GETNAME_STORED ""
MI_INLINE
void _Strand_TracePostMsg( _In_ Strand* self, _In_ Message* msg, _In_ const char * info )
{
}
#endif
#if defined(STRAND_ENABLE_DEBUG)
#define STRAND_SETDEBUG( strand, strandDebugInfo ) (strand)->debug = strandDebugInfo; STRAND_LOGWITHNAME( strand, "Initialize Itself" )
#else
#define STRAND_SETDEBUG( strand, strandDebugInfo )
#endif
#if STRAND_ENABLE_DEBUG == 2
static void _StrandLogState( _In_ Strand* self, _In_ const char * infoName, _In_ InteractionInfo * info )
{
trace_StrandFlags(
self,
STRAND_DEBUG_GETNAME(self),
infoName,
info->interaction.other,
info->opened,
info->thisClosedOther,
info->thisAckPending,
info->otherClosedThis,
info->otherAckPending,
info->ackPassthru );
}
#define STRAND_DEBUGSTATE( strand ) _StrandLogState( (strand), "S.:", &((strand)->info) )
#define STRAND_DEBUGSTATE_LEFT( strand ) _StrandLogState( &((strand)->base), "S.Left", &((strand)->base.info) ); \
_StrandLogState( &((strand)->base), "S.(Right)", &((strand)->infoRight) )
#define STRAND_DEBUGSTATE_RIGHT( strand ) _StrandLogState( &((strand)->base), "S.Right", &((strand)->infoRight) ); \
_StrandLogState( &((strand)->base), "S.(Left)", &((strand)->base.info) )
#else
#define STRAND_DEBUGSTATE( strand )
#define STRAND_DEBUGSTATE_LEFT( strand )
#define STRAND_DEBUGSTATE_RIGHT( strand )
#endif
//------------------------------------------------------------------------------------------------------------
#define _GetMethodBit( bitIndex ) ((ptrdiff_t)1 << ((bitIndex)-1))
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Cancel( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitCancel );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_PostControl( _In_ Interaction* interaction, _In_ Message* msg )
{
Strand* self = Strand_FromInteraction( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->info.stored.controlMsg = msg;
_Strand_Schedule( self, BitPostControl );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Post( _In_ Interaction* interaction, _In_ Message* msg )
{
Strand* self = Strand_FromInteraction( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->info.stored.msg = msg;
_Strand_Schedule( self, BitPost );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Ack( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitAck );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Close( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitClose );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Left_Cancel( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitCancel_Left );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Left_PostControl( _In_ Interaction* interaction, _In_ Message* msg )
{
Strand* self = Strand_FromInteraction( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->info.stored.controlMsg = msg;
_Strand_Schedule( self, BitPostControl_Left );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Left_Post( _In_ Interaction* interaction, _In_ Message* msg )
{
Strand* self = Strand_FromInteraction( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->info.stored.msg = msg;
_Strand_Schedule( self, BitPost_Left );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Left_Ack( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitAck_Left );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Left_Close( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitClose_Left );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Right_Cancel( _In_ Interaction* interaction )
{
StrandBoth* self = Strand_FromInteractionRight( interaction );
_Strand_Schedule( (Strand*)self, BitCancel_Right );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Right_PostControl( _In_ Interaction* interaction, _In_ Message* msg )
{
StrandBoth* self = Strand_FromInteractionRight( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->infoRight.stored.controlMsg = msg;
_Strand_Schedule( (Strand*)self, BitPostControl_Right );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Right_Post( _In_ Interaction* interaction, _In_ Message* msg )
{
StrandBoth* self = Strand_FromInteractionRight( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->infoRight.stored.msg = msg;
_Strand_Schedule( (Strand*)self, BitPost_Right );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Right_Ack( _In_ Interaction* interaction )
{
StrandBoth* self = Strand_FromInteractionRight( interaction );
_Strand_Schedule( (Strand*)self, BitAck_Right );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Right_Close( _In_ Interaction* interaction )
{
StrandBoth* self = Strand_FromInteractionRight( interaction );
_Strand_Schedule( (Strand*)self, BitClose_Right );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Many_Cancel( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitCancel_Many );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Many_PostControl( _In_ Interaction* interaction, _In_ Message* msg )
{
Strand* self = Strand_FromInteraction( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->info.stored.controlMsg = msg;
_Strand_Schedule( self, BitPostControl_Many );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Many_Post( _In_ Interaction* interaction, _In_ Message* msg )
{
Strand* self = Strand_FromInteraction( interaction );
Message_AddRef( msg ); // since the actual message use can be delayed
self->info.stored.msg = msg;
_Strand_Schedule( self, BitPost_Many );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Many_Ack( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitAck_Many );
}
//------------------------------------------------------------------------------------------------------------
void _StrandInteraction_Many_Close( _In_ Interaction* interaction )
{
Strand* self = Strand_FromInteraction( interaction );
_Strand_Schedule( self, BitClose_Many );
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE
MI_Boolean _StrandMethodImp_CheckFinished( _In_ InteractionInfo* info )
{
return
info->thisClosedOther
&& !info->thisAckPending
&& info->otherClosedThis
&& !info->otherAckPending;
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE
MI_Boolean _StrandMethodImp_Cancel( _In_ Strand* self, _In_ InteractionInfo* info)
{
DEBUG_ASSERT( !self->canceled );
self->canceled = MI_TRUE;
if( NULL != info->userFT->Cancel )
{
(*info->userFT->Cancel)(self);
}
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE
MI_Boolean _StrandMethodImp_PostControl( _In_ Strand* self, _In_ InteractionInfo* info)
{
DEBUG_ASSERT( info->opened );
DEBUG_ASSERT( !info->otherClosedThis );
DEBUG_ASSERT( NULL != info->userFT->PostControl );
DEBUG_ASSERT( NULL != info->stored.controlMsg );
info->userFT->PostControl( self, info->stored.controlMsg );
Message_Release( info->stored.controlMsg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE
MI_Boolean _StrandMethodImp_Post( _In_ Strand* self, _In_ InteractionInfo* info)
{
DEBUG_ASSERT( info->opened );
DEBUG_ASSERT( !info->otherClosedThis );
DEBUG_ASSERT( NULL != info->userFT->Post );
DEBUG_ASSERT( NULL != info->stored.msg );
DEBUG_ASSERT( !info->otherAckPending );
info->otherAckPending = MI_TRUE;
info->userFT->Post( self, info->stored.msg );
Message_Release( info->stored.msg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE
MI_Boolean _StrandMethodImp_Ack( _In_ Strand* self, _In_ InteractionInfo* info)
{
// note that info->otherClosedThis may be true here
DEBUG_ASSERT( info->thisAckPending );
DEBUG_ASSERT( info->opened );
info->thisAckPending = MI_FALSE;
if( NULL != info->userFT->Ack )
{
info->userFT->Ack( self );
}
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE
MI_Boolean _StrandMethodImp_Close( _In_ Strand* self, _In_ InteractionInfo* info)
{
DEBUG_ASSERT( info->opened );
DEBUG_ASSERT( !info->otherClosedThis );
info->otherClosedThis = MI_TRUE;
if( NULL != info->userFT->Close )
{
(*info->userFT->Close)(self);
}
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE
MI_Boolean _StrandMethodImp_PostOther( _In_ InteractionInfo* info)
{
_Strand_Post_Imp( info, info->otherMsg );
Message_Release( info->otherMsg ); // now we can remove the reference added on Strand_SchedulePost*
info->otherMsg = NULL;
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_CheckFinished( _In_ Strand* self )
{
return _StrandMethodImp_CheckFinished( &self->info ) && !self->delayFinish;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Timer( _In_ Strand* self)
{
TimerReason reason;
DEBUG_ASSERT( NULL != self->timer );
DEBUG_ASSERT( NULL != self->info.userFT->Timer );
STRAND_DEBUGSTATE( self );
reason = self->timer->reason;
Timer_Close(self->timer);
self->timer = NULL;
(*self->info.userFT->Timer)(self, reason);
// A canceled timer shouldn't be re-started
DEBUG_ASSERT( NULL == self->timer || reason != TimerReason_Canceled );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
// Internal use
void _Strand_CancelPropagate(
_In_ Strand * self);
// Internal use
MI_INLINE
void _Strand_CompleteOpenAsyncImp(
_In_ Strand * self,
_In_ InteractionInfo* info )
{
STRAND_ASSERTONSTRAND(self);
DEBUG_ASSERT( !info->opened );
info->opened = MI_TRUE;
if( self->canceled )
{
_Strand_CancelPropagate( self );
}
}
MI_Boolean _StrandMethod_CompleteOpenAsync( _In_ Strand* self)
{
STRAND_DEBUGSTATE( self );
_Strand_CompleteOpenAsyncImp( self, &self->info );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Cancel( _In_ Strand* self)
{
STRAND_DEBUGSTATE( self );
if( !self->canceled )
{
if( STRAND_TYPE_RIGHTMOST == self->strandType && self->info.opened && !self->info.thisClosedOther )
{
// return it to the other side
self->info.interaction.other->ft->Cancel( self->info.interaction.other );
}
_StrandMethodImp_Cancel(self, &self->info );
}
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_PostControl( _In_ Strand* self)
{
_Strand_TracePostMsg(self, self->info.stored.controlMsg, "(Control)");
STRAND_DEBUGSTATE( self );
return _StrandMethodImp_PostControl(self, &self->info );
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Post( _In_ Strand* self)
{
_Strand_TracePostMsg(self, self->info.stored.msg, "");
STRAND_DEBUGSTATE( self );
return _StrandMethodImp_Post(self, &self->info );
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Ack( _In_ Strand* self)
{
STRAND_DEBUGSTATE( self );
return _StrandMethodImp_Ack(self, &self->info );
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Close( _In_ Strand* self)
{
STRAND_DEBUGSTATE( self );
return _StrandMethodImp_Close(self, &self->info );
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_PostOther( _In_ Strand* self)
{
MI_Boolean res = _StrandMethodImp_PostOther( &self->info );
STRAND_LOGWITHNAME( self, "Returning from PostOther" );
return res;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_AckOther( _In_ Strand* self)
{
_Strand_Ack_Imp( &self->info );
STRAND_LOGWITHNAME( self, "Returning from AckOther" );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_CloseOther( _In_ Strand* self)
{
_Strand_Close_Imp( &self->info );
STRAND_LOGWITHNAME( self, "Returning from CloseOther" );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_CancelSelf( _In_ Strand* self)
{
Strand_Cancel( self );
STRAND_LOGWITHNAME( self, "Returning from CancelSelf" );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Aux0( _In_ Strand* self)
{
DEBUG_ASSERT( NULL != self->info.userFT->Aux0 );
self->info.userFT->Aux0( self );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Aux1( _In_ Strand* self)
{
DEBUG_ASSERT( NULL != self->info.userFT->Aux1 );
self->info.userFT->Aux1( self );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Aux2( _In_ Strand* self)
{
DEBUG_ASSERT( NULL != self->info.userFT->Aux2 );
self->info.userFT->Aux2( self );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Aux3( _In_ Strand* self)
{
DEBUG_ASSERT( NULL != self->info.userFT->Aux3 );
self->info.userFT->Aux3( self );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Aux4( _In_ Strand* self)
{
DEBUG_ASSERT( NULL != self->info.userFT->Aux4 );
self->info.userFT->Aux4( self );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Both_CheckFinished( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
if (self->asyncOpenInProgress)
return MI_FALSE;
return _StrandMethodImp_CheckFinished( &self->base.info )
&& ((!self->infoRight.opened) || _StrandMethodImp_CheckFinished( &self->infoRight )) //check right interation state only if its opeened
&& !self->base.delayFinish;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Both_CompleteOpenAsync( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
STRAND_DEBUGSTATE( self );
_Strand_CompleteOpenAsyncImp( &self->base, &self->infoRight );
self->asyncOpenInProgress = MI_FALSE;
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Left_Cancel( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
STRAND_DEBUGSTATE_LEFT( self );
if( !self->base.canceled )
{
// We pass to the right only if we have not been cancel already becase in that case it has go to the right already
// (cancelations always flow to the right first)
// Note that we need to check this first as the canceled below may close the interaction with the right side
if( self->infoRight.opened && !self->infoRight.thisClosedOther )
{
self->infoRight.interaction.other->ft->Cancel( self->infoRight.interaction.other );
}
_StrandMethodImp_Cancel(self_, &self->base.info );
}
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Left_PostControl( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
_Strand_TracePostMsg(self_, self->base.info.stored.controlMsg, "(Control Left)");
STRAND_DEBUGSTATE_LEFT( self );
if( NULL == self->base.info.userFT->PostControl && self->infoRight.opened )
{
DEBUG_ASSERT( !self->infoRight.thisClosedOther );
// post passing thru to the other side
StrandBoth_PostControlRight( self, self->base.info.stored.controlMsg );
Message_Release( self->base.info.stored.controlMsg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
else
{
return _StrandMethodImp_PostControl(self_, &self->base.info );
}
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Left_Post( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
_Strand_TracePostMsg(self_, self->base.info.stored.msg, "Left");
STRAND_DEBUGSTATE_LEFT( self );
if( NULL == self->base.info.userFT->Post && self->infoRight.opened )
{
DEBUG_ASSERT( !self->infoRight.thisClosedOther );
// post passing thru to the other side
StrandBoth_PostPassthruRight( self, self->base.info.stored.msg );
Message_Release( self->base.info.stored.msg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
else
{
return _StrandMethodImp_Post(self_, &self->base.info );
}
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Left_Ack( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
STRAND_DEBUGSTATE_LEFT( self );
DEBUG_ASSERT( self->base.info.opened );
if( self->base.info.ackPassthru || NULL == self->base.info.userFT->Ack )
{
DEBUG_ASSERT( self->base.info.thisAckPending );
self->base.info.thisAckPending = MI_FALSE;
if( self->infoRight.otherAckPending )
{
self->base.info.ackPassthru = MI_FALSE;
StrandBoth_AckRight( self );
}
else
{
DEBUG_ASSERT( !self->base.info.ackPassthru );
}
return MI_FALSE;
}
else
{
return _StrandMethodImp_Ack(self_, &self->base.info );
}
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Left_Close( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
STRAND_DEBUGSTATE_LEFT( self );
if( NULL == self->base.info.userFT->Close )
{
// we take care of sending the close as passthru
if( self->infoRight.opened && !self->infoRight.thisClosedOther )
{
StrandBoth_CloseRight( self );
}
}
return _StrandMethodImp_Close(self_, &self->base.info );
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_Cancel( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
STRAND_DEBUGSTATE_RIGHT( self );
// We need to check this first as the canceled below may close the interaction with the left side
if( self->base.info.opened && !self->base.info.thisClosedOther && !self->leftCanceled )
{
self->leftCanceled = MI_TRUE;
// We pass to the left even if we have been cancel already becase thet should be the cancelation coming back from the right
self->base.info.interaction.other->ft->Cancel( self->base.info.interaction.other );
}
if( !self->base.canceled )
{
_StrandMethodImp_Cancel(self_, &self->infoRight );
}
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_PostControl( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
_Strand_TracePostMsg(self_, self->infoRight.stored.controlMsg, "(Control Right)");
STRAND_DEBUGSTATE_RIGHT( self );
if( NULL == self->infoRight.userFT->PostControl && self->base.info.opened )
{
DEBUG_ASSERT( !self->base.info.thisClosedOther );
// post passing thru to the other side
StrandBoth_PostControlLeft( self, self->infoRight.stored.controlMsg );
Message_Release( self->infoRight.stored.controlMsg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
else
{
return _StrandMethodImp_PostControl(self_, &self->infoRight );
}
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_Post( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
_Strand_TracePostMsg(self_, self->infoRight.stored.msg, "Right");
STRAND_DEBUGSTATE_RIGHT( self );
if( NULL == self->infoRight.userFT->Post && self->base.info.opened )
{
DEBUG_ASSERT( !self->base.info.thisClosedOther );
// post passing thru to the other side
StrandBoth_PostPassthruLeft( self, self->infoRight.stored.msg );
Message_Release( self->infoRight.stored.msg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
else
{
return _StrandMethodImp_Post(self_, &self->infoRight );
}
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_Ack( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
STRAND_DEBUGSTATE_RIGHT( self );
DEBUG_ASSERT( self->infoRight.opened );
if( self->infoRight.ackPassthru || NULL == self->infoRight.userFT->Ack )
{
DEBUG_ASSERT( self->infoRight.thisAckPending );
self->infoRight.thisAckPending = MI_FALSE;
if( self->base.info.otherAckPending )
{
self->infoRight.ackPassthru = MI_FALSE;
StrandBoth_AckLeft( self );
}
else
{
DEBUG_ASSERT( !self->infoRight.ackPassthru );
}
return MI_FALSE;
}
else
{
return _StrandMethodImp_Ack(self_, &self->infoRight );
}
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_Close( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
STRAND_DEBUGSTATE_RIGHT( self );
if( NULL == self->infoRight.userFT->Close )
{
// we take care of sending the close as passthru
if( self->base.info.opened && !self->base.info.thisClosedOther )
{
StrandBoth_CloseLeft( self );
}
}
return _StrandMethodImp_Close(self_, &self->infoRight );
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_PostOther( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
MI_Boolean res = _StrandMethodImp_PostOther( &self->infoRight );
STRAND_LOGWITHNAME( self_, "Returning from PostOther(Right)" );
return res;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_AckOther( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
_Strand_Ack_Imp( &self->infoRight );
STRAND_LOGWITHNAME( self_, "Returning from AckOther(Right)" );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_CloseOther( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
_Strand_Close_Imp( &self->infoRight );
STRAND_LOGWITHNAME( self_, "Returning from CloseOther(Right)" );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Both_CancelSelf( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
Strand_Cancel( &self->base );
STRAND_LOGWITHNAME( self_, "Returning from CancelSelf(Both)" );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Right_Aux0( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
DEBUG_ASSERT( NULL != self->infoRight.userFT->Aux0 );
self->infoRight.userFT->Aux0( self_ );
return MI_FALSE;
}
MI_Boolean _StrandMethod_Right_Aux1( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
DEBUG_ASSERT( NULL != self->infoRight.userFT->Aux1 );
self->infoRight.userFT->Aux1( self_ );
return MI_FALSE;
}
MI_Boolean _StrandMethod_Right_Aux2( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
DEBUG_ASSERT( NULL != self->infoRight.userFT->Aux2 );
self->infoRight.userFT->Aux2( self_ );
return MI_FALSE;
}
MI_Boolean _StrandMethod_Right_Aux3( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
DEBUG_ASSERT( NULL != self->infoRight.userFT->Aux3 );
self->infoRight.userFT->Aux3( self_ );
return MI_FALSE;
}
MI_Boolean _StrandMethod_Right_Aux4( _In_ Strand* self_)
{
StrandBoth* self = (StrandBoth*)self_;
DEBUG_ASSERT( NULL != self->infoRight.userFT->Aux4 );
self->infoRight.userFT->Aux4( self_ );
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
typedef enum _EntryOperationAction
{
EOContinue,
EORetry,
EODeleted
} EntryOperationAction;
typedef EntryOperationAction (*StrandEntryOperation)( _In_ StrandMany* );
EntryOperationAction _StrandEntryOperation_Add( _In_ StrandMany* self )
{
MI_Boolean failed = MI_FALSE;
MI_Boolean added = MI_FALSE;
Message * msg = self->currentEntry->strand.info.stored.msg;
DEBUG_ASSERT( NULL != self->userInternalFT->NewEntry );
if( self->strand.canceled )
{
trace_StrandEntryOperation_AddCanceled(
(unsigned int)self->numEntries,
self,
STRAND_DEBUG_GETNAME(&self->strand),
&self->currentEntry->strand,
STRAND_DEBUG_GETNAME(&self->currentEntry->strand) );
failed = MI_TRUE;
// we dont even call the user method in this case
}
else
{
if( HashMap_Insert( &self->many, &self->currentEntry->bucket ) )
{
trace_StrandEntryOperation_AddFailed(
(unsigned int)self->numEntries,
self,
STRAND_DEBUG_GETNAME(&self->strand),
&self->currentEntry->strand,
STRAND_DEBUG_GETNAME(&self->currentEntry->strand) );
failed = MI_TRUE;
}
else
{
++(self->numEntries);
trace_StrandEntryOperation_Add(
(unsigned int)self->numEntries,
self,
STRAND_DEBUG_GETNAME(&self->strand),
&self->currentEntry->strand,
STRAND_DEBUG_GETNAME(&self->currentEntry->strand) );
added = MI_TRUE;
}
if( NULL != self->userInternalFT && NULL != self->userInternalFT->NewEntry )
{
self->userInternalFT->NewEntry( self, self->currentEntry, msg, &failed );
}
}
if( NULL != msg )
{
if( failed || NULL == self->userInternalFT || NULL == self->userInternalFT->AddedToParent )
{
self->currentEntry->strand.info.stored.msg = NULL;
Message_Release(msg);
}
// Else: We dont release the message in this case (we keep it on 'stored')
// It will be deleted once the AddedToParent method is executed
}
if( failed )
{
if( added )
{
StrandMany_DeleteEntry( self->currentEntry );
}
else
{
SList_Free( self->currentEntry );
}
return EODeleted;
}
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_Cancel( _In_ StrandMany* self )
{
trace_StrandEntryOperation_Cancel( self, STRAND_DEBUG_GETNAME(&self->strand),self->strand.canceled );
if( !self->strand.canceled )
{
// since we are in the middle, always forward to the other side
self->strand.info.interaction.other->ft->Cancel( self->strand.info.interaction.other );
_StrandMethodImp_Cancel( &self->strand, &self->strand.info );
}
else if( self->strand.strandType == STRAND_TYPE_PARENTLEFT )
{
// since we are in the left-middle this can be cancelation coming back from right,
// so pass back to the left
self->strand.info.interaction.other->ft->Cancel( self->strand.info.interaction.other );
}
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_PostControl( _In_ StrandMany* self )
{
DEBUG_ASSERT( NULL != self->currentEntry->toParent.controlMsg );
if( self->strand.info.thisClosedOther )
{
trace_StrandEntryOperation_PostControl_PostIgnored(
self,
STRAND_DEBUG_GETNAME(&self->strand),
self->currentEntry->toParent.controlMsg);
}
else
{
trace_StrandEntryOperation_PostControl_ToParent(
self,
STRAND_DEBUG_GETNAME(&self->strand),
self->currentEntry->toParent.controlMsg);
if( NULL != self->userInternalFT && NULL != self->userInternalFT->EntryPostControl )
{
self->userInternalFT->EntryPostControl( self, self->currentEntry->toParent.controlMsg );
}
else
{
Strand_Post( &self->strand, self->currentEntry->toParent.controlMsg );
}
}
Message_Release( self->currentEntry->toParent.controlMsg ); // ref added on StrandEntry_PostControlParent
self->currentEntry->toParent.controlMsg = NULL;
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_Post( _In_ StrandMany* self )
{
DEBUG_ASSERT( NULL != self->currentEntry->toParent.msg );
if( self->strand.info.thisClosedOther )
{
trace_StrandEntryOperation_Post_IgnoredAfterClose(
self,
STRAND_DEBUG_GETNAME(&self->strand),
self->currentEntry->toParent.msg);
Message_Release( self->currentEntry->toParent.msg ); // ref added on StrandEntry_PostParent
self->currentEntry->toParent.msg = NULL;
return EOContinue;
}
if( !self->strand.info.thisAckPending )
{
trace_StrandEntryOperation_Post_ToParent(
self,
STRAND_DEBUG_GETNAME(&self->strand),
self->currentEntry->toParent.msg);
if( NULL != self->userInternalFT && NULL != self->userInternalFT->EntryPost )
{
self->userInternalFT->EntryPost( self, self->currentEntry->toParent.msg );
}
else
{
Strand_Post( &self->strand, self->currentEntry->toParent.msg );
}
Message_Release( self->currentEntry->toParent.msg ); // ref added on StrandEntry_PostParent
self->currentEntry->toParent.msg = NULL;
return EOContinue;
}
else
{
// trace_StrandEntryOperation_Post_CannotPostWaitingOnAck(
// self,
// STRAND_DEBUG_GETNAME(&self->strand),
// self->currentEntry->toParent.msg);
return EORetry;
}
}
EntryOperationAction _StrandEntryOperation_Close( _In_ StrandMany* self )
{
trace_StrandEntryOperation_Close(
self,
STRAND_DEBUG_GETNAME(&self->strand),
self->strand.info.thisClosedOther );
if( !self->strand.info.thisClosedOther )
{
if( NULL != self->userInternalFT && NULL != self->userInternalFT->EntryClose )
{
self->userInternalFT->EntryClose( self );
}
else
{
Strand_Close( &self->strand );
}
}
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_Deleted( _In_ StrandMany* self )
{
trace_StrandEntryOperation_Deleted(
self,
STRAND_DEBUG_GETNAME(&self->strand),
self->currentEntry,
STRAND_DEBUG_GETNAME(&self->currentEntry->strand) );
StrandMany_DeleteEntry(self->currentEntry);
if( NULL != self->userInternalFT && NULL != self->userInternalFT->EntryDeleted )
{
self->userInternalFT->EntryDeleted( self );
}
return EODeleted;
}
EntryOperationAction _StrandEntryOperation_Aux0( _In_ StrandMany* self )
{
DEBUG_ASSERT( NULL != self->strand.info.userFT->Aux0 );
self->strand.info.userFT->Aux0( &self->strand );
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_Aux1( _In_ StrandMany* self )
{
DEBUG_ASSERT( NULL != self->strand.info.userFT->Aux1 );
self->strand.info.userFT->Aux1( &self->strand );
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_Aux2( _In_ StrandMany* self )
{
DEBUG_ASSERT( NULL != self->strand.info.userFT->Aux2 );
self->strand.info.userFT->Aux2( &self->strand );
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_Aux3( _In_ StrandMany* self )
{
DEBUG_ASSERT( NULL != self->strand.info.userFT->Aux3 );
self->strand.info.userFT->Aux3( &self->strand );
return EOContinue;
}
EntryOperationAction _StrandEntryOperation_Aux4( _In_ StrandMany* self )
{
DEBUG_ASSERT( NULL != self->strand.info.userFT->Aux4 );
self->strand.info.userFT->Aux4( &self->strand );
return EOContinue;
}
//------------------------------------------------------------------------------------------------------------
static StrandEntryOperation _StrandEntryOperations_FT[] = {
_StrandEntryOperation_Add,
_StrandEntryOperation_Cancel,
_StrandEntryOperation_PostControl,
_StrandEntryOperation_Post,
_StrandEntryOperation_Close,
_StrandEntryOperation_Aux0,
_StrandEntryOperation_Aux1,
_StrandEntryOperation_Aux2,
_StrandEntryOperation_Aux3,
_StrandEntryOperation_Aux4,
_StrandEntryOperation_Deleted,
NULL, // just give some overflow NULL methods for safety
NULL,
NULL };
//------------------------------------------------------------------------------------------------------------
size_t _StrandMany_HashMapHashProc(const HashBucket* bucket)
{
return (size_t)bucket;
}
int _StrandMany_HashMapEqualProc(_In_ const HashBucket* bucket1, _In_ const HashBucket* bucket2)
{
return bucket1 == bucket2;
}
void _StrandMany_HashMapReleaseProc(_In_ HashBucket* bucket1)
{
// nothing to do here (entry is deleted on _StrandMethod_RunPendingOperations)
}
//------------------------------------------------------------------------------------------------------------
void StrandMany_CancelAllEntries( _In_ StrandMany* self )
{
StrandEntry* entry;
STRAND_ASSERTONSTRAND( &self->strand );
StrandMany_BeginIteration( self );
while( NULL != (entry = StrandMany_Iterate( self )) )
{
StrandMany_CancelEntry( entry );
}
}
void StrandMany_CloseAllEntries( _In_ StrandMany* self )
{
StrandEntry* entry;
STRAND_ASSERTONSTRAND( &self->strand );
StrandMany_BeginIteration( self );
while( NULL != (entry = StrandMany_Iterate( self )) )
{
StrandMany_CloseEntry( entry );
}
}
void StrandMany_PostEntry( _In_ StrandEntry* entry, _In_ Message* msg )
{
STRAND_ASSERTONSTRAND( &entry->parent->strand );
Message_AddRef( msg ); // add ref before assigning and scheduling
entry->fromParent.msg = msg;
_Strand_Schedule( &entry->strand, BitPostInternal );
}
void StrandMany_PostAll( _In_ StrandMany* self, _In_ Message* msg )
{
StrandEntry* entry;
STRAND_ASSERTONSTRAND( &self->strand );
StrandMany_BeginIteration( self );
while( NULL != (entry = StrandMany_Iterate( self )) )
{
StrandMany_PostEntry( entry, msg );
}
}
MI_Boolean StrandMany_PostFindEntry( _In_ StrandMany* self, _In_ Message* msg )
{
StrandEntry* entry;
STRAND_ASSERTONSTRAND( &self->strand );
DEBUG_ASSERT( NULL != self->findEntryProc );
entry = self->findEntryProc( self, msg );
if( NULL != entry )
{
StrandMany_PostEntry( entry, msg );
return MI_TRUE;
}
else
{
return MI_FALSE;
}
}
void StrandMany_PostControlEntry( _In_ StrandEntry* entry, _In_ Message* msg )
{
STRAND_ASSERTONSTRAND( &entry->parent->strand );
Message_AddRef( msg ); // add ref before assigning and scheduling
entry->fromParent.controlMsg = msg;
_Strand_Schedule( &entry->strand, BitPostControlInternal );
}
void StrandMany_PostControlAll( _In_ StrandMany* self, _In_ Message* msg )
{
StrandEntry* entry;
STRAND_ASSERTONSTRAND( &self->strand );
StrandMany_BeginIteration( self );
while( NULL != (entry = StrandMany_Iterate( self )) )
{
StrandMany_PostControlEntry( entry, msg );
}
}
MI_Boolean StrandMany_PostControlFindEntry( _In_ StrandMany* self, _In_ Message* msg )
{
StrandEntry* entry;
STRAND_ASSERTONSTRAND( &self->strand );
DEBUG_ASSERT( NULL != self->findEntryProc );
entry = self->findEntryProc( self, msg );
if( NULL != entry )
{
StrandMany_PostControlEntry( entry, msg );
return MI_TRUE;
}
else
{
return MI_FALSE;
}
}
//------------------------------------------------------------------------------------------------------------
void _StrandEntry_ScheduleParent( _In_ StrandEntry* self, EntryOperationMaskType entryOperationBit )
{
// this can be called during creation (StrandEntry_ScheduleAdd) so we cannot call STRAND_ASSERTONSTRAND( &self->strand );
if( 0 == self->operationScheduled )
{
self->operationScheduled = entryOperationBit;
SList_PushAtomic( &self->parent->pending, &self->entry );
_Strand_ScheduleEntryOperation( &self->parent->strand, MI_TRUE, &self->strand, entryOperationBit );
}
else
{
DEBUG_ASSERT( 0 == (self->operationsPending & entryOperationBit) );
self->operationsPending |= entryOperationBit;
}
}
void StrandEntry_PostParent( _In_ StrandEntry* self, _In_ Message* msg )
{
STRAND_ASSERTONSTRAND( &self->strand );
DEBUG_ASSERT( NULL == self->toParent.msg );
Message_AddRef( msg );
self->toParent.msg = msg;
_StrandEntry_ScheduleParent( self, BitEntryPost );
}
void StrandEntry_PostParentPassthru( _In_ StrandEntry* self, _In_ Message* msg )
{
DEBUG_ASSERT( !self->ackPassthru );
self->ackPassthru = MI_TRUE;
StrandEntry_PostParent( self, msg );
}
void StrandEntry_PostControlParent( _In_ StrandEntry* self, _In_ Message* msg )
{
STRAND_ASSERTONSTRAND( &self->strand );
DEBUG_ASSERT( NULL == self->toParent.controlMsg );
Message_AddRef( msg );
self->toParent.controlMsg = msg;
_StrandEntry_ScheduleParent( self, BitEntryPostControl );
}
//------------------------------------------------------------------------------------------------------------
#ifdef _PREFAST_
#pragma prefast (push)
#pragma prefast (disable: 26001) // bogus "we know the strand points to the middle of the StrandMany struct" and Linux sal parser doesnt recognize something like _Readable_elements_(_Inexpressible_(StrandMany))
#endif /* _PREFAST_ */
MI_Boolean _StrandMethod_Parent_CheckFinished( _In_ Strand* self_ )
{
StrandMany* self = StrandMany_FromStrand(self_);
return ( 0 == self->numEntries ) && _StrandMethodImp_CheckFinished( &self->strand.info ) && !self->strand.delayFinish;
}
MI_Boolean _StrandMethod_Parent_Cancel( _In_ Strand* self_)
{
StrandMany* self = StrandMany_FromStrand(self_);
STRAND_DEBUGSTATE( self_ );
if( !self->strand.canceled )
{
// if it is on the right, we dont need to pass cancel to the right
// if it is on the left, canceling entries passes cancel to the right
// now cancel all entries
StrandMany_CancelAllEntries( self );
_StrandMethodImp_Cancel( &self->strand, &self->strand.info );
}
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_PostControl( _In_ Strand* self_)
{
StrandMany* self = StrandMany_FromStrand(self_);
_Strand_TracePostMsg(self_, self->strand.info.stored.controlMsg, "(Control Parent)");
STRAND_DEBUGSTATE( self_ );
DEBUG_ASSERT( NULL != self->strand.info.stored.controlMsg );
if( NULL != self->strand.info.userFT->PostControl )
{
self->strand.info.userFT->PostControl( &self->strand, self->strand.info.stored.controlMsg );
}
else
{
if( NULL == self->findEntryProc )
{
// Since we dont have true searching capabilities sent to all entries
StrandMany_PostControlAll( self, self->strand.info.stored.controlMsg );
}
else
{
MI_Boolean ret = StrandMany_PostControlFindEntry( self, self->strand.info.stored.controlMsg );
if (!ret)
{
DEBUG_ASSERT( ret );
}
}
}
Message_Release( self->strand.info.stored.controlMsg ); // now we can remove the reference added on _StrandInteraction_*
self->strand.info.stored.controlMsg = NULL;
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_Post( _In_ Strand* self_)
{
StrandMany* self = StrandMany_FromStrand( self_);
Message* storedmsg = self->strand.info.stored.msg;
_Strand_TracePostMsg(self_, storedmsg, "(Parent)");
STRAND_DEBUGSTATE( self_ );
DEBUG_ASSERT( !self->strand.info.otherAckPending );
DEBUG_ASSERT( NULL != storedmsg );
self->strand.info.stored.msg = NULL;
self->strand.info.otherAckPending = MI_TRUE;
if( NULL != self->strand.info.userFT->Post )
{
self->strand.info.userFT->Post( &self->strand, storedmsg );
}
else
{
if( NULL == self->findEntryProc )
{
// Since we dont have true searching capabilities sent to all entries
StrandMany_PostAll( self, storedmsg );
}
else
{
MI_Boolean ret = StrandMany_PostFindEntry( self, storedmsg );
if (!ret)
{
DEBUG_ASSERT( ret );
}
}
Strand_Ack( &self->strand );
}
Message_Release( storedmsg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
#ifdef _PREFAST_
#pragma prefast (pop)
#endif /* _PREFAST_ */
void _StrandMethod_Parent_RunPendingOperations( _In_ StrandMany * self )
{
unsigned long bitIndex;
SListHead retryList;
SListEntry * retryEntry;
DEBUG_ASSERT( NULL == self->currentEntry );
SList_Init( &retryList );
self->pendingRetry = MI_FALSE;
while( NULL != ( self->currentEntry = (StrandEntry*)SList_PopAtomic( &self->pending ) ) )
{
bitIndex = GetFirstSetLSB( self->currentEntry->operationScheduled );
DEBUG_ASSERT( bitIndex );
// trace_Strand_RunPendingOp(
// &self->strand,
// STRAND_DEBUG_GETNAME(&self->strand),
// bitIndex,
// STRAND_DEBUG_GETOPERATIONINDEX(&self->strand,bitIndex) );
switch( (*_StrandEntryOperations_FT[ bitIndex-1 ])(self) )
{
case EORetry:
// trace_Strand_RunPendingOp_CannotComplete(
// &self->strand,
// STRAND_DEBUG_GETNAME(&self->strand),
// bitIndex,
// STRAND_DEBUG_GETOPERATIONINDEX(&self->strand,bitIndex) );
// We will retry when ack arrives
self->pendingRetry = MI_TRUE;
SList_PushAtomic( &retryList, &self->currentEntry->entry );
break;
case EODeleted:
// Special case, we delete the entry here and we do not send back any completion to the entry
trace_Strand_RunPendingOp_EntryDeleted(
&self->strand,
STRAND_DEBUG_GETNAME(&self->strand),
bitIndex,
STRAND_DEBUG_GETOPERATIONINDEX(&self->strand,bitIndex) );
break;
case EOContinue:
// schedule completion to entry
_Strand_ScheduleEntryOperation( &self->currentEntry->strand, MI_FALSE, &self->strand, self->currentEntry->operationScheduled );
break;
default:
DEBUG_ASSERT( MI_FALSE );
}
}
// push back into the list the ones we couldn't execute
while( NULL != ( retryEntry = SList_PopAtomic( &retryList ) ) )
{
SList_PushAtomic( &self->pending, retryEntry );
}
self->currentEntry = NULL;
}
MI_Boolean _StrandMethod_Parent_Ack( _In_ Strand* self_)
{
StrandMany* self = StrandMany_FromStrand(self_);
STRAND_DEBUGSTATE( self_ );
_StrandMethodImp_Ack( &self->strand, &self->strand.info );
// Check if there is any entry waiting to be processed
if( self->pendingRetry )
{
// Can't run _StrandMethod_RunPendingOperations directly,
// because otherwise that can do a Post and then
// the Ack to that post may come while we have not leave
// the current Ack (and therefore schedule will assert
// because it would try to schedule an Ack while other is
// already running)
_Strand_ScheduleEntryOperation(&self->strand, MI_TRUE, NULL, 0 );
}
// Note that there is no problem if some entry race to insert something in the list
// after the check, as that entry will also schedule the EntryOperation itself
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_EntryOperation( _In_ Strand* self_)
{
StrandMany* self = StrandMany_FromStrand(self_);
_StrandMethod_Parent_RunPendingOperations( self );
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_Close( _In_ Strand* self_)
{
StrandMany* self = StrandMany_FromStrand(self_);
STRAND_DEBUGSTATE( self_ );
_StrandMethodImp_Close( &self->strand, &self->strand.info );
// now if there is no method close all entries
if( NULL == self->strand.info.userFT->Close )
{
StrandMany_CloseAllEntries( self );
}
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_CancelInternal( _In_ Strand* self_)
{
DEBUG_ASSERT( MI_FALSE ); // this should never be scheduled on the parent but a EntryOperation should be used instead
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_PostControlInternal( _In_ Strand* self_)
{
DEBUG_ASSERT( MI_FALSE ); // this should never be scheduled on the parent but a EntryOperation should be used instead
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_PostInternal( _In_ Strand* self_)
{
DEBUG_ASSERT( MI_FALSE ); // this should never be scheduled on the parent but a EntryOperation should be used instead
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_AckInternal( _In_ Strand* self_)
{
DEBUG_ASSERT( MI_FALSE ); // this should never be scheduled on the parent (EntryOperation cannot be used either)
return MI_FALSE;
}
MI_Boolean _StrandMethod_Parent_CloseInternal( _In_ Strand* self_)
{
DEBUG_ASSERT( MI_FALSE ); // this should never be scheduled on the parent but a EntryOperation should be used instead
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
MI_Boolean _StrandMethod_Entry_CheckFinished( _In_ Strand* self_ )
{
StrandEntry* self = StrandEntry_FromStrand(self_);
return ( 0 == self->operationScheduled ) && ( 0 == self->operationsPending ) && _StrandMethodImp_CheckFinished( &self->strand.info ) && !self->strand.delayFinish;
}
MI_Boolean _StrandMethod_Entry_Cancel( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand(self_);
STRAND_DEBUGSTATE( self_ );
if( !self->strand.canceled )
{
self->strand.canceled = MI_TRUE;
// if it is on the right, we dont need to pass cancel to the right
// if it is on the left, canceling parent passes cancel to the right
if( NULL != self->strand.info.userFT->Cancel )
{
(*self->strand.info.userFT->Cancel)(self_);
}
else
{
StrandEntry_CancelParent(self);
}
}
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_PostControl( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand(self_);
_Strand_TracePostMsg(self_, self->strand.info.stored.controlMsg, "(Control Entry)");
STRAND_DEBUGSTATE( self_ );
DEBUG_ASSERT( NULL != self->strand.info.stored.controlMsg );
if( NULL != self->strand.info.userFT->PostControl )
{
self->strand.info.userFT->PostControl( &self->strand, self->strand.info.stored.controlMsg );
}
else
{
StrandEntry_PostControlParent( self, self->strand.info.stored.controlMsg );
}
Message_Release( self->strand.info.stored.controlMsg ); // now we can remove the reference added on _StrandInteraction_*
self->strand.info.stored.controlMsg = NULL;
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_Post( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand(self_);
Message* storedmsg = self->strand.info.stored.msg;
_Strand_TracePostMsg(self_, storedmsg, "(Entry)");
STRAND_DEBUGSTATE( self_ );
DEBUG_ASSERT( !self->strand.info.otherAckPending );
DEBUG_ASSERT( NULL != storedmsg );
self->strand.info.stored.msg = NULL;
self->strand.info.otherAckPending = MI_TRUE;
if( NULL != self->strand.info.userFT->Post )
{
self->strand.info.userFT->Post( &self->strand, storedmsg );
}
else
{
StrandEntry_PostParentPassthru( self, storedmsg );
}
Message_Release( storedmsg ); // now we can remove the reference added on _StrandInteraction_*
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_Ack( _In_ Strand* self_)
{
STRAND_DEBUGSTATE( self_ );
_StrandMethodImp_Ack( self_, &self_->info );
return MI_FALSE;
}
#ifdef _PREFAST_
#pragma prefast (push)
#pragma prefast (disable: 26001) // bogus "we know the strand points to the middle of the StrandEntry struct" and Linux sal parser doesnt recognize something like _Readable_elements_(_Inexpressible_(StrandEntry))
#endif /* _PREFAST_ */
MI_Boolean _StrandMethod_Entry_EntryOperation( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand(self_);
EntryOperationMaskType operationLastScheduled = self->operationScheduled;
if( 0 != self->operationsPending )
{
// schedule the next one
unsigned long bitIndex;
EntryOperationMaskType entryOperationBit;
bitIndex = GetFirstSetLSB( self->operationsPending );
DEBUG_ASSERT( bitIndex );
entryOperationBit = (EntryOperationMaskType)_GetMethodBit( bitIndex );
self->operationScheduled = entryOperationBit;
self->operationsPending &= (~entryOperationBit);
SList_PushAtomic( &self->parent->pending, &self->entry );
_Strand_ScheduleEntryOperation( &self->parent->strand, MI_TRUE, &self->strand, entryOperationBit );
}
else
{
self->operationScheduled = 0;
}
if( BitEntryPost == operationLastScheduled )
{
if( NULL != self->parent->userInternalFT && NULL != self->parent->userInternalFT->ParentAck )
{
self->parent->userInternalFT->ParentAck( self );
}
else if( self->ackPassthru )
{
// Now we can send the Ack to the interaction
Strand_Ack(self_);
}
self->ackPassthru = MI_FALSE;
}
else if( BitEntryAdd == operationLastScheduled && NULL != self->parent->userInternalFT && NULL != self->parent->userInternalFT->AddedToParent )
{
Message* storedmsg = self->strand.info.stored.msg;
self->strand.info.stored.msg = NULL;
self->parent->userInternalFT->AddedToParent( self, storedmsg );
if( NULL != storedmsg )
{
_Strand_TracePostMsg(self_, storedmsg, "(EntryAddedToParent)");
Message_Release( storedmsg ); // now we can finally remove the reference added on ScheduleAdd
}
}
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_Close( _In_ Strand* self_)
{
STRAND_DEBUGSTATE( self_ );
_StrandMethodImp_Close( self_, &self_->info );
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_CancelInternal( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand(self_);
trace_StrandMethod_Entry_CancelInternal( self_, STRAND_DEBUG_GETNAME(self_) );
if( !self_->canceled )
{
if( self_->info.opened )
{
if( !self_->info.thisClosedOther )
{
// since we are in the middle, forward to the other side
self_->info.interaction.other->ft->Cancel( self_->info.interaction.other );
}
_StrandMethodImp_Cancel( self_, &self_->info );
}
else
{
// If it was not even opened we dont even call the cancel method
self_->canceled = MI_TRUE;
}
}
else if( self->parent->strand.strandType == STRAND_TYPE_PARENTRIGHT && self_->info.opened && !self_->info.thisClosedOther )
{
// since we are in the left-middle this can be cancelation coming back from right,
// so pass back to the left
self_->info.interaction.other->ft->Cancel( self_->info.interaction.other );
}
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_PostControlInternal( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand(self_);
DEBUG_ASSERT( NULL != self->fromParent.controlMsg );
trace_StrandMethod_Entry_PostControlInternal(self_, STRAND_DEBUG_GETNAME(self_), self->fromParent.controlMsg);
if( NULL != self->parent->userInternalFT && NULL != self->parent->userInternalFT->ParentPostControl )
{
self->parent->userInternalFT->ParentPostControl( self, self->fromParent.controlMsg );
}
else
{
Strand_PostControl( self_, self->fromParent.controlMsg );
}
Message_Release( self->fromParent.controlMsg ); // ref added on StrandMany_PostControlEntry
self->fromParent.controlMsg = NULL;
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_PostInternal( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand( self_);
DEBUG_ASSERT( NULL != self->fromParent.msg );
DEBUG_ASSERT( !self->strand.info.thisAckPending ); // user is responsible to manage flow control if there are secondary messages
trace_StrandMethod_Entry_PostInternal(self_, STRAND_DEBUG_GETNAME(self_), self->fromParent.msg);
if( NULL != self->parent->userInternalFT && NULL != self->parent->userInternalFT->ParentPost )
{
self->parent->userInternalFT->ParentPost( self, self->fromParent.msg );
}
else
{
Strand_Post( self_, self->fromParent.msg );
}
Message_Release( self->fromParent.msg ); // ref added on StrandMany_PostEntry
self->fromParent.msg = NULL;
return MI_FALSE;
}
#ifdef _PREFAST_
#pragma prefast (pop)
#endif /* _PREFAST_ */
MI_Boolean _StrandMethod_Entry_AckInternal( _In_ Strand* self_)
{
trace_StrandMethod_Entry_AckInternal( self_, STRAND_DEBUG_GETNAME(self_) );
Strand_Ack( self_ );
return MI_FALSE;
}
MI_Boolean _StrandMethod_Entry_CloseInternal( _In_ Strand* self_)
{
StrandEntry* self = StrandEntry_FromStrand(self_);
trace_StrandMethod_Entry_CloseInternal(
self_,
STRAND_DEBUG_GETNAME(self_),
self_->info.opened,
self_->info.thisClosedOther );
if( NULL != self->parent->userInternalFT && NULL != self->parent->userInternalFT->ParentClose )
{
self->parent->userInternalFT->ParentClose( self );
}
else if( self_->info.opened && !self_->info.thisClosedOther )
{
Strand_Close( self_ );
}
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
static InteractionFT _StrandInteraction_FT = {
_StrandInteraction_Post,
_StrandInteraction_PostControl,
_StrandInteraction_Ack,
_StrandInteraction_Cancel,
_StrandInteraction_Close };
//------------------------------------------------------------------------------------------------------------
static InteractionFT _StrandInteraction_Left_FT = {
_StrandInteraction_Left_Post,
_StrandInteraction_Left_PostControl,
_StrandInteraction_Left_Ack,
_StrandInteraction_Left_Cancel,
_StrandInteraction_Left_Close };
//------------------------------------------------------------------------------------------------------------
static InteractionFT _StrandInteraction_Right_FT = {
_StrandInteraction_Right_Post,
_StrandInteraction_Right_PostControl,
_StrandInteraction_Right_Ack,
_StrandInteraction_Right_Cancel,
_StrandInteraction_Right_Close };
//------------------------------------------------------------------------------------------------------------
static InteractionFT _StrandInteraction_Many_FT = {
_StrandInteraction_Many_Post,
_StrandInteraction_Many_PostControl,
_StrandInteraction_Many_Ack,
_StrandInteraction_Many_Cancel,
_StrandInteraction_Many_Close };
//------------------------------------------------------------------------------------------------------------
static StrandMethod _StrandMethods_FT[] = {
_StrandMethod_CheckFinished,
_StrandMethod_CompleteOpenAsync,
_StrandMethod_Timer,
_StrandMethod_Cancel,
_StrandMethod_CancelSelf,
_StrandMethod_PostControl,
_StrandMethod_Post,
_StrandMethod_PostOther,
_StrandMethod_Ack,
_StrandMethod_AckOther,
_StrandMethod_Close,
_StrandMethod_CloseOther,
_StrandMethod_Aux0,
_StrandMethod_Aux1,
_StrandMethod_Aux2,
_StrandMethod_Aux3,
_StrandMethod_Aux4,
NULL, // just give some overflow NULL methods for safety
NULL,
NULL };
//------------------------------------------------------------------------------------------------------------
static StrandMethod _StrandMethods_Both_FT[] = {
_StrandMethod_Both_CheckFinished,
_StrandMethod_Both_CompleteOpenAsync,
_StrandMethod_Timer,
_StrandMethod_Left_Cancel,
_StrandMethod_Right_Cancel,
_StrandMethod_Both_CancelSelf,
_StrandMethod_Left_PostControl,
_StrandMethod_Right_PostControl,
_StrandMethod_Left_Post,
_StrandMethod_Right_Post,
_StrandMethod_PostOther,
_StrandMethod_Right_PostOther,
_StrandMethod_Left_Ack,
_StrandMethod_Right_Ack,
_StrandMethod_AckOther,
_StrandMethod_Right_AckOther,
_StrandMethod_Left_Close,
_StrandMethod_Right_Close,
_StrandMethod_CloseOther,
_StrandMethod_Right_CloseOther,
_StrandMethod_Aux0,
_StrandMethod_Aux1,
_StrandMethod_Aux2,
_StrandMethod_Aux3,
_StrandMethod_Aux4,
_StrandMethod_Right_Aux0,
_StrandMethod_Right_Aux1,
_StrandMethod_Right_Aux2,
_StrandMethod_Right_Aux3,
_StrandMethod_Right_Aux4,
NULL, // just give some overflow NULL methods for safety
NULL,
NULL };
//------------------------------------------------------------------------------------------------------------
static StrandMethod _StrandMethods_Parent_FT[] = {
_StrandMethod_Parent_CheckFinished,
_StrandMethod_CompleteOpenAsync,
_StrandMethod_Timer,
_StrandMethod_Parent_Cancel,
_StrandMethod_CancelSelf,
_StrandMethod_Parent_CancelInternal,
_StrandMethod_Parent_PostControl,
_StrandMethod_Parent_PostControlInternal,
_StrandMethod_Parent_Post,
_StrandMethod_PostOther,
_StrandMethod_Parent_PostInternal,
_StrandMethod_Parent_Ack,
_StrandMethod_AckOther,
_StrandMethod_Parent_AckInternal,
_StrandMethod_Parent_EntryOperation,
_StrandMethod_Parent_Close,
_StrandMethod_CloseOther,
_StrandMethod_Parent_CloseInternal,
_StrandMethod_Aux0,
_StrandMethod_Aux1,
_StrandMethod_Aux2,
_StrandMethod_Aux3,
_StrandMethod_Aux4,
NULL, // just give some overflow NULL methods for safety
NULL,
NULL };
//------------------------------------------------------------------------------------------------------------
static StrandMethod _StrandMethods_Entry_FT[] = {
_StrandMethod_Entry_CheckFinished,
_StrandMethod_CompleteOpenAsync,
_StrandMethod_Timer,
_StrandMethod_Entry_Cancel,
_StrandMethod_CancelSelf,
_StrandMethod_Entry_CancelInternal,
_StrandMethod_Entry_PostControl,
_StrandMethod_Entry_PostControlInternal,
_StrandMethod_Entry_Post,
_StrandMethod_PostOther,
_StrandMethod_Entry_PostInternal,
_StrandMethod_Entry_Ack,
_StrandMethod_AckOther,
_StrandMethod_Entry_AckInternal,
_StrandMethod_Entry_EntryOperation,
_StrandMethod_Entry_Close,
_StrandMethod_CloseOther,
_StrandMethod_Entry_CloseInternal,
_StrandMethod_Aux0,
_StrandMethod_Aux1,
_StrandMethod_Aux2,
_StrandMethod_Aux3,
_StrandMethod_Aux4,
NULL, // just give some overflow NULL methods for safety
NULL,
NULL };
//------------------------------------------------------------------------------------------------------------
MI_INLINE
void _Strand_EnterStrand( _In_ Strand* self )
{
trace_Strand_EnterStrand( self, STRAND_DEBUG_GETNAME(self) );
DEBUG_ASSERT( NULL == self->strandStealedFlag );
DEBUG_ASSERT( 0 == self->stateScheduled );
self->stateScheduled = BitExecuting;
_Strand_SetCurrentStrandThread( self );
}
// Only used internally
MI_INLINE
void _Strand_ExitStrand( _In_ Strand* self )
{
trace_Strand_ExitStrand( self, STRAND_DEBUG_GETNAME(self) );
DEBUG_ASSERT( NULL == self->strandStealedFlag );
DEBUG_ASSERT( BitExecuting == self->stateScheduled );
self->stateScheduled = 0;
_Strand_ExitCurrentStrandThread( self );
}
MI_INLINE
Strand* _Strand_Create(
size_t structSize,
_Inout_ StrandFlags* flags )
{
Strand* self;
DEBUG_ASSERT( structSize >= sizeof(Strand) );
/* Allocate heap space for Strand */
if( 0 != (*flags & STRAND_FLAG_NOZEROALLOCATED) )
{
*flags &= (~STRAND_FLAG_NOZEROALLOCATED); // To indicate Strand*_Init that it does need to zero the strand fields
self = PAL_Malloc( structSize );
}
else
{
*flags |= STRAND_FLAG_NOZEROALLOCATED; // To indicate Strand*_Init that it doesnt need to zero the strand fields
self = PAL_Calloc( 1, structSize );
}
return self;
}
MI_INLINE
void _Strand_CreateEnterStrand(
#if defined(STRAND_ENABLE_DEBUG)
_In_ StrandDebugInfo debug,
#endif
_In_ Strand* self)
{
STRAND_SETDEBUG( self, debug );
_Strand_EnterStrand(self);
}
MI_INLINE
void _Strand_CreateExitStrand(
_In_ Strand* self)
{
if( 0 == (self->flags & STRAND_FLAG_ENTERSTRAND) )
{
_Strand_ExitStrand(self);
}
}
//------------------------------------------------------------------------------------------------------------
Strand* Strand_New(
#if defined(STRAND_ENABLE_DEBUG)
_In_ StrandDebugInfo debug,
#endif
_In_ StrandFT * userFT,
size_t structSize,
StrandFlags flags,
_In_opt_ InteractionOpenParams* interactionOpenParams )
{
Strand* self;
if( 0 == structSize )
structSize = sizeof( Strand );
self = _Strand_Create( structSize, &flags );
if( self )
{
Strand_Init( STRAND_PASSDEBUG(debug) self, userFT, flags, interactionOpenParams );
}
return self;
}
void Strand_Init(
#if defined(STRAND_ENABLE_DEBUG)
_In_ StrandDebugInfo debug,
#endif
_Out_ Strand* self,
_In_ StrandFT* userFT,
StrandFlags flags,
_In_opt_ InteractionOpenParams* interactionOpenParams )
{
DEBUG_ASSERT(self);
DEBUG_ASSERT(userFT);
if( 0 == (flags & STRAND_FLAG_NOZEROALLOCATED) )
{
// Clear all fields in case previously they have not been cleared
memset(self, 0, sizeof(Strand) );
}
self->strandMethods = _StrandMethods_FT;
self->flags = flags;
self->info.interaction.ft = &_StrandInteraction_FT;
self->info.userFT = userFT;
_Strand_CreateEnterStrand( STRAND_PASSDEBUG(debug) self );
if( NULL != interactionOpenParams || 0 != (flags & STRAND_FLAG_DELAYACCEPTOPEN) )
{
// we are being opened on the right most
self->strandType = STRAND_TYPE_RIGHTMOST;
if( 0 == (flags & STRAND_FLAG_DELAYACCEPTOPEN) )
{
Strand_AcceptOpen( self, interactionOpenParams );
}
}
else
{
if( ( flags & STRAND_FLAG_NOINTERACTION ) != 0 )
{
self->strandType = STRAND_TYPE_NOINTERACTION;
self->info.thisClosedOther = self->info.otherClosedThis = MI_TRUE;
}
else
{
self->strandType = STRAND_TYPE_LEFTMOST;
}
}
_Strand_CreateExitStrand( self );
}
//------------------------------------------------------------------------------------------------------------
StrandBoth* StrandBoth_New(
#if defined(STRAND_ENABLE_DEBUG)
_In_ StrandDebugInfo debug,
#endif
_In_ StrandFT* userLeftFT,
_In_ StrandFT* userRightFT,
size_t structSize,
StrandFlags flags,
_In_opt_ InteractionOpenParams* interactionOpenParams )
{
StrandBoth* self;
if( 0 == structSize )
structSize = sizeof( StrandBoth );
DEBUG_ASSERT( structSize >= sizeof(StrandBoth) );
self = (StrandBoth*) _Strand_Create( structSize, &flags );
if( NULL != self )
{
StrandBoth_Init( STRAND_PASSDEBUG(debug) self, userLeftFT, userRightFT, flags, interactionOpenParams );
}
return self;
}
//------------------------------------------------------------------------------------------------------------
void StrandBoth_Init(
#if defined(STRAND_ENABLE_DEBUG)
_In_ StrandDebugInfo debug,
#endif
_Out_ StrandBoth* self,
_In_ StrandFT* userLeftFT,
_In_ StrandFT* userRightFT,
StrandFlags flags,
_In_opt_ InteractionOpenParams* interactionOpenParams )
{
DEBUG_ASSERT(userLeftFT);
DEBUG_ASSERT(userRightFT);
// close should be the same for both
DEBUG_ASSERT( userLeftFT->Finish == userRightFT->Finish || NULL == userRightFT->Finish );
DEBUG_ASSERT( NULL != interactionOpenParams || 0 != (flags & STRAND_FLAG_DELAYACCEPTOPEN) );
if( 0 == (flags & STRAND_FLAG_NOZEROALLOCATED) )
{
// Clear all fields in case previously they have not been cleared
memset(self, 0, sizeof(StrandBoth) );
}
self->base.strandType = STRAND_TYPE_MIDDLE;
self->base.strandMethods = _StrandMethods_Both_FT;
self->base.flags = flags;
self->base.info.interaction.ft = &_StrandInteraction_Left_FT;
self->base.info.userFT = userLeftFT;
self->infoRight.interaction.ft = &_StrandInteraction_Right_FT;
self->infoRight.userFT = userRightFT;
_Strand_CreateEnterStrand( STRAND_PASSDEBUG(debug) &self->base );
if( 0 == (flags & STRAND_FLAG_DELAYACCEPTOPEN) )
{
DEBUG_ASSERT( NULL != interactionOpenParams );
Strand_AcceptOpen( &self->base, interactionOpenParams );
}
_Strand_CreateExitStrand( &self->base );
}
//------------------------------------------------------------------------------------------------------------
StrandMany* StrandMany_New(
#if defined(STRAND_ENABLE_DEBUG)
_In_ StrandDebugInfo debug,
#endif
_In_ StrandFT* userParentFT,
_In_opt_ StrandManyInternalFT* userInternalFT,
size_t structSize,
StrandFlags flags,
_In_opt_ InteractionOpenParams* interactionOpenParams, // if STRAND_FLAG_NOINTERACTION is not used and interactionOpenParams is NULL it is assumed it will open an interaction on the right
size_t numLists, // numlist for the underlying Hash table, use 1 if it is not going to be searched
_In_opt_ HashMapHashProc hash, // hashing func for the underlying Hash table, if NULL will use the StrandEntry pointer
_In_opt_ HashMapEqualProc equal, // equal func for the underlying Hash table, can be NULL if it is not going to be searched or entry ptr comparison is ok
_In_opt_ FindEntryProc findEntryProc ) // finds a entry based on a message, can be NULL if msg should not be automatically redirected to entries
{
StrandMany* self;
DEBUG_ASSERT(userParentFT);
if( 0 == structSize )
structSize = sizeof( StrandMany );
DEBUG_ASSERT( structSize >= sizeof(StrandMany) );
self = (StrandMany*) SList_Alloc( structSize );
if( NULL != self )
{
if( 0 != (flags & STRAND_FLAG_NOZEROALLOCATED) )
{
// Clear only strand fields
memset(self, 0, sizeof(StrandMany) );
}
else
{
// Clear all allocated fields
memset(self, 0, structSize );
}
if( NULL == hash )
hash = _StrandMany_HashMapHashProc;
if( NULL == equal )
equal = _StrandMany_HashMapEqualProc;
if( HashMap_Init( &self->many, numLists, hash, equal, _StrandMany_HashMapReleaseProc ) )
{
SList_Free( self );
return NULL;
}
SList_Init( &self->pending );
self->findEntryProc = findEntryProc;
self->userInternalFT = userInternalFT;
self->strand.flags = flags;
self->strand.strandMethods = _StrandMethods_Parent_FT;
self->strand.info.interaction.ft = &_StrandInteraction_Many_FT;
self->strand.info.userFT = userParentFT;
_Strand_CreateEnterStrand( STRAND_PASSDEBUG(debug) &self->strand );
if( 0 == (flags&STRAND_FLAG_NOINTERACTION) )
{
if( NULL != interactionOpenParams || 0 != (flags & STRAND_FLAG_DELAYACCEPTOPEN) )
{
// Parent is being opened (it is on the left)
self->strand.strandType = STRAND_TYPE_PARENTLEFT;
if( 0 == (flags & STRAND_FLAG_DELAYACCEPTOPEN) )
{
Strand_AcceptOpen( &self->strand, interactionOpenParams );
}
}
else
{
self->strand.strandType = STRAND_TYPE_PARENTRIGHT;
}
}
else
{
DEBUG_ASSERT( NULL == interactionOpenParams );
self->strand.strandType = STRAND_TYPE_PARENTNOINTERACTION;
self->strand.info.thisClosedOther = self->strand.info.otherClosedThis = MI_TRUE;
}
_Strand_CreateExitStrand( &self->strand );
}
return self;
}
StrandEntry* StrandEntry_New(
#if defined(STRAND_ENABLE_DEBUG)
_In_ StrandDebugInfo debug,
#endif
_In_ StrandMany* parent,
_In_ StrandFT* userEntryFT,
size_t structSize,
StrandFlags flags,
_In_opt_ InteractionOpenParams* interactionOpenParams )
{
StrandEntry* self;
DEBUG_ASSERT( NULL != parent );
DEBUG_ASSERT( NULL != userEntryFT );
if( 0 == structSize )
structSize = sizeof( StrandEntry );
DEBUG_ASSERT( structSize >= sizeof(StrandEntry) );
self = (StrandEntry*) SList_Alloc( structSize );
if( NULL != self )
{
if( 0 != (flags & STRAND_FLAG_NOZEROALLOCATED) )
{
// Clear only strand fields
memset(self, 0, sizeof(StrandEntry) );
}
else
{
// Clear all allocated fields
memset(self, 0, structSize );
}
self->parent = parent;
self->strand.strandMethods = _StrandMethods_Entry_FT;
self->strand.flags = flags;
self->strand.info.interaction.ft = &_StrandInteraction_Many_FT;
self->strand.info.userFT = userEntryFT;
self->strand.strandType = STRAND_TYPE_ENTRY;
_Strand_CreateEnterStrand( STRAND_PASSDEBUG(debug) &self->strand );
if( NULL != interactionOpenParams || 0 != (flags & STRAND_FLAG_DELAYACCEPTOPEN) )
{
// Entry is being opened (it is on the left)
DEBUG_ASSERT( ( flags & STRAND_FLAG_NOINTERACTION ) == 0 );
DEBUG_ASSERT( parent->strand.strandType == STRAND_TYPE_PARENTRIGHT || parent->strand.strandType == STRAND_TYPE_PARENTNOINTERACTION );
if( 0 == (flags & STRAND_FLAG_DELAYACCEPTOPEN) )
{
Strand_AcceptOpen( &self->strand, interactionOpenParams );
}
}
else
{
if( ( flags & STRAND_FLAG_NOINTERACTION ) != 0 )
{
self->strand.info.thisClosedOther = self->strand.info.otherClosedThis = MI_TRUE;
}
else
{
DEBUG_ASSERT( parent->strand.strandType == STRAND_TYPE_PARENTLEFT || parent->strand.strandType == STRAND_TYPE_PARENTNOINTERACTION );
}
}
_Strand_CreateExitStrand( &self->strand );
}
return self;
}
MI_Result StrandMany_AddEntry(
_In_ StrandEntry* self )
{
STRAND_ASSERTONSTRAND( &self->parent->strand );
DEBUG_ASSERT( self->strand.info.stored.msg == NULL );
if( HashMap_Insert( &self->parent->many, &self->bucket ) )
{
SList_Free( self );
return MI_RESULT_FAILED;
}
++(self->parent->numEntries);
if( NULL != self->parent->userInternalFT && NULL != self->parent->userInternalFT->AddedToParent )
{
STRAND_ASSERTONSTRAND( &self->strand );
self->parent->userInternalFT->AddedToParent( self, NULL );
}
return MI_RESULT_OK;
}
void StrandEntry_ScheduleAdd(
_In_ StrandEntry* self,
_In_opt_ Message * msg ) // optional initial message
{
DEBUG_ASSERT( NULL != self );
if( NULL != msg )
{
Message_AddRef(msg);
self->strand.info.stored.msg = msg;
}
_StrandEntry_ScheduleParent(self,BitEntryAdd);
}
void Strand_Delete( _In_ Strand* self )
{
if( STRAND_ISTYPE_ENTRY(self) )
{
StrandEntry_Delete( StrandEntry_FromStrand(self) );
}
else if( STRAND_ISTYPE_PARENT(self) )
{
StrandMany_Delete( StrandMany_FromStrand(self) );
}
else
{
STRAND_LOGWITHNAME( self, "Deleting Strand" );
PAL_Free( self );
}
}
void StrandMany_Delete( _In_ StrandMany* self )
{
SListEntry* entry;
STRAND_LOGWITHNAME( &self->strand, "Deleting StrandMany" );
DEBUG_ASSERT( 0 == self->numEntries );
DEBUG_ASSERT( NULL == self->currentEntry );
HashMap_Destroy( &self->many );
entry = SList_FlushAtomic( &self->pending );
if (entry != NULL)
{
DEBUG_ASSERT( NULL == entry );
}
SList_Free( self );
}
void StrandMany_DeleteEntry( _In_ StrandEntry* entry )
{
StrandMany* self = entry->parent;
STRAND_ASSERTONSTRAND( &self->strand );
trace_Strand_DeletedEntry( &self->strand, STRAND_DEBUG_GETNAME(&self->strand), &entry->strand, STRAND_DEBUG_GETNAME(&entry->strand) );
DEBUG_ASSERT( self->numEntries >= 1 );
--(self->numEntries);
if( HashMap_Remove( &self->many, &entry->bucket ) )
{
trace_Strand_CannotDelete( &self->strand, STRAND_DEBUG_GETNAME(&self->strand), &entry->strand, STRAND_DEBUG_GETNAME(&entry->strand) );
DEBUG_ASSERT( MI_FALSE ); // should not happen
}
SList_Free( entry );
}
void StrandEntry_Delete( _In_ StrandEntry* self )
{
STRAND_LOGWITHNAME( &self->strand, "Scheduling deletion of StrandEntry" );
DEBUG_ASSERT( 0 == self->operationScheduled );
_StrandEntry_ScheduleParent( self, BitEntryDeleted );
}
//------------------------------------------------------------------------------------------------------------
void _Strand_CancelPropagate(
_In_ Strand * self)
{
STRAND_ASSERTONSTRAND( self );
switch( self->strandType )
{
case STRAND_TYPE_MIDDLE:
{
StrandBoth * selfBoth = (StrandBoth*)self;
if( selfBoth->infoRight.opened && !selfBoth->infoRight.thisClosedOther )
{
STRAND_LOGWITHNAME( self, "Canceling to the right" );
// first go to the right
selfBoth->infoRight.interaction.other->ft->Cancel( selfBoth->infoRight.interaction.other );
}
}
break;
case STRAND_TYPE_LEFTMOST:
case STRAND_TYPE_RIGHTMOST:
case STRAND_TYPE_PARENTRIGHT:
{
if( self->info.opened && !self->info.thisClosedOther )
{
STRAND_LOGWITHNAME( self, "Canceling" );
// just go to the other side (start going right or initiate return to the left)
self->info.interaction.other->ft->Cancel( self->info.interaction.other );
}
}
break;
case STRAND_TYPE_PARENTLEFT:
{
StrandMany* stranMany = StrandMany_FromStrand(self);
STRAND_LOGWITHNAME( self, "Canceling all entries to the left" );
StrandMany_CancelAllEntries( stranMany );
}
case STRAND_TYPE_ENTRY:
{
StrandEntry* entry = StrandEntry_FromStrand(self);
if( entry->parent->strand.strandType == STRAND_TYPE_PARENTRIGHT )
{
if( NULL == self->info.userFT->Cancel )
{
STRAND_LOGWITHNAME( self, "Canceling parent from entry" );
StrandEntry_CancelParent( entry );
}
}
else
{
if( self->info.opened && !self->info.thisClosedOther )
{
STRAND_LOGWITHNAME( self, "Canceling interaction from entry" );
// start going right
self->info.interaction.other->ft->Cancel( self->info.interaction.other );
}
}
}
break;
case STRAND_TYPE_PARENTNOINTERACTION:
default:
DEBUG_ASSERT(MI_FALSE);
}
STRAND_LOGWITHNAME( self, "Returning from Canceling other (would cancel method if existent) " );
}
void Strand_Cancel(
_In_ Strand * self)
{
STRAND_ASSERTONSTRAND( self );
if( !self->canceled )
{
_Strand_CancelPropagate( self );
_StrandMethodImp_Cancel(self, &self->info );
}
}
//------------------------------------------------------------------------------------------------------------
ptrdiff_t _EnableMethodBit( _In_ Strand* self, ptrdiff_t methodBit )
{
ptrdiff_t initialState, newState = ReadWithFence( &self->stateScheduled );
do
{
initialState = newState;
// check if bit already set
DEBUG_ASSERT( (initialState & methodBit) == 0 );
}
while( (newState = Atomic_CompareAndSwap( &self->stateScheduled, initialState, initialState|methodBit )) != initialState );
return initialState|methodBit;
}
//------------------------------------------------------------------------------------------------------------
MI_INLINE ptrdiff_t _DisableMethodBit( _In_ Strand* self, ptrdiff_t methodBit )
{
return Atomic_And( &self->stateScheduled, ~methodBit ) & (~methodBit);
}
//------------------------------------------------------------------------------------------------------------
void _Strand_ExecuteLoop( _In_ Strand* self, ptrdiff_t state )
{
unsigned long bitIndex;
MI_Boolean strandStealedFlag;
STRAND_DEBUG_GETSTATE_USED;
STRAND_DEBUG_GETINFOSTATE_USED;
_Strand_SetCurrentStrandThread( self );
for(;;) // check new state loop
{
DEBUG_ASSERT( ( state & BitExecuting ) != 0 );
// we dont want to check on BitExecuting
bitIndex = GetFirstSetLSB( state & (~BitExecuting) );
if( bitIndex )
{
ptrdiff_t methodBit = _GetMethodBit( bitIndex );
strandStealedFlag = MI_FALSE;
self->strandStealedFlag = &strandStealedFlag;
self->currentMethodBit = methodBit;
DEBUG_ASSERT( bitIndex > FirstRealMethodBit );
STRAND_DEBUG_GETINFOSTATE_STORE(self);
trace_Strand_ExecLoop(
self,
STRAND_DEBUG_GETNAME(self),
state,
STRAND_DEBUG_GETSTATE(self, state ),
bitIndex,
STRAND_DEBUG_GETMETHODINDEX(self,bitIndex),
STRAND_DEBUG_GETINFOSTATE_STORED );
(*self->strandMethods[ bitIndex-FirstRealMethodBit ])(self);
if( strandStealedFlag )
{
// if someone stealed the strand synchronously (Strand_Leave)
// while the function was being executed then just bail out
trace_Strand_ExecLoop_Leave(
self,
state,
bitIndex,
methodBit);
return;
}
else
{
state = _DisableMethodBit( self, methodBit );
if( BitTimer == methodBit && NULL != self->timer )
{
// timer was restarted inside the timer method, start it now
Timer_Start( self->timer, self );
}
else if( STRAND_ISTYPE_PARENT( self ) && BitEntryOperation == methodBit )
{
_StrandMethod_Parent_RunPendingOperations( StrandMany_FromStrand(self) );
}
}
}
else
{
ptrdiff_t newState;
// Nothing else scheduled, we are going to stop executing (if nothing else has changed)
MI_Boolean aboutToFinish = _Strand_ShouldFinish(self);
DEBUG_ASSERT( BitExecuting == state );
/* trace_Strand_ExecLoop( self, state, aboutToFinish ); */
_Strand_ExitCurrentStrandThread( self );
// Get these two before the Atomic_CompareAndSwap (strand may be deleted after that)
STRAND_DEBUG_GETNAME_STORE(self);
STRAND_DEBUG_GETINFOSTATE_STORE(self);
newState = Atomic_CompareAndSwap( &self->stateScheduled, state, 0 );
// If the state has not changed (no new method was scheduled) we can stop now
if( newState == state )
{
trace_Strand_ExecLoop_Exits(
self,
STRAND_DEBUG_GETNAME_STORED,
state,
aboutToFinish,
STRAND_DEBUG_GETINFOSTATE_STORED );
if( aboutToFinish )
{
_Strand_Finish( self );
}
return;
}
else
{
trace_Strand_ExecLoop_DoesntExit(
self,
STRAND_DEBUG_GETNAME(self),
state,
newState,
STRAND_DEBUG_GETSTATE(self, newState),
aboutToFinish,
STRAND_DEBUG_GETINFOSTATE_STORED );
state = newState;
// revert this
_Strand_SetCurrentStrandThread( self );
}
}
}
}
//------------------------------------------------------------------------------------------------------------
// Schedules a method in the strand
//------------------------------------------------------------------------------------------------------------
#if defined(STRAND_ENABLE_DEBUG)
void _Strand_ScheduleImp(
_In_ Strand* self,
unsigned int methodBit,
MI_Boolean allowMultiSchedule,
_In_opt_ Strand* fromStrand,
EntryOperationMaskType entryOperationBit )
#else
void _Strand_ScheduleImp(
_In_ Strand* self,
unsigned int methodBit )
#endif
{
ptrdiff_t initialState, newState = ReadWithFence( &self->stateScheduled );
STRAND_DEBUG_GETSTATE_USED;
DEBUG_ASSERT( methodBit > 0 );
#if defined(STRAND_ENABLE_DEBUG)
if( allowMultiSchedule && BitTimer != methodBit )
{
trace_Strand_ScheduleParent(
self,
STRAND_DEBUG_GETNAME(self),
newState,
STRAND_DEBUG_GETSTATE(self, newState),
entryOperationBit,
0 == entryOperationBit ? "" : STRAND_DEBUG_GETOPERATION(self, entryOperationBit ),
fromStrand,
NULL == fromStrand ? "" : STRAND_DEBUG_GETNAME(fromStrand),
methodBit,
STRAND_DEBUG_GETMETHOD(self,methodBit) );
}
else if( NULL != fromStrand )
{
trace_Strand_ScheduleEntry(
self,
STRAND_DEBUG_GETNAME(self),
newState,
STRAND_DEBUG_GETSTATE(self, newState),
entryOperationBit,
STRAND_DEBUG_GETOPERATION(fromStrand, entryOperationBit ),
fromStrand,
STRAND_DEBUG_GETNAME(fromStrand),
methodBit,
STRAND_DEBUG_GETMETHOD(self,methodBit) );
}
else
{
trace_Strand_Schedule(
self,
STRAND_DEBUG_GETNAME(self),
newState,
STRAND_DEBUG_GETSTATE(self, newState),
methodBit,
STRAND_DEBUG_GETMETHOD(self,methodBit) );
}
#else
trace_Strand_Schedule2(
self,
newState,
methodBit );
#endif
do
{
initialState = newState;
#if defined(STRAND_ENABLE_DEBUG)
// check if bit already set
// otherwise Caller is violating the contract!
DEBUG_ASSERT( allowMultiSchedule || (initialState & methodBit) == 0 );
#endif
}
while( (newState = Atomic_CompareAndSwap( &self->stateScheduled, initialState, initialState|methodBit|BitExecuting )) != initialState );
// Check if nobody was executing before now, then start executing
if( ( initialState & BitExecuting ) == 0 )
{
_Strand_ExecuteLoop(self, initialState|methodBit|BitExecuting);
}
}
//------------------------------------------------------------------------------------------------------------
void Strand_Leave( _In_ Strand* self )
{
STRAND_ASSERTONSTRAND( self );
trace_Strand_Leave( self, STRAND_DEBUG_GETNAME(self), self->strandStealedFlag );
// If there is an encompasing loop, then set this to false so it will bail out
if( NULL != self->strandStealedFlag )
{
*(self->strandStealedFlag) = MI_TRUE;
}
_Strand_ExecuteLoop( self, _DisableMethodBit( self, self->currentMethodBit ) );
}
//------------------------------------------------------------------------------------------------------------
void Strand_StartTimer( _In_ Strand* self, _In_ Timer* timer, _In_ MI_Uint64 timeusecs )
{
STRAND_ASSERTONSTRAND( self );
DEBUG_ASSERT( NULL == self->timer );
DEBUG_ASSERT( timer );
self->timer = timer;
Timer_SetTime( timer, timeusecs ); // doesnt start the actual timer
if( BitTimer != self->currentMethodBit )
{
TimerResult result = Timer_Start( timer, self );
if (TimerResult_Success != result)
{
// Cancel timer start request
self->timer = NULL;
trace_Strand_Cannot_Start_Timer( timer, self );
}
}
else
{
trace_Strand_Cannot_Start_Timer( timer, self );
}
}
void Strand_FireTimer( _In_ Strand* self )
{
STRAND_ASSERTONSTRAND( self );
if( NULL != self->timer )
{
Timer_Fire( self->timer, self, TimerReason_ManuallyFired );
}
else
{
trace_Strand_Cannot_Fire_Timer( self );
}
}
MI_Boolean Strand_HaveTimer( _In_ Strand* self )
{
STRAND_ASSERTONSTRAND( self );
if ( self->timer )
return MI_TRUE;
else
return MI_FALSE;
}
//------------------------------------------------------------------------------------------------------------
void _Strand_AcceptOpenCommon(
_In_ Strand* self,
_In_ Interaction* interaction )
{
STRAND_ASSERTONSTRAND( self );
DEBUG_ASSERT( !self->info.otherAckPending );
DEBUG_ASSERT( !self->info.thisAckPending );
DEBUG_ASSERT( !self->info.ackPassthru );
self->info.interaction.other = interaction;
interaction->other = &self->info.interaction;
self->info.opened = MI_TRUE;
self->info.thisClosedOther = MI_FALSE;
self->info.otherClosedThis = MI_FALSE;
}
void Strand_AcceptOpenAsync(
_In_ Strand* self,
_In_ Strand* otherStrand )
{
DEBUG_ASSERT( !STRAND_ISTYPE_MIDDLE( otherStrand ) );
_Strand_AcceptOpenCommon( self, &otherStrand->info.interaction);
_Strand_Schedule( otherStrand, BitCompleteOpenAsync );
}
void Strand_AcceptOpenAsyncFromStrandBoth(
_In_ Strand* self,
_In_ StrandBoth* otherStrand )
{
DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &otherStrand->base ) );
_Strand_AcceptOpenCommon( self, &otherStrand->infoRight.interaction );
otherStrand->asyncOpenInProgress = MI_TRUE;
_Strand_Schedule( &otherStrand->base, BitCompleteOpenAsync );
}
void Strand_AcceptOpen(
_In_ Strand* self,
_In_ InteractionOpenParams* params )
{
_Strand_AcceptOpenCommon( self, params->interaction );
if( NULL != params->msg )
{
self->info.otherAckPending = MI_TRUE;
}
if( NULL != params->origin )
{
Strand_Leave( params->origin );
}
}
static void _DoNothingPost( _In_ Interaction* self, _In_ Message* msg )
{
MI_UNUSED( self );
MI_UNUSED( msg );
}
static void _DoNothingOther( _In_ Interaction* self )
{
MI_UNUSED( self );
}
static InteractionFT _failOpenInteractionFT = {
_DoNothingPost,
_DoNothingPost,
_DoNothingOther,
_DoNothingOther,
_DoNothingOther };
static Interaction _failOpenInteraction = { &_failOpenInteractionFT, NULL };
void Strand_FailOpenWithMsg(
_In_ InteractionOpenParams* params,
_In_opt_ Message* msg)
{
params->interaction->other = &_failOpenInteraction;
if( NULL != params->origin )
{
Strand_Leave( params->origin );
}
if( NULL != msg )
{
params->interaction->ft->Post( params->interaction, msg );
}
else
{
if( NULL != params->msg )
{
DEBUG_ASSERT( Message_IsRequest( params->msg ) );
//TODO convert the original request to ResultMsg and post it back
//params->interaction->ft->Post( params->interaction, params->msg );
// Cancel in the meantime:
}
//TODO else
{
params->interaction->ft->Cancel( params->interaction );
}
}
if( NULL != params->msg )
{
params->interaction->ft->Ack( params->interaction );
}
params->interaction->ft->Close( params->interaction );
}
void Strand_FailOpenWithResult(
_In_ InteractionOpenParams* params,
MI_Result result,
_In_ MakeResultMessageCallback callback)
{
PostResultMsg* resultMsg;
resultMsg = (*callback)( params->msg, NULL, NULL, MI_RESULT_TYPE_MI, result);
if( NULL != resultMsg )
{
Strand_FailOpenWithMsg( params, &resultMsg->base );
PostResultMsg_Release(resultMsg);
}
else
{
Strand_FailOpen( params );
}
}