/* **============================================================================== ** ** 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. ** **============================================================================== */ #ifndef _Strand_h #define _Strand_h #include #include #include #include #include #include #include #if defined(CONFIG_OS_WINDOWS) #include /* Required for Timer struct */ #endif BEGIN_EXTERNC //------------------------------------------------------------------------------------------------------------ // General Utility functions //------------------------------------------------------------------------------------------------------------ MI_INLINE ptrdiff_t ReadWithFence ( _In_ volatile ptrdiff_t* source ) { return Atomic_CompareAndSwap( source, 0, 0 ); } #if defined(_MSC_VER) MI_INLINE unsigned long GetFirstSetLSB( ptrdiff_t x ) { unsigned long r = 0; if( _BitScanForward(&r, (unsigned long)x) ) return r+1; else return 0; } #else MI_INLINE unsigned long GetFirstSetLSB( ptrdiff_t x ) { if( x != 0 ) { #if defined(CONFIG_HAVE_BUILTIN_CTZ) return __builtin_ctz( (unsigned long) x ) + 1; #else unsigned long count = 0; unsigned long ulX = (unsigned long)x; while(ulX) { if((ulX & 1) != 0) break; count++; ulX = ulX >> 1; } return count + 1; #endif } else return 0; } #endif #define FromOffset( type, memberName, memberPtr ) ( (type*) ( ((char*)(memberPtr)) - offsetof(type,memberName) ) ) #define FromOffsetConst( type, memberName, memberPtr ) ( (const type*) ( ((const char*)(memberPtr)) - offsetof(type,memberName) ) ) //------------------------------------------------------------------------------------------------------------ #if defined(CONFIG_ENABLE_DEBUG) #define STRAND_ENABLE_DEBUG 1 #endif //------------------------------------------------------------------------------------------------------------ // Strand Types #define STRAND_TYPE_NOINTERACTION 0 #define STRAND_TYPE_LEFTMOST 1 #define STRAND_TYPE_RIGHTMOST 2 #define STRAND_TYPE_MIDDLE 3 #define STRAND_TYPE_PARENTLEFT 4 #define STRAND_TYPE_PARENTRIGHT 5 #define STRAND_TYPE_PARENTNOINTERACTION 6 #define STRAND_TYPE_ENTRY 7 #define STRAND_ISTYPE_EDGE( strand ) ( (strand)->strandType == STRAND_TYPE_LEFTMOST || (strand)->strandType == STRAND_TYPE_RIGHTMOST ) #define STRAND_ISTYPE_MIDDLE( strand ) ( (strand)->strandType == STRAND_TYPE_MIDDLE ) #define STRAND_ISTYPE_PARENT( strand ) ( (strand)->strandType == STRAND_TYPE_PARENTLEFT || (strand)->strandType == STRAND_TYPE_PARENTRIGHT || (strand)->strandType == STRAND_TYPE_PARENTNOINTERACTION ) #define STRAND_ISTYPE_ENTRY( strand ) ( (strand)->strandType == STRAND_TYPE_ENTRY ) #define STRAND_ISTYPE_MANY( strand ) ( STRAND_ISTYPE_ENTRY(strand) || STRAND_ISTYPE_PARENT(strand) ) typedef enum _TimerReason { TimerReason_Expired = 0, TimerReason_ManuallyFired = 1, TimerReason_Canceled = 2 } TimerReason; //------------------------------------------------------------------------------------------------------------ typedef MI_Boolean (*StrandMethod)( _In_ Strand* ); // Similar to the interaction one, but this called from the strand typedef struct _StrandFT { // - On a StrandBoth this can be NULL // and if the other side is opened message will always pass thru to the other side // - On a StrandMany(parent) this can be NULL // and the message will be passed either a found entry or all entries (if findEntryProc is NULL) // - On a StrandEntry this can be NULL // and the message will be passed thru to the to the parent (once possible) void (*Post)( _In_ Strand* self, _In_ Message* msg); // - On a StrandBoth this can be NULL // and if the other side is opened message will always pass thru to the other side // - On a StrandMany(parent) this can be NULL // and the message will be passed either a found entry or all entries (if findEntryProc is NULL) // - On a StrandEntry this can be NULL // and the message will be passed to the to the parent (once possible) void (*PostControl)( _In_ Strand* self, _In_ Message* msg); // If this one is set to NULL (It should be NULL in a StrandMany(parent)): // - On normal Strand it will no do anything // - On StrandBoth it will pass thru to the other side if that side is Pending Ack (even if ackPassThru parameter is not set on Open/Post) // - On a StrandMany(parent) it will take care of keep sending messages from entries // - On a StrandEntry it will no do anything (NOTE that user is responsible to manage flow control if there are secondary messages) void (*Ack)( _In_ Strand* self); // If this one is set to NULL the strand will take care itself // - On a Strand it will not do anything (only sending back to the left it is on the right) // - On a StrandBoth (even if it is not NULL) it always take care of passing thru cancel to the other side (if interaction is opened and not closed yet) // - On a StrandMany(parent) it cancels all entries // - On a StrandEntry it cancels the parent void (*Cancel)( _In_ Strand* self); // If this one is set to NULL the strand will take care itself, // including passing thru close to the other side (only in that case) // - On a StrandMany(parent) it closes all entries // - On a StrandEntry it does nothing (user should call StrandEntry_CloseParent if wants to close the parent) void (*Close)( _In_ Strand* self); // Called when everything is finished (there are no ack pendings and everything is closed) // if NULL the strand will just delete itself void (*Finish)( _In_ Strand* self); // Called when either the timer expires or Strand_FireTimer is called // Should not be NULL if the timer is used void (*Timer)( _In_ Strand* self, TimerReason reason); // Auxiliary (optional) strand methods void (*Aux0)( _In_ Strand* self); void (*Aux1)( _In_ Strand* self); void (*Aux2)( _In_ Strand* self); void (*Aux3)( _In_ Strand* self); void (*Aux4)( _In_ Strand* self); } StrandFT; typedef struct _StoredMsg { Message * msg; // scheduled msg Message * controlMsg; // control msg (Note if we have more than one control message type we will need to add more of this) } StoredMsg; // information about one side interaction typedef struct _InteractionInfo { Interaction interaction; // interaction used StrandFT * userFT; // user methods MI_Boolean opened; // if the interaction have been opened (initial Ack is pending otherwise) MI_Boolean thisClosedOther; // if we closed the interaction with other component MI_Boolean thisAckPending; // if we posted to the other component and have an ack pending from it MI_Boolean otherClosedThis; // if the other component closed this interaction MI_Boolean otherAckPending; // if the other component posted to this component and have an ack pending from us MI_Boolean ackPassthru; // if the ack to last post/open should be relayed automatically to the other side (in a pass-thru fashion) StoredMsg stored; // stored scheduled msg(s) Message * otherMsg; // scheduled msg for other } InteractionInfo; typedef struct _Timer Timer; #if defined(STRAND_ENABLE_DEBUG) typedef const char ** StrandDebugInfo; #define STRAND_DEBUG( debugInfoName ) _Strand_DebugInfo_##debugInfoName, #define STRAND_PASSDEBUG( debugInfo ) debugInfo, #else #define STRAND_DEBUG( debugInfoName ) #define STRAND_PASSDEBUG( debugInfo ) #endif //--------------------------------------------------------------------------------------------------------------------- // Used during creation (*_New/*_Init) to indicate that we are entering the strand // that we just created (It SHOULD call Strand_Leave to leave it) #define STRAND_FLAG_ENTERSTRAND 0x1 // Only used on Strand/StrandMany/StrandEntry (it doesnt make sense on StrandBoth) #define STRAND_FLAG_NOINTERACTION 0x2 // Can be issued by Strand_New/StrandBoth_New/StrandMany_New/StrandEntry_New // (does NOT zeroes all allocated fields) // Note that Strand_Init/StrandBoth_Init only initialize strand fields // so since it doesnt have additional fields, that flag SHOULD NOT be used there // (it is used internally to indicate that fields have NOT been zeroed already) #define STRAND_FLAG_NOZEROALLOCATED 0x4 // When a Strand object is going to be deleted (just before calling Finish), // if it has a running timer then it is going to automatically fire/close it first // and only then (after timer callback) call Finish. // If the implementer wants instead to wait until the timer expires before calling Finish // it can specify the following flag // IMPORTANT: Please note that the Strand will not be deleted in that case until // no timer is set (what can delay general shutdown). #define STRAND_FLAG_FINISHWAITSFORTIMER 0x8 // Delays accepting the open/acking open until Strand_AcceptOpen() is called #define STRAND_FLAG_DELAYACCEPTOPEN 0x10 typedef unsigned short StrandFlags; //--------------------------------------------------------------------------------------------------------------------- // Strand with interactions in one side struct _Strand { #if defined(STRAND_ENABLE_DEBUG) StrandDebugInfo debug; ThreadID threadId; // threadId of the thread executing strand (if any) ThreadID testThreadId; #endif volatile ptrdiff_t stateScheduled; // methods marked for execution (by order bit) StrandMethod * strandMethods; MI_Boolean canceled; // if we have been canceled (either way) MI_Boolean delayFinish; // if the object wants to delay its finalization (false by default) ptrdiff_t currentMethodBit; MI_Boolean * strandStealedFlag; // used to notify bailing out when an opening is completed while running a strand method unsigned char strandType; // see STRAND_TYPE_* above StrandFlags flags; Timer* timer; InteractionInfo info; // Interaction info (for the left on StrandBoth) }; // Strand with interactions in both directions typedef struct _StrandBoth { Strand base; InteractionInfo infoRight; // Interaction info used for the right interaction MI_Boolean leftCanceled; // If we have passed the cancelation to the left side //Flag to track an async open on right interaction initiated but not yet completed. //This is introduced to have a simple solution to prevent WSManCD leaks on //different failure paths that did not have the right interaction opened yet //Note that only WSManCD currently does an async open on the right interaction. //TODO: This needs to be integrated into base strand or we need a better alternative MI_Boolean asyncOpenInProgress; // False initially, True upon an async open, False upon open completion } StrandBoth; typedef struct _StrandMany StrandMany; typedef struct _StrandEntry StrandEntry; typedef StrandEntry* (*FindEntryProc)(_In_ const StrandMany* parent, _In_ const Message* msg); // Used on StrandMany between parent and entries typedef struct _StrandManyInternalFT { // Called on the parent when StrandEntry_ScheduleNew is called // Can be NULL if StrandEntry_ScheduleNew is never used // note that if it is used and it is NULL failure on creation // may go unnoticed. // *failed can be MI_TRUE if the entry failed to be added, // or the user function can also set it to MI_TRUE if it has // a problem (like starting the interaction); // The entry will be automatically deleted in either case. void (*NewEntry)( _In_ StrandMany* self, _In_ StrandEntry* newEntry, _In_opt_ Message* msg, _Inout_ MI_Boolean* failed ); // Called when (after) a entry is deleted (by the Entry itself, not by the parent) // If NULL does nothing void (*EntryDeleted)( _In_ StrandMany* self ); // If NULL message is just posted thru the interaction (when possible) // If NOT NULL, it is only called once ACK is not pending void (*EntryPost)( _In_ StrandMany* self, _In_ Message* msg); // If NULL message is just posted thru the interaction void (*EntryPostControl)( _In_ StrandMany* self, _In_ Message* msg); // If NULL interaction is closed (if not already) // If NOT NULL, it is only called if the interaction is not closed already void (*EntryClose)( _In_ StrandMany* self); // Called once the parent has added us // If NULL nothing is done void (*AddedToParent)( _In_ StrandEntry* self, _In_opt_ Message* msg); // If NULL message is just posted thru the interaction void (*ParentPost)( _In_ StrandEntry* self, _In_ Message* msg); // If NULL message is just posted thru the interaction void (*ParentPostControl)( _In_ StrandEntry* self, _In_ Message* msg); // If NULL ack is just passed thru (if passthru version was used) void (*ParentAck)( _In_ StrandEntry* self); // If NULL interaction is closed void (*ParentClose)( _In_ StrandEntry* self); } StrandManyInternalFT; // Strand with interactions on to many struct _StrandMany { SListHead pending; // pending operations on main strand HashMap many; // collection with interactions on the MANY direction (a StrandEntry) HashMapIterator iter; // to interato thru "many" size_t numEntries; // number of entries on "many" FindEntryProc findEntryProc; Strand strand; // includes interaction for the Parent StrandManyInternalFT* userInternalFT; StrandEntry* currentEntry; // current entry whose operation is being executed MI_Boolean pendingRetry; // if there are items in the pending list for retry }; typedef unsigned short EntryOperationMaskType; // A Strand with interaction belonging to the MANY part of StrandMany struct _StrandEntry { SListEntry entry; // to be used on parent "pending" list HashBucket bucket; // to be used on parent "many" list Strand strand; // Includes Interaction info for the entry StrandMany * parent; EntryOperationMaskType operationScheduled; // entry operation scheduled on parent EntryOperationMaskType operationsPending; // entry operations pending to be scheduled on parent MI_Boolean ackPassthru; // if the ack for the post parent operation should go pass thru to interaction StoredMsg toParent; // stored scheduled msg(s) to/for parent StoredMsg fromParent; // stored scheduled msg(s) from parent }; // If interaction is NULL then is assumed that is going to open an interaction to the right // (and thisAckPending is going to be set to true) Strand* Strand_New( #if defined(STRAND_ENABLE_DEBUG) _In_ StrandDebugInfo debug, #endif _In_ StrandFT * userFT, size_t structSize, StrandFlags flags, _In_opt_ InteractionOpenParams* interactionOpenParams ); // If interaction is NULL then is assumed that is going to open an interaction to the right // (and thisAckPending is going to be set to true) void Strand_Init( #if defined(STRAND_ENABLE_DEBUG) _In_ StrandDebugInfo debug, #endif _Out_ Strand* self, _In_ StrandFT* userFT, StrandFlags flags, _In_opt_ InteractionOpenParams* interactionOpenParams ); // interaction indicates the interaction opening from the left // It is always assumed that is going to open an interaction to the right 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 ); // interaction indicates the interaction opening from the left // It is always assumed that is going to open an interaction to the right 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 ); // interaction indicates the interaction opening from the left // otherwise if parentHasInteraction is MI_TRUE it is then assumed that is going to open an interaction to the right // (and infoRight.thisAckPending is going to be set to true) 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 // This can be called from outside (StrandMany) parent strand // IMPORTANT: The entry is not completely created/added until StrandEntry_ScheduleAdd/StrandMany_AddEntry is called 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 ); // Completes adding a entry created by StrandEntry_New // if the entry cannot be added it will be automatically deleted void StrandEntry_ScheduleAdd( _In_ StrandEntry* self, _In_opt_ Message * msg ); // optional initial message // Completes adding a entry created by StrandEntry_New, but // this should ONLY be called from parent (StrandMany) strand // if the entry cannot be added it will be automatically deleted MI_Result StrandMany_AddEntry( _In_ StrandEntry* self ); // Can be used with any type of strand (this should be called in the object strand) void Strand_Delete( _In_ Strand* self ); // It should delete all entries first (this should be called in the Parent object strand) void StrandMany_Delete( _In_ StrandMany* self ); // Deletes a StrandEntry FROM the parent (StrandMany) strand void StrandMany_DeleteEntry( _In_ StrandEntry* entry ); // Deletes a StrandEntry (this should be called in the Entry object strand) void StrandEntry_Delete( _In_ StrandEntry* self ); // Deletes a StrandEntry that was NOT ADDED yet to the StrandMany // (not StrandEntry_ScheduleAdd or StrandMany_AddEntry called) MI_INLINE void StrandEntry_DeleteNoAdded( _In_ StrandEntry* self ) { SList_Free( self ); } // Called inside a strand method (or when creating strand with STRAND_FLAG_ENTERSTRAND) // to explictly leave the strand (prematurely on the case of the strand method) // Note that additional strand methods can be called if they are already // scheduled when Strand_Leave is called void Strand_Leave( _In_ Strand* self ); //------------------------------------------------------------------------------------------------------------ // For testing purposes only #if defined(STRAND_ENABLE_DEBUG) #define STRAND_SETTESTSTRANDTHREAD( strand ) ((strand)->testThreadId = Thread_ID()) // internal use MI_INLINE void _Strand_SetCurrentStrandThread( _In_ Strand* strand ) { strand->threadId = Thread_ID(); } // internal use MI_INLINE void _Strand_ExitCurrentStrandThread( _In_ Strand* strand ) { strand->threadId = Thread_ID_Null(); } #else #define STRAND_SETTESTSTRANDTHREAD( strand ) #define _Strand_SetCurrentStrandThread( strand ) #define _Strand_ExitCurrentStrandThread( strand ) #endif // internal use MI_INLINE void _Strand_Finish( _In_ Strand* self) { if( NULL != self->info.userFT->Finish ) { (*self->info.userFT->Finish)(self); } else { Strand_Delete( self ); } } // internal use MI_INLINE void _Strand_Post_CommonImp( _In_ InteractionInfo* info, _In_ Message * msg) { DEBUG_ASSERT( !info->thisClosedOther ); DEBUG_ASSERT( !info->thisAckPending ); info->thisAckPending = MI_TRUE; } // internal use MI_INLINE void _Strand_Post_Imp( _In_ InteractionInfo* info, _In_ Message * msg) { DEBUG_ASSERT( !info->ackPassthru ); _Strand_Post_CommonImp( info, msg ); info->interaction.other->ft->Post( info->interaction.other, msg ); } // internal use MI_INLINE void _Strand_PostPassthru_Imp( _In_ InteractionInfo* info, _In_ Message * msg) { DEBUG_ASSERT( !info->ackPassthru ); info->ackPassthru = MI_TRUE; _Strand_Post_CommonImp( info, msg ); info->interaction.other->ft->Post( info->interaction.other, msg ); } // internal use MI_INLINE void _Strand_PostAndLeaveStrand_Imp( _In_ Strand* strand, _In_ InteractionInfo* info, _In_ Message * msg) { Interaction* other = info->interaction.other; DEBUG_ASSERT( !info->ackPassthru ); _Strand_Post_CommonImp( info, msg ); Strand_Leave(strand); other->ft->Post( other, msg ); } // internal use MI_INLINE void _Strand_PostControl_Imp( _In_ InteractionInfo* info, _In_ Message * msg) { DEBUG_ASSERT( !info->thisClosedOther ); info->interaction.other->ft->PostControl( info->interaction.other, msg ); } // internal use MI_INLINE void _Strand_Ack_Imp( _In_ InteractionInfo* info) { DEBUG_ASSERT( info->otherAckPending ); info->otherAckPending = MI_FALSE; info->opened = MI_TRUE; // as soon as it send an Ack it means the interaction has been opened info->interaction.other->ft->Ack( info->interaction.other ); } // internal use MI_INLINE void _Strand_Close_Imp( _In_ InteractionInfo* info) { DEBUG_ASSERT( !info->thisClosedOther ); info->thisClosedOther = MI_TRUE; if( NULL != info->interaction.other ) { info->interaction.other->ft->Close( info->interaction.other ); } // otherwise implies a strand without interaction } //------------------------------------------------------------------------------------------------------------ #define BitExecuting 0x1 #define FirstMethodBit 1 #define FirstRealMethodBit 1 // For all Strands #define BitCompleteOpenAsync 0x2 #define BitTimer 0x4 // For Strands with just one interaction #define BitCancel 0x8 #define BitCancelSelf 0x10 #define BitPostControl 0x20 // Note if we have more than one control message type we will need to add more of this #define BitPost 0x40 #define BitPostOther 0x80 #define BitAck 0x100 #define BitAckOther 0x200 #define BitClose 0x400 #define BitCloseOther 0x800 #define BitAux0 0x1000 #define BitAux1 0x2000 #define BitAux2 0x4000 #define BitAux3 0x8000 #define BitAux4 0x10000 #define Strand_NumBaseMethods 11 #define Strand_NumAuxMethods 5 #define Strand_NumMethods (Strand_NumBaseMethods+Strand_NumAuxMethods) // For Strands with both interactions #define BitCancel_Left 0x8 #define BitCancel_Right 0x10 #define BitCancelSelf_Both 0x20 #define BitPostControl_Left 0x40 // Note if we have more than one control message type we will need to add more of this #define BitPostControl_Right 0x80 // (and same here) #define BitPost_Left 0x100 #define BitPost_Right 0x200 #define BitPostOther_Left 0x400 #define BitPostOther_Right 0x800 #define BitAck_Left 0x1000 #define BitAck_Right 0x2000 #define BitAckOther_Left 0x4000 #define BitAckOther_Right 0x8000 #define BitClose_Left 0x10000 #define BitClose_Right 0x20000 #define BitCloseOther_Left 0x40000 #define BitCloseOther_Right 0x80000 #define BitAux0_Left 0x100000 #define BitAux1_Left 0x200000 #define BitAux2_Left 0x400000 #define BitAux3_Left 0x800000 #define BitAux4_Left 0x1000000 #define BitAux0_Right 0x2000000 #define BitAux1_Right 0x4000000 #define BitAux2_Right 0x8000000 #define BitAux3_Right 0x10000000 #define BitAux4_Right 0x20000000 #define StrandBoth_NumBaseMethods 19 #define StrandBoth_NumAuxMethods 10 #define StrandBoth_NumMethods (StrandBoth_NumBaseMethods+StrandBoth_NumAuxMethods) // for StrandMany/StrandEntry #define BitCancel_Many 0x8 #define BitCancelSelf_Many 0x10 #define BitCancelInternal 0x20 #define BitPostControl_Many 0x40 // Note if we have more than one control message type we will need to add more of this #define BitPostControlInternal 0x80 // Note if we have more than one control message type we will need to add more of this #define BitPost_Many 0x100 #define BitPostOther_Many 0x200 #define BitPostInternal 0x400 #define BitAck_Many 0x800 #define BitAckOther_Many 0x1000 #define BitAckInternal 0x2000 #define BitEntryOperation 0x4000 // Entry operation (for scheduling operations on parent from entries) #define BitClose_Many 0x8000 #define BitCloseOther_Many 0x10000 #define BitCloseInternal 0x20000 #define BitAux0_Many 0x40000 #define BitAux1_Many 0x80000 #define BitAux2_Many 0x100000 #define BitAux3_Many 0x200000 #define BitAux4_Many 0x400000 #define StrandMany_NumBaseMethods 17 #define StrandMany_NumAuxMethods 5 #define StrandMany_NumMethods (StrandMany_NumBaseMethods+StrandMany_NumAuxMethods) // for StrandMany/StrandEntry, bits of EntryOperations scheduled on one particular entry #define BitEntryAdd 0x1 #define BitEntryCancel 0x2 #define BitEntryPostControl 0x4 #define BitEntryPost 0x8 #define BitEntryClose 0x10 #define BitEntryAux0 0x20 #define BitEntryAux1 0x40 #define BitEntryAux2 0x80 #define BitEntryAux3 0x100 #define BitEntryAux4 0x200 #define BitEntryDeleted 0x400 #define StrandMany_FirstBitEntryOperationAux 6 #define StrandMany_LastBitEntryOperationAux 10 #define StrandMany_NumEntryOperations 11 // internal use #if defined(STRAND_ENABLE_DEBUG) extern void _Strand_ScheduleImp( _In_ Strand* self, unsigned int methodBit, MI_Boolean allowMultiSchedule, _In_opt_ Strand* fromStrand, EntryOperationMaskType entryOperationBit ); #define _Strand_Schedule( self, methodBit ) \ ( _Strand_ScheduleImp( (self), (methodBit), MI_FALSE, NULL, 0 ) ) #define _Strand_ScheduleEntryOperation( self, allowMultiSchedule, fromStrand, entryOperationBit ) \ ( _Strand_ScheduleImp( (self), BitEntryOperation, (allowMultiSchedule), (fromStrand), (entryOperationBit) ) ) #define _Strand_ScheduleTimer( self ) \ ( _Strand_ScheduleImp( (self), BitTimer, MI_TRUE, NULL, 0 ) ) #else extern void _Strand_ScheduleImp( _In_ Strand* self, unsigned int methodBit ); #define _Strand_Schedule( self, methodBit ) \ ( _Strand_ScheduleImp( (self), (methodBit) ) ) #define _Strand_ScheduleEntryOperation( self, allowMultiSchedule, fromStrand, entryOperationBit ) \ ( _Strand_ScheduleImp( (self), BitEntryOperation ) ) #define _Strand_ScheduleTimer( self ) \ ( _Strand_ScheduleImp( (self), BitTimer ) ) #endif //------------------------------------------------------------------------------------------------------------ // Debugging aid //------------------------------------------------------------------------------------------------------------ #if defined(STRAND_ENABLE_DEBUG) typedef struct _StrandBaseDebugInfo { const char * name; char letter; } StrandBaseDebugInfo; extern StrandBaseDebugInfo _Strand_BaseNameDebugInfo[Strand_NumMethods+5]; extern StrandBaseDebugInfo _StrandBoth_BaseNameDebugInfo[StrandBoth_NumMethods+5]; extern StrandBaseDebugInfo _StrandMany_BaseNameDebugInfo[StrandMany_NumMethods+5]; extern char * _StrandMany_BaseEntryOperationDebugInfo[StrandMany_NumEntryOperations+3]; #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME( strandName ) const char * _Strand_DebugInfo_##strandName [1] = { #strandName } #else #define STRAND_DEBUGNAME( strandName ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME1( strandName, auxName0 ) const char * _Strand_DebugInfo_##strandName [Strand_NumAuxMethods+1] = { #strandName, #auxName0 } #else #define STRAND_DEBUGNAME1( strandName, auxName0 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME2( strandName, auxName0, auxName1 ) const char * _Strand_DebugInfo_##strandName [Strand_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1 } #else #define STRAND_DEBUGNAME2( strandName, auxName0, auxName1 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME3( strandName, auxName0, auxName1, auxName2 ) const char * _Strand_DebugInfo_##strandName [Strand_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2 } #else #define STRAND_DEBUGNAME3( strandName, auxName0, auxName1, auxName2 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME4( strandName, auxName0, auxName1, auxName2, auxName3 ) const char * _Strand_DebugInfo_##strandName [Strand_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2, #auxName3 } #else #define STRAND_DEBUGNAME4( strandName, auxName0, auxName1, auxName2, auxName3 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME5( strandName, auxName0, auxName1, auxName2, auxName3, auxName4 ) const char * _Strand_DebugInfo_##strandName [Strand_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2, #auxName3, #auxName4 } #else #define STRAND_DEBUGNAME5( strandName, auxName0, auxName1, auxName2, auxName3, auxName4 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME6( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5 ) const char * _Strand_DebugInfo_##strandName [StrandBoth_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2, #auxName3, #auxName4, #auxName5 } #else #define STRAND_DEBUGNAME6( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME7( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6 ) const char * _Strand_DebugInfo_##strandName [StrandBoth_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2, #auxName3, #auxName4, #auxName5, #auxName6 } #else #define STRAND_DEBUGNAME7( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME8( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6, auxName7 ) const char * _Strand_DebugInfo_##strandName [StrandBoth_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2, #auxName3, #auxName4, #auxName5, #auxName6, #auxName7 } #else #define STRAND_DEBUGNAME8( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6, auxName7 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME9( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6, auxName7, auxName8 ) const char * _Strand_DebugInfo_##strandName [StrandBoth_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2, #auxName3, #auxName4, #auxName5, #auxName6, #auxName7, #auxName8 } #else #define STRAND_DEBUGNAME9( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6, auxName7, auxName8 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUGNAME10( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6, auxName7, auxName8, auxName9 ) const char * _Strand_DebugInfo_##strandName [StrandBoth_NumAuxMethods+1] = { #strandName, #auxName0, #auxName1, #auxName2, #auxName3, #auxName4, #auxName5, #auxName6, #auxName7, #auxName8, #auxName9 } #else #define STRAND_DEBUGNAME10( strandName, auxName0, auxName1, auxName2, auxName3, auxName4, auxName5, auxName6, auxName7, auxName8, auxName9 ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUG_GETBASEINFO( strand ) ( STRAND_TYPE_MIDDLE == (strand)->strandType ? _StrandBoth_BaseNameDebugInfo : ( STRAND_ISTYPE_EDGE(strand) ? _Strand_BaseNameDebugInfo : _StrandMany_BaseNameDebugInfo ) ) #define STRAND_DEBUG_GETINFOSIZE( strand ) ( STRAND_TYPE_MIDDLE == (strand)->strandType ? StrandBoth_NumMethods : ( STRAND_ISTYPE_EDGE(strand) ? Strand_NumMethods : StrandMany_NumMethods ) ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUG_GETNAME( strand ) ( NULL != (strand)->debug ? (strand)->debug[0] : STRAND_DEBUG_GETBASEINFO(strand)[0].name ) #else #define STRAND_DEBUG_GETNAME( strand ) "" #endif #if defined(STRAND_ENABLE_DEBUG) #define _STRAND_DEBUG_GETMETHOD( strand, bitIndex0 ) \ ( (bitIndex0) >= Strand_NumBaseMethods && NULL != (strand)->debug && NULL != (strand)->debug[(bitIndex0)-Strand_NumBaseMethods+1] ? \ (strand)->debug[(bitIndex0)-Strand_NumBaseMethods+1] : \ _Strand_BaseNameDebugInfo[(bitIndex0)+2].name ) #define _STRANDBOTH_DEBUG_GETMETHOD( strand, bitIndex0 ) \ ( (bitIndex0) >= StrandBoth_NumBaseMethods && NULL != (strand)->debug && NULL != (strand)->debug[(bitIndex0)-StrandBoth_NumBaseMethods+1] ? \ (strand)->debug[(bitIndex0)-StrandBoth_NumBaseMethods+1] : \ _StrandBoth_BaseNameDebugInfo[(bitIndex0)+2].name ) #define _STRANDMANY_DEBUG_GETMETHOD( strand, bitIndex0 ) \ ( (bitIndex0) >= StrandMany_NumBaseMethods && NULL != (strand)->debug && NULL != (strand)->debug[(bitIndex0)-StrandMany_NumBaseMethods+1] ? \ (strand)->debug[(bitIndex0)-StrandMany_NumBaseMethods+1] : \ _StrandMany_BaseNameDebugInfo[(bitIndex0)+2].name ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUG_GETMETHODINDEX( strand, bitIndex ) \ ( STRAND_TYPE_MIDDLE == (strand)->strandType ? \ _STRANDBOTH_DEBUG_GETMETHOD( strand, (bitIndex)-FirstMethodBit-1 ) : \ ( STRAND_ISTYPE_EDGE(strand) ? \ _STRAND_DEBUG_GETMETHOD( strand, (bitIndex)-FirstMethodBit-1 ) : \ _STRANDMANY_DEBUG_GETMETHOD( strand, (bitIndex)-FirstMethodBit-1 ) ) ) #else #define STRAND_DEBUG_GETMETHODINDEX( strand, bitIndex ) "" #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUG_GETMETHOD( strand, methodBit ) ( (methodBit == 0) ? "EnterStrand" : STRAND_DEBUG_GETMETHODINDEX( strand, GetFirstSetLSB(methodBit) ) ) #else #define STRAND_DEBUG_GETMETHOD( strand, methodBit ) "" #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUG_GETOPERATIONINDEX( strand, bitIndex ) \ ( (bitIndex) >= StrandMany_FirstBitEntryOperationAux && (bitIndex) <= StrandMany_LastBitEntryOperationAux \ && NULL != (strand)->debug && NULL != (strand)->debug[(bitIndex)-StrandMany_FirstBitEntryOperationAux+1] ? \ (strand)->debug[(bitIndex)-StrandMany_FirstBitEntryOperationAux+1] : _StrandMany_BaseEntryOperationDebugInfo[(bitIndex)-1] ) #else #define STRAND_DEBUG_GETOPERATIONINDEX( strand, bitIndex ) "" #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_DEBUG_GETOPERATION( strand, operationMethodBit ) STRAND_DEBUG_GETOPERATIONINDEX( strand, GetFirstSetLSB(operationMethodBit) ) #else #define STRAND_DEBUG_GETOPERATION( strand, operationMethodBit ) "" #endif #if defined(STRAND_ENABLE_DEBUG) #define STRAND_ASSERTEXECUTING( strand ) DEBUG_ASSERT( ( ReadWithFence(&(strand)->stateScheduled) & BitExecuting ) != 0 ) #else #define STRAND_ASSERTEXECUTING( strand ) #endif #if defined(STRAND_ENABLE_DEBUG) void _Strand_AssertOnStrand( _In_ Strand* strand ); #define STRAND_ASSERTONSTRAND( strand ) _Strand_AssertOnStrand( strand ) #else #define STRAND_ASSERTONSTRAND( strand ) #endif #if defined(STRAND_ENABLE_DEBUG) extern void _StrandLogWithName( _In_ Strand* self, _In_ const char * operation ); #define STRAND_LOGWITHNAME( strand, operation ) \ _StrandLogWithName( strand, operation ) #else #define STRAND_LOGWITHNAME( strand, operation ) #endif /* #if defined(STRAND_ENABLE_DEBUG) #define STRAND_VERIFYPROPERLYOPENED( strand ) \ DEBUG_ASSERT( (strand)->info.opened || \ ( STRAND_ISTYPE_EDGE( strand ) && (strand)->currentMethodBit == BitAck ) || \ ( STRAND_ISTYPE_MANY( strand ) && (strand)->currentMethodBit == BitAck_Many ) ) #else #define STRAND_VERIFYPROPERLYOPENED( strand ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ) \ DEBUG_ASSERT( (strand)->base.info.opened || (strand)->base.currentMethodBit == BitAck_Left ) #else #define STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ) \ DEBUG_ASSERT( (strand)->infoRight.opened || (strand)->base.currentMethodBit == BitAck_Right ) #else #define STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ) #endif */ #if defined(STRAND_ENABLE_DEBUG) #define STRAND_VERIFYPROPERLYOPENED( strand ) DEBUG_ASSERT( (strand)->info.opened ) #else #define STRAND_VERIFYPROPERLYOPENED( strand ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ) DEBUG_ASSERT( (strand)->base.info.opened ) #else #define STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ) #endif #if defined(STRAND_ENABLE_DEBUG) #define STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ) DEBUG_ASSERT( (strand)->infoRight.opened ) #else #define STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ) #endif //------------------------------------------------------------------------------------------------------------ // initiates a Post in the other interaction (should be running on the strand) // Can also be used on StrandMany and StrandEntry (but not with StrandBoth) MI_INLINE void Strand_Post( _In_ Strand * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( strand ) || STRAND_ISTYPE_MANY( strand ) ); STRAND_ASSERTONSTRAND( strand ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRAND_VERIFYPROPERLYOPENED( strand ); STRAND_LOGWITHNAME( strand, "Post" ); _Strand_Post_Imp( &strand->info, msg ); STRAND_LOGWITHNAME( strand, "Returning from Post" ); } // initiates a Post in the other left interaction (should be running on the strand) MI_INLINE void StrandBoth_PostLeft( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostLeft" ); _Strand_Post_Imp( &strand->base.info, msg ); STRAND_LOGWITHNAME( &strand->base, "Returning from PostLeft" ); } // initiates a Post in the other right interaction (should be running on the strand) MI_INLINE void StrandBoth_PostRight( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostRight" ); _Strand_Post_Imp( &strand->infoRight, msg ); STRAND_LOGWITHNAME( &strand->base, "Returning from PostRight" ); } // initiates a Post in the other interaction (should be running on the strand) // In this version the ack is relayed automatically to the other side (in a pass-thru fashion) // ( no user Ack will be called ) MI_INLINE void Strand_PostPassthru( _In_ Strand * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( strand ) || STRAND_ISTYPE_MANY( strand ) ); STRAND_ASSERTONSTRAND( strand ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRAND_VERIFYPROPERLYOPENED( strand ); STRAND_LOGWITHNAME( strand, "PostPassthru" ); _Strand_PostPassthru_Imp( &strand->info, msg ); STRAND_LOGWITHNAME( strand, "Returning from PostPassthru" ); } // initiates a Post in the other left interaction (should be running on the strand) // In this version the ack is relayed automatically to the other side (in a pass-thru fashion) // ( no user Ack will be called ) MI_INLINE void StrandBoth_PostPassthruLeft( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostPassthruLeft" ); _Strand_PostPassthru_Imp( &strand->base.info, msg ); STRAND_LOGWITHNAME( &strand->base, "Returning from PostPassthruLeft" ); } // initiates a Post in the other right interaction (should be running on the strand) // In this version the ack is relayed automatically to the other side (in a pass-thru fashion) // ( no user Ack will be called ) MI_INLINE void StrandBoth_PostPassthruRight( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostPassthruRight" ); _Strand_PostPassthru_Imp( &strand->infoRight, msg ); STRAND_LOGWITHNAME( &strand->base, "Returning from PostPassthruRight" ); } // initiates a Post in the other interaction (should be running on the strand) // and leaves the strand before returning // Can also be used on StrandMany and StrandEntry (but not with StrandBoth) MI_INLINE void Strand_PostAndLeaveStrand( _In_ Strand * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( strand ) || STRAND_ISTYPE_MANY( strand ) ); STRAND_ASSERTONSTRAND( strand ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRAND_VERIFYPROPERLYOPENED( strand ); STRAND_LOGWITHNAME( strand, "PostAndLeaveStrand" ); _Strand_PostAndLeaveStrand_Imp( strand, &strand->info, msg ); } // initiates a Post in the other left interaction (should be running on the strand) // and leaves the strand before returning MI_INLINE void StrandBoth_PostLeftAndLeaveStrand( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostLeftAndLeaveStrand" ); _Strand_PostAndLeaveStrand_Imp( &strand->base, &strand->base.info, msg ); } // initiates a Post in the other right interaction (should be running on the strand) // and leaves the strand before returning MI_INLINE void StrandBoth_PostRightAndLeaveStrand( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostRightAndLeaveStrand" ); _Strand_PostAndLeaveStrand_Imp( &strand->base, &strand->infoRight, msg ); } // initiates a PostControl in the other interaction (should be running on the strand) // Can also be used on StrandMany and StrandEntry (but not with StrandBoth) MI_INLINE void Strand_PostControl( _In_ Strand * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( strand ) || STRAND_ISTYPE_MANY( strand ) ); STRAND_ASSERTONSTRAND( strand ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRAND_VERIFYPROPERLYOPENED( strand ); STRAND_LOGWITHNAME( strand, "PostControl" ); _Strand_PostControl_Imp( &strand->info, msg ); STRAND_LOGWITHNAME( strand, "Returning from PostControl" ); } // initiates a PostControl in the other left interaction (should be running on the strand) MI_INLINE void StrandBoth_PostControlLeft( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostControlLeft" ); _Strand_PostControl_Imp( &strand->base.info, msg ); STRAND_LOGWITHNAME( &strand->base, "Returning from PostControlLeft" ); } // initiates a PostControl in the other right interaction (should be running on the strand) MI_INLINE void StrandBoth_PostControlRight( _In_ StrandBoth * strand, _In_ Message * msg) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ); STRAND_LOGWITHNAME( &strand->base, "PostControlRight" ); _Strand_PostControl_Imp( &strand->infoRight, msg ); STRAND_LOGWITHNAME( &strand->base, "Returning from PostControlRight" ); } // initiates an Ack in the other interaction (should be running on the strand) // Can also be used on StrandMany and StrandEntry (but not with StrandBoth) MI_INLINE void Strand_Ack( _In_ Strand * strand) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( strand ) || STRAND_ISTYPE_MANY( strand ) ); STRAND_ASSERTONSTRAND( strand ); STRAND_LOGWITHNAME( strand, "Ack" ); _Strand_Ack_Imp( &strand->info ); STRAND_LOGWITHNAME( strand, "Returning from Ack" ); } // initiates an Ack in the other left interaction (should be running on the strand) MI_INLINE void StrandBoth_AckLeft( _In_ StrandBoth * strand) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); STRAND_LOGWITHNAME( &strand->base, "AckLeft" ); _Strand_Ack_Imp( &strand->base.info ); STRAND_LOGWITHNAME( &strand->base, "Returning from AckLeft" ); } // initiates an Ack in the other right interaction (should be running on the strand) MI_INLINE void StrandBoth_AckRight( _In_ StrandBoth * strand) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); STRAND_LOGWITHNAME( &strand->base, "AckRight" ); _Strand_Ack_Imp( &strand->infoRight ); STRAND_LOGWITHNAME( &strand->base, "Returning from AckRight" ); } // initiates a cancel wherever necessary // ( can be used with any type of strand) void Strand_Cancel( _In_ Strand * self); // initiates a Close in the other interaction (should be running on the strand) // Can also be used on StrandMany and StrandEntry (but not with StrandBoth) MI_INLINE void Strand_Close( _In_ Strand * strand) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( strand ) || STRAND_ISTYPE_MANY( strand ) ); STRAND_ASSERTONSTRAND( strand ); // During ack the opened state is not set until it returns back from user method, so the user can Post while opened is not set yet STRAND_VERIFYPROPERLYOPENED( strand ); STRAND_LOGWITHNAME( strand, "Close" ); _Strand_Close_Imp( &strand->info ); STRAND_LOGWITHNAME( strand, "Returning from Close" ); } // initiates a Close in the other left interaction (should be running on the strand) MI_INLINE void StrandBoth_CloseLeft( _In_ StrandBoth * strand) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Close while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_LEFT( strand ); STRAND_LOGWITHNAME( &strand->base, "CloseLeft" ); _Strand_Close_Imp( &strand->base.info ); STRAND_LOGWITHNAME( &strand->base, "Returning from CloseLeft" ); } // initiates a Close in the other right interaction (should be running on the strand) MI_INLINE void StrandBoth_CloseRight( _In_ StrandBoth * strand) { DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &strand->base ) ); STRAND_ASSERTONSTRAND( &strand->base ); // During ack the opened state is not set until it returns back from user method, so the user can Close while opened is not set yet STRANDBOTH_VERIFYPROPERLYOPENED_RIGHT( strand ); STRAND_LOGWITHNAME( &strand->base, "CloseRight" ); _Strand_Close_Imp( &strand->infoRight ); STRAND_LOGWITHNAME( &strand->base, "Returning from CloseRight" ); } MI_INLINE void Strand_SetDelayFinish( _In_ Strand* self ) { STRAND_ASSERTONSTRAND( self ); self->delayFinish = MI_TRUE; } MI_INLINE void Strand_ResetDelayFinish( _In_ Strand* self ) { STRAND_ASSERTONSTRAND( self ); self->delayFinish = MI_FALSE; } //------------------------------------------------------------------------------------------------------------ // Internal use MI_INLINE void _Strand_StartOpenImp( _In_ InteractionInfo* info, MI_Boolean initialAckPending ) { DEBUG_ASSERT( !info->otherAckPending ); DEBUG_ASSERT( !info->thisAckPending ); info->thisClosedOther = MI_FALSE; info->otherClosedThis = MI_FALSE; info->thisAckPending = initialAckPending; info->otherAckPending = MI_FALSE; info->opened = MI_TRUE; } // Internal use MI_INLINE void _Strand_StartOpenCancelImp( _In_ InteractionInfo* info ) { DEBUG_ASSERT( info->opened ); info->thisAckPending = MI_FALSE; info->opened = MI_FALSE; } // Internal use MI_INLINE void _Strand_OpenPrepareImp( _In_ Strand * self, _In_ InteractionInfo* info, _Out_ InteractionOpenParams* interactionParams, _In_opt_ void* callbackData, // Optional callbackData (usually the object being called back) _In_opt_ Message* msg, // Optional initial message, if not NULL receiver should call Message_Release once interaction is opened/done with the message MI_Boolean leaveStrand ) // If true it will leave the strand once opened { STRAND_ASSERTONSTRAND(self); _Strand_StartOpenImp( info, NULL != msg ); interactionParams->callbackData = callbackData; interactionParams->interaction = &info->interaction; interactionParams->msg = msg; interactionParams->origin = leaveStrand ? self : NULL; } // Internal use MI_INLINE void _Strand_OpenImp( _In_ Strand * self, _In_ InteractionInfo* info, _In_ OpenCallback callback, _In_opt_ void* callbackData, // Optional callbackData (usually the object being called back) _In_opt_ Message* msg, // Optional initial message, if not NULL receiver should call Message_Release once interaction is opened/done with the message MI_Boolean leaveStrand ) // If true it will leave the strand once opened { InteractionOpenParams params; _Strand_OpenPrepareImp(self, info, ¶ms, callbackData, msg, leaveStrand ); (*callback)( ¶ms ); } // Used on Strand/StrandMany/StrandEntry (but not StrandBoth, see below) MI_INLINE void Strand_Open( _In_ Strand * self, _In_ OpenCallback callback, _In_opt_ void* callbackData, // Optional callbackData (usually the object being called back) _In_opt_ Message* msg, // Optional initial message, if not NULL receiver should call Message_Release once interaction is opened/done with the message MI_Boolean leaveStrand ) // If true it will leave the strand once opened { DEBUG_ASSERT( !self->info.ackPassthru ); DEBUG_ASSERT( !STRAND_ISTYPE_MIDDLE( self ) ); _Strand_OpenImp( self, &self->info, callback, callbackData, msg, leaveStrand ); } // Make sure of call Strand_OpenCancel if the open doesnt actually occurs // Used on Strand/StrandMany/StrandEntry (but not StrandBoth, see below) MI_INLINE void Strand_OpenPrepare( _In_ Strand * self, _Out_ InteractionOpenParams* interactionParams, _In_opt_ void* callbackData, // Optional callbackData (usually the object being called back) _In_opt_ Message* msg, // Optional initial message, if not NULL receiver should call Message_Release once interaction is opened/done with the message MI_Boolean leaveStrand ) // If true it will leave the strand once opened { DEBUG_ASSERT( !self->info.ackPassthru ); DEBUG_ASSERT( !STRAND_ISTYPE_MIDDLE( self ) ); _Strand_OpenPrepareImp( self, &self->info, interactionParams, callbackData, msg, leaveStrand ); } // If after call Strand_OpenPrepare the opend didnt happen // Used on Strand/StrandMany/StrandEntry (but not StrandBoth, see below) MI_INLINE void Strand_OpenCancel( _In_ Strand * self ) { STRAND_ASSERTONSTRAND(self); _Strand_StartOpenCancelImp( &self->info ); } // Always open to the right // ackPassthru indicates if the initial Ack from open is just going to be passed thru // or delivered to the Ack user method instead (it will always be passthru if the ack // user method is NULL) MI_INLINE void StrandBoth_Open( _In_ StrandBoth * self, _In_ OpenCallback callback, _In_opt_ void* callbackData, // Optional callbackData (usually the object being called back) _In_opt_ Message* msg, // Optional initial message, if not NULL receiver should call Message_Release once interaction is opened/done with the message MI_Boolean leaveStrand, // If true it will leave the strand once opened MI_Boolean ackPassthru ) { DEBUG_ASSERT( !self->infoRight.ackPassthru ); DEBUG_ASSERT( !ackPassthru || NULL != msg ); // No sense on indicating passthru if there is no message DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &self->base ) ); self->infoRight.ackPassthru = ackPassthru; _Strand_OpenImp( &self->base, &self->infoRight, callback, callbackData, msg, leaveStrand ); } // Make sure of call Strand_OpenCancel if the open doesnt actually occurs // Always open to the right // ackPassthru indicates if the initial Ack from open is just going to be passed thru // or delivered to the Ack user method instead (it will always be passthru if the ack // user method is NULL) MI_INLINE void StrandBoth_OpenPrepare( _In_ StrandBoth * self, _Out_ InteractionOpenParams* interactionParams, _In_opt_ void* callbackData, // Optional callbackData (usually the object being called back) _In_opt_ Message* msg, // Optional initial message, if not NULL receiver should call Message_Release once interaction is opened/done with the message MI_Boolean leaveStrand, // If true it will leave the strand once opened MI_Boolean ackPassthru ) { DEBUG_ASSERT( !self->infoRight.ackPassthru ); DEBUG_ASSERT( !ackPassthru || NULL != msg ); // No sense on indicating passthru if there is no message DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &self->base ) ); self->infoRight.ackPassthru = ackPassthru; _Strand_OpenPrepareImp( &self->base, &self->infoRight, interactionParams, callbackData, msg, leaveStrand ); } // If after call Strand_OpenPrepare the opend didnt happen // Used on Strand/StrandMany/StrandEntry (but not StrandBoth, see below) MI_INLINE void StrandBoth_OpenCancel( _In_ StrandBoth * self ) { STRAND_ASSERTONSTRAND(&self->base); _Strand_StartOpenCancelImp( &self->infoRight ); } // Used on Strand/StrandMany/StrandEntry (but not StrandBoth, see below) MI_INLINE void Strand_StartOpenAsync( _In_ Strand * self ) { STRAND_ASSERTONSTRAND(self); DEBUG_ASSERT( !self->info.ackPassthru ); DEBUG_ASSERT( !STRAND_ISTYPE_MIDDLE( self ) ); _Strand_StartOpenImp( &self->info, MI_FALSE ); // this is different in this case // not opened until Strand_CompleteOpenAsync self->info.opened = MI_FALSE; } // Always open to the right MI_INLINE void StrandBoth_StartOpenAsync( _In_ StrandBoth * self ) { STRAND_ASSERTONSTRAND(&self->base); DEBUG_ASSERT( !self->infoRight.ackPassthru ); DEBUG_ASSERT( STRAND_ISTYPE_MIDDLE( &self->base ) ); _Strand_StartOpenImp( &self->infoRight, MI_FALSE ); // this is different in this case // not opened until StrandBoth_CompleteOpenAsync self->infoRight.opened = MI_FALSE; } // Used on Strand/StrandMany/StrandEntry AND StrandBoth (which always accepts on the left) void Strand_AcceptOpen( _In_ Strand* self, _In_ InteractionOpenParams* params ); // Used on Strand/StrandMany/StrandEntry AND StrandBoth (which always accepts on the left) // However this should only be used if the 'otherStrand' is a Strand/StrandMany/StrandEntry // (use Strand_AcceptOpenAsyncFromStrandBoth if it is StrandBoth) void Strand_AcceptOpenAsync( _In_ Strand* self, _In_ Strand* otherStrand ); // Used on Strand/StrandMany/StrandEntry AND StrandBoth (which always accepts on the left) // However this should only be used if the 'otherStrand' is a StrandBoth void Strand_AcceptOpenAsyncFromStrandBoth( _In_ Strand* self, _In_ StrandBoth* otherStrand ); // Also used on all kind of strands void Strand_FailOpenWithMsg( _In_ InteractionOpenParams* params, _In_opt_ Message* msg ); // Optional failed response message (it will be canceled otherwise). Note that it is NOT released inside // Also used on all kind of strands MI_INLINE void Strand_FailOpen( _In_ InteractionOpenParams* params ) { Strand_FailOpenWithMsg( params, NULL ); } typedef PostResultMsg* (*MakeResultMessageCallback)( _In_ Message* req, _In_opt_ const MI_Instance* err, _In_opt_z_ const MI_Char* errmsg, _In_z_ const MI_Char *errorType, _In_ MI_Uint32 r); // Also used on all kind of strands void Strand_FailOpenWithResult( _In_ InteractionOpenParams* params, MI_Result result, _In_ MakeResultMessageCallback callback); //------------------------------------------------------------------------------------------------------------ // Needed in a StrandBoth if we are opening one side but the other side is in error // and therefore we need to force it closed MI_INLINE void Strand_ForceClose( _In_ InteractionInfo * info ) { DEBUG_ASSERT( !info->otherAckPending ); info->thisClosedOther = MI_TRUE; info->otherClosedThis = MI_TRUE; info->thisAckPending = MI_FALSE; } #ifdef _PREFAST_ #pragma prefast (push) #pragma prefast (disable: 26001) // bogus "we know the interaction points to the middle of the Strand struct" #endif /* _PREFAST_ */ MI_INLINE Strand* Strand_FromInteraction( _In_ Interaction* interaction ) { return FromOffset(Strand,info,interaction); } MI_INLINE StrandBoth* Strand_FromInteractionRight( _In_ Interaction* interaction ) { return FromOffset(StrandBoth,infoRight,interaction); } // For use on StrandMany user methods MI_INLINE StrandMany* StrandMany_FromStrand( _In_ Strand * strand ) { return FromOffset(StrandMany,strand,strand); } // For use on StrandMany user methods MI_INLINE StrandEntry* StrandEntry_FromStrand( _In_ Strand * strand ) { return FromOffset(StrandEntry,strand,strand); } // For use on HashMapHashProc and HashMapEqualProc MI_INLINE StrandEntry* StrandEntry_FromBucket( _In_ HashBucket * bucket ) { return FromOffset(StrandEntry,bucket,bucket); } MI_INLINE const StrandEntry* StrandEntry_FromBucketConst( _In_ const HashBucket * bucket ) { return FromOffsetConst(StrandEntry,bucket,bucket); } //------------------------------------------------------------------------------------------------------------ // note that an internal "iter" variable is used, so this can only be used inside the strand MI_INLINE void StrandMany_BeginIteration( _In_ StrandMany* self ) { STRAND_ASSERTONSTRAND( &self->strand ); HashMap_BeginIteration( &self->many, &self->iter); } MI_INLINE StrandEntry * StrandMany_Iterate( _In_ StrandMany* self ) { HashBucket* bucket; STRAND_ASSERTONSTRAND( &self->strand ); bucket = (HashBucket*)HashMap_Iterate(&self->many, &self->iter); if( NULL == bucket ) return NULL; else return StrandEntry_FromBucket( bucket ); } #ifdef _PREFAST_ #pragma prefast (pop) #endif /* _PREFAST_ */ //------------------------------------------------------------------------------------------------------------ MI_INLINE void _Strand_SchedulePostCommon( _In_ InteractionInfo* info, _In_ Message * msg ) { DEBUG_ASSERT( NULL == info->otherMsg ); Message_AddRef( msg ); // since the actual message use can be delayed info->otherMsg = msg; } // Dont use with a StrandBoth (use StrandBoth_SchedulePostLeft or StrandBoth_SchedulePostRight instead) // (neither with a StrandMany/StrandEntry, see below instead) MI_INLINE void Strand_SchedulePost( _In_ Strand* self, _In_ Message * msg ) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( self ) ); _Strand_SchedulePostCommon( &self->info, msg ); _Strand_Schedule( self, BitPostOther ); } MI_INLINE void StrandBoth_SchedulePostLeft( _In_ StrandBoth* self, _In_ Message * msg ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_SchedulePostCommon( &self->base.info, msg ); _Strand_Schedule( &self->base, BitPostOther_Left ); } MI_INLINE void StrandBoth_SchedulePostRight( _In_ StrandBoth* self, _In_ Message * msg ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_SchedulePostCommon( &self->infoRight, msg ); _Strand_Schedule( &self->base, BitPostOther_Right ); } MI_INLINE void StrandMany_SchedulePost( _In_ StrandMany* self, _In_ Message * msg ) { DEBUG_ASSERT( STRAND_ISTYPE_PARENT(&self->strand) ); _Strand_SchedulePostCommon( &self->strand.info, msg ); _Strand_Schedule( &self->strand, BitPostOther_Many ); } MI_INLINE void StrandEntry_SchedulePost( _In_ StrandEntry* self, _In_ Message * msg ) { DEBUG_ASSERT( STRAND_ISTYPE_ENTRY(&self->strand) ); _Strand_SchedulePostCommon( &self->strand.info, msg ); _Strand_Schedule( &self->strand, BitPostOther_Many ); } // Dont use with a StrandBoth (use StrandBoth_ScheduleAckLeft or StrandBoth_ScheduleAckRight instead) // (neither with a StrandMany/StrandEntry, see below instead) MI_INLINE void Strand_ScheduleAck( _In_ Strand* self ) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( self ) ); _Strand_Schedule( self, BitAckOther ); } MI_INLINE void StrandBoth_ScheduleAckLeft( _In_ StrandBoth* self ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_Schedule( &self->base, BitAckOther_Left ); } MI_INLINE void StrandBoth_ScheduleAckRight( _In_ StrandBoth* self ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_Schedule( &self->base, BitAckOther_Right ); } MI_INLINE void StrandMany_ScheduleAck( _In_ StrandMany* self ) { DEBUG_ASSERT( STRAND_ISTYPE_PARENT(&self->strand) ); _Strand_Schedule( &self->strand, BitAckOther_Many ); } MI_INLINE void StrandEntry_ScheduleAck( _In_ StrandEntry* self ) { DEBUG_ASSERT( STRAND_ISTYPE_ENTRY(&self->strand) ); _Strand_Schedule( &self->strand, BitAckOther_Many ); } // Dont use with a StrandBoth (use StrandBoth_ScheduleCloseLeft or StrandBoth_ScheduleCloseRight instead) // (neither with a StrandMany/StrandEntry, see below instead) MI_INLINE void Strand_ScheduleClose( _In_ Strand* self ) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( self ) ); _Strand_Schedule( self, BitCloseOther ); } MI_INLINE void StrandBoth_ScheduleCloseLeft( _In_ StrandBoth* self ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_Schedule( &self->base, BitCloseOther_Left ); } MI_INLINE void StrandBoth_ScheduleCloseRight( _In_ StrandBoth* self ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_Schedule( &self->base, BitCloseOther_Right ); } MI_INLINE void StrandMany_ScheduleClose( _In_ StrandMany* self ) { DEBUG_ASSERT( STRAND_ISTYPE_PARENT(&self->strand) ); _Strand_Schedule( &self->strand, BitCloseOther_Many ); } MI_INLINE void StrandEntry_ScheduleClose( _In_ StrandEntry* self ) { DEBUG_ASSERT( STRAND_ISTYPE_ENTRY(&self->strand) ); _Strand_Schedule( &self->strand, BitCloseOther_Many ); } // Dont use with a StrandBoth (use StrandBoth_ScheduleCancel instead) // (neither with a StrandMany/StrandEntry, see below instead) MI_INLINE void Strand_ScheduleCancel( _In_ Strand* self ) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( self ) ); _Strand_Schedule( self, BitCancelSelf ); } MI_INLINE void StrandBoth_ScheduleCancel( _In_ StrandBoth* self ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_Schedule( &self->base, BitCancelSelf_Both ); } MI_INLINE void StrandMany_ScheduleCancel( _In_ StrandMany* self ) { DEBUG_ASSERT( STRAND_ISTYPE_PARENT(&self->strand) ); _Strand_Schedule( &self->strand, BitCancelSelf_Many ); } MI_INLINE void StrandEntry_ScheduleCancel( _In_ StrandEntry* self ) { DEBUG_ASSERT( STRAND_ISTYPE_ENTRY(&self->strand) ); _Strand_Schedule( &self->strand, BitCancelSelf_Many ); } //------------------------------------------------------------------------------------------------------------ // Dont use with a StrandBoth/StrandMany/StrandEntry (use StrandBoth_ScheduleAuxLeft, StrandBoth_ScheduleAuxRight, StrandMany_ScheduleAux or StrandEntry_ScheduleAux instead) MI_INLINE void Strand_ScheduleAux( _In_ Strand* self, unsigned int auxMethodNumber ) { DEBUG_ASSERT( STRAND_ISTYPE_EDGE( self ) ); _Strand_Schedule( self, BitAux0 << auxMethodNumber ); } MI_INLINE void StrandBoth_ScheduleAuxLeft( _In_ StrandBoth* self, unsigned int auxMethodNumber ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_Schedule( &self->base, BitAux0_Left << auxMethodNumber ); } MI_INLINE void StrandBoth_ScheduleAuxRight( _In_ StrandBoth* self, unsigned int auxMethodNumber ) { DEBUG_ASSERT( STRAND_TYPE_MIDDLE == self->base.strandType ); _Strand_Schedule( &self->base, BitAux0_Right << auxMethodNumber ); } MI_INLINE void StrandMany_ScheduleAux( _In_ StrandMany* self, unsigned int auxMethodNumber ) { DEBUG_ASSERT( STRAND_ISTYPE_PARENT( &self->strand ) ); _Strand_Schedule( &self->strand, BitAux0_Many << auxMethodNumber ); } MI_INLINE void StrandEntry_ScheduleAux( _In_ StrandEntry* self, unsigned int auxMethodNumber ) { DEBUG_ASSERT( STRAND_TYPE_ENTRY == self->strand.strandType ); _Strand_Schedule( &self->strand, BitAux0_Many << auxMethodNumber ); } //------------------------------------------------------------------------------------------------------------ // Call these ONLY from Parent strand MI_INLINE void StrandMany_CancelEntry( _In_ StrandEntry* entry ) { STRAND_ASSERTONSTRAND( &entry->parent->strand ); _Strand_Schedule( &entry->strand, BitCancelInternal ); } MI_INLINE void StrandMany_CloseEntry( _In_ StrandEntry* entry ) { STRAND_ASSERTONSTRAND( &entry->parent->strand ); _Strand_Schedule( &entry->strand, BitCloseInternal ); } MI_INLINE void StrandMany_AckEntry( _In_ StrandEntry* entry ) { STRAND_ASSERTONSTRAND( &entry->parent->strand ); _Strand_Schedule( &entry->strand, BitAckInternal ); } void StrandMany_CancelAllEntries( _In_ StrandMany* self ); void StrandMany_CloseAllEntries( _In_ StrandMany* self ); void StrandMany_PostEntry( _In_ StrandEntry* entry, _In_ Message* msg ); void StrandMany_PostAll( _In_ StrandMany* self, _In_ Message* msg ); MI_Boolean StrandMany_PostFindEntry( _In_ StrandMany* self, _In_ Message* msg ); void StrandMany_PostControlEntry( _In_ StrandEntry* entry, _In_ Message* msg ); void StrandMany_PostControlAll( _In_ StrandMany* self, _In_ Message* msg ); MI_Boolean StrandMany_PostControlFindEntry( _In_ StrandMany* self, _In_ Message* msg ); // internal use only void _StrandEntry_ScheduleParent( _In_ StrandEntry* self, EntryOperationMaskType entryOperationBit ); // Call these ONLY from a Entry strand MI_INLINE void StrandEntry_CancelParent( _In_ StrandEntry* self ) { STRAND_ASSERTONSTRAND( &self->strand ); _StrandEntry_ScheduleParent( self, BitEntryCancel ); } MI_INLINE void StrandEntry_CloseParent( _In_ StrandEntry* self ) { STRAND_ASSERTONSTRAND( &self->strand ); _StrandEntry_ScheduleParent( self, BitEntryClose ); } void StrandEntry_PostParent( _In_ StrandEntry* self, _In_ Message* msg ); // Use this if the msg comes from the interaction // Takes care of delivering Ack on the interaction once Parent has actually // being able to Post (manages flow control internally) void StrandEntry_PostParentPassthru( _In_ StrandEntry* self, _In_ Message* msg ); void StrandEntry_PostControlParent( _In_ StrandEntry* self, _In_ Message* msg ); MI_INLINE void StrandEntry_ScheduleAuxParent( _In_ StrandEntry* self, unsigned int auxMethodNumber ) { STRAND_ASSERTONSTRAND( &self->strand ); DEBUG_ASSERT( (BitEntryAux0 << auxMethodNumber) < BitEntryDeleted ); _StrandEntry_ScheduleParent( self, BitEntryAux0 << auxMethodNumber ); } //------------------------------------------------------------------------------------------------------------ // Timer methods and types // // Note: These were moved from a separate header file to resolve the circular // dependency caused by Strand using Timer and Timer functions taking a // Strand as an argument. typedef enum _TimerResult { TimerResult_Success = 0, TimerResult_Failed = 1, TimerResult_InvalidArgument = 2 } TimerResult; /* * Specifies a single use timer that will timeout after a specified amount * of time. The timer may be triggered early through manual intervention. */ struct _Timer { /* The timeout value to use, specified in microseconds. */ MI_Uint64 timeoutInUsec; /* Specifies the reason of calling the timeout callback: expired, manually fired, etc. */ TimerReason reason; /* Internal OS-Specific data */ #if defined(CONFIG_OS_WINDOWS) PTP_TIMER pTimer; #endif #if defined(CONFIG_POSIX) Handler handler; Selector* selector; #endif /* End OS-Specific data */ }; /* * Doesn't start the actual timer or extend an already scheduled Timer. * It just sets the Timer's specified timeout period. This should be set * prior to the first call to Timer_Start. * * Updates to its value will take effect the next time Timer_Start is called. */ MI_INLINE void Timer_SetTime( _In_ Timer* timer, _In_ MI_Uint64 timeUsec ) { DEBUG_ASSERT( timer ); timer->timeoutInUsec = timeUsec; } /* * Starts an initialized Timer with a previously specified timeout value. * Also used to restart a timer after completion. */ TimerResult Timer_Start( _In_ Timer* timer, _In_ Strand* strand ); /* * Manually force early execution of a Timer. */ void Timer_Fire( _In_ Timer* timer, _In_ Strand* strand, TimerReason reason ); /* * Cleans up a Timer from a previous run, but leaves it ready for re-use via * Timer_Start(). * * Note: This is designed to be used by a Strand and not to be used via * independent calls. */ void Timer_Close( _In_ Timer* timer ); /* * Sets a global internal selector to avoid requiring it to be passed in via * Timer_Init(). */ void Timer_SetSelector( _In_ Selector* selector ); /* * Timer manipulation functions for Strands */ void Strand_StartTimer( _In_ Strand* self, _In_ Timer* timer, _In_ MI_Uint64 timeusecs ); void Strand_FireTimer( _In_ Strand* self ); MI_Boolean Strand_HaveTimer( _In_ Strand* self ); // Internal Use MI_INLINE MI_Boolean _Strand_ShouldFinish( _In_ Strand* self ) { MI_Boolean shouldFinish = (*self->strandMethods[0])(self); if( shouldFinish && NULL != self->timer ) { if( (self->flags & STRAND_FLAG_FINISHWAITSFORTIMER ) == 0 ) { // Canceled reason overwrites any other reason Timer_Fire( self->timer, self, TimerReason_Canceled ); } shouldFinish = MI_FALSE; // In any case we cannot finish until timer is not set anymore } return shouldFinish; } // Internal Use // This is not needed if everything is called inside the strand (for UT use) // If close/ack are called outside the strand then call this afterwards // ( can be used with any type of strand ) MI_INLINE void _Strand_CheckDelete( _In_ Strand* self ) { STRAND_ASSERTONSTRAND( self ); if( (*self->strandMethods[0])(self) ) { _Strand_Finish( self ); } } /* **============================================================================== */ END_EXTERNC #endif /* _Strand_h */