|
|
#include "precomp.h"
//
// OM.CPP
// Object Manager
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_OM
//
// Function profile ID <--> name mapping
//
typedef struct tagOMFP_MAP { char szName[16]; } OMFP_MAP;
const OMFP_MAP c_aFpMap[OMFP_MAX] = { { AL_FP_NAME }, { OM_FP_NAME }, { WB_FP_NAME } };
//
// Workset Group ID <--> name mapping
//
typedef struct tagOMWSG_MAP { char szName[16]; } OMWSG_MAP;
const OMWSG_MAP c_aWsgMap[OMWSG_MAX] = { { OMC_WSG_NAME }, { AL_WSG_NAME }, { WB_WSG_NAME } };
//
// OMP_Init()
//
BOOL OMP_Init(BOOL * pfCleanup) { BOOL fInit = FALSE;
DebugEntry(OMP_Init);
UT_Lock(UTLOCK_OM);
//
// Register the OM service
//
if (g_putOM || g_pomPrimary) { *pfCleanup = FALSE; ERROR_OUT(("Can't start OM primary task; already running")); DC_QUIT; }
*pfCleanup = TRUE;
if (!UT_InitTask(UTTASK_OM, &g_putOM)) { ERROR_OUT(("Failed to start OM task")); DC_QUIT; }
g_pomPrimary = (POM_PRIMARY)UT_MallocRefCount(sizeof(OM_PRIMARY), TRUE); if (!g_pomPrimary) { ERROR_OUT(("Failed to allocate OM memory block")); DC_QUIT; }
SET_STAMP(g_pomPrimary, OPRIMARY); g_pomPrimary->putTask = g_putOM; g_pomPrimary->correlator = 1;
COM_BasedListInit(&(g_pomPrimary->domains));
UT_RegisterExit(g_putOM, OMPExitProc, g_pomPrimary); g_pomPrimary->exitProcReg = TRUE;
UT_RegisterEvent(g_putOM, OMPEventsHandler, g_pomPrimary, UT_PRIORITY_NORMAL); g_pomPrimary->eventProcReg = TRUE;
if (!MG_Register(MGTASK_OM, &(g_pomPrimary->pmgClient), g_putOM)) { ERROR_OUT(("Couldn't register OM with the MG layer")); DC_QUIT; }
if (!CMS_Register(g_putOM, CMTASK_OM, &(g_pomPrimary->pcmClient))) { ERROR_OUT(("Couldn't register OM as call secondary")); DC_QUIT; }
//
// Allocate our GDC buffer.
//
g_pomPrimary->pgdcWorkBuf = new BYTE[GDC_WORKBUF_SIZE]; if (!g_pomPrimary->pgdcWorkBuf) { ERROR_OUT(("SendMessagePkt: can't allocate GDC work buf, not compressing")); DC_QUIT; }
fInit = TRUE;
DC_EXIT_POINT: UT_Unlock(UTLOCK_OM);
DebugExitBOOL(OMP_Init, fInit); return(fInit); }
//
// OMP_Term()
//
void OMP_Term(void) { DebugEntry(OMP_Term);
UT_Lock(UTLOCK_OM);
if (g_pomPrimary) { ValidateOMP(g_pomPrimary);
//
// Deregister from Call Manager
//
if (g_pomPrimary->pcmClient) { CMS_Deregister(&g_pomPrimary->pcmClient); }
//
// Deregister from MG
//
if (g_pomPrimary->pmgClient) { MG_Deregister(&g_pomPrimary->pmgClient); }
OMPExitProc(g_pomPrimary); }
UT_TermTask(&g_putOM);
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OMP_Term); }
//
// OMPExitProc()
//
void CALLBACK OMPExitProc(LPVOID uData) { POM_PRIMARY pomPrimary = (POM_PRIMARY)uData; POM_DOMAIN pDomain; POM_WSGROUP pWSGroup; POM_CLIENT_LIST pClient;
DebugEntry(OMPExitProc);
UT_Lock(UTLOCK_OM);
ValidateOMP(pomPrimary); ASSERT(pomPrimary == g_pomPrimary);
if (pomPrimary->exitProcReg) { UT_DeregisterExit(pomPrimary->putTask, OMPExitProc, pomPrimary); pomPrimary->exitProcReg = FALSE; }
if (pomPrimary->eventProcReg) { UT_DeregisterEvent(pomPrimary->putTask, OMPEventsHandler, pomPrimary); pomPrimary->eventProcReg = FALSE; }
//
// Free domains
//
while (pDomain = (POM_DOMAIN)COM_BasedListFirst(&(pomPrimary->domains), FIELD_OFFSET(OM_DOMAIN, chain))) { TRACE_OUT(("OMPExitProc: Freeing domain 0x%08x call ID 0x%08x", pDomain, pDomain->callID));
//
// Free workset groups
// NOTE:
// WSGDiscard() may destroy the domain, hence the weird
// loop
//
if (pWSGroup = (POM_WSGROUP)COM_BasedListFirst(&(pDomain->wsGroups), FIELD_OFFSET(OM_WSGROUP, chain))) { TRACE_OUT(("OMPExitProc: Freeing wsg 0x%08x domain 0x%08x", pWSGroup, pDomain));
//
// Free clients
//
while (pClient = (POM_CLIENT_LIST)COM_BasedListFirst(&(pWSGroup->clients), FIELD_OFFSET(OM_CLIENT_LIST, chain))) { TRACE_OUT(("OMPExitProc: Freeing client 0x%08x wsg 0x%08x", pClient, pWSGroup));
COM_BasedListRemove(&(pClient->chain)); UT_FreeRefCount((void**)&pClient, FALSE); }
WSGDiscard(pomPrimary, pDomain, pWSGroup, TRUE); } else { FreeDomainRecord(&pDomain); } }
if (pomPrimary->pgdcWorkBuf) { delete[] pomPrimary->pgdcWorkBuf; pomPrimary->pgdcWorkBuf = NULL; }
UT_FreeRefCount((void**)&g_pomPrimary, TRUE);
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OMPExitProc); }
//
// OMPEventsHandler(...)
//
BOOL CALLBACK OMPEventsHandler ( LPVOID uData, UINT event, UINT_PTR param1, UINT_PTR param2 ) { POM_PRIMARY pomPrimary = (POM_PRIMARY)uData; POM_DOMAIN pDomain = NULL; BOOL fProcessed = TRUE;
DebugEntry(OMPEventsHandler);
UT_Lock(UTLOCK_OM);
ValidateOMP(pomPrimary);
//
// Check event is in the range we deal with:
//
if ((event < CM_BASE_EVENT) || (event > CM_LAST_EVENT)) { goto CHECK_OM_EVENTS; }
switch (event) { case CMS_NEW_CALL: {
TRACE_OUT(( "CMS_NEW_CALL"));
//
// We ignore the return code - it will have been handled lower
// down.
//
DomainRecordFindOrCreate(pomPrimary, (UINT)param2, &pDomain); } break;
case CMS_END_CALL: { TRACE_OUT(( "CMS_END_CALL"));
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), (DWORD)param2, FIELD_SIZE(OM_DOMAIN, callID));
if (pDomain == NULL) { //
// We don't have a record for this Domain so either we
// never attached or we've already detached. Do nothing.
//
TRACE_OUT(( "No record for Domain %u found", param2)); } else { ProcessOwnDetach(pomPrimary, pDomain); } } break;
case CMS_TOKEN_ASSIGN_CONFIRM: { TRACE_OUT(( "CMS_TOKEN_ASSIGN_CONFIRM")); //
// There is a flaw in the CMS_ASSIGN_TOKEN_CONFIRM API in that
// it does not tell us which domain it refers to. So, we
// operate under the assumption that this event relates to the
// most recent domain we created i.e. the first one in the
// list (they go in at the beginning).
//
pDomain = (POM_DOMAIN)COM_BasedListFirst(&(pomPrimary->domains), FIELD_OFFSET(OM_DOMAIN, chain));
if (pDomain != NULL) { ProcessCMSTokenAssign(pomPrimary, pDomain, (param1 != 0), LOWORD(param2)); } else { WARNING_OUT(( "No domain found for CMS_TOKEN_ASSIGN_CONFIRM")); } } break; }
TRACE_OUT(( "Processed Call Manager event %#x", event)); DC_QUIT;
CHECK_OM_EVENTS:
//
// Check event is in the range we deal with:
//
if ((event < OM_BASE_EVENT) || (event > OM_LAST_EVENT)) { goto CHECK_NET_EVENTS; }
switch (event) { case OMINT_EVENT_LOCK_TIMEOUT: { ProcessLockTimeout(pomPrimary, (UINT)param1, (UINT)param2); } break;
case OMINT_EVENT_SEND_QUEUE: { //
// Param2 is the domain record.
//
pDomain = (POM_DOMAIN)param2; ProcessSendQueue(pomPrimary, pDomain, TRUE); } break;
case OMINT_EVENT_PROCESS_MESSAGE: { ProcessBouncedMessages(pomPrimary, (POM_DOMAIN) param2); } break;
case OMINT_EVENT_WSGROUP_DISCARD: { ProcessWSGDiscard(pomPrimary, (POM_WSGROUP)param2); } break;
case OMINT_EVENT_WSGROUP_MOVE: case OMINT_EVENT_WSGROUP_REGISTER: { ProcessWSGRegister(pomPrimary, (POM_WSGROUP_REG_CB)param2); } break;
case OMINT_EVENT_WSGROUP_REGISTER_CONT: { WSGRegisterStage1(pomPrimary, (POM_WSGROUP_REG_CB) param2); } break;
//
// The remaining events are ones we get by virtue of being
// considered as a client of the ObManControl workset group
//
case OM_WORKSET_LOCK_CON: { switch (((POM_EVENT_DATA16)¶m1)->worksetID) { case OM_INFO_WORKSET: ProcessOMCLockConfirm(pomPrimary, ((POM_EVENT_DATA32) ¶m2)->correlator, ((POM_EVENT_DATA32) ¶m2)->result); break;
case OM_CHECKPOINT_WORKSET: ProcessCheckpoint(pomPrimary, ((POM_EVENT_DATA32) ¶m2)->correlator, ((POM_EVENT_DATA32) ¶m2)->result); break; } } break;
case OM_WORKSET_NEW_IND: { ProcessOMCWorksetNew(pomPrimary, ((POM_EVENT_DATA16) ¶m1)->hWSGroup, ((POM_EVENT_DATA16) ¶m1)->worksetID); } break;
case OM_PERSON_JOINED_IND: case OM_PERSON_LEFT_IND: case OM_PERSON_DATA_CHANGED_IND: case OM_WSGROUP_MOVE_IND: case OM_WORKSET_UNLOCK_IND: { //
// We ignore these events.
//
} break;
case OM_OBJECT_ADD_IND: case OM_OBJECT_REPLACED_IND: case OM_OBJECT_UPDATED_IND: case OM_OBJECT_DELETED_IND: { ProcessOMCObjectEvents(pomPrimary, event, ((POM_EVENT_DATA16) ¶m1)->hWSGroup, ((POM_EVENT_DATA16) ¶m1)->worksetID, (POM_OBJECT) param2); } break;
default: { ERROR_OUT(( "Unexpected ObMan event 0x%08x", event)); } }
TRACE_OUT(( "Processed ObMan event %x", event)); DC_QUIT;
CHECK_NET_EVENTS:
//
// This function is only for network layer events so we quit if we've
// got something else:
//
if ((event < NET_BASE_EVENT) || (event > NET_LAST_EVENT)) { fProcessed = FALSE; DC_QUIT; }
//
// Now switch on the event type:
//
switch (event) { case NET_EVENT_USER_ATTACH: { //
// Find the domain data for this call
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), param2, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { ProcessNetAttachUser(pomPrimary, pDomain, LOWORD(param1), HIWORD(param1)); } break; }
case NET_EVENT_USER_DETACH: { //
// Find the domain data for this call
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), param2, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { ProcessNetDetachUser(pomPrimary, pDomain, LOWORD(param1)); } break; }
case NET_EVENT_CHANNEL_LEAVE: { //
// Find the domain data for this call
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), param2, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { ProcessNetLeaveChannel(pomPrimary, pDomain, LOWORD(param1)); } break; }
case NET_EVENT_TOKEN_GRAB: { //
// Find the domain data for this call
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), param2, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { ProcessNetTokenGrab(pomPrimary, pDomain, LOWORD(param1)); } break; }
case NET_EVENT_TOKEN_INHIBIT: { //
// Find the domain data for this call
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), param2, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { ProcessNetTokenInhibit(pomPrimary, pDomain, LOWORD(param1)); } break; }
case NET_EVENT_CHANNEL_JOIN: { PNET_JOIN_CNF_EVENT pEvent = (PNET_JOIN_CNF_EVENT)param2;
//
// Find the domain data for this call
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), pEvent->callID, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { ProcessNetJoinChannel(pomPrimary, pDomain, pEvent); }
MG_FreeBuffer(pomPrimary->pmgClient, (void **)&pEvent); break; }
case NET_EVENT_DATA_RECEIVED: { PNET_SEND_IND_EVENT pEvent = (PNET_SEND_IND_EVENT)param2;
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), pEvent->callID, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { ProcessNetData(pomPrimary, pDomain, pEvent); }
MG_FreeBuffer(pomPrimary->pmgClient, (void**)&pEvent); break; }
case NET_FEEDBACK: { //
// A NET_FEEDBACK event includes the pmgUser which identifies
// the send pool from which the buffer has been freed. We use
// it to find the Domain:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), (DWORD)param2, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain) { //
// Generating a FEEDBACK event doesn't cause the use count
// of the Domain record to be bumped, so set the
// <domainRecBumped> flag to FALSE on the call to
// ProcessSendQueue:
//
ProcessSendQueue(pomPrimary, pDomain, FALSE); }
break; }
case NET_FLOW: { ERROR_OUT(("OMPEventsHandler received NET_FLOW; shouldn't have")); break; } }
DC_EXIT_POINT: UT_Unlock(UTLOCK_OM);
DebugExitBOOL(OMPEventsHandler, fProcessed); return(fProcessed); }
//
// DomainRecordFindOrCreate(...)
//
UINT DomainRecordFindOrCreate ( POM_PRIMARY pomPrimary, UINT callID, POM_DOMAIN * ppDomain ) { POM_DOMAIN pDomain; UINT rc = 0;
DebugEntry(DomainRecordFindOrCreate);
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), (DWORD)callID, FIELD_SIZE(OM_DOMAIN, callID)); if (pDomain == NULL) { //
// We don't have a record for this Domain so create one:
//
rc = DomainAttach(pomPrimary, callID, &pDomain); if (rc != 0) { DC_QUIT; } }
*ppDomain = pDomain;
DC_EXIT_POINT: DebugExitDWORD(DomainRecordFindOrCreate, rc); return(rc);
}
//
// DomainAttach(...)
//
UINT DomainAttach ( POM_PRIMARY pomPrimary, UINT callID, POM_DOMAIN * ppDomain ) { POM_DOMAIN pDomain = NULL; NET_FLOW_CONTROL netFlow; UINT rc = 0;
DebugEntry(DomainAttach);
TRACE_OUT(( "Attaching to Domain 0x%08x...", callID));
if (callID != OM_NO_CALL) { CM_STATUS status;
CMS_GetStatus(&status); if (!(status.attendeePermissions & NM_PERMIT_USEOLDWBATALL)) { WARNING_OUT(("Joining Meeting with no OLDWB OM at all")); rc = NET_RC_MGC_NOT_CONNECTED; DC_QUIT; } }
//
// This function does the following:
//
// - create a new Domain record
//
// - if the Domain is our local Domain (OM_NO_CALL) call
// ObManControlInit
//
// - else call MG_AttachUser to start attaching to the Domain.
//
rc = NewDomainRecord(pomPrimary, callID, &pDomain); if (rc != 0) { DC_QUIT; }
//
// What we do now depends on whether this is our "local" Domain (i.e.
// callID == OM_NO_CALL):
//
if (callID == OM_NO_CALL) { TRACE_OUT(( "Is local domain - skipping forward"));
//
// This is our "local" Domain, so don't call MG_AttachUser.
// Instead, we fake up a successful token grab event and rejoin the
// domain attach processing there:
//
TRACE_OUT(( "Faking successful token grab for local domain")); pDomain->state = PENDING_TOKEN_GRAB; rc = ProcessNetTokenGrab(pomPrimary, pDomain, NET_RESULT_OK); if (rc != 0) { DC_QUIT; } } else { TRACE_OUT(( "Is real domain - attaching"));
//
// Set up our target latencies. Don't bother restricting the max
// stream sizes.
//
ZeroMemory(&netFlow, sizeof(netFlow));
netFlow.latency[NET_TOP_PRIORITY] = 0; netFlow.latency[NET_HIGH_PRIORITY] = 2000L; netFlow.latency[NET_MEDIUM_PRIORITY] = 5000L; netFlow.latency[NET_LOW_PRIORITY] = 10000L;
rc = MG_Attach(pomPrimary->pmgClient, callID, &netFlow); if (rc != 0) { DC_QUIT; }
//
// Set up the remaining fields of the Domain record:
//
pDomain->state = PENDING_ATTACH;
//
// The <userID> field is set when the NET_ATTACH event arrives.
//
//
// The next stage in the Domain attach process is when the
// NET_ATTACH event arrives. This will cause the
// ProcessNetAttachUser function to be called.
//
}
//
// Finally, set caller's pointer:
//
*ppDomain = pDomain;
DC_EXIT_POINT:
if (rc != 0) { //
// Do not trace an error if we get NOT_CONNECTED - it is a valid
// race condition (but we still must do the cleanup below).
//
if (rc != NET_RC_MGC_NOT_CONNECTED) { // lonchanc: rc=0x706 can happen here, bug #942.
// this was ERROR_OUT
WARNING_OUT(( "Error %d attaching to Domain %u", rc, callID)); }
if (pDomain != NULL) { ProcessOwnDetach(pomPrimary, pDomain); } }
DebugExitDWORD(DomainAttach, rc); return(rc);
}
//
// DomainDetach(...)
//
void DomainDetach ( POM_PRIMARY pomPrimary, POM_DOMAIN * ppDomain, BOOL fExit ) { POM_DOMAIN pDomain;
DebugEntry(DomainDetach);
ASSERT(ppDomain != NULL);
pDomain = *ppDomain;
//
// This function does all the network cleanup required, then calls on
// to discard the ObMan memory etc associated with the domain. Note
// that we don't bother releasing tokens, leaving channels, etc since
// the network layer will do this for us automatically.
//
if (!fExit && (pDomain->callID != OM_NO_CALL) && (pDomain->state >= PENDING_ATTACH)) { MG_Detach(pomPrimary->pmgClient); }
TRACE_OUT(( "Detached from Domain %u", pDomain->callID));
FreeDomainRecord(ppDomain);
DebugExitVOID(DomainDetach); }
//
// NewDomainRecord(...)
//
UINT NewDomainRecord ( POM_PRIMARY pomPrimary, UINT callID, POM_DOMAIN* ppDomain ) { POM_WSGROUP pOMCWSGroup = NULL; POM_DOMAIN pDomain; BOOL noCompression; BOOL inserted = FALSE; UINT rc = 0;
DebugEntry(NewDomainRecord);
//
// Allocate Domain record:
//
pDomain = (POM_DOMAIN)UT_MallocRefCount(sizeof(OM_DOMAIN), TRUE); if (!pDomain) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pDomain, DOMAIN);
//
// Fill in the fields:
//
pDomain->callID = callID; pDomain->valid = TRUE;
//
// Set up our maximum compression caps. They are subsequently
// negotiated as follows:
//
// - if there are any other nodes out there, we will negotiate down
// when we receive a WELCOME message from one of them
//
// - if any other nodes join subsequently, we will negotiate down when
// we receive their HELLO message.
//
COM_ReadProfInt(DBG_INI_SECTION_NAME, OM_INI_NOCOMPRESSION, FALSE, &noCompression); if (noCompression) { WARNING_OUT(("NewDomainRecord: compression off")); pDomain->compressionCaps = OM_CAPS_NO_COMPRESSION; } else { pDomain->compressionCaps = OM_CAPS_PKW_COMPRESSION; }
//
// This will be ObMan's workset group handle for the ObManControl
// workset group in this domain. Since we know that domain handles are
// only ever -1 or 0, we just cast the domain handle down to 8 bits to
// give the hWSGroup. If the way domain handles are allocated changes,
// will need to do something cleverer here.
//
pDomain->omchWSGroup = (BYTE) callID;
COM_BasedListInit(&(pDomain->wsGroups)); COM_BasedListInit(&(pDomain->pendingRegs)); COM_BasedListInit(&(pDomain->pendingLocks)); COM_BasedListInit(&(pDomain->receiveList)); COM_BasedListInit(&(pDomain->bounceList)); COM_BasedListInit(&(pDomain->helperCBs)); COM_BasedListInit(&(pDomain->sendQueue[ NET_TOP_PRIORITY ])); COM_BasedListInit(&(pDomain->sendQueue[ NET_HIGH_PRIORITY ])); COM_BasedListInit(&(pDomain->sendQueue[ NET_MEDIUM_PRIORITY ])); COM_BasedListInit(&(pDomain->sendQueue[ NET_LOW_PRIORITY ]));
//
// Insert the record for this new Domain in the list hung off the root
// data structure:
//
TRACE_OUT((" Inserting record for Domain %u in global list", callID));
COM_BasedListInsertAfter(&(pomPrimary->domains), &(pDomain->chain)); inserted = TRUE;
//
// Here we create a record for the ObManControl workset group and cause
// it to be inserted in the list hung off the Domain record:
//
// Note that this does not involve sending any data; it merely creates
// the record locally.
//
rc = WSGRecordCreate(pomPrimary, pDomain, OMWSG_OM, OMFP_OM, &pOMCWSGroup); if (rc != 0) { DC_QUIT; }
//
// Create a single, empty workset (this function broadcasts the
// creation throughout the Domain):
//
rc = WorksetCreate(pomPrimary->putTask, pOMCWSGroup, OM_INFO_WORKSET, FALSE, NET_TOP_PRIORITY); if (rc != 0) { DC_QUIT; }
//
// Fill in the fixed workset group ID (normally, we would call
// WSGGetNewID to allocate an unused one).
//
pOMCWSGroup->wsGroupID = WSGROUPID_OMC;
//
// We fill in the channel ID when we get the result from JoinByKey
//
//
// Add ObMan's putTask to the workset group's client list, so it will
// get events posted to it.
//
rc = AddClientToWSGList(pomPrimary->putTask, pOMCWSGroup, pDomain->omchWSGroup, PRIMARY); if (rc != 0) { DC_QUIT; }
*ppDomain = pDomain;
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d creating record for domain %u", callID)); if (pOMCWSGroup != NULL) { COM_BasedListRemove(&(pOMCWSGroup->chain)); UT_FreeRefCount((void**)&pOMCWSGroup, FALSE); }
if (inserted) { COM_BasedListRemove(&(pDomain->chain)); }
if (pDomain != NULL) { UT_FreeRefCount((void**)&pDomain, FALSE); } }
DebugExitDWORD(NewDomainRecord, rc); return(rc); }
//
// FreeDomainRecord(...)
//
void FreeDomainRecord ( POM_DOMAIN * ppDomain ) { POM_DOMAIN pDomain; NET_PRIORITY priority; POM_SEND_INST pSendInst;
DebugEntry(FreeDomainRecord);
//
// This function
//
// - frees any outstanding send requests (and their associated CBs)
//
// - invalidates, removes from the global list and frees the Domain
// record.
//
pDomain = *ppDomain;
//
// Free all the send instructions queued in the domain:
//
for (priority = NET_TOP_PRIORITY;priority <= NET_LOW_PRIORITY;priority++) { for (; ; ) { pSendInst = (POM_SEND_INST)COM_BasedListFirst(&(pDomain->sendQueue[priority]), FIELD_OFFSET(OM_SEND_INST, chain));
if (pSendInst == NULL) { break; }
TRACE_OUT(( "Freeing send instruction at priority %u", priority)); FreeSendInst(pSendInst); } }
pDomain->valid = FALSE;
COM_BasedListRemove(&(pDomain->chain)); UT_FreeRefCount((void**)ppDomain, FALSE);
DebugExitVOID(FreeDomainRecord); }
//
// ProcessNetAttachUser(...)
//
void ProcessNetAttachUser ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_UID userId, NET_RESULT result ) { NET_CHANNEL_ID channelCorrelator; UINT rc = 0;
DebugEntry(ProcessNetAttachUser);
TRACE_OUT(( "Got NET_ATTACH for Domain %u (userID: %hu, result: %hu)", pDomain->callID, userId, result));
//
// Check that this Domain is in the pending attach state:
//
if (pDomain->state != PENDING_ATTACH) { WARNING_OUT(( "Unexpected NET_ATTACH - Domain %u is in state %hu)", pDomain->callID, pDomain->state)); DC_QUIT; }
//
// If we failed to attach, set the retCode so we tidy up below:
//
if (result != NET_RESULT_OK) { ERROR_OUT(( "Failed to attach to Domain %u; cleaning up...", pDomain->callID));
rc = result; DC_QUIT; }
//
// Otherwise, record our user ID for this Domain and then join our user
// ID channel:
//
pDomain->userID = userId;
TRACE_OUT(("Asking to join own channel %hu", pDomain->userID));
rc = MG_ChannelJoin(pomPrimary->pmgClient, &channelCorrelator, pDomain->userID); if (rc != 0) { DC_QUIT; }
//
// Set the Domain <state>:
//
pDomain->state = PENDING_JOIN_OWN;
//
// The next step in the Domain attach process happens when the NET_JOIN
// event arrives for the channel we've just joined. This event causes
// the ProcessNetJoinChannel function to be called.
//
DC_EXIT_POINT:
if (rc != 0) { WARNING_OUT(("Error %d joining own user channel %hu", rc, pDomain->userID));
ProcessOwnDetach(pomPrimary, pDomain); }
DebugExitVOID(ProcessNetAttachUser);
}
//
// ProcessNetJoinChannel(...)
//
void ProcessNetJoinChannel ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, PNET_JOIN_CNF_EVENT pNetJoinCnf ) { POM_WSGROUP pOMCWSGroup; NET_CHANNEL_ID channelCorrelator; POM_WSGROUP_REG_CB pRegistrationCB = NULL; BOOL success = TRUE;
DebugEntry(ProcessNetJoinChannel);
TRACE_OUT(( "JOIN_CON - channel %hu - result %hu", pNetJoinCnf->channel, pNetJoinCnf->result));
switch (pDomain->state) { case PENDING_JOIN_OWN: { //
// This event is in response to us trying to join our own user
// channel, as part of the mutli-stage Domain attach process.
// The next step is to join the ObManControl channel.
//
//
// First check that the join was successful:
//
if (pNetJoinCnf->result != NET_RESULT_OK) { ERROR_OUT(("Failed to join own user ID channel (reason: %hu)", pNetJoinCnf->result)); success = FALSE; DC_QUIT; }
//
// Verify that this is a join event for the correct channel
//
ASSERT(pNetJoinCnf->channel == pDomain->userID);
//
// The next step in the process of attaching to a Domain is to
// join the ObManControl channel; we set the state accordingly:
//
TRACE_OUT(( "Asking to join ObManControl channel using key"));
if (MG_ChannelJoinByKey(pomPrimary->pmgClient, &channelCorrelator, GCC_OBMAN_CHANNEL_KEY) != 0) { success = FALSE; DC_QUIT; }
pDomain->state = PENDING_JOIN_OMC;
//
// The next stage in the Domain attach process happens when the
// NET_JOIN event arrives for the ObManControl channel. This
// will cause this function to be executed again, but this time
// the next case statement will be executed.
//
} break;
case PENDING_JOIN_OMC: { //
// This event is in response to us trying to join the
// ObManControl workset group channel, as part of the
// multi-stage Domain attach process.
//
//
// Check that the join was successful:
//
if (pNetJoinCnf->result != NET_RESULT_OK) { WARNING_OUT(( "Bad result %#hx joining ObManControl channel", pNetJoinCnf->result)); success = FALSE; DC_QUIT; }
//
// If so, store the value returned in the domain record:
//
pDomain->omcChannel = pNetJoinCnf->channel; pOMCWSGroup = GetOMCWsgroup(pDomain);
if( NULL == pOMCWSGroup ) { TRACE_OUT(( "NULL pOMCWSGroup" )); success = FALSE; DC_QUIT; }
pOMCWSGroup->channelID = pDomain->omcChannel;
//
// We need a token to determine which ObMan is going to
// initialise the ObManControl workset group. Get GCC to
// assign us one (this returns a static value for R1.1 calls).
//
if (!CMS_AssignTokenId(pomPrimary->pcmClient, GCC_OBMAN_TOKEN_KEY)) { success = FALSE; DC_QUIT; }
pDomain->state = PENDING_TOKEN_ASSIGN; } break;
case DOMAIN_READY: { //
// This should be a join event for a regular workset group
// channel. We check that we have indeed set up a workset
// group registration CB containing the channel correlator
// associated with this event:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->pendingRegs), (void**)&pRegistrationCB, FIELD_OFFSET(OM_WSGROUP_REG_CB, chain), FIELD_OFFSET(OM_WSGROUP_REG_CB, channelCorrelator), pNetJoinCnf->correlator, FIELD_SIZE(OM_WSGROUP_REG_CB, channelCorrelator));
if (pRegistrationCB == NULL) { ERROR_OUT(( "Unexpected JOIN for channel %hu - no reg CB found", pNetJoinCnf->channel)); DC_QUIT; }
//
// Check that the join was successful:
//
if (pNetJoinCnf->result != NET_RESULT_OK) { //
// If not, trace then try again:
//
WARNING_OUT(("Failure 0x%08x joining channel %hu for WSG %d, trying again", pNetJoinCnf->result, pNetJoinCnf->channel, pRegistrationCB->wsg));
pRegistrationCB->pWSGroup->state = INITIAL; WSGRegisterRetry(pomPrimary, pRegistrationCB); DC_QUIT; }
//
// Otherwise, call WSGRegisterStage3 to continue the
// registration process:
//
WSGRegisterStage3(pomPrimary, pDomain, pRegistrationCB, pNetJoinCnf->channel); } break;
case PENDING_ATTACH: case PENDING_WELCOME: case GETTING_OMC: { //
// Shouldn't get any join indications in these states.
//
ERROR_OUT(( "Unexpected JOIN in domain state %hu", pDomain->state)); } break;
default: { //
// This is also an error:
//
ERROR_OUT(( "Invalid state %hu for domain %u", pDomain->state, pDomain->callID)); } }
DC_EXIT_POINT:
if (!success) { //
// For any error here, we react as if we've been kicked out of the
// domain:
//
ProcessOwnDetach(pomPrimary, pDomain); }
DebugExitVOID(ProcessNetJoinChannel); }
//
//
//
// ProcessCMSTokenAssign(...)
//
//
//
void ProcessCMSTokenAssign ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, BOOL success, NET_TOKEN_ID tokenID ) { DebugEntry(ProcessCMSTokenAssign);
TRACE_OUT(( "TOKEN_ASSIGN_CONFIRM: result %hu, token ID %#hx", success, tokenID));
if (pDomain->state != PENDING_TOKEN_ASSIGN) { WARNING_OUT(("Got TOKEN_ASSIGN_CONFIRM in state %hu", pDomain->state)); DC_QUIT; }
if (!success) { //
// Nothing to do - the domain attach process will time out.
//
ERROR_OUT(( "Failed to get token assigned")); DC_QUIT; }
pDomain->tokenID = tokenID;
//
// Now that we know what the token ID is, try to grab it:
//
if (MG_TokenGrab(pomPrimary->pmgClient, pDomain->tokenID) != 0) { ERROR_OUT(( "Failed to grab token")); DC_QUIT; }
pDomain->state = PENDING_TOKEN_GRAB;
DC_EXIT_POINT: DebugExitVOID(ProcessCMSTokenAssign); }
//
// ProcessNetTokenGrab(...)
//
UINT ProcessNetTokenGrab ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_RESULT result ) { POM_WSGROUP pOMCWSGroup = NULL;
UINT rc = 0;
DebugEntry(ProcessNetTokenGrab);
TRACE_OUT(( "Got token grab confirm - result = %hu", result));
if (pDomain->state != PENDING_TOKEN_GRAB) { ERROR_OUT(( "Got TOKEN_GRAB_CONFIRM in state %hu", pDomain->state)); rc = OM_RC_NETWORK_ERROR; DC_QUIT; }
//
// What to do here depends on whether we've succeeded in grabbing the
// token:
//
if (result == NET_RESULT_OK) { //
// We're the "top ObMan" in the Domain, so it's up to us to
// initialise the ObManControl workset group and welcome any others
// into the Domain (the Welcome message is broadcast on the
// ObManControl channel):
//
rc = ObManControlInit(pomPrimary, pDomain); if (rc != 0) { DC_QUIT; }
//
// If we get here, then the Domain attach process has finished.
// Phew! Any workset group registration attempts in progress will
// be processed shortly, next time the bouncing
// OMINT_EVENT_WSG_REGISTER_CONT event is processed
//
} else { //
// Someone else is in charge, so we need to get a copy of
// ObManControl from them (or anyone else who's prepared to give it
// to us). So, we need to discover the user ID of one of them so
// we can send our request there (if we just broadcasted our
// request, then each node would reply, flooding the Domain)
//
rc = SayHello(pomPrimary, pDomain); if (rc != 0) { DC_QUIT; }
//
// The next step in the Domain attach process happens when one of
// the other nodes out there replies to our HELLO with a WELCOME
// message. Execution continues in the ProcessWelcome function.
//
}
DC_EXIT_POINT:
if (rc != 0) { if (pOMCWSGroup != NULL) { //
// This will remove the ObManControl workset group from the
// Domain and subsequently call DomainDetach to detach from the
// Domain and free the Domain record:
//
DeregisterLocalClient(pomPrimary, &pDomain, pOMCWSGroup, FALSE);
UT_FreeRefCount((void**)&pOMCWSGroup, FALSE);
ASSERT((pDomain == NULL)); } }
DebugExitDWORD(ProcessNetTokenGrab, rc); return(rc); }
//
//
//
// ProcessNetTokenInhibit(...)
//
//
//
UINT ProcessNetTokenInhibit(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_RESULT result) { UINT rc = 0;
DebugEntry(ProcessNetTokenInhibit);
TRACE_OUT(( "Got token inhibit confirm - result = %hu", result)); if (result == NET_RESULT_OK) { //
// Now send a Welcome message on the ObManControl channel. It is
// crucial that this happens at the same time as we set the Domain
// state to READY, because if another node is joining the call at
// the same time it will send a Hello message:
//
// - if the message has already arrived, we will have thrown it
// away
// because the Domain state was not READY, so we must send it now
//
// - if it has yet to arrive, then setting the Domain state to
// READY
// now means we'll respond with another Welcome when it does
// arrive.
//
pDomain->state = DOMAIN_READY; rc = SayWelcome(pomPrimary, pDomain, pDomain->omcChannel); if (rc != 0) { DC_QUIT; }
//
// OK, the domain attach process has finished. We need to take no
// further action other than setting the state. Any pending
// workset group registrations will continue back at the
// WSGRegisterStage1 function, where hopefully the bounced
// OMINT_EVENT_WSGROUP_REGISTER event is just about to arrive...
//
} else { //
// Again, no action. We cannot join the domain, but the workset
// group registrations will time out in due course.
//
WARNING_OUT(( "Token inhibit failed!")); }
DC_EXIT_POINT: DebugExitDWORD(ProcessNetTokenInhibit, rc); return(rc);
}
//
//
//
// ObManControlInit(...)
//
//
//
UINT ObManControlInit(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain) { POM_WSGROUP pOMCWSGroup; UINT rc = 0;
DebugEntry(ObManControlInit);
//
// First, set up a pointer to the ObManControl workset group, which
// should already have been put in the Domain record:
//
pOMCWSGroup = GetOMCWsgroup(pDomain);
//
// Initialising the ObManControl workset group involves
//
// - adding a WSGROUP_INFO object to it, which identifies ObManControl
// itself.
//
TRACE_OUT(( "Initialising ObManControl in Domain %u", pDomain->callID));
//
// Now we must add a workset group identification object, identifying
// ObManControl, to workset #0 in ObManControl.
//
// Slightly circular, but we try to treat ObManControl as a regular
// workset group as much as possible; if we didn't add this
// identification object then when a Client (e.g. AppLoader) tries to
// register with ObManControl, we would look in workset #0 for a
// reference to it, not find one and then create it again!
//
rc = CreateAnnounce(pomPrimary, pDomain, pOMCWSGroup); if (rc != 0) { DC_QUIT; }
//
// In addition, we add our registration object to ObManControl workset
// #0 and update it immediately to status READY_TO_SEND:
//
rc = RegAnnounceBegin(pomPrimary, pDomain, pOMCWSGroup, pDomain->userID, &(pOMCWSGroup->pObjReg)); if (rc != 0) { DC_QUIT; }
rc = RegAnnounceComplete(pomPrimary, pDomain, pOMCWSGroup); if (rc != 0) { DC_QUIT; }
//
// OK, we've initialised ObManControl for this call - inhibit the token
// so that no one else can do the same (if this is the local domain,
// just fake up an inhibit confirm):
//
if (pDomain->callID == OM_NO_CALL) { TRACE_OUT(( "Faking successful token inhibit for local domain")); rc = ProcessNetTokenInhibit(pomPrimary, pDomain, NET_RESULT_OK); if (rc != 0) { DC_QUIT; } } else { rc = MG_TokenInhibit(pomPrimary->pmgClient, pDomain->tokenID); if (rc != 0) { DC_QUIT; }
pDomain->state = PENDING_TOKEN_INHIBIT; }
DC_EXIT_POINT:
if (rc != 0) { WARNING_OUT(("Error %d initialising ObManControl WSG for Domain %u", rc, pDomain->callID)); }
DebugExitDWORD(ObManControlInit, rc); return(rc);
}
//
//
//
// SayHello(...)
//
//
//
UINT SayHello(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain)
{ POMNET_JOINER_PKT pHelloPkt; UINT rc = 0;
DebugEntry(SayHello);
//
// Generate and queue an OMNET_HELLO message:
//
TRACE_OUT(( "Saying hello in Domain %u", pDomain->callID));
pHelloPkt = (POMNET_JOINER_PKT)UT_MallocRefCount(sizeof(OMNET_JOINER_PKT), TRUE); if (!pHelloPkt) { rc = UT_RC_NO_MEM; DC_QUIT; }
pHelloPkt->header.sender = pDomain->userID; pHelloPkt->header.messageType = OMNET_HELLO;
//
// All fields in the joiner packet after <capsLen> are capabilities. To
// calculate the size of these capabilities, we use the offset and size
// of the caps len field itself to determine the amount of data after
// it.
//
pHelloPkt->capsLen = sizeof(OMNET_JOINER_PKT) - (offsetof(OMNET_JOINER_PKT, capsLen) + sizeof(pHelloPkt->capsLen));
TRACE_OUT(( "Our caps len is 0x%08x", pHelloPkt->capsLen));
//
// Take our compression caps from the domain record:
//
pHelloPkt->compressionCaps = pDomain->compressionCaps;
TRACE_OUT(( "Broadcasting compression caps 0x%08x in HELLO", pHelloPkt->compressionCaps));
rc = QueueMessage(pomPrimary->putTask, pDomain, pDomain->omcChannel, NET_TOP_PRIORITY, NULL, // no wsgroup
NULL, // no workset
NULL, // no object
(POMNET_PKT_HEADER) pHelloPkt, NULL, // no associated object data
FALSE); if (rc != 0) { DC_QUIT; }
//
// When the associated response (OMNET_WELCOME) is received from another
// node, we will ask that node for a copy of the ObManControl workset
// group. In the meantime, there's nothing else to do.
//
pDomain->state = PENDING_WELCOME;
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d saying hello in Domain %u", rc, pDomain->callID)); }
DebugExitDWORD(SayHello, rc); return(rc);
}
//
//
//
// ProcessHello(...)
//
//
//
UINT ProcessHello(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POMNET_JOINER_PKT pHelloPkt, UINT lengthOfPkt) { NET_CHANNEL_ID lateJoiner;
UINT rc = 0;
DebugEntry(ProcessHello);
lateJoiner = pHelloPkt->header.sender;
//
// A late joiner has said hello. If we are not fully attached yet, we
// trace and quit:
//
if (pDomain->state != DOMAIN_READY) { WARNING_OUT(( "Can't process HELLO on channel %#hx - domain state %hu", lateJoiner, pDomain->state)); DC_QUIT; }
//
// Merge in the late joiner's capabilities with our view of the
// domain-wide caps.
//
MergeCaps(pDomain, pHelloPkt, lengthOfPkt);
//
// Now send a welcome message to the late joiner.
//
rc = SayWelcome(pomPrimary, pDomain, lateJoiner); if (rc != 0) { DC_QUIT; }
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d processing hello from node %#hx in Domain %u", rc, lateJoiner, pDomain->callID)); }
DebugExitDWORD(ProcessHello, rc); return(rc);
} // ProcessHello
//
//
//
// MergeCaps(...)
//
//
//
void MergeCaps(POM_DOMAIN pDomain, POMNET_JOINER_PKT pJoinerPkt, UINT lengthOfPkt) { NET_CHANNEL_ID sender; UINT compressionCaps;
DebugEntry(MergeCaps);
sender = pJoinerPkt->header.sender; compressionCaps = 0;
//
// We have received a HELLO or WELCOME packet from another node.
//
// - For a HELLO packet, these caps will be the caps of a late joiner.
//
// - For a WELCOME packet, these caps will be the domain-wide caps as
// viewed by our helper node.
//
// Either way, we need to merge in the capabilities from the packet into
// our view of the domain-wide capabilities.
//
// Note that in some backlevel calls, the joiner packet will not contain
// capabilities - so check the length of the packet first
//
if (lengthOfPkt >= (offsetof(OMNET_JOINER_PKT, capsLen) + sizeof(pJoinerPkt->capsLen))) { //
// OK, this packet contains a capsLen field. See if it contains
// compression capabilities (these immediately follow the capsLen
// field and are four bytes long).
//
TRACE_OUT(( "Caps len from node 0x%08x is 0x%08x", sender, pJoinerPkt->capsLen));
if (pJoinerPkt->capsLen >= 4) { //
// Packet contains compression caps - record them:
//
compressionCaps = pJoinerPkt->compressionCaps; TRACE_OUT(( "Compression caps in joiner packet from 0x%08x: 0x%08x", sender, compressionCaps)); } else { //
// If not specified, assume NO compression is supported. This
// should never happen in practice, because if someone supports
// any capabilities at all, they should support compression
// capabilities.
//
compressionCaps = OM_CAPS_NO_COMPRESSION; ERROR_OUT(( "Party 0x%08x supports caps but not compression caps", sender)); } } else { //
// If no capabilities specified at all, assume PKW compression plus
// no compression (since that is how LSP20 behaves).
//
compressionCaps = (OM_CAPS_PKW_COMPRESSION | OM_CAPS_NO_COMPRESSION); TRACE_OUT(( "No caps in joiner pkt - assume PKW + NO compress (0x%08x)", compressionCaps)); }
//
// OK, we've determined the capabilities from the packet. Now merge
// them into our view of the domain-wide caps:
//
pDomain->compressionCaps &= compressionCaps;
TRACE_OUT(( "Domain-wide compression caps now 0x%08x", pDomain->compressionCaps));
DebugExitVOID(MergeCaps); } // MergeCaps
//
//
//
// SayWelcome(...)
//
//
//
UINT SayWelcome(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_CHANNEL_ID channel) { POMNET_JOINER_PKT pWelcomePkt;
UINT rc = 0;
DebugEntry(SayWelcome);
//
// The <channel> passed in is one of the following:
//
// - the channel of a late-joiner which just sent us a HELLO message, or
//
// - the broadcast ObManControl channel, in the case where this is a
// Welcome we're sending at start of day.
//
TRACE_OUT(( "Sending welcome on channel %hu ", channel));
pWelcomePkt = (POMNET_JOINER_PKT)UT_MallocRefCount(sizeof(OMNET_JOINER_PKT), TRUE); if (!pWelcomePkt) { rc = UT_RC_NO_MEM; DC_QUIT; }
pWelcomePkt->header.sender = pDomain->userID; // own user ID
pWelcomePkt->header.messageType = OMNET_WELCOME;
//
// All fields in the joiner packet after <capsLen> are capabilities. To
// calculate the size of these capabilities, we use the offset and size
// of the <capsLen> field itself to determine the amount of data after
// it.
//
pWelcomePkt->capsLen = sizeof(OMNET_JOINER_PKT) - (offsetof(OMNET_JOINER_PKT, capsLen) + sizeof(pWelcomePkt->capsLen));
//
// The value we use for the compressionCaps is our current view of the
// domain-wide compression capabilities.
//
pWelcomePkt->compressionCaps = pDomain->compressionCaps;
TRACE_OUT(( "Sending caps 0x%08x in WELCOME on channel 0x%08x", pWelcomePkt->compressionCaps, channel));
rc = QueueMessage(pomPrimary->putTask, pDomain, channel, NET_TOP_PRIORITY, NULL, // no wsgroup
NULL, // no workset
NULL, // no object
(POMNET_PKT_HEADER) pWelcomePkt, NULL, // no object data
FALSE); if (rc != 0) { DC_QUIT; }
//
// When this WELCOME message is received at the other end, the
// ProcessWelcome function is invoked.
//
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d sending welcome on channel 0x%08x in Domain %u", rc, channel, pDomain->callID)); }
DebugExitDWORD(SayWelcome, rc); return(rc); } // SayWelcome
//
//
//
// ProcessWelcome(...)
//
//
//
UINT ProcessWelcome(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POMNET_JOINER_PKT pWelcomePkt, UINT lengthOfPkt) { POM_WSGROUP pOMCWSGroup; UINT rc = 0;
DebugEntry(ProcessWelcome);
//
// This function is called when a remote instance of ObMan has replied
// to an OMNET_HELLO message which we sent.
//
// We sent the HELLO message as part of the procedure to get a copy of
// the ObManControl workset group; now we know someone who has it, we
// send them an OMNET_WSGROUP_SEND_REQ on their single-user channel,
// enclosing our own single-user channel ID for the response.
//
// However, every node in the Domain will respond to our initial HELLO,
// but we only need to ask the first respondent for the workset group.
// So, we check the Domain state and then change it so we will ignore
// future WELCOMES for this Domain:
//
// (No mutex required for this test-and-set since only ever executed in
// ObMan task).
//
if (pDomain->state == PENDING_WELCOME) { //
// OK, this is the first WELCOME we've got since we broadcast the
// HELLO. So, we reply to it with a SEND_REQUEST for ObManControl.
//
TRACE_OUT(( "Got first WELCOME message in Domain %u, from node 0x%08x", pDomain->callID, pWelcomePkt->header.sender));
//
// Merge in the capabilities which our helper node has told us
// about:
//
MergeCaps(pDomain, pWelcomePkt, lengthOfPkt);
pOMCWSGroup = GetOMCWsgroup(pDomain); if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
//
// ...and call the IssueSendReq function specifying the sender of
// the WELCOME message as the node to get the workset group from:
//
rc = IssueSendReq(pomPrimary, pDomain, pOMCWSGroup, pWelcomePkt->header.sender); if (rc != 0) { ERROR_OUT(( "Error %d requesting OMC from 0x%08x in Domain %u", rc, pWelcomePkt->header.sender, pDomain->callID)); DC_QUIT; }
pDomain->state = GETTING_OMC;
//
// Next, the remote node which welcomed us will send us the
// contents of the ObManControl workset group. When it has
// finished, it will send an OMNET_WSGROUP_SEND_COMPLETE message,
// which is where we take up the next step of the multi-stage
// Domain attach process.
//
} else { //
// OK, we're in some other state i.e. not waiting for a WELCOME
// message - so just ignore it.
//
TRACE_OUT(( "Ignoring WELCOME from 0x%08x - in state %hu", pWelcomePkt->header.sender, pDomain->state)); }
TRACE_OUT(( "Processed WELCOME message from node 0x%08x in Domain %u", pWelcomePkt->header.sender, pDomain->callID));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d processing WELCOME message from " "node 0x%08x in Domain %u", rc, pWelcomePkt->header.sender, pDomain->callID)); }
DebugExitDWORD(ProcessWelcome, rc); return(rc); }
//
// ProcessNetDetachUser()
//
void ProcessNetDetachUser ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_UID detachedUserID ) { DebugEntry(ProcessNetDetachUser);
//
// There are two cases here:
//
// 1. this is a detach indication for ourselves i.e. we have been
// booted off the network by MCS for some reason
//
// 2. this is a detach indication for someone else i.e. another user
// has left (or been booted off) the MCS Domain.
//
// We differentiate the two cases by checking the ID of the detached
// user against our own.
//
if (detachedUserID == pDomain->userID) { //
// It's for us, so call the ProcessOwnDetach function:
//
ProcessOwnDetach(pomPrimary, pDomain); } else { //
// It's someone else, so we call the ProcessOtherDetach function:
//
ProcessOtherDetach(pomPrimary, pDomain, detachedUserID); }
DebugExitVOID(ProcessNetDetachUser); }
//
// ProcessOtherDetach(...)
//
UINT ProcessOtherDetach ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_UID detachedUserID ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; OM_WORKSET_ID worksetID; UINT rc = 0;
DebugEntry(ProcessOtherDetach);
TRACE_OUT(( "DETACH_IND for user 0x%08x in domain %u", detachedUserID, pDomain->callID));
//
// Someone else has left the Domain. What this means is that we must
//
// - release any locks they may have acquired for worksets/objects in
// this Domain
//
// - remove any registration objects they might have added to worksets
// in ObManControl
//
// - remove any objects they have added to non-persistent worksets
//
// - if we are catching up from them then select another node to catch
// up from or stop catch up if no one else is left.
//
//
// The processing is as follows:
//
// FOR each registration workset in ObManControl which is in use
//
// FOR each object in the workset
//
// IF it relates to the node which has just/has just been
// detached, then that node was registered with the
// workset group, so
//
// - delete the object and post a DELETE_IND to
// any local Clients which have the workset open
// - search this workset group for any locks held by this
// node and release them.
//
//
// OK, to work: first we derive a pointer to the ObManControl workset
// group:
//
pOMCWSGroup = GetOMCWsgroup(pDomain); if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
//
// Now begin the outer FOR loop:
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { //
// Get a pointer to the workset:
//
pOMCWorkset = pOMCWSGroup->apWorksets[worksetID]; if (pOMCWorkset == NULL) { //
// There is no workset with this ID so we skip to the next one:
//
continue; }
ValidateWorkset(pOMCWorkset);
//
// OK, worksetID corresponds to the ID of an actual workset group
// in the domain. These functions will do any clearup on behalf of
// the detached node.
//
RemovePersonObject(pomPrimary, pDomain, (OM_WSGROUP_ID) worksetID, detachedUserID);
ReleaseAllNetLocks(pomPrimary, pDomain, (OM_WSGROUP_ID) worksetID, detachedUserID);
PurgeNonPersistent(pomPrimary, pDomain, (OM_WSGROUP_ID) worksetID, detachedUserID);
//
// Finished this workset so go on to the next.
//
}
//
// Well, that's it:
//
TRACE_OUT(( "Cleaned up after node 0x%08x detached from Domain %u", detachedUserID, pDomain->callID));
DC_EXIT_POINT: DebugExitDWORD(ProcessOtherDetach, rc); return(rc); }
//
// ProcessOwnDetach(..)
//
UINT ProcessOwnDetach ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain ) { POM_DOMAIN pLocalDomainRec; POM_WSGROUP pWSGroup; POM_LOCK_REQ pLockReq; POM_LOCK_REQ pTempLockReq; POM_WSGROUP pTempWSGroup; POM_WSGROUP_REG_CB pRegistrationCB; POM_WSGROUP_REG_CB pTempRegCB; UINT callID; UINT rc = 0;
DebugEntry(ProcessOwnDetach);
//
// First of all, remove all traces of everybody else (because the call
// may have ended already, we may not get explicit DETACH_INDICATIONs
// for them):
//
ProcessOtherDetach(pomPrimary, pDomain, NET_ALL_REMOTES);
//
// We proceed as follows:
//
// - get a pointer to the record for the "local" Domain (or create it
// if it doesn't exist)
//
// - move all the pending lock requests, registrations and workset
// groups in this Domain into the local Domain.
//
callID = pDomain->callID;
if (callID == OM_NO_CALL) { WARNING_OUT(( "Detach for local domain - avoiding recursive cleanup")); FreeDomainRecord(&pDomain); DC_QUIT; }
TRACE_OUT(( "Processing own detach/end call etc. for Domain %u", callID)); rc = DomainRecordFindOrCreate(pomPrimary, OM_NO_CALL, &pLocalDomainRec); if (rc != 0) { DC_QUIT; }
//
// Move the pending lock requests (need the pTemp... variables since we
// need to chain from the old position):
//
pLockReq = (POM_LOCK_REQ)COM_BasedListFirst(&(pDomain->pendingLocks), FIELD_OFFSET(OM_LOCK_REQ, chain));
while (pLockReq != NULL) { TRACE_OUT((" Moving lock for workset %hu in WSG ID %hu", pLockReq->worksetID, pLockReq->wsGroupID));
pTempLockReq = (POM_LOCK_REQ)COM_BasedListNext(&(pDomain->pendingLocks), pLockReq, FIELD_OFFSET(OM_LOCK_REQ, chain));
COM_BasedListRemove(&(pLockReq->chain)); COM_BasedListInsertBefore(&(pLocalDomainRec->pendingLocks), &(pLockReq->chain));
pLockReq = pTempLockReq; }
//
// Now cancel any outstanding registrations:
//
pRegistrationCB = (POM_WSGROUP_REG_CB)COM_BasedListFirst(&(pDomain->pendingRegs), FIELD_OFFSET(OM_WSGROUP_REG_CB, chain)); while (pRegistrationCB != NULL) { TRACE_OUT(("Aborting registration for WSG %d", pRegistrationCB->wsg));
pTempRegCB = (POM_WSGROUP_REG_CB)COM_BasedListNext(&(pDomain->pendingRegs), pRegistrationCB, FIELD_OFFSET(OM_WSGROUP_REG_CB, chain));
WSGRegisterResult(pomPrimary, pRegistrationCB, OM_RC_NETWORK_ERROR);
pRegistrationCB = pTempRegCB; }
//
// Move the workset groups.
//
// Note that we will move the ObManControl workset group for the Domain
// we've detached from into the local Domain as well; it does not
// replace the OMC workset group for the local Domain, but we can't just
// throw it away since the Application Loader Primary and Secondaries
// still have valid workset group handles for it. They will eventually
// deregister from it and it will be thrown away.
//
// Since WSGMove relies on the fact that there is an OMC workset group
// in the Domain out of which workset groups are being moved, we must
// move the OMC workset group last.
//
// So, start at the end and work backwards:
//
pWSGroup = (POM_WSGROUP)COM_BasedListLast(&(pDomain->wsGroups), FIELD_OFFSET(OM_WSGROUP, chain)); while (pWSGroup != NULL) { //
// Move each one into the local Domain. We need pTempWSGroup
// since we have to do the chaining before calling WSGroupMove.
// That function removes the workset group from the list.
//
pTempWSGroup = (POM_WSGROUP)COM_BasedListPrev(&(pDomain->wsGroups), pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain));
WSGMove(pomPrimary, pLocalDomainRec, pWSGroup);
pWSGroup = pTempWSGroup; }
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d processing NET_DETACH for self in Domain %u", rc, callID)); }
DebugExitDWORD(ProcessOwnDetach, rc); return(rc);
}
//
//
//
// ProcessNetLeaveChannel(...)
//
//
//
UINT ProcessNetLeaveChannel ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_CHANNEL_ID channel ) { POM_DOMAIN pLocalDomainRec; POM_WSGROUP pWSGroup; UINT callID;
UINT rc = 0;
DebugEntry(ProcessNetLeaveChannel);
callID = pDomain->callID;
//
// We've been forced out of the channel by MCS. We don't try to rejoin
// as this usually indicates a serious error. Instead, we treat this
// as a move of the associated workset group into the local Domain
// (unless it's our own user ID channel or the ObManControl channel, in
// which case we can't really do anything useful in this Domain, so we
// detach completely).
//
if ((channel == pDomain->userID) || (channel == pDomain->omcChannel)) { //
// This is our own user ID channel, so we behave as if we were
// booted out by MCS:
//
rc = ProcessOwnDetach(pomPrimary, pDomain); if (rc != 0) { DC_QUIT; } } else { //
// Not our own single-user channel or the ObManControl channel, so
// we don't need to take such drastic action. Instead, we process
// it as if it's a regular move of a workset group into the "local"
// Domain (i.e. NET_INVALID_DOMAIN_ID).
//
// SFR ? { Purge our list of outstanding receives for channel
PurgeReceiveCBs(pDomain, channel);
//
// So, find the workset group which is involved...
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->wsGroups), (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, channelID), (DWORD)channel, FIELD_SIZE(OM_WSGROUP, channelID)); if (pWSGroup == NULL) { ERROR_OUT(( "Got NET_LEAVE for channel %hu but no workset group!", channel)); DC_QUIT; }
//
// ...and move it into the local Domain:
//
rc = DomainRecordFindOrCreate(pomPrimary, OM_NO_CALL, &pLocalDomainRec); if (rc != 0) { DC_QUIT; }
WSGMove(pomPrimary, pLocalDomainRec, pWSGroup); }
TRACE_OUT(( "Processed NET_LEAVE for channel %hu in Domain %u", channel, callID));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d processing NET_LEAVE for %hu in Domain %u", rc, channel, callID)); }
DebugExitDWORD(ProcessNetLeaveChannel, rc); return(rc);
}
//
//
// LOCKING - OVERVIEW
//
// Workset locking operates on a request/reply protocol, which means that
// when we want a lock, we ask everyone else on the channel if we can have
// it. If they all say yes, we get it; otherwise we don't.
//
// This is non-trivial. Some nodes might disappear before they send us
// their reply, while some might disappear after they've send their reply.
// Others might just be far away and take a long time to reply. In
// addition, new nodes can join the channel at any time.
//
// To cope with all this, to lock a workset we build up a list of the
// remote nodes in the call which are using the workset group (the
// "expected respondents" list) and if the list is non-empty, we broadcast
// an OMNET_LOCK_REQ message on the channel for the workset group which
// contains the workset
//
// As each reply comes in, we check it off against the list of expected
// respondents. If we weren't expecting a reply from that node we ignore
// it. Otherwise, if the reply is a GRANT, we remove that node from the
// list and continue waiting for the others. If the reply is a DENY, we
// give up, discard all the memory allocated for the lock request and its
// associated CBs and post a failure event to the client.
//
// If the list of expected respondents becomes empty because everyone has
// replied with a GRANT, we again free up any memory used and post an event
// to the client.
//
// While all this is going on, we have a timer running in the background.
// It ticks every second for ten seconds (both configurable via .INI file)
// and when it does, we re-examine our list of expected respondents to see
// if any of them have deregistered from the workset group (or detached
// from the domain, which implies the former). If they have, we fake up a
// GRANT message from them, thus potentially triggering the success event
// to our local client.
//
// If anyone ever requests a lock while we have the lock, we DENY them the
// lock. If anyone ever requests a lock while we are also requesting the
// lock, we compare their MCS user IDs. If the other node has a higher
// numerical value, we abort our attempt in favour of them and send back a
// GRANT; otherwise we DENY the lock.
//
// If ever a node detaches when it has a lock, we trap this in
// ReleaseAllNetLocks, which compares the ID of the lock owner against the
// ID of the detached node and unlocks the workset if they match. For this
// reason, it is vital that we always know exactly who has the lock. We
// achieve this by, whenever we grant the lock to someone, we record their
// user ID.
//
// So, if we ever abort the locking of a workset in favour of someone else,
// we must broadcast this info to everyone else (since they must be told
// who really has the lock, and they will think that we have the lock if we
// don't tell them otherwise). We use a LOCK_NOTIFY message for this.
//
//
//
// ProcessLockRequest(...)
//
void ProcessLockRequest ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POMNET_LOCK_PKT pLockReqPkt ) { POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; NET_UID sender; OM_WORKSET_ID worksetID; OMNET_MESSAGE_TYPE reply = OMNET_LOCK_DENY; UINT rc = 0;
DebugEntry(ProcessLockRequest);
sender = pLockReqPkt->header.sender; worksetID = pLockReqPkt->worksetID;
//
// Find the workset group and workset this lock request relates to:
//
rc = PreProcessMessage(pDomain, pLockReqPkt->wsGroupID, worksetID, NULL, pLockReqPkt->header.messageType, &pWSGroup, &pWorkset, NULL); switch (rc) { case 0: { //
// Fine, this is what we want.
//
} break;
case OM_RC_WSGROUP_NOT_FOUND: { //
// We shouldn't be getting network events for this workset
// group if we don't have a workset group record for it!
//
WARNING_OUT(( "Got LOCK_REQUEST for unknown workset group %hu", pLockReqPkt->wsGroupID));
//
// Grant the lock anyway:
//
reply = OMNET_LOCK_GRANT; DC_QUIT; } break;
case OM_RC_WORKSET_NOT_FOUND: { //
// If we don't have this workset, that means that the lock
// request has got here before the WORKSET_NEW event for the
// workset. This means that we're in the early stages of
// registering with the workset group, and somebody else is
// trying to lock the workset. So, we create the workset now
// and continue as normal.
//
// In the DC_ABSence of any other information, we create the
// workset with TOP_PRIORITY and PERSISTENT - it will be set to
// the correct priority when the WORKSET_CATCHUP/NEW arrives.
//
WARNING_OUT(( "Lock req for unknown WSG %d workset %d - creating", pWSGroup->wsg, worksetID)); rc = WorksetCreate(pomPrimary->putTask, pWSGroup, worksetID, FALSE, NET_TOP_PRIORITY); if (rc != 0) { reply = OMNET_LOCK_DENY; DC_QUIT; }
pWorkset = pWSGroup->apWorksets[worksetID]; } break;
default: { ERROR_OUT(( "Error %d from PreProcessMessage", rc)); reply = OMNET_LOCK_DENY; DC_QUIT; } }
//
// Whether we grant this lock to the remote node depends on whether
// we're trying to lock it for ourselves, so switch according to the
// workset's lock state:
//
ValidateWorkset(pWorkset);
switch (pWorkset->lockState) { case LOCKING: { //
// We're trying to lock it ourselves, so compare MCS user IDs
// to resolve the conflict:
//
if (pDomain->userID > sender) { //
// We win, so deny the lock:
//
reply = OMNET_LOCK_DENY; } else { //
// The other node wins, so grant the lock to the node which
// requested it (marking it as granted to that node) and
// cancel our own attempt to get it:
//
WARNING_OUT(( "Aborting attempt to lock workset %u in WSG %d " "in favour of node 0x%08x", pWorkset->worksetID, pWSGroup->wsg, sender));
reply = OMNET_LOCK_GRANT;
//
// To cancel our own attempt, we must find the lock request
// CBs which we set up when we sent out our own
// OMNET_LOCK_REQ.
//
// To do this, call HandleMultLockReq which will find and
// deal with all the pending requests for this workset:
//
pWorkset->lockState = LOCK_GRANTED; pWorkset->lockCount = 0; pWorkset->lockedBy = sender;
HandleMultLockReq(pomPrimary, pDomain, pWSGroup, pWorkset, OM_RC_WORKSET_LOCK_GRANTED);
//
// Since we are aborting in favour of another node, need to
// broadcast a LOCK_NOTIFY so that evryone else stays in
// sync with who's got the lock.
//
// Note: we do not do this in R1.1 calls since this message
// is not part of the ObMan R1.1 protocol.
//
QueueLockNotify(pomPrimary, pDomain, pWSGroup, pWorkset, sender); } } break;
case LOCKED: { //
// We already have the workset locked so we deny the lock:
//
reply = OMNET_LOCK_DENY; } break;
case LOCK_GRANTED: { //
// If the state is LOCK_GRANTED, we allow this node to have the
// lock - the other node to which it was previously granted may
// refuse, but that's not our problem. We don't change the
// <lockedBy> field - if the node we think has the lock grants
// it to the other one, we will receive a LOCK_NOTIFY in due
// course.
//
reply = OMNET_LOCK_GRANT; } break;
case UNLOCKED: { //
// If the state is UNLOCKED, the other node can have the lock;
// we don't care, but make sure to record the ID of the node
// we're granting the lock to:
//
reply = OMNET_LOCK_GRANT;
//
// SFR5900: Only change the internal state if this is not a
// check point workset.
//
if (pWorkset->worksetID != OM_CHECKPOINT_WORKSET) { pWorkset->lockState = LOCK_GRANTED; pWorkset->lockCount = 0; pWorkset->lockedBy = sender; } } break;
default: { //
// We should have covered all the options so if we get here
// there's something wrong.
//
ERROR_OUT(("Reached default case in workset lock switch (state: %hu)", pWorkset->lockState)); } }
DC_EXIT_POINT:
QueueLockReply(pomPrimary, pDomain, reply, sender, pLockReqPkt);
DebugExitVOID(ProcessLockRequest); }
//
// QueueLockReply(...)
//
void QueueLockReply ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OMNET_MESSAGE_TYPE message, NET_CHANNEL_ID channel, POMNET_LOCK_PKT pLockReqPkt ) { POMNET_LOCK_PKT pLockReplyPkt; NET_PRIORITY priority;
DebugEntry(QueueLockReply);
//
// The reply is identical to the request with the exception of the
// <messageType> and <sender> fields. However, we can't just queue the
// same chunk of memory to be sent, because pLockReqPkt points to a NET
// buffer which will be freed soon. So, we allocate some new memory,
// copy the data across and set the fields:
//
pLockReplyPkt = (POMNET_LOCK_PKT)UT_MallocRefCount(sizeof(OMNET_LOCK_PKT), TRUE); if (!pLockReplyPkt) { ERROR_OUT(("Out of memory for QueueLockReply")); DC_QUIT; }
pLockReplyPkt->header.sender = pDomain->userID; pLockReplyPkt->header.messageType = message;
pLockReplyPkt->wsGroupID = pLockReqPkt->wsGroupID; pLockReplyPkt->worksetID = pLockReqPkt->worksetID;
//
// The <data1> field of the lock packet is the correlator the requester
// put in the original LOCK_REQUEST packet.
//
pLockReplyPkt->data1 = pLockReqPkt->data1;
//
// Lock replies normally go LOW_PRIORITY (with NET_SEND_ALL_PRIORITIES)
// so that they do not overtake any data queued at this node.
//
// However, if they're for ObManControl we send them TOP_PRIORITY
// (WITHOUT NET_SEND_ALL_PRIORITIES). This is safe because _all_
// ObManControl data is sent TOP_PRIORITY so there's no fear of a lock
// reply overtaking a data packet.
//
// Correspondingly, when we request a lock, we expect one reply at each
// priority unless it is for ObManControl.
//
if (pLockReqPkt->wsGroupID == WSGROUPID_OMC) { priority = NET_TOP_PRIORITY; } else { priority = NET_LOW_PRIORITY | NET_SEND_ALL_PRIORITIES; }
if (QueueMessage(pomPrimary->putTask, pDomain, channel, priority, NULL, NULL, NULL, (POMNET_PKT_HEADER) pLockReplyPkt, NULL, TRUE) != 0) { ERROR_OUT(("Error queueing lock reply for workset %hu, WSG %hu", pLockReqPkt->worksetID, pLockReqPkt->wsGroupID));
UT_FreeRefCount((void**)&pLockReplyPkt, FALSE); }
DC_EXIT_POINT: DebugExitVOID(QueueLockReply); }
//
// QueueLockNotify(...)
//
void QueueLockNotify ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, NET_UID sender ) { POMNET_LOCK_PKT pLockNotifyPkt; NET_PRIORITY priority;
DebugEntry(QueueLockNotify);
ValidateWorkset(pWorkset);
pLockNotifyPkt = (POMNET_LOCK_PKT)UT_MallocRefCount(sizeof(OMNET_LOCK_PKT), TRUE); if (!pLockNotifyPkt) { ERROR_OUT(("Out of memory for QueueLockNotify")); DC_QUIT; }
//
// For a LOCK_NOTIFY, the <data1> field is the user ID of the node
// we've granted the lock to.
//
pLockNotifyPkt->header.sender = pDomain->userID; pLockNotifyPkt->header.messageType = OMNET_LOCK_NOTIFY;
pLockNotifyPkt->wsGroupID = pWSGroup->wsGroupID; pLockNotifyPkt->worksetID = pWorkset->worksetID; pLockNotifyPkt->data1 = sender;
//
// LOCK_NOTIFY messages go at the priority of the workset involved. If
// this is OBMAN_CHOOSES_PRIORITY, then all bets are off and we send
// them TOP_PRIORITY.
//
priority = pWorkset->priority; if (priority == OM_OBMAN_CHOOSES_PRIORITY) { priority = NET_TOP_PRIORITY; }
if (QueueMessage(pomPrimary->putTask, pDomain, pWSGroup->channelID, priority, NULL, NULL, NULL, (POMNET_PKT_HEADER) pLockNotifyPkt, NULL, TRUE) != 0) { ERROR_OUT(("Error queueing lock notify for workset %hu in WSG %hu", pWorkset->worksetID, pWSGroup->wsGroupID));
UT_FreeRefCount((void**)&pLockNotifyPkt, FALSE); }
DC_EXIT_POINT: DebugExitVOID(QueueLockNotify); }
//
// ProcessLockNotify(...)
//
void ProcessLockNotify ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, NET_UID owner ) { POM_WORKSET pOMCWorkset; POM_OBJECT pObjPerson;
DebugEntry(ProcessLockNotify);
ValidateWSGroup(pWSGroup); ValidateWorkset(pWorkset); //
// This message is sent when one remote node has granted the lock to
// another. We use it to update our view of who has got the lock.
//
TRACE_OUT(("Got LOCK_NOTIFY for workset %u in WSG %d - node 0x%08x has the lock", pWorkset->worksetID, pWSGroup->wsg, owner));
//
// Check the lock state for the workset:
//
switch (pWorkset->lockState) { case LOCKED: { //
// A remote node has just told us that another remote node has
// got this workset lock - but we think we've got it!
//
ERROR_OUT(( "Bad LOCK_NOTIFY for WSG %d workset %d, owner 0x%08x", pWSGroup->wsg, pWorkset->worksetID, owner)); DC_QUIT; } break;
case LOCKING: { //
// We should get a LOCK_DENY or a LOCK_GRANT later - do nothing
// now.
//
DC_QUIT; } break;
case LOCK_GRANTED: case UNLOCKED: { //
// One remote node has granted the lock to another. Check the
// latter is still attached, by looking in the control workset:
//
pOMCWorkset = GetOMCWorkset(pDomain, pWSGroup->wsGroupID);
FindPersonObject(pOMCWorkset, owner, FIND_THIS, &pObjPerson);
if (pObjPerson != NULL) { ValidateObject(pObjPerson);
//
// If our internal state is LOCK_GRANTED and we have just
// received a LOCK_NOTIFY from another node then we can
// just ignore it - it is for a lock request that we have
// just abandoned.
//
if ( (pWorkset->lockState == LOCK_GRANTED) && (owner == pDomain->userID) ) { TRACE_OUT(( "Ignoring LOCK_NOTIFY for ourselves")); DC_QUIT; }
//
// Only store the new ID it is greater than the last ID we
// were notified of - it is possible for LOCK_NOTIFIES to
// get crossed on the wire. Consider the following
// scenario:
//
// Machines 1, 2, 3 and 4 are all in a call and all try and
// lock at the same time.
//
// - 2 grants to 3 and sends a LOCK_NOTIFY saying that 3
// has the lock.
//
// - 3 grants to 4 and sends a LOCK_NOTIFY saying that 4
// has the lock
//
// 4 actually has the lock at this point.
//
// Machine 1 gets the lock notification from 3 and sets its
// 'lockedBy' field to 4.
// Machine 1 then gets the lock notification from 2 and
// resets the 'lockedBy' field to 3.
//
// 4 then unlocks and sends the unlock notification. When
// 1 gets the unlock, it does not recognise the ID of the
// unlocking machine (it thinks 3 has the lock) so doesnt
// bother to reset the local locked state. Any subsequent
// attempts to lock the workset on 1 fail because it still
// still thinks 3 has the lock.
//
if (owner > pWorkset->lockedBy) { pWorkset->lockedBy = owner; TRACE_OUT(( "Node ID 0x%08x has the lock (?)", pWorkset->lockedBy)); } } else { //
// If not, we assume that this node was granted the lock
// but then went away. If we did think the workset was
// locked, mark it as unlocked and post an unlock event.
//
if (pWorkset->lockState == LOCK_GRANTED) { TRACE_OUT(("node 0x%08x had lock on workset %d in WSG %d but has left", owner, pWorkset->worksetID, pWSGroup->wsg));
WorksetUnlockLocal(pomPrimary->putTask, pWorkset); } } } break;
default: { //
// We should have covered all the options so if we get here
// there's something wrong.
//
ERROR_OUT(("Reached deafult case in workset lock switch (state: %hu)", pWorkset->lockState)); } }
DC_EXIT_POINT: DebugExitVOID(ProcessLockNotify); }
//
// ProcessLockReply(...)
//
void ProcessLockReply ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, NET_UID sender, OM_CORRELATOR correlator, OMNET_MESSAGE_TYPE replyType) { POM_WSGROUP pWSGroup = NULL; POM_WORKSET pWorkset; POM_LOCK_REQ pLockReq; POM_NODE_LIST pNodeEntry;
DebugEntry(ProcessLockReply);
//
// Search the domain's list of pending locks for one which matches the
// correlator (we do it this way rather than using the workset group ID
// and workset ID to ensure that we don't get confused between
// successive lock requests for the same workset).
//
TRACE_OUT(( "Searching domain %u's list for lock corr %hu", pDomain->callID, correlator));
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->pendingLocks), (void**)&pLockReq, FIELD_OFFSET(OM_LOCK_REQ, chain), FIELD_OFFSET(OM_LOCK_REQ, correlator), (DWORD)correlator, FIELD_SIZE(OM_LOCK_REQ, correlator)); if (pLockReq == NULL) { //
// Could be any of the following:
//
// - This reply is from a node we were never expecting a lock
// request from in the first place, and we've got all the other
// replies so we've thrown away the lock request.
//
// - Someone else has denied us the lock so we've given up.
//
// - The node was too slow to reply and we've given up on the lock
// request.
//
// - We've left the domain and so moved all the pending lock
// requests into the local domain.
//
// - A logic error.
//
// The only thing we can do here is quit.
//
WARNING_OUT(( "Unexpected lock correlator 0x%08x (domain %u)", correlator, pDomain->callID)); DC_QUIT; }
//
// Otherwise, we search the list of expected respondents looking for
// the node which has just replied:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pLockReq->nodes), (void**)&pNodeEntry, FIELD_OFFSET(OM_NODE_LIST, chain), FIELD_OFFSET(OM_NODE_LIST, userID), (DWORD)sender, FIELD_SIZE(OM_NODE_LIST, userID)); if (pNodeEntry == NULL) { //
// Could be any of the following:
//
// - We removed the node from the list because it had deregistered
// when the timeout expired (will only happen when delete of
// person object overtakes lock reply and timeout expires locally
// betweem the two).
//
// - The node joined since we compiled the list.
//
// - A logic error.
//
TRACE_OUT(("Recd unexpected lock reply from node 0x%08x in Domain %u", sender, pDomain->callID)); DC_QUIT; }
//
// Otherwise, this is a normal lock reply so we just remove the node
// from the list and free up its chunk of memory.
//
COM_BasedListRemove(&(pNodeEntry->chain)); UT_FreeRefCount((void**)&pNodeEntry, FALSE);
pWSGroup = pLockReq->pWSGroup;
//
// If the client has just deregistered from the workset group, we'll
// be throwing it away soon, so don't do any more processing:
//
if (!pWSGroup->valid) { WARNING_OUT(("Ignoring lock reply for discarded WSG %d", pWSGroup->wsg)); DC_QUIT; }
pWorkset = pWSGroup->apWorksets[pLockReq->worksetID]; ASSERT((pWorkset != NULL));
//
// Now check the workset's lock state: if we're not/no longer trying to
// lock it, quit.
//
// Note, however, that checkpointing worksets are never marked as
// LOCKING, even when we're locking them, so exclude them from the
// test:
//
if ((pWorkset->lockState != LOCKING) && (pWorkset->worksetID != OM_CHECKPOINT_WORKSET)) { WARNING_OUT(( "Recd unwanted lock reply from %hu for workset %d WSG %d", sender, pWorkset->worksetID, pWSGroup->wsg)); DC_QUIT; }
//
// If this is a negative reply, then we have failed to get the lock so
// inform our local client and then quit:
//
if (replyType == OMNET_LOCK_DENY) { //
// We do not expect this for a CHECKPOINT_WORKSET:
//
ASSERT((pWorkset->worksetID != OM_CHECKPOINT_WORKSET));
WARNING_OUT(( "node 0x%08x has denied the lock for workset %u in WSG %d", sender, pWorkset->worksetID, pWSGroup->wsg));
pWorkset->lockState = UNLOCKED; pWorkset->lockCount = 0;
HandleMultLockReq(pomPrimary, pDomain, pWSGroup, pWorkset, OM_RC_WORKSET_LOCK_GRANTED);
//
// Since we have given up our lock request in favour of another
// node, need to broadcast a LOCK_NOTIFY so that everyone else
// stays in sync with who's got the lock.
//
QueueLockNotify(pomPrimary, pDomain, pWSGroup, pWorkset, sender);
DC_QUIT; }
TRACE_OUT(( "Affirmative lock reply received from node 0x%08x", sender));
//
// Check if the list of expected respondents is now empty:
//
if (COM_BasedListIsEmpty(&(pLockReq->nodes))) { //
// List is now empty, so all nodes have replied to the request,
// therefore lock has succeeded:
//
TRACE_OUT(( "Got all LOCK_GRANT replies for workset %u in WSG %d", pWorkset->worksetID, pWSGroup->wsg));
if (pWorkset->worksetID == OM_CHECKPOINT_WORKSET) { //
// This is a checkpointing workset. We do not set the state to
// LOCKED (we never do for these worksets) and we only process
// the particular pending lock request which this packet came
// in reply to - otherwise we couldn't guarantee an end-to-end
// ping on each checkpoint:
//
WorksetLockResult(pomPrimary->putTask, &pLockReq, 0); } else { //
// This is not a checkpointing workset, so set the state to
// LOCKED and process ALL pending locks for this workset:
//
pWorkset->lockState = LOCKED;
HandleMultLockReq(pomPrimary, pDomain, pWSGroup, pWorkset, 0); } } else { //
// Otherwise, still awaiting some replies, so we do nothing more
// for the moment except trace.
//
TRACE_OUT(( "Still need lock replies for workset %u in WSG %d", pLockReq->worksetID, pWSGroup->wsg)); }
DC_EXIT_POINT: DebugExitVOID(ProcessLockReply); }
//
// PurgeLockRequests(...)
//
void PurgeLockRequests ( POM_DOMAIN pDomain, POM_WSGROUP pWSGroup ) { POM_LOCK_REQ pLockReq; POM_LOCK_REQ pNextLockReq; POM_NODE_LIST pNodeEntry;
DebugEntry(PurgeLockRequests);
//
// Search this domain's list of lock requests looking for a match on
// workset group ID:
//
pLockReq = (POM_LOCK_REQ)COM_BasedListFirst(&(pDomain->pendingLocks), FIELD_OFFSET(OM_LOCK_REQ, chain)); while (pLockReq != NULL) { //
// This loop might remove pLockReq from the list, so chain first:
//
pNextLockReq = (POM_LOCK_REQ)COM_BasedListNext(&(pDomain->pendingLocks), pLockReq, FIELD_OFFSET(OM_LOCK_REQ, chain));
//
// For each match...
//
if (pLockReq->wsGroupID == pWSGroup->wsGroupID) { TRACE_OUT(( "'%s' still has lock req oustanding - discarding"));
//
// Discard any node list entries remaining...
//
pNodeEntry = (POM_NODE_LIST)COM_BasedListFirst(&(pLockReq->nodes), FIELD_OFFSET(OM_NODE_LIST, chain)); while (pNodeEntry != NULL) { COM_BasedListRemove(&(pNodeEntry->chain)); UT_FreeRefCount((void**)&pNodeEntry, FALSE);
pNodeEntry = (POM_NODE_LIST)COM_BasedListFirst(&(pLockReq->nodes), FIELD_OFFSET(OM_NODE_LIST, chain)); }
//
// ...and discard the lock request itself:
//
COM_BasedListRemove(&(pLockReq->chain)); UT_FreeRefCount((void**)&pLockReq, FALSE); }
pLockReq = pNextLockReq; }
DebugExitVOID(PurgeLockRequests); }
//
// ProcessLockTimeout(...)
//
void ProcessLockTimeout ( POM_PRIMARY pomPrimary, UINT retriesToGo, UINT callID ) { POM_DOMAIN pDomain; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_LOCK_REQ pLockReq = NULL; POM_WORKSET pOMCWorkset; POM_OBJECT pObj; POM_NODE_LIST pNodeEntry; POM_NODE_LIST pNextNodeEntry;
DebugEntry(ProcessLockTimeout);
//
// When we broadcast a lock request, we start a timer going so that we
// don't hang around for ever waiting for replies from nodes which have
// gone away. This timer has now popped, so we validate our list of
// expected respondents by checking that each entry relates to a node
// still in the domain.
//
//
// First, find the lock request CB by looking in each domain and then
// at the correlators of each pending lock request:
//
pDomain = (POM_DOMAIN)COM_BasedListFirst(&(pomPrimary->domains), FIELD_OFFSET(OM_DOMAIN, chain));
while (pDomain != NULL) { COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->pendingLocks), (void**)&pLockReq, FIELD_OFFSET(OM_LOCK_REQ, chain), FIELD_OFFSET(OM_LOCK_REQ, retriesToGo), (DWORD)retriesToGo, FIELD_SIZE(OM_LOCK_REQ, retriesToGo)); if (pLockReq != NULL) { TRACE_OUT(( "Found correlated lock request")); break; }
//
// Didn't find anything in this domain - go on to the next:
//
pDomain = (POM_DOMAIN)COM_BasedListNext(&(pomPrimary->domains), pDomain, FIELD_OFFSET(OM_DOMAIN, chain)); }
if (pLockReq == NULL) { TRACE_OUT(( "Lock timeout expired after lock granted/refused")); DC_QUIT; }
pWSGroup = pLockReq->pWSGroup;
//
// If the client has just deregistered from the workset group, we'll
// be throwing it away soon, so don't do any more processing:
//
if (!pWSGroup->valid) { WARNING_OUT(( "Ignoring lock timeout for discarded WSG %d", pWSGroup->wsg)); DC_QUIT; }
//
// We know the workset must still exist because worksets don't get
// discarded unless the whole workset group is being discarded.
//
pWorkset = pWSGroup->apWorksets[pLockReq->worksetID]; ASSERT((pWorkset != NULL));
//
// The workset must be in the LOCKING state because if it is LOCKED or
// UNLOCKED, then we shouldn't have found a lock request CB for it
// (unless of course it's a checkpointing workset):
//
if (pWorkset->lockState != LOCKING) { if (pWorkset->worksetID != OM_CHECKPOINT_WORKSET) { WARNING_OUT(( "Got lock timeout for workset %u in WSG %d but state is %u", pWorkset->worksetID, pWSGroup->wsg, pWorkset->lockState)); DC_QUIT; } }
//
// Go through the relevant control workset to see if any of the
// expected respondents have disappeared.
//
pOMCWorkset = GetOMCWorkset(pDomain, pLockReq->wsGroupID);
ASSERT((pOMCWorkset != NULL));
//
// Chain through each of the objects in our expected respondents list
// as follows:
//
// FOR each object in the expected respondents list
//
// FOR each person object in the relevant ObManControl workset
//
// IF they match on user ID, this node is still around so
// don't delete it
//
// IF no match found then node has gone away so remove it from
// expected respondents list.
//
//
pNodeEntry = (POM_NODE_LIST)COM_BasedListFirst(&(pLockReq->nodes), FIELD_OFFSET(OM_NODE_LIST, chain)); while (pNodeEntry != NULL) { //
// We might free up pNodeEntry on a pass through the loop (in
// ProcessLockReply), but we will need to be able to chain from it
// all the same. So, we chain at the START of the loop, putting a
// pointer to the next item in pTempNodeEntry; at the end of the
// loop, we assign this value to pNodeEntry:
//
pNextNodeEntry = (POM_NODE_LIST)COM_BasedListNext(&(pLockReq->nodes), pNodeEntry, FIELD_OFFSET(OM_NODE_LIST, chain));
//
// Now, search for this user's person object:
//
FindPersonObject(pOMCWorkset, pNodeEntry->userID, FIND_THIS, &pObj);
if (pObj == NULL) { //
// We didn't find this node in the workset, so it must have
// disappeared. Therefore, we fake a LOCK_GRANT message from
// it. ProcessLockReply will duplicate some of the processing
// we've done but it saves duplicating code.
//
WARNING_OUT(( "node 0x%08x has disappeared - faking LOCK_GRANT message", pNodeEntry->userID));
ProcessLockReply(pomPrimary, pDomain, pNodeEntry->userID, pLockReq->correlator, OMNET_LOCK_GRANT); }
//
// Now, go on to the next item in the expected respondents list:
//
pNodeEntry = pNextNodeEntry; }
//
// ProcessLockReply may have determined, with the faked messages we
// gave it, that the lock attempt has succeeded completely. If so, the
// workset's lock state will now be LOCKED. If it isn't, we'll need to
// post another timeout event.
//
if (pWorkset->lockState == LOCKING) { TRACE_OUT(( "Replies to lock request still expected"));
if (pLockReq->retriesToGo == 0) { //
// We've run out of retries so give up now:
//
WARNING_OUT(( "Timed out trying to lock workset %u in WSG %d", pLockReq->worksetID, pWSGroup->wsg));
pWorkset->lockState = UNLOCKED; pWorkset->lockedBy = 0; pWorkset->lockCount = 0;
HandleMultLockReq(pomPrimary, pDomain, pWSGroup, pWorkset, OM_RC_OUT_OF_RESOURCES);
//
// Now send an unlock message to all nodes, so that they don't
// think we still have it locked.
//
if (QueueUnlock(pomPrimary->putTask, pDomain, pWSGroup->wsGroupID, pWorkset->worksetID, pWSGroup->channelID, pWorkset->priority) != 0) { DC_QUIT; } } else // retriesToGo == 0
{ pLockReq->retriesToGo--;
UT_PostEvent(pomPrimary->putTask, pomPrimary->putTask, OM_LOCK_RETRY_DELAY_DFLT, OMINT_EVENT_LOCK_TIMEOUT, retriesToGo, callID); } }
DC_EXIT_POINT: DebugExitVOID(ProcessLockTimeout); }
//
// HandleMultLockReq
//
void HandleMultLockReq ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, UINT result ) { POM_LOCK_REQ pLockReq;
DebugEntry(HandleMultLockReq);
//
// We need to search this Domain's list of lock requests for every one
// which matches the workset group and workset specified in the
// parameter list. Find the primary record first as a sanity check:
//
FindLockReq(pDomain, pWSGroup, pWorkset, &pLockReq, LOCK_PRIMARY);
if (pLockReq == NULL) { ERROR_OUT(( "No primary lock request CB found for workset %u!", pWorkset->worksetID)); DC_QUIT; }
while (pLockReq != NULL) { WorksetLockResult(pomPrimary->putTask, &pLockReq, result); FindLockReq(pDomain, pWSGroup, pWorkset, &pLockReq, LOCK_SECONDARY); }
DC_EXIT_POINT: DebugExitVOID(HandleMultLockReq); }
//
//
//
// FindLockReq
//
//
//
void FindLockReq(POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_LOCK_REQ * ppLockReq, BYTE lockType) { POM_LOCK_REQ pLockReq;
DebugEntry(FindLockReq);
//
// We need to search this Domain's list of lock requests for every one
// which matches the workset group, workset and lock type specified in
// the parameter list.
//
// So, we search the list to find a match on workset group ID, then
// compare the workset ID. If that doesn't match, we continue down the
// list:
//
pLockReq = (POM_LOCK_REQ)COM_BasedListFirst(&(pDomain->pendingLocks), FIELD_OFFSET(OM_LOCK_REQ, chain)); while (pLockReq != NULL) { if ((pLockReq->wsGroupID == pWSGroup->wsGroupID) && (pLockReq->worksetID == pWorkset->worksetID) && (pLockReq->type == lockType)) { break; }
pLockReq = (POM_LOCK_REQ)COM_BasedListNext(&(pDomain->pendingLocks), pLockReq, FIELD_OFFSET(OM_LOCK_REQ, chain)); }
*ppLockReq = pLockReq;
DebugExitVOID(FindLockReq); }
//
// ProcessUnlock(...)
//
void ProcessUnlock ( POM_PRIMARY pomPrimary, POM_WORKSET pWorkset, NET_UID sender ) { DebugEntry(ProcessUnlock);
//
// Check the workset was locked by the node that's now unlocking it:
//
if (pWorkset->lockedBy != sender) { WARNING_OUT(( "Unexpected UNLOCK from node 0x%08x for %hu!", sender, pWorkset->worksetID)); } else { TRACE_OUT(( "Unlocking:%hu for node 0x%08x", pWorkset->worksetID, sender));
WorksetUnlockLocal(pomPrimary->putTask, pWorkset); }
DebugExitVOID(ProcessUnlock); }
//
// ReleaseAllNetLocks(...)
//
void ReleaseAllNetLocks ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, NET_UID userID ) { POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; OM_WORKSET_ID worksetID;
DebugEntry(ReleaseAllNetLocks);
//
// Find the workset group:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->wsGroups), (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, wsGroupID), (DWORD)wsGroupID, FIELD_SIZE(OM_WSGROUP, wsGroupID)); if (pWSGroup == NULL) { //
// This will happen for a workset group which the other node is
// registered with but we're not, so just trace and quit:
//
TRACE_OUT(("No record found for WSG ID %hu", wsGroupID)); DC_QUIT; }
TRACE_OUT(( "Releasing all locks held by node 0x%08x in WSG %d", userID, pWSGroup->wsg));
//
// For each workset in it, if the lock has been granted to the detached
// node, unlock it:
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { pWorkset = pWSGroup->apWorksets[worksetID]; if (pWorkset == NULL) { continue; }
//
// If this workset is locked by someone other than us...
//
if (pWorkset->lockState == LOCK_GRANTED) { //
// ...and if it is locked by the departed node (or if everyone
// has been detached)...
//
if ((userID == pWorkset->lockedBy) || (userID == NET_ALL_REMOTES)) { //
// ...unlock it.
//
TRACE_OUT(( "Unlocking workset %u in WSG %d for detached node 0x%08x", worksetID, pWSGroup->wsg, userID));
WorksetUnlockLocal(pomPrimary->putTask, pWorkset); } } }
DC_EXIT_POINT: DebugExitVOID(ReleaseAllNetLocks); }
//
// ProcessWSGRegister(...)
//
void ProcessWSGRegister ( POM_PRIMARY pomPrimary, POM_WSGROUP_REG_CB pRegistrationCB ) { POM_DOMAIN pDomain; POM_WSGROUP pWSGroup; POM_USAGE_REC pUsageRec = NULL; POM_CLIENT_LIST pClientListEntry; UINT mode; UINT type; UINT rc = 0;
DebugEntry(ProcessWSGRegister);
//
// Check if this registration has been aborted already:
//
if (!pRegistrationCB->valid) { WARNING_OUT(( "Reg CB for WSG %d no longer valid - aborting registration", pRegistrationCB->wsg)); UT_FreeRefCount((void**)&pRegistrationCB, FALSE); DC_QUIT; }
//
// Determine whether we're doing a REGISTER or a MOVE (we use the
// string values for tracing):
//
mode = pRegistrationCB->mode; type = pRegistrationCB->type;
TRACE_OUT(( "Processing %d request (pre-Stage1) for WSG %d", pRegistrationCB->wsg));
//
// Find the Domain record (in the case of a MOVE, this will be the
// record for the Domain INTO WHICH the Client wants to move the WSG).
//
// Note that this process will cause us to attach to the Domain if
// we're not already attached.
//
rc = DomainRecordFindOrCreate(pomPrimary, pRegistrationCB->callID, &pDomain); if (rc != 0) { DC_QUIT; }
//
// Save the pointer to the Domain record because we'll need it later:
//
pRegistrationCB->pDomain = pDomain;
//
// Put the registration CB in the list hung off the Domain record:
//
COM_BasedListInsertAfter(&(pDomain->pendingRegs), &(pRegistrationCB->chain));
//
// OK, now we need to look for the workset group.
//
// If this is a MOVE, we can find the workset group record immediately
// using the offset stored in the request CB.
//
// If this is a REGISTER, we need to look for the record in the list
// hung off the Domain record, and, if none is found, create one:
//
if (type == WSGROUP_REGISTER) { WSGRecordFind(pDomain, pRegistrationCB->wsg, pRegistrationCB->fpHandler, &pWSGroup);
if (pWSGroup == NULL) { //
// The workset group was not found in the list hung off the
// Domain record, which means that there is no workset group
// with this name/FP combination present ON THIS MACHINE for
// this Domain.
//
rc = WSGRecordCreate(pomPrimary, pDomain, pRegistrationCB->wsg, pRegistrationCB->fpHandler, &pWSGroup); if (rc != 0) { DC_QUIT; } }
//
// Now that we've got a pointer to the workset group, we put a
// Client pointer to it into the usage record.
//
// We use the <clientPRootData> field of the registration CB as the
// base and to it we add the offset of the workset group we've just
// found/created.
//
// First, however, to get access to the usage record we need to
// generate an ObMan pointer to it:
//
pUsageRec = pRegistrationCB->pUsageRec;
//
// ...and add it to the Client pointer to the root of OMGLOBAL,
// putting the result in the relevant field in the usage record:
//
pUsageRec->pWSGroup = pWSGroup; pUsageRec->flags &= ~PWSGROUP_IS_PREGCB;
//
// Now add this Client to the workset group's client list (as a
// PRIMARY):
//
rc = AddClientToWSGList(pRegistrationCB->putTask, pWSGroup, pRegistrationCB->hWSGroup, PRIMARY); if (rc != 0) { DC_QUIT; }
pUsageRec->flags |= ADDED_TO_WSGROUP_LIST; } else // type == WSGROUP_MOVE
{ //
// Get pointer to WSGroup from the offset stored in the
// Registration CB:
//
pWSGroup = pRegistrationCB->pWSGroup;
//
// If it has become invalid, then all local Clients must have
// deregistered from it in the time it took for this event to to be
// processed. This is unusual, but not wrong, so we alert:
//
if (!pWSGroup->valid) { WARNING_OUT(( "Aborting Move req for WSG %d - record is invalid", pWSGroup->wsg)); DC_QUIT; } }
//
// So, whatever just happened above, we should now have a valid pointer
// to a valid workset group record which is the one the Client wanted
// to move/register with in the first place.
//
//
// This workset group might be marked TO_BE_DISCARDED, if the last
// local Client deregistered from it a while ago but it hasn't actually
// been discarded. We don't want it discardable any more:
//
if (pWSGroup->toBeDiscarded) { WARNING_OUT(("WSG %d marked TO_BE_DISCARDED - clearing flag for new registration", pWSGroup->wsg)); pWSGroup->toBeDiscarded = FALSE; }
//
// We'll need the ObMan-context pointer to the workset group later, so
// store it in the CB:
//
pRegistrationCB->pWSGroup = pWSGroup;
//
// OK, now we've set up the various records and put the necessary
// pointers in the registration CB, so start the workset group
// registration/move process in earnest. To do this, we post another
// event to the ObMan task which will result in WSGRegisterStage1 being
// called.
//
// The reason we don't call the function directly is that this event
// may have to be bounced, and if so, we want to restart the
// registration process at the beginning of WSGRegisterStage1 (rather
// than the beginning of this function).
//
// Before we post the event, bump up the use counts of the Domain
// record and workset group, since the CB holds references to them and
// they may be freed by something else before we process the event.
//
// In addition, bump up the use count of the registration CB because if
// the call goes down before the event is processed, the reg CB will
// have been freed.
//
UT_BumpUpRefCount(pDomain); UT_BumpUpRefCount(pWSGroup); UT_BumpUpRefCount(pRegistrationCB);
pRegistrationCB->flags |= BUMPED_CBS;
UT_PostEvent(pomPrimary->putTask, pomPrimary->putTask, 0, // no delay
OMINT_EVENT_WSGROUP_REGISTER_CONT, 0, // no param1
(UINT_PTR) pRegistrationCB);
TRACE_OUT(( "Processed initial request for WSG %d TASK 0x%08x", pRegistrationCB->wsg, pRegistrationCB->putTask));
DC_EXIT_POINT:
if (rc != 0) { //
// We hit an error, so let the Client know:
//
WSGRegisterResult(pomPrimary, pRegistrationCB, rc);
// lonchanc: bug #942 happened here
// this was ERROR_OUT
WARNING_OUT(( "Error %d processing WSG %d", rc, pRegistrationCB->wsg));
//
// Calling WSGRegisterResult above will have dealt with our bad
// return code, so we don't need to return it to our caller. So,
// swallow:
//
rc = 0; }
DebugExitVOID(ProcessWSGRegister); }
//
//
//
// WSGRegisterAbort(...)
//
//
//
void WSGRegisterAbort(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP_REG_CB pRegistrationCB) { DebugEntry(WSGRegisterAbort);
//
// This function can be called at any stage of the workset group
// registration process if for some reason the registration has to be
// aborted.
//
//
// Now remove this Client from the list of Clients registered with the
// workset group and if there are none left, discard the workset group:
//
RemoveClientFromWSGList(pomPrimary->putTask, pRegistrationCB->putTask, pRegistrationCB->pWSGroup);
//
// Now post failure to the Client and finish up the cleanup:
//
WSGRegisterResult(pomPrimary, pRegistrationCB, OM_RC_OUT_OF_RESOURCES);
DebugExitVOID(WSGRegisterAbort); }
//
// WSGRecordCreate(...)
//
UINT WSGRecordCreate ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OMWSG wsg, OMFP fpHandler, POM_WSGROUP * ppWSGroup ) { POM_WSGROUP pWSGroup; BOOL opened = FALSE; UINT rc = 0;
DebugEntry(WSGRecordCreate);
pWSGroup = (POM_WSGROUP)UT_MallocRefCount(sizeof(OM_WSGROUP), TRUE); if (!pWSGroup) { rc = UT_RC_NO_MEM; DC_QUIT; }
SET_STAMP(pWSGroup, WSGROUP); pWSGroup->pDomain = pDomain; pWSGroup->valid = TRUE; pWSGroup->wsg = wsg; pWSGroup->fpHandler = fpHandler;
COM_BasedListInit(&(pWSGroup->clients));
pWSGroup->state = INITIAL;
//
// Finally insert the new WSG record into the domain's list. We insert
// at the end of the list so if we get forced out of a channel
// (a LEAVE_IND event) and the channel happens to be reused by MCS
// for another WSG before we have a chance to process the LEAVE_IND,
// the record for the old WSG will be found first.
//
COM_BasedListInsertBefore(&(pDomain->wsGroups), &(pWSGroup->chain));
//
// *** NEW FOR MULTI-PARTY ***
//
// The checkpointing process used when helping a late joiner catch up
// uses a dummy workset (#255) in each workset group. Create this now:
//
rc = WorksetCreate(pomPrimary->putTask, pWSGroup, OM_CHECKPOINT_WORKSET, FALSE, NET_TOP_PRIORITY); if (rc != 0) { DC_QUIT; }
//
// Set up caller's pointer:
//
*ppWSGroup = pWSGroup;
TRACE_OUT(( "Created record for WSG %d FP %d in Domain %u", wsg, fpHandler, pDomain->callID));
DC_EXIT_POINT:
//
// Cleanup:
//
if (rc != 0) { ERROR_OUT(( "Error %d creating record for WSG %d FP %d in Domain %u", rc, wsg, fpHandler, pDomain->callID));
if (pWSGroup != NULL) { COM_BasedListRemove(&(pWSGroup->chain)); UT_FreeRefCount((void**)&pWSGroup, FALSE); } }
DebugExitDWORD(WSGRecordCreate, rc); return(rc); }
//
//
//
// WSGRegisterStage1(...)
//
//
//
void WSGRegisterStage1(POM_PRIMARY pomPrimary, POM_WSGROUP_REG_CB pRegistrationCB) { POM_DOMAIN pDomain; POM_WSGROUP pWSGroup;
UINT type;
DebugEntry(WSGRegisterStage1);
//
// If the registration CB has been marked invalid, then just quit
// (don't have to do any abort processing since that will have been
// done by whatever marked the CB invalid):
//
if (!pRegistrationCB->valid ) { WARNING_OUT(( "Reg CB for WSG %d marked invalid, quitting", pRegistrationCB->wsg)); DC_QUIT; }
//
// Determine whether we're doing a REGISTER or a MOVE (we use the
// string values for tracing):
//
type = pRegistrationCB->type;
TRACE_OUT(( "Processing %d request (Stage1) for WSG %d", type, pRegistrationCB->wsg));
//
// Set up pointers
//
pDomain = pRegistrationCB->pDomain; pWSGroup = pRegistrationCB->pWSGroup;
//
// Check they're still valid:
//
if (!pDomain->valid) { WARNING_OUT(( "Record for Domain %u not valid, aborting registration", pDomain->callID)); WSGRegisterAbort(pomPrimary, pDomain, pRegistrationCB); DC_QUIT; }
ValidateWSGroup(pWSGroup);
if (!pWSGroup->valid) { WARNING_OUT(( "Record for WSG %d in Domain %u not valid, aborting", pWSGroup->wsg, pDomain->callID)); WSGRegisterAbort(pomPrimary, pDomain, pRegistrationCB); DC_QUIT; }
//
// Now examine the Domain state. If it is
//
// - READY, then this is a Domain that we are fully attached to
//
// - anything else, then we are some way through the process of
// attaching to the Domain (in some other part of the code).
//
// We react to each situation as follows:
//
// - continue with the workset group registration/move
//
// - repost the event with a delay to retry the registration/move in a
// short while.
//
if (pDomain->state != DOMAIN_READY) { //
// Since we are in the process of attaching to the Domain, we can
// do nothing else at the moment. Therefore, we bounce this event
// back to our event queue, with a delay.
//
TRACE_OUT(( "State for Domain %u is %hu", pDomain->callID, pDomain->state)); WSGRegisterRetry(pomPrimary, pRegistrationCB); DC_QUIT; }
//
// OK, so the Domain is in the READY state. What we do next depends on
// two things:
//
// - whether this is a WSGMove or a WSGRegister
//
// - what state the workset group is in.
//
//
// If this is a REGISTER, then if the workset group state is
//
// - READY, then there's another local Client registered with the
// workset, and everything is all set up so we just call
// WSGRegisterSuccess straight away.
//
// - INITIAL, then this is the first time we've been here for this
// workset group, so we start the process of locking
// ObManControl etc. (see below)
//
// - anything else, then we're somewhere in between the two:
// another reqeust to register with the workset group is in
// progress so we repost the event with a delay; by the time it
// comes back to us the workset group should be in the READY
// state.
//
//
// If this is a MOVE, then if the workset group state is
//
// - READY, then the workset group is fully set up in whatever
// Domain it's in at the moment so we allow the move to proceed
//
// - anything else, then we're somewhere in the middle of the
// registration process for the workset group. We do not want
// to interfere with the registration by trying to do a move
// simultaneously (for the simple reason that it introduces far
// more complexity into the state machine) so we bounce the
// event (i.e. we only process a MOVE when the workset group
// is fully set up).
//
TRACE_OUT(( "State for WSG %d is %u", pWSGroup->wsg, pWSGroup->state));
switch (pWSGroup->state) { case INITIAL: { //
// Workset group record has just been created, but nothing else
// has been done.
//
//
// OK, proceed with processing the Client's move/registration
// attempt. Whichever is involved, we start by locking the
// ObManControl workset group; when that completes, we continue
// in WSGRegisterStage2.
//
// Note: this function returns a lock correlator which it
// will be the same as the correlator returned in
// the WORKSET_LOCK_CON event. We will use this
// correlator to look up the registration CB, so
// stuff the return value from the function in it
//
// Note: in the case of a move, we will only ever get
// here because we had to retry the move from the
// top after failing to lock ObManControl
//
LockObManControl(pomPrimary, pDomain, &(pRegistrationCB->lockCorrelator));
pRegistrationCB->flags |= LOCKED_OMC;
pWSGroup->state = LOCKING_OMC; } break;
case LOCKING_OMC: case PENDING_JOIN: case PENDING_SEND_MIDWAY: { //
// We're already in the process of either registering another
// Client with this workset group, or moving the workset group
// into a new Domain, so we delay this Client's
// registration/move attempt for the moment:
//
// Don't expect to get here - remove if error not hit
//
// CMF 21/11/95
ERROR_OUT(( "Should not be here")); WSGRegisterRetry(pomPrimary, pRegistrationCB); DC_QUIT; } break;
case PENDING_SEND_COMPLETE: { //
// WSG Already exists locally, and is fully set up.
//
if (type == WSGROUP_REGISTER) { //
// If we're doing a REGISTER, this means that some other
// Client must be registered with it. If we've passed the
// Clients-per-wsgroup check in ProcessWSGRegister, we must
// be OK, so we post a result straight away (0 indicates
// success):
//
WSGRegisterResult(pomPrimary, pRegistrationCB, 0); } else // type == WSGROUP_MOVE
{ //
// We prohibit moves until we're fully caught up:
//
// Don't expect to get here - remove if error not hit
//
// CMF 21/11/95
ERROR_OUT(( "Should not be here")); WSGRegisterRetry(pomPrimary, pRegistrationCB); DC_QUIT; } } break;
case WSGROUP_READY: { if (type == WSGROUP_REGISTER) { //
// As above:
//
WSGRegisterResult(pomPrimary, pRegistrationCB, 0); } else // type == WSGROUP_MOVE
{ //
// If we're doing a MOVE, then we start by locking
// ObManControl, just as above:
//
LockObManControl(pomPrimary, pDomain, &(pRegistrationCB->lockCorrelator));
pRegistrationCB->flags |= LOCKED_OMC; pWSGroup->state = LOCKING_OMC; } } break;
default: { ERROR_OUT(("Invalid state %u for WSG %d", pWSGroup->state, pWSGroup->wsg)); } }
TRACE_OUT(( "Completed Stage 1 of %d for WSG %d", type, pRegistrationCB->wsg));
DC_EXIT_POINT:
//
// We bumped up the use count of the registration CB when we posted the
// REGISTER_CONT event which got us here, so now free the CB to
// decrement the use count. Unless it's already been freed (e.g.
// because the call went down and the registration was cancelled) it
// will still be around so future stages of the registration process
// will be able to use it.
//
// NB: Although future stages of the registration process are
// asynchronous, they will abort if they cannot find the reg CB in
// the Domain list, so we don't have to worry about bumping it for
// them (since if it is finally freed, then it must have been
// removed from the Domain list).
//
UT_FreeRefCount((void**)&pRegistrationCB, FALSE);
DebugExitVOID(WSGRegisterStage1); }
//
// LockObManControl(...)
//
void LockObManControl(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OM_CORRELATOR * pLockCorrelator) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; UINT rc = 0;
DebugEntry(LockObManControl);
//
// Get pointers to the ObManControl workset group and workset #0 in it:
//
pOMCWSGroup = GetOMCWsgroup(pDomain); if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[0];
//
// Start the lock procedure to lock the workset:
//
WorksetLockReq(pomPrimary->putTask, pomPrimary, pOMCWSGroup, pOMCWorkset, 0, pLockCorrelator);
TRACE_OUT(( "Requested lock for ObManControl in Domain %u", pDomain->callID));
DC_EXIT_POINT: DebugExitVOID(LockObManControl); }
//
//
//
// MaybeUnlockObManControl(...)
//
//
//
void MaybeUnlockObManControl(POM_PRIMARY pomPrimary, POM_WSGROUP_REG_CB pRegistrationCB) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset;
DebugEntry(MaybeUnlockObManControl);
//
// If we've got ObManControl locked for THIS registration, unlock it
//
if (pRegistrationCB->flags & LOCKED_OMC) { pOMCWSGroup = GetOMCWsgroup(pRegistrationCB->pDomain); if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[0];
TRACE_OUT(( "Unlocking OMC for %d in WSG %d", pRegistrationCB->type, pRegistrationCB->wsg));
WorksetUnlock(pomPrimary->putTask, pOMCWSGroup, pOMCWorkset);
pRegistrationCB->flags &= ~LOCKED_OMC; }
DC_EXIT_POINT: DebugExitVOID(MaybeUnlockObManControl); }
//
// ProcessOMCLockConfirm(...)
//
void ProcessOMCLockConfirm ( POM_PRIMARY pomPrimary, OM_CORRELATOR correlator, UINT result ) { POM_WSGROUP_REG_CB pRegistrationCB = NULL; POM_DOMAIN pDomain;
DebugEntry(ProcessOMCLockConfirm);
TRACE_OUT(( "Got LOCK_CON with result = 0x%08x and correlator = %hu", result, correlator));
//
// Next step is to find the registration attempt this lock relates to.
// It could be in any domain, so search through all of them:
//
pDomain = (POM_DOMAIN)COM_BasedListFirst(&(pomPrimary->domains), FIELD_OFFSET(OM_DOMAIN, chain));
while (pDomain != NULL) { COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->pendingRegs), (void**)&pRegistrationCB, FIELD_OFFSET(OM_WSGROUP_REG_CB, chain), FIELD_OFFSET(OM_WSGROUP_REG_CB, lockCorrelator), (DWORD)correlator, FIELD_SIZE(OM_WSGROUP_REG_CB, lockCorrelator));
if (pRegistrationCB != NULL) { TRACE_OUT(( "Found correlated reg CB in domain %u, for WSG %d", pDomain->callID, pRegistrationCB->wsg)); break; }
//
// Didn't find anything in this domain - go on to the next:
//
pDomain = (POM_DOMAIN)COM_BasedListNext(&(pomPrimary->domains), pDomain, FIELD_OFFSET(OM_DOMAIN, chain)); }
//
// If we didn't find it in any of the Domains, it's probably because
// we've detached from the Domain and thrown away its pending
// registrations CBs. So trace and quit:
//
if (pRegistrationCB == NULL) { TRACE_OUT(( "Got LOCK_CON event (correlator: 0x%08x) but no reg CB found", correlator)); DC_QUIT; }
//
// Now check whether the lock succeeded:
//
if (result != 0) { //
// Failed to get the lock on ObManControl for some reason. This
// could be because of contention, or else a more general problem.
// In any event, we call WSGRegisterRetry which will retry (or call
// WSGRegisterResult if we've run out of retries).
//
// Note: since WSGRegisterRetry handles move requests as well, we
// don't need to check here which type of request it is:
//
pRegistrationCB->flags &= ~LOCKED_OMC; WSGRegisterRetry(pomPrimary, pRegistrationCB); } else { //
// We've got the lock on ObManControl workset #0, so now we proceed
// to the next step of the registration process.
//
// As above, this function handles both MOVE and REGISTER attempts.
//
WSGRegisterStage2(pomPrimary, pRegistrationCB); }
DC_EXIT_POINT: DebugExitVOID(ProcessOMCLockConfirm); }
//
// ProcessCheckpoint(...)
//
void ProcessCheckpoint ( POM_PRIMARY pomPrimary, OM_CORRELATOR correlator, UINT result ) { POM_DOMAIN pDomain; POM_WSGROUP pWSGroup; POM_HELPER_CB pHelperCB = NULL;
DebugEntry(ProcessCheckpoint);
//
// Next step is to find the helper CB this lock relates to. It could
// be in any domain, so search through all of them:
//
pDomain = (POM_DOMAIN)COM_BasedListLast(&(pomPrimary->domains), FIELD_OFFSET(OM_DOMAIN, chain)); while (pDomain != NULL) { COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->helperCBs), (void**)&pHelperCB, FIELD_OFFSET(OM_HELPER_CB, chain), FIELD_OFFSET(OM_HELPER_CB, lockCorrelator), (DWORD)correlator, FIELD_SIZE(OM_HELPER_CB, lockCorrelator));
if (pHelperCB != NULL) { TRACE_OUT(( "Found correlated helper CB, for WSG %d", pHelperCB->pWSGroup->wsg)); break; }
//
// Didn't find anything in this domain - go on to the next:
//
pDomain = (POM_DOMAIN)COM_BasedListPrev(&(pomPrimary->domains), pDomain, FIELD_OFFSET(OM_DOMAIN, chain)); }
//
// If we didn't find it in any of the Domains, it's probably because
// we've detached from the Domain and thrown away its pending helper
// CBs. So trace and quit:
//
if (pHelperCB == NULL) { WARNING_OUT(( "No helper CB found with lock correlator 0x%08x!", correlator)); DC_QUIT; }
//
// Set up local pointers:
//
pWSGroup = pHelperCB->pWSGroup; ValidateWSGroup(pWSGroup);
//
// If the "lock" failed, we send a SEND_DENY message to the late
// joiner.
//
if (result != 0) { WARNING_OUT(( "Failed to checkpoint WSG %d for %u - giving up", pWSGroup->wsg, pHelperCB->lateJoiner));
IssueSendDeny(pomPrimary, pDomain, pWSGroup->wsGroupID, pHelperCB->lateJoiner, pHelperCB->remoteCorrelator); DC_QUIT; }
//
// The lock succeeded, so check to see if the workset group pointer we
// stored is still valid:
//
if (!pWSGroup->valid) { WARNING_OUT(("Discarded WSG %d while checkpointing it for %hu", pWSGroup->wsg, pHelperCB->lateJoiner));
IssueSendDeny(pomPrimary, pDomain, pWSGroup->wsGroupID, pHelperCB->lateJoiner, pHelperCB->remoteCorrelator); DC_QUIT; }
//
// All is well - go ahead and send the workset group to the late
// joiner:
//
TRACE_OUT(("Checkpoint succeeded for WSG %d - sending to late joiner %hu", pWSGroup->wsg, pHelperCB->lateJoiner));
SendWSGToLateJoiner(pomPrimary, pDomain, pWSGroup, pHelperCB->lateJoiner, pHelperCB->remoteCorrelator);
DC_EXIT_POINT:
//
// If we found a helper CB, then we just discard it now:
//
if (pHelperCB != NULL) { FreeHelperCB(&pHelperCB); }
DebugExitVOID(ProcessCheckpoint); }
//
// NewHelperCB(...)
//
BOOL NewHelperCB ( POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, NET_UID lateJoiner, OM_CORRELATOR remoteCorrelator, POM_HELPER_CB * ppHelperCB ) { POM_HELPER_CB pHelperCB; BOOL rc = FALSE;
DebugEntry(NewHelperCB);
//
// This function
//
// - allocates a new helper CB
//
// - fills in the fields
//
// - stores it in the domain's list of helper CBs
//
// - bumps the use count of the workset group referenced.
//
pHelperCB = (POM_HELPER_CB)UT_MallocRefCount(sizeof(OM_HELPER_CB), TRUE); if (!pHelperCB) { ERROR_OUT(("Out of memory in NewHelperCB")); DC_QUIT; }
UT_BumpUpRefCount(pWSGroup);
SET_STAMP(pHelperCB, HELPERCB); pHelperCB->pWSGroup = pWSGroup; pHelperCB->lateJoiner = lateJoiner; pHelperCB->remoteCorrelator = remoteCorrelator;
//
// The lock correlator field is filled in later.
//
COM_BasedListInsertBefore(&(pDomain->helperCBs), &(pHelperCB->chain)); rc = TRUE;
DC_EXIT_POINT:
*ppHelperCB = pHelperCB;
DebugExitBOOL(NewHelperCB, rc); return(rc); }
//
// FreeHelperCB(...)
//
void FreeHelperCB ( POM_HELPER_CB * ppHelperCB ) {
DebugEntry(FreeHelperCB);
//
// This function
//
// - frees the workset group referenced in the helper CB
//
// - removes the helper CB from the domain's list
//
// - frees the helper CB.
//
UT_FreeRefCount((void**)&((*ppHelperCB)->pWSGroup), FALSE);
COM_BasedListRemove(&((*ppHelperCB)->chain)); UT_FreeRefCount((void**)ppHelperCB, FALSE);
DebugExitVOID(FreeHelperCB); }
//
// WSGRegisterStage2(...)
//
void WSGRegisterStage2 ( POM_PRIMARY pomPrimary, POM_WSGROUP_REG_CB pRegistrationCB ) { POM_DOMAIN pDomain; POM_WSGROUP pWSGroup; POM_OBJECT pObjInfo; POM_WSGROUP_INFO pInfoObject; NET_CHANNEL_ID channelID; UINT type; UINT rc = 0;
DebugEntry(WSGRegisterStage2);
//
// Determine whether we're doing a REGISTER or a MOVE (we use the string
// value for tracing):
//
type = pRegistrationCB->type;
TRACE_OUT(( "Processing %d request (Stage2) for WSG %d", type, pRegistrationCB->wsg));
//
// We'll need these below:
//
pDomain = pRegistrationCB->pDomain; pWSGroup = pRegistrationCB->pWSGroup;
//
// Check they're still valid:
//
if (!pDomain->valid) { WARNING_OUT(( "Record for Domain %u not valid, aborting registration", pDomain->callID)); WSGRegisterAbort(pomPrimary, pDomain, pRegistrationCB); DC_QUIT; }
if (!pWSGroup->valid) { WARNING_OUT(( "Record for WSG %d in Domain %u not valid, " "aborting registration", pWSGroup->wsg, pDomain->callID)); WSGRegisterAbort(pomPrimary, pDomain, pRegistrationCB); DC_QUIT; }
//
// Sanity check:
//
ASSERT(pWSGroup->state == LOCKING_OMC);
//
// Now find the information object in workset #0 of ObManControl which
// matches the WSG name/FP that the Client requested to register with:
//
FindInfoObject(pDomain, 0, // don't know the ID yet
pWSGroup->wsg, pWSGroup->fpHandler, &pObjInfo);
if (pObjInfo == NULL) { //
// The workset group doesn't already exist in the Domain.
//
// If this is a REGISTER, this means we must create it. If this is a
// MOVE, then we can move it into the Domain, which is essentially
// creating it in the Domain with pre-existing contents.
//
// So, for both types of operation, our behaviour is the same at this
// point; we've already created the workset group record so what we
// do now is
//
// 1. get the Network layer to allocate a new channel ID,
//
// 2. allocate a new workset group ID and
//
// 3. announce the new workset group to the rest of the Domain.
//
// However, the network layer will not assign us a new channel ID
// synchronously, so steps 2 and 3 must be delayed until we receive
// the Join event.
//
// So, now we set the channel to be joined to 0 (this tells the
// Network layer to join us to a currently unused channel).
//
channelID = 0; } else { //
// Otherwise, the workset group already exists.
//
ValidateObject(pObjInfo);
if (type == WSGROUP_REGISTER) { //
// We're registering the Client with an existing workset group, so
// set the workset group ID to the existing value, and ditto for
// the channel ID:
//
pInfoObject = (POM_WSGROUP_INFO) pObjInfo->pData; if (!pInfoObject) { ERROR_OUT(("WSGRegisterStage2 object 0x%08x has no data", pObjInfo)); rc = OM_RC_OBJECT_DELETED; DC_QUIT; }
ValidateObjectDataWSGINFO(pInfoObject);
channelID = pInfoObject->channelID; } else // type == WSGROUP_MOVE
{ //
// We can't move a workset group into a Domain where there already
// exists a workest group with the same name/FP, so we abort our
// move attempt at this point (we set the workset group sate back
// to READY, since that is its state in the Domain it was
// originally in):
//
WARNING_OUT(( "Cannot move WSG %d into Domain %u - WSG/FP clash", pWSGroup->wsg, pDomain->callID));
pWSGroup->state = WSGROUP_READY;
rc = OM_RC_CANNOT_MOVE_WSGROUP; DC_QUIT; } }
//
// Now join the relevant channel (possibly a new one, if <channel> was
// set to 0 above) and stuff the correlator in the <channelCorrelator>
// field of the registration CB (when the Join event arrives,
// ProcessNetJoinChannel will search for the registration CB by channel
// correlator)
//
// Note: if this is our "local" Domain, we skip this step.
//
if (pDomain->callID != NET_INVALID_DOMAIN_ID) { TRACE_OUT(( "Joining channel %hu, Domain %u", channelID, pDomain->callID));
rc = MG_ChannelJoin(pomPrimary->pmgClient, &(pRegistrationCB->channelCorrelator), channelID); if (rc != 0) { DC_QUIT; }
pWSGroup->state = PENDING_JOIN;
//
// OK, that's it for the moment. The saga of workset group
// move/registration will be picked up by the ProcessNetJoinChannel
// function, which will invoke the WSGRegisterStage3 function.
//
} else { //
// Since we didn't do a join just now, we won't be getting a JOIN
// event from the Network layer, so we better call WSGRegisterStage3
// directly:
//
pWSGroup->state = PENDING_JOIN;
// channel ID not relevant here so use zero
WSGRegisterStage3(pomPrimary, pDomain, pRegistrationCB, 0); }
TRACE_OUT(( "Completed Register/Move Stage 2 for WSG %d", pWSGroup->wsg));
DC_EXIT_POINT:
if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(( "Error %d at Stage 2 of %d for WSG %d", rc, pWSGroup->wsg));
WSGRegisterResult(pomPrimary, pRegistrationCB, rc); }
DebugExitVOID(WSGRegisterStage2); }
//
// WSGRegisterStage3(...)
//
void WSGRegisterStage3 ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP_REG_CB pRegistrationCB, NET_CHANNEL_ID channelID ) { POM_WSGROUP pWSGroup; POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_OBJECT pObjInfo; POM_OBJECT pObjReg; POM_WSGROUP_INFO pInfoObject = NULL; UINT type; BOOL catchUpReqd = FALSE; BOOL success = FALSE; // SFR 2744
UINT rc = 0;
DebugEntry(WSGRegisterStage3);
//
// We get here when a Join event has been received containing a channel
// correlator for a channel which is a regular workset group channel.
//
//
// Determine whether we're doing a REGISTER or a MOVE (we use the
// string values for tracing):
//
type = pRegistrationCB->type;
TRACE_OUT(( "Processing %d request (Stage3) for WSG %d", type, pRegistrationCB->wsg));
//
// Get a pointer to the workset group:
//
pWSGroup = pRegistrationCB->pWSGroup;
//
// Check it's still valid:
//
if (!pWSGroup->valid) { WARNING_OUT(("WSG %d' discarded from domain %u - aborting registration", pWSGroup->wsg, pDomain->callID)); WSGRegisterAbort(pomPrimary, pDomain, pRegistrationCB); DC_QUIT; }
//
// Check that this workset group is pending join:
//
if (pWSGroup->state != PENDING_JOIN) { WARNING_OUT(( "Received unexpected Join indication for WSG (state: %hu)", pWSGroup->state)); rc = OM_RC_NETWORK_ERROR; DC_QUIT; }
//
// Now set the channel ID value in the workset group record:
//
pWSGroup->channelID = channelID;
TRACE_OUT(( "Channel ID for WSG %d in Domain %u is %hu", pWSGroup->wsg, pDomain->callID, channelID));
//
// We'll need this below:
//
pOMCWSGroup = GetOMCWsgroup(pDomain);
//
// What we do next depends on whether we just created the workset
// group:
//
// - if it already existed, we need to catch up by asking another node
// for a copy
//
// - if we've just created it, we need to allocate a new workset group
// ID and add an INFO object to workset #0 in ObManControl.
//
// So, we search workset #0 for an INFO object to see if the workset
// group exists.
//
// Note: we did a similar search in Stage2 to find out the channel to
// join for the workset group. The reason we search again here
// is that the workset group could have been discarded by the
// other node in the time taken for the join to complete.
//
FindInfoObject(pDomain, 0, // don't know the ID yet
pWSGroup->wsg, pWSGroup->fpHandler, &pObjInfo);
if (!pObjInfo || !pObjInfo->pData) { //
// Doesn't already exist, so no catch-up required:
//
catchUpReqd = FALSE; } else { //
// OK, so we found an INFO object, but there might not be any
// registration record objects in the relevant registration
// workset, so check:
//
ValidateObject(pObjInfo); pInfoObject = (POM_WSGROUP_INFO) pObjInfo->pData; ValidateObjectDataWSGINFO(pInfoObject);
pOMCWorkset = pOMCWSGroup->apWorksets[pInfoObject->wsGroupID]; if (pOMCWorkset == NULL) { catchUpReqd = TRUE; } else { FindPersonObject(pOMCWorkset, pDomain->userID, FIND_OTHERS, &pObjReg);
if (pObjReg == NULL) { //
// This will happen when the remote node has deleted its
// registration record object but hasn't yet deleted the
// info object. Because the reg rec object is gone, we
// can't catch up from that node (or any node):
//
TRACE_OUT(( "INFO object found but no reg object - creating"));
catchUpReqd = FALSE; } else { ValidateObject(pObjReg); catchUpReqd = TRUE; } } }
//
// We should never try to catch up in the local Domain:
//
if (catchUpReqd && (pDomain->callID == OM_NO_CALL)) { ERROR_OUT(( "Nearly tried to catch up in local Domain!")); catchUpReqd = FALSE; }
if (catchUpReqd) { //
// The workset group already exists, so we need to
//
// - set the workset group ID to the value in the INFO object, and
//
// - start the catch up process.
//
// Note: this will only happen in the case of a REGISTER, so we
// assert
//
ASSERT((pRegistrationCB->type == WSGROUP_REGISTER));
ASSERT((pInfoObject != NULL));
pWSGroup->wsGroupID = pInfoObject->wsGroupID;
rc = WSGCatchUp(pomPrimary, pDomain, pWSGroup);
if (rc == OM_RC_NO_NODES_READY) { //
// We get this return code when there are nodes out there with
// a copy but none of them are ready to send us the workset
// group.
//
// The correct thing to do is to give up for the moment and try
// again:
//
WSGRegisterRetry(pomPrimary, pRegistrationCB); rc = 0; DC_QUIT; }
//
// Any other error is more serious:
//
if (rc != 0) { DC_QUIT; }
//
// We won't be ready to send the workset group to a late-joiner
// node until we've caught up ourselves; when we have, the
// ProcessSendComplete function will call RegAnnounceComplete to
// update the reg object added for us by our helper node.
//
} else { if (type == WSGROUP_MOVE) { //
// If this is a MOVE, pWSGroup refers to a workset group record
// which currently belongs to its "old" Domain. Since we're
// just about to announce the workset group's presence in its
// new Domain, this is the time to do the move:
//
WSGRecordMove(pomPrimary, pRegistrationCB->pDomain, pWSGroup);
//
// This will have reset the channel ID in the workset group
// record so we set it again here (yeah, it's naff):
//
pWSGroup->channelID = channelID; }
//
// We've either just created a new workset group, or moved one into
// a new Domain, so we need to create a new ID for it in this
// Domain:
//
rc = WSGGetNewID(pomPrimary, pDomain, &(pWSGroup->wsGroupID)); if (rc != 0) { DC_QUIT; }
TRACE_OUT(( "Workset group ID for WSG %d in Domain %u is %hu", pWSGroup->wsg, pDomain->callID, pWSGroup->wsGroupID));
//
// Now call CreateAnnounce to add a WSG_INFO object to workset #0
// in ObManControl.
//
rc = CreateAnnounce(pomPrimary, pDomain, pWSGroup); if (rc != 0) { DC_QUIT; }
//
// Since we have completed our registration with the workset group,
// we announce to the world that we have a copy and will send it to
// others on request:
//
rc = RegAnnounceBegin(pomPrimary, pDomain, pWSGroup, pDomain->userID, &(pWSGroup->pObjReg)); if (rc != 0) { DC_QUIT; }
rc = SetPersonData(pomPrimary, pDomain, pWSGroup); if (rc != 0) { DC_QUIT; }
rc = RegAnnounceComplete(pomPrimary, pDomain, pWSGroup); if (rc != 0) { DC_QUIT; }
//
// If we're not catching up, we call Result immediately (if we are
// catching up, Result will be called when we get the SEND_MIDWAY
// message):
//
// SFR 2744 : Can't call result here because we refer to the reg
// CB below. So, just set a flag and act on it below.
//
success = TRUE; }
TRACE_OUT(( "Completed Register/Move Stage 3 for WSG %d", pWSGroup->wsg));
DC_EXIT_POINT:
//
// OK, the critical test-and-set on the ObManControl workset group is
// finished, so we unlock workset #0 in ObManControl:
//
MaybeUnlockObManControl(pomPrimary, pRegistrationCB);
// SFR 2744 { : Call WSGRegResult AFTER checks on the flags in reg CB
if (success == TRUE) { WSGRegisterResult(pomPrimary, pRegistrationCB, 0); } // SFR 2744 }
if (rc != 0) { WARNING_OUT(( "Error %d at Stage 3 of %d with WSG %d", rc, type, pWSGroup->wsg));
WSGRegisterResult(pomPrimary, pRegistrationCB, rc); rc = 0; }
DebugExitVOID(WSGRegisterStage2); }
//
// WSGGetNewID(...)
//
UINT WSGGetNewID ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP_ID pWSGroupID ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_OBJECT pObj; POM_WSGROUP_INFO pInfoObject; OM_WSGROUP_ID wsGroupID; BOOL found; BYTE wsGroupIDsInUse[OM_MAX_WSGROUPS_PER_DOMAIN]; UINT rc = 0;
DebugEntry(WSGGetNewID);
TRACE_OUT(( "Searching for new WSG ID in Domain %u", pDomain->callID));
ZeroMemory(wsGroupIDsInUse, sizeof(wsGroupIDsInUse));
//
// Need to pick a workset group ID so far unused in this Domain to
// identify this new workset group. So, we build up a list of the IDs
// currently in use (by examining the INFO objects in workset #0) and
// then choose one that's not in use.
//
pOMCWSGroup = GetOMCWsgroup(pDomain); if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[0];
pObj = (POM_OBJECT)COM_BasedListFirst(&(pOMCWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain));
while (pObj != NULL) { ValidateObject(pObj);
if (pObj->flags & DELETED) { //
// Do nothing
//
} else if (!pObj->pData) { //
// Do nothing
//
ERROR_OUT(("WSGGetNewID: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pObj->pData); pInfoObject = (POM_WSGROUP_INFO)pObj->pData;
if (pInfoObject->idStamp != OM_WSGINFO_ID_STAMP) { //
// Do nothing
//
} else { //
// OK, we've found a WSGROUP_INFO object, so cross off the
// workset group ID which its workset group is using:
//
wsGroupID = pInfoObject->wsGroupID;
wsGroupIDsInUse[wsGroupID] = TRUE; } }
pObj = (POM_OBJECT)COM_BasedListNext(&(pOMCWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); }
//
// Now go through the array to find an ID that wasn't marked as being in
// use:
//
found = FALSE;
for (wsGroupID = 0; wsGroupID < OM_MAX_WSGROUPS_PER_DOMAIN; wsGroupID++) { if (!wsGroupIDsInUse[wsGroupID]) { TRACE_OUT(( "Workset group ID %hu is not in use, using", wsGroupID)); found = TRUE; break; } }
//
// We checked earlier that the number of workset groups in the Domain
// hadn't exceeded the maximum (in WSGRecordCreate).
//
// However, if the Domain has run out of workset groups in the period
// since then, we won't have found any:
//
if (found == FALSE) { WARNING_OUT(( "No more workset group IDs for Domain %u!", pDomain->callID)); rc = OM_RC_TOO_MANY_WSGROUPS; DC_QUIT; }
//
// If this is the first time that this ID has been used, then the
// associated registration workset won't exist. In this case, we create
// it now.
//
// If the ID has been used before, it will exist but it should be empty.
// In this case, we check that it really is empty.
//
pOMCWorkset = pOMCWSGroup->apWorksets[wsGroupID];
if (pOMCWorkset == NULL) { TRACE_OUT(( "Registration workset %u not used yet, creating", wsGroupID));
rc = WorksetCreate(pomPrimary->putTask, pOMCWSGroup, wsGroupID, FALSE, NET_TOP_PRIORITY); if (rc != 0) { DC_QUIT; } } else { ASSERT((pOMCWorkset->numObjects == 0));
TRACE_OUT(( "Registration workset %u previously used, re-using", wsGroupID)); }
//
// Set the caller's pointer:
//
*pWSGroupID = wsGroupID;
DC_EXIT_POINT:
if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(( "Error %d allocating ID for new workset group", rc)); }
DebugExitDWORD(WSGGetNewID, rc); return(rc); }
//
// CreateAnnounce(...)
//
UINT CreateAnnounce ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_WSGROUP_INFO pInfoObject; POM_OBJECT pObj; OM_OBJECT_ID infoObjectID; UINT rc = 0;
DebugEntry(CreateAnnounce);
TRACE_OUT(("Announcing creation of WSG %d in Domain %u", pWSGroup->wsg, pDomain->callID));
//
// Announcing a new workset group involves adding an object which
// defines the workset group to workset #0 in ObManControl.
//
// So, we derive a pointer to the workset...
//
pOMCWSGroup = GetOMCWsgroup(pDomain); if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[0]; ASSERT((pOMCWorkset != NULL));
//
// ...create a definition object...
//
pInfoObject = (POM_WSGROUP_INFO)UT_MallocRefCount(sizeof(OM_WSGROUP_INFO), TRUE); if (!pInfoObject) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// ...fill in the fields...
//
// (length = sizeof - 4 since value of length field doesn't include the
// size of the length field itself).
//
pInfoObject->length = sizeof(OM_WSGROUP_INFO) - sizeof(OM_MAX_OBJECT_SIZE); pInfoObject->idStamp = OM_WSGINFO_ID_STAMP; pInfoObject->channelID = pWSGroup->channelID; pInfoObject->creator = pDomain->userID; pInfoObject->wsGroupID = pWSGroup->wsGroupID;
lstrcpy(pInfoObject->wsGroupName, OMMapWSGToName(pWSGroup->wsg)); lstrcpy(pInfoObject->functionProfile, OMMapFPToName(pWSGroup->fpHandler));
//
// ...and add the object to the workset...
//
rc = ObjectAdd(pomPrimary->putTask, pomPrimary, pOMCWSGroup, pOMCWorkset, (POM_OBJECTDATA) pInfoObject, 0, // update size == 0
LAST, &infoObjectID, &pObj); if (rc != 0) { DC_QUIT; }
TRACE_OUT(( "Announced new WSG %d in Domain %u", pWSGroup->wsg, pDomain->callID));
DC_EXIT_POINT:
if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(("Error %d announcing new WSG %d in Domain %u", rc, pWSGroup->wsg, pDomain->callID)); }
DebugExitDWORD(CreateAnnounce, rc); return(rc); }
//
// WSGCatchUp(...)
//
UINT WSGCatchUp ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup) { POM_WORKSET pOMCWorkset; POM_OBJECT pObj; POM_WSGROUP_REG_REC pRegObject; NET_UID remoteUserID; UINT rc = 0;
DebugEntry(WSGCatchUp);
TRACE_OUT(( "Starting catch-up for WSG %d in Domain %u", pWSGroup->wsg, pDomain->callID));
//
// This should never be for the "local" Domain:
//
ASSERT((pDomain->callID != NET_INVALID_DOMAIN_ID));
//
// The catch-up procedure is as follows:
//
// - look in ObManControl workset group for the ID of an instance of
// ObMan which has a copy of this workset group
//
// - send it an OMNET_WSGROUP_SEND_REQ message
//
// So, start by getting a pointer to the relevant workset:
//
pOMCWorkset = GetOMCWorkset(pDomain, pWSGroup->wsGroupID); ValidateWorkset(pOMCWorkset);
//
// Now we chain through the workset looking for a reg object which has
// status READY_TO_SEND:
//
pObj = (POM_OBJECT)COM_BasedListFirst(&(pOMCWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain));
remoteUserID = 0;
while (pObj != NULL) { ValidateObject(pObj);
if (pObj->flags & DELETED) { //
// Skip this one
//
} else if (!pObj->pData) { //
// Skip this one
//
ERROR_OUT(("WSGCatchUp: object 0x%08x has no data", pObj)); } else { pRegObject = (POM_WSGROUP_REG_REC)pObj->pData; ValidateObjectDataWSGREGREC(pRegObject);
if ((pRegObject->status == READY_TO_SEND) && (pRegObject->userID != pDomain->userID)) { //
// OK, this node has a full copy, so we'll try to get it from
// there:
//
remoteUserID = pRegObject->userID; break; } }
pObj = (POM_OBJECT)COM_BasedListNext(&(pOMCWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); }
//
// ...check that we did actually find a node to get the data from:
//
if (remoteUserID == 0) { WARNING_OUT(( "No node in Domain %u is ready to send WSG %d - retrying", pDomain->callID, pWSGroup->wsg)); rc = OM_RC_NO_NODES_READY; DC_QUIT; }
//
// ...then send that node a request to send us the workset group:
//
rc = IssueSendReq(pomPrimary, pDomain, pWSGroup, remoteUserID);
DC_EXIT_POINT:
if ((rc != 0) && (rc != OM_RC_NO_NODES_READY)) { ERROR_OUT(( "Error %d starting catch-up for WSG %d in Domain %u", rc, pWSGroup->wsg, pDomain->callID)); }
DebugExitDWORD(WSGCatchUp, rc); return(rc); }
//
// IssueSendDeny(...)
//
void IssueSendDeny ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, NET_UID sender, OM_CORRELATOR remoteCorrelator ) { POMNET_WSGROUP_SEND_PKT pWSGSendPkt;
DebugEntry(IssueSendDeny);
//
// Now issue the SEND_DENY.
//
TRACE_OUT(( "Sending SEND_DENY message to late joiner 0x%08x", sender));
//
// We start by allocating some memory:
//
pWSGSendPkt = (POMNET_WSGROUP_SEND_PKT)UT_MallocRefCount(sizeof(OMNET_WSGROUP_SEND_PKT), TRUE); if (!pWSGSendPkt) { ERROR_OUT(("Out of memory in IssueSendDeny")); DC_QUIT; }
//
// Now fill in the fields:
//
pWSGSendPkt->header.sender = pDomain->userID; pWSGSendPkt->header.messageType = OMNET_WSGROUP_SEND_DENY;
pWSGSendPkt->wsGroupID = wsGroupID;
//
// SFR 7124. Return the correlator for this catchup.
//
pWSGSendPkt->correlator = remoteCorrelator;
//
// Queue the message to be sent.
//
QueueMessage(pomPrimary->putTask, pDomain, sender, NET_TOP_PRIORITY, NULL, // no WSG
NULL, // no workset
NULL, // no object
(POMNET_PKT_HEADER) pWSGSendPkt, NULL, // no object data
TRUE);
DC_EXIT_POINT: DebugExitVOID(IssueSendDeny); }
//
//
//
// IssueSendReq(...)
//
//
//
UINT IssueSendReq(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, NET_UID helperNode) { POMNET_WSGROUP_SEND_PKT pWSGSendPkt; UINT rc = 0;
DebugEntry(IssueSendReq);
//
// We start by allocating some memory for the OMNET_SEND_REQ message:
//
pWSGSendPkt = (POMNET_WSGROUP_SEND_PKT)UT_MallocRefCount(sizeof(OMNET_WSGROUP_SEND_PKT), TRUE); if (!pWSGSendPkt) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// Now fill in the fields:
//
// SFR 7124. Generate a correlator so we can match
// SEND_MIDWAY,SEND_COMPLETE and SEND_DENY messages to this catchup.
//
pWSGSendPkt->header.sender = pDomain->userID; pWSGSendPkt->header.messageType = OMNET_WSGROUP_SEND_REQ;
pWSGSendPkt->wsGroupID = pWSGroup->wsGroupID; pWSGroup->catchupCorrelator = NextCorrelator(pomPrimary); pWSGSendPkt->correlator = pWSGroup->catchupCorrelator;
//
// The <helperNode> parameter is the node which the calling function
// has identified as a remote node which is capable of sending us the
// workset group we want. So, we send that instance of ObMan an
// OMNET_WSGROUP_SEND_REQ on its single-user channel, enclosing our own
// single-user channel ID for the response:
//
// Note: the SEND_REQ must not overtake any data on its way from us to
// the remote node (e.g. if we've just added an object,
// deregistered and then reregistered). Therefore, set the
// NET_SEND_ALL_PRIORITIES flag.
//
// SFR 6117: Don't believe this is a problem for R2.0, so just send at
// low priority.
//
rc = QueueMessage(pomPrimary->putTask, pDomain, helperNode, NET_LOW_PRIORITY, pWSGroup, NULL, // no workset
NULL, // no object
(POMNET_PKT_HEADER) pWSGSendPkt, NULL, // no object data
TRUE); if (rc != 0) { DC_QUIT; }
//
// Set the workset group state, and record the number of SEND_MIDWAY
// and SEND_COMPLETE messages we're expecting (one for R11, one per
// priority for R20).
//
// Note: we set the counts up here because we may get some of the
// SEND_COMPLETEs before we get all the SEND_MIDWAYs, so to set the
// count in ProcessSendMidway would be too late.
//
pWSGroup->state = PENDING_SEND_MIDWAY;
pWSGroup->sendMidwCount = NET_NUM_PRIORITIES; pWSGroup->sendCompCount = NET_NUM_PRIORITIES;
//
// Store the helper node ID in the WSG structure.
//
pWSGroup->helperNode = helperNode;
DC_EXIT_POINT:
if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(( "Error %d requesting send from node 0x%08x " "for WSG %d in Domain %u", rc, pWSGroup->wsg, helperNode, pDomain->callID)); } else { //
// Success:
//
TRACE_OUT(("Requested copy of WSG %d' from node 0x%08x (in Domain %u), correlator %hu", pWSGroup->wsg, helperNode, pDomain->callID, pWSGroup->catchupCorrelator)); }
DebugExitDWORD(IssueSendReq, rc); return(rc);
}
//
// ProcessSendReq(...)
//
void ProcessSendReq ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POMNET_WSGROUP_SEND_PKT pSendReqPkt ) { POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_HELPER_CB pHelperCB; NET_UID sender; BOOL sendDeny = FALSE;
DebugEntry(ProcessSendReq);
//
// This is the user ID of the late joiner:
//
sender = pSendReqPkt->header.sender;
//
// We start by finding our copy of the workset group:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->wsGroups), (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, wsGroupID), (DWORD)pSendReqPkt->wsGroupID, FIELD_SIZE(OM_WSGROUP, wsGroupID));
//
// Quit and deny the send if workset group not found:
//
if (pWSGroup == NULL) { WARNING_OUT(( "Don't have workset group %hu to send to node 0x%08x", pSendReqPkt->wsGroupID, sender));
sendDeny = TRUE; DC_QUIT; }
//
// Quit and deny the send if we don't have ALL the workset group:
//
if (pWSGroup->state != WSGROUP_READY) { WARNING_OUT(("WSG %d is in state %hu - can't send to node 0x%08x", pWSGroup->wsg, pWSGroup->state, sender));
sendDeny = TRUE; DC_QUIT; }
TRACE_OUT(( "Processing SEND_REQUEST from node 0x%08x for WSG %d, correlator %hu", sender, pWSGroup->wsg, pSendReqPkt->correlator));
//
// Right, we're fully registered with the workset group, so we will be
// its helper node. First, allocate a helper CB to keep track of the
// process:
//
if (!NewHelperCB(pDomain, pWSGroup, sender, pSendReqPkt->correlator, &pHelperCB)) { //
// Deny the workset send request
//
sendDeny = TRUE;
WARNING_OUT(( "Failed to allocate helper CB - issuing SEND_DENY")); DC_QUIT; }
//
// Before we can send the contents of the workset group to the late
// joiner, we must ensure that our view of the contents is up to date.
// We do this by checkpointing the workset group, which means locking
// the dummy workset which exists in all workset groups. Do this now:
//
pWorkset = pWSGroup->apWorksets[OM_CHECKPOINT_WORKSET];
WorksetLockReq(pomPrimary->putTask, pomPrimary, pWSGroup, pWorkset, 0, &(pHelperCB->lockCorrelator));
//
// We will shortly get a WORKSET_LOCK_CON event containing the
// correlator just stored in the helper CB. We will look this up and
// continue the catch-up process then.
//
DC_EXIT_POINT:
//
// If we set the sendDeny flag above then now send the SEND_DENY
// message to the late joiner.
//
if (sendDeny) { IssueSendDeny(pomPrimary, pDomain, pSendReqPkt->wsGroupID, sender, pSendReqPkt->correlator); }
DebugExitVOID(ProcessSendReq); }
//
// SendWSGToLateJoiner(...)
//
void SendWSGToLateJoiner ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, NET_UID lateJoiner, OM_CORRELATOR remoteCorrelator ) { POM_WORKSET pWorkset; POMNET_OPERATION_PKT pPacket; POM_OBJECT pObj; POMNET_WSGROUP_SEND_PKT pSendMidwayPkt; POMNET_WSGROUP_SEND_PKT pSendCompletePkt; POM_OBJECTDATA pData; OM_WORKSET_ID worksetID; UINT maxSeqUsed = 0; NET_PRIORITY catchupPriority = 0; UINT rc = 0;
DebugEntry(SendWSGToLateJoiner);
//
// The first thing to do is to announce that the remote node is
// registering with the workset group:
//
rc = RegAnnounceBegin(pomPrimary, pDomain, pWSGroup, lateJoiner, &pObj); if (rc != 0) { DC_QUIT; }
//
// We then start flow control on the user channel of the node that we
// are sending the data to. We only start flow control on the low
// priority channel and don't bother to restrict the maximum stream
// size. If flow control is already started on this stream then this
// call will have no effect. Note that flow control will automatically
// be stopped when the call ends.
//
MG_FlowControlStart(pomPrimary->pmgClient, lateJoiner, NET_LOW_PRIORITY, 0, 8192);
//
// Now, cycle through each of the worksets and generate and send
//
// - WORKSET_NEW messages for each workset,
//
// - a WSG_SEND_MIDWAY message to indicate we've sent all the worksets
//
// - OBJECT_ADD messages for each of the objects in each of the
// worksets.
//
// - a WSG_SEND_COMPLETE message to indicate we've sent all the
// objects.
//
// NOTE: We do not send CHECKPOINT worksets, so the for loop should
// stop before it gets 255.
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { pWorkset = pWSGroup->apWorksets[worksetID]; if (!pWorkset) { continue; }
TRACE_OUT(( "Sending WORKSET_CATCHUP for workset %u", worksetID));
rc = GenerateOpMessage(pWSGroup, worksetID, NULL, // no object ID
NULL, // no object data
OMNET_WORKSET_CATCHUP, &pPacket); if (rc != 0) { DC_QUIT; }
rc = QueueMessage(pomPrimary->putTask, pWSGroup->pDomain, lateJoiner, NET_TOP_PRIORITY, pWSGroup, pWorkset, NULL, // no object
(POMNET_PKT_HEADER) pPacket, NULL, // no object data
TRUE); if (rc != 0) { DC_QUIT; } }
//
// Now send the SEND_MIDWAY message to indicate that all the
// WORKSET_NEW messages have been sent:
//
pSendMidwayPkt = (POMNET_WSGROUP_SEND_PKT)UT_MallocRefCount(sizeof(OMNET_WSGROUP_SEND_PKT), TRUE); if (!pSendMidwayPkt) { rc = UT_RC_NO_MEM; DC_QUIT; }
pSendMidwayPkt->header.sender = pDomain->userID; pSendMidwayPkt->header.messageType = OMNET_WSGROUP_SEND_MIDWAY;
pSendMidwayPkt->wsGroupID = pWSGroup->wsGroupID; pSendMidwayPkt->correlator = remoteCorrelator;
//
// The next field is the ID of the reg object which we added above.
// So, convert the handle of the reg object returned by RegAnnouncBegin
// to a pointer to the object record and then copy the object ID into
// the message packet:
//
memcpy(&(pSendMidwayPkt->objectID), &(pObj->objectID), sizeof(OM_OBJECT_ID));
//
// The last field, which is the highest object ID sequence number
// previously used by the late joiner in this workset group, is not yet
// know; it will be filled in below. However (see note below), we
// queue the message now to ensure it doesn't get stuck behind lots of
// objects:
//
TRACE_OUT(("Queueing WSG_SEND_MIDWAY message to node 0x%08x for WSG %d, correlator %hu", lateJoiner, pWSGroup->wsg, remoteCorrelator));
rc = QueueMessage(pomPrimary->putTask, pWSGroup->pDomain, lateJoiner, NET_TOP_PRIORITY | NET_SEND_ALL_PRIORITIES, pWSGroup, NULL, // no workset
NULL, // no object
(POMNET_PKT_HEADER) pSendMidwayPkt, NULL, // no object data
TRUE); if (rc != 0) { DC_QUIT; }
//
// If the workset group is ObMan control then we should send it at top
// priority to ensure that it can overtake any slower pending sends to
// other nodes. Otherwise we send the send the data at the lowest
// priority.
//
if (pWSGroup->wsGroupID == WSGROUPID_OMC) { catchupPriority = NET_TOP_PRIORITY; } else { catchupPriority = NET_LOW_PRIORITY; } TRACE_OUT(( "Sending catchup data at priority %hu for 0x%08x", catchupPriority, lateJoiner));
//
// Now start the loop which does the OBJECT_ADDs:
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { pWorkset = pWSGroup->apWorksets[worksetID]; if (pWorkset == NULL) { continue; }
TRACE_OUT(( "Sending OBJECT_CATCHUPs for workset %u", worksetID));
//
// Note that we must send deleted objects too, since late-joiners
// have just as much need as we do to detect out of date
// operations:
//
pObj = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObj != NULL) { ValidateObject(pObj);
//
// The workset group that the late joiner is catching up with
// may contain objects which it has added in a previous call
// (with the same network user ID). Since that call is over,
// it may reuse IDs present in this workset group - to prevent
// this, we must tell it the highest sequence count it used for
// object IDs for this workset group, so while we're going
// through the objects, keep a count:
//
if (pObj->objectID.creator == lateJoiner) { maxSeqUsed = max(maxSeqUsed, pObj->objectID.sequence); }
if (pObj->flags & PENDING_DELETE) { //
// If the object is pending delete at this node, we do not
// send the object data. The way to avoid this is to set
// pData to NULL (must be done before call to
// GenerateOpMessage):
//
pData = NULL; } else { pData = pObj->pData;
if (pData) { ValidateObjectData(pData); } }
//
// Now generate the message packet:
//
rc = GenerateOpMessage(pWSGroup, worksetID, &(pObj->objectID), pData, OMNET_OBJECT_CATCHUP, &pPacket); if (rc != 0) { DC_QUIT; }
//
// Now fill in the catchup-specific fields (note that the
// <seqStamp> will already have been filled in, but with the
// current sequence stamp for the workset; for a CatchUp
// message, this should be the add stamp for the object):
//
pPacket->position = pObj->position; pPacket->flags = pObj->flags; pPacket->updateSize = pObj->updateSize;
if (pObj->flags & PENDING_DELETE) { //
// If the object is pending delete at this node, we send it
// as if it has been delete-confirmed (since local
// delete-confirms or their DC_ABSence should have no effect
// outside this box). To do this, we just set the DELETED
// flag in the packet:
//
pPacket->flags &= ~PENDING_DELETE; pPacket->flags |= DELETED; }
COPY_SEQ_STAMP(pPacket->seqStamp, pObj->addStamp); COPY_SEQ_STAMP(pPacket->positionStamp, pObj->positionStamp); COPY_SEQ_STAMP(pPacket->updateStamp, pObj->updateStamp); COPY_SEQ_STAMP(pPacket->replaceStamp, pObj->replaceStamp);
//
// ...and queue the message:
//
rc = QueueMessage(pomPrimary->putTask, pWSGroup->pDomain, lateJoiner, catchupPriority, pWSGroup, pWorkset, NULL, // no object
(POMNET_PKT_HEADER) pPacket, pData, TRUE); if (rc != 0) { DC_QUIT; }
//
// Now go around the loop again:
//
pObj = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); } }
//
// Now that we know the max sequence number used by this user ID in
// this workset group, we can set the field in the SEND_MIDWAY packet:
//
// NOTE: because the ObMan task is single threaded (in the DC_ABSence of
// assertion failure which cause a sort of multithreading while
// the assert box is up) it is safe to alter this value AFTER the
// message has been queued because we know that the queue will
// not have been serviced yet.
//
pSendMidwayPkt->maxObjIDSeqUsed = maxSeqUsed;
//
// Now we send the OMNET_SEND_COMPLETE message. First, allocate some
// memory...
//
pSendCompletePkt = (POMNET_WSGROUP_SEND_PKT)UT_MallocRefCount(sizeof(OMNET_WSGROUP_SEND_PKT), TRUE); if (!pSendCompletePkt) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// ...fill in the fields...
//
pSendCompletePkt->header.sender = pDomain->userID; pSendCompletePkt->header.messageType = OMNET_WSGROUP_SEND_COMPLETE;
pSendCompletePkt->wsGroupID = pWSGroup->wsGroupID; pSendCompletePkt->correlator = remoteCorrelator;
//
// ...and queue the message for sending (it musn't overtake any of the
// data so send it at all priorities):
//
TRACE_OUT(( "Sending WSG_SEND_COMPLETE message, correlator %hu", remoteCorrelator));
rc = QueueMessage(pomPrimary->putTask, pWSGroup->pDomain, lateJoiner, NET_LOW_PRIORITY | NET_SEND_ALL_PRIORITIES, pWSGroup, NULL, // no workset
NULL, // no object
(POMNET_PKT_HEADER) pSendCompletePkt, NULL, // no object data
TRUE); if (rc != 0) { DC_QUIT; }
TRACE_OUT(( "Processed send request from node 0x%08x for WSG %d", lateJoiner, pWSGroup->wsg));
DC_EXIT_POINT:
if (rc != 0) { //
// An error occurred. We must issue a SEND_DENY message to the
// remote node.
//
ERROR_OUT(( "Error %d sending WSG %d to node 0x%08x", rc, pWSGroup->wsg, lateJoiner));
IssueSendDeny(pomPrimary, pDomain, pWSGroup->wsGroupID, lateJoiner, remoteCorrelator); }
DebugExitVOID(SendWSGToLateJoiner); }
//
// ProcessSendMidway(...)
//
void ProcessSendMidway ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POMNET_WSGROUP_SEND_PKT pSendMidwayPkt ) { POM_WORKSET pOMCWorkset; POM_WSGROUP_REG_CB pRegistrationCB = NULL; POM_WSGROUP pWSGroup; BOOL fSetPersonData; NET_UID sender; POM_OBJECT pObjReg; UINT rc = 0;
DebugEntry(ProcessSendMidway);
sender = pSendMidwayPkt->header.sender;
//
// OK, this is an message indicating that the helper node has sent us
// all the WORKSET_CATCHUPs in the workset group we're catching up with
// (but note that the objects haven't yet been sent).
//
// So, search the list of pending registrations using the correlator
// value in the packet (we can't use the workset group ID since if it
// is zero i.e. ObManControl, we'll match on workset groups which
// haven't yet had their IDs determined (since they are initially
// zero).
//
if (pSendMidwayPkt->wsGroupID == WSGROUPID_OMC) { //
// This is a SEND_MIDWAY message for ObManControl.
//
pWSGroup = GetOMCWsgroup(pDomain); fSetPersonData = FALSE; } else { //
// Not for ObManControl so we search the list of pending
// registrations.
//
pRegistrationCB = (POM_WSGROUP_REG_CB)COM_BasedListFirst(&(pDomain->pendingRegs), FIELD_OFFSET(OM_WSGROUP_REG_CB, chain));
while ((pRegistrationCB != NULL) && (pRegistrationCB->pWSGroup->wsGroupID != pSendMidwayPkt->wsGroupID)) { pRegistrationCB = (POM_WSGROUP_REG_CB)COM_BasedListNext(&(pDomain->pendingRegs), pRegistrationCB, FIELD_OFFSET(OM_WSGROUP_REG_CB, chain)); }
if (pRegistrationCB == NULL) { WARNING_OUT(( "Unexpected SEND_MIDWAY for WSG %hu from 0x%08x", pSendMidwayPkt->wsGroupID, sender)); DC_QUIT; }
pWSGroup = pRegistrationCB->pWSGroup; fSetPersonData = TRUE; }
if (NULL == pWSGroup) { TRACE_OUT(( "NULL pWSGroup" )); DC_QUIT; }
if (!pWSGroup->valid) { WARNING_OUT(( "Recd SEND_MIDWAY too late for WSG %d (marked invalid)", pWSGroup->wsg)); DC_QUIT; }
//
// We should be in the PENDING_SEND_MIDWAY state:
//
if (pWSGroup->state != PENDING_SEND_MIDWAY) { WARNING_OUT(( "Recd SEND_MIDWAY with WSG %d in state %hu", pWSGroup->wsg, pWSGroup->state)); DC_QUIT; }
//
// SFR 7124. Check the correlator of this SEND_MIDWAY against the
// correlator we generated locally when we sent the last SEND_REQUEST.
// If they dont match, this is part of an out of date catchup which we
// can ignore.
//
if (pSendMidwayPkt->correlator != pWSGroup->catchupCorrelator) { WARNING_OUT(("Ignoring SEND_MIDWAY with old correlator %hu (expecting %hu)", pSendMidwayPkt->correlator, pWSGroup->catchupCorrelator)); DC_QUIT; }
//
// We should get four of these messages, one at each priority (except
// in a backlevel call when we only get one). Check how many are
// outstanding:
//
pWSGroup->sendMidwCount--; if (pWSGroup->sendMidwCount != 0) { TRACE_OUT(( "Still need %hu SEND_MIDWAY(s) for WSG %d", pWSGroup->sendMidwCount, pWSGroup->wsg)); DC_QUIT; }
TRACE_OUT(( "Last SEND_MIDWAY for WSG %d, ID %hu, from 0x%08x", pWSGroup->wsg, pWSGroup->wsGroupID, sender));
//
// Set up pointers to the ObManControl workset which holds the reg
// objects for the workset group we've just registered with:
//
pOMCWorkset = GetOMCWorkset(pDomain, pWSGroup->wsGroupID);
//
// If we don't have an associated OMC workset, something's wrong...
//
if (pOMCWorkset == NULL) { //
// ...unless it's ObManControl itself that we're catching up with -
// since we can get its SEND_MIDWAY before we've got any of the
// WORKSET_CATCHUPs:
//
if (pWSGroup->wsGroupID != WSGROUPID_OMC) { ERROR_OUT(( "Got SEND_MIDWAY for unknown workset group %hu!", pWSGroup->wsGroupID)); } DC_QUIT; }
//
// Convert the ID of our reg object (as sent by our helper who added it
// in the first place) to an object handle:
//
rc = ObjectIDToPtr(pOMCWorkset, pSendMidwayPkt->objectID, &pObjReg); if (rc != 0) { DC_QUIT; }
//
// If we haven't yet stored a reg object handle for this workset
// group...
//
if (pWSGroup->pObjReg == NULL) { //
// ...store it now...
//
pWSGroup->pObjReg = pObjReg; } //
// ...but if we have...
//
else // pWSGroup->pObjReg != NULL
{ //
// ...and if it's a different one, something's wrong:
//
if (pWSGroup->pObjReg != pObjReg) { WARNING_OUT(( "Recd SEND_MIDWAY from node 0x%08x claiming our reg object " "for WSG %d is 0x%08x but we think it's 0x%08x", sender, pWSGroup->wsg, pObjReg,pWSGroup->pObjReg)); } }
//
// OK, if we've passed all the above tests then everything is normal,
// so proceed:
//
pWSGroup->state = PENDING_SEND_COMPLETE;
if (pSendMidwayPkt->maxObjIDSeqUsed > pomPrimary->objectIDsequence) { TRACE_OUT(( "We've already used ID sequence numbers up to %u for " "this workset group - setting global sequence count to this value", pSendMidwayPkt->objectID.sequence));
pomPrimary->objectIDsequence = pSendMidwayPkt->objectID.sequence; }
//
// Our registration object (added by the remote node) should have
// arrived by now. We need to add the FE/person data to it (unless
// this is for ObManControl, in which case there won't be any):
//
if (fSetPersonData) { rc = SetPersonData(pomPrimary, pDomain, pWSGroup); if (rc != 0) { DC_QUIT; } }
//
// Now post the successful REGISTER_CON event back to the Client, if we
// found a reg CB above:
//
if (pRegistrationCB != NULL) { WSGRegisterResult(pomPrimary, pRegistrationCB, 0); }
DC_EXIT_POINT: DebugExitVOID(ProcessSendMidway); }
//
// ProcessSendComplete(...)
//
UINT ProcessSendComplete ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POMNET_WSGROUP_SEND_PKT pSendCompletePkt ) { POM_WSGROUP pWSGroup; NET_UID sender; UINT rc = 0;
DebugEntry(ProcessSendComplete);
//
// We are now "fully-caught-up" and so are eligible to be helpers
// ourselves, i.e. if someone wants to ask us for the workset group,
// we will be able to send them a copy.
//
sender = pSendCompletePkt->header.sender;
//
// First, we find the workset group the message relates to:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->wsGroups), (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, wsGroupID), (DWORD)pSendCompletePkt->wsGroupID, FIELD_SIZE(OM_WSGROUP, wsGroupID));
if (pWSGroup == NULL) { //
// This will happen just after we have deregistered from a WSGroup
//
WARNING_OUT(( "Unexpected SEND_COMPLETE (ID %hu) from node 0x%08x", pSendCompletePkt->wsGroupID, sender)); DC_QUIT; }
if (!pWSGroup->valid) { //
// This will happen while we are in the process of deregistering
// from a workset group.
//
WARNING_OUT(( "Recd SEND_COMPLETE too late for WSG %d (marked invalid)", pWSGroup->wsg)); DC_QUIT; }
//
// Check it has come from the correct node and that we are in an
// appropriate state to receive it.
//
// The correct state is either PENDING_SEND_COMPLETE or
// PENDING_SEND_MIDWAY (we can receive SEND_COMPLETEs in
// PENDING_SEND_MIDWAY state because of MCS packet reordering).
//
if (pSendCompletePkt->header.sender != pWSGroup->helperNode) { //
// This will happen if we get a late SEND_COMPLETE after we have
// decided to catch up from someone else - don't think this should
// happen!
//
// lonchanc: this actually happened in bug #1554.
// Changed ERROR_OUT to WARNING_OUT
WARNING_OUT(( "Got SEND_COMPLETE from 0x%08x for WSG %d but helper is 0x%08x", sender, pWSGroup->wsg, pWSGroup->helperNode)); DC_QUIT; }
if ((pWSGroup->state != PENDING_SEND_MIDWAY) && (pWSGroup->state != PENDING_SEND_COMPLETE)) { WARNING_OUT(( "Got SEND_COMPLETE for WSG %d from 0x%08x in bad state %hu", pWSGroup->wsg, sender, pWSGroup->state)); DC_QUIT; }
//
// SFR 7124. Check the correlator of this SEND_COMPLETE against the
// correlator we generated locally when we sent the last SEND_REQUEST.
// If they dont match, this is part of an out of date catchup which we
// can ignore.
//
if (pSendCompletePkt->correlator != pWSGroup->catchupCorrelator) { WARNING_OUT(( "Ignoring SEND_COMPLETE with old correlator %hu (expecting %hu)", pSendCompletePkt->correlator, pWSGroup->catchupCorrelator)); DC_QUIT; }
//
// We should get four of these messages, one at each priority (except
// in a backlevel call when we only get one). Check how many are
// outstanding:
//
pWSGroup->sendCompCount--; if (pWSGroup->sendCompCount != 0) { TRACE_OUT(( "Still need %hu SEND_COMPLETE(s) for WSG %d obj 0x%08x", pWSGroup->sendCompCount, pWSGroup->wsg, pWSGroup->pObjReg)); DC_QUIT; }
//
// If so, we announce that we are registered:
//
TRACE_OUT(( "Last SEND_COMPLETE for WSG %d, ID %hu, from 0x%08x obj 0x%08x", pWSGroup->wsg, pWSGroup->wsGroupID, sender, pWSGroup->pObjReg));
rc = RegAnnounceComplete(pomPrimary, pDomain, pWSGroup); if (rc != 0) { DC_QUIT; }
//
// In addition to the above, if this send-completion message is for the
// ObManControl workset group we must also set the Domain state:
//
if (pSendCompletePkt->wsGroupID == WSGROUPID_OMC) { //
// If this message relates to the ObManControl workset group, its
// arrival signifies that we have completed the Domain attach
// process, and are now free to continue the processing of the
// workset group registration attempt which prompted the attach in
// the first place.
//
// The way we "continue" is to set the Domain state to
// DOMAIN_READY, so that next time the delayed-and-retried
// OMINT_EVENT_WSGROUP_REGISTER event arrives, it will actually be
// processed rather than bounced again.
//
TRACE_OUT(( "ObManControl fully arrived for Domain %u - inhibiting token", pDomain->callID));
rc = MG_TokenInhibit(pomPrimary->pmgClient, pDomain->tokenID); if (rc != 0) { DC_QUIT; } pDomain->state = PENDING_TOKEN_INHIBIT; }
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d processing SEND_COMPLETE for WSG %u:%hu", rc, pDomain->callID, pSendCompletePkt->wsGroupID)); }
DebugExitDWORD(ProcessSendComplete, rc); return(rc);
}
//
// RegAnnounceBegin(...)
//
UINT RegAnnounceBegin ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, NET_UID nodeID, POM_OBJECT * ppObjReg ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_WSGROUP_REG_REC pRegObject = NULL; OM_OBJECT_ID regObjectID; UINT updateSize; UINT rc = 0;
DebugEntry(RegAnnounceBegin);
//
// Trace out who this reg object is for:
//
if (nodeID == pDomain->userID) { TRACE_OUT(("Announcing start of our reg with WSG %d in Domain %u", pWSGroup->wsg, pDomain->callID)); } else { TRACE_OUT(( "Announcing start of reg with WSG %d in Domain %u for node 0x%08x", pWSGroup->wsg, pDomain->callID, nodeID)); }
//
// To announce the fact that a node has registered with a workset group,
// we add a registration object to the relevant workset in ObManControl.
//
//
// The "relevant" ObManControl workset is that whose ID is the same as
// the ID of the workset group. To add an object to this workset, we
// will need pointers to the workset itself and to the ObManControl
// workset group:
//
pOMCWSGroup = GetOMCWsgroup(pDomain);
if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[pWSGroup->wsGroupID];
//
// If the ObManControl workset group is not transferred correctly, this
// assertion may fail:
//
ASSERT((pOMCWorkset != NULL));
//
// Now, alloc some memory for the registration record object...
//
pRegObject = (POM_WSGROUP_REG_REC)UT_MallocRefCount(sizeof(OM_WSGROUP_REG_REC), TRUE); if (!pRegObject) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// ...set its fields...
//
pRegObject->length = sizeof(OM_WSGROUP_REG_REC) - sizeof(OM_MAX_OBJECT_SIZE); // == 4
pRegObject->idStamp = OM_WSGREGREC_ID_STAMP; pRegObject->userID = nodeID; pRegObject->status = CATCHING_UP;
//
// ...determine the update size, which is meant to be all fields in the
// REG_REC object except the CPI stuff. We also subtract the size of
// the <length> field because of the way object update sizes are
// defined.
//
updateSize = (sizeof(OM_WSGROUP_REG_REC) - sizeof(TSHR_PERSON_DATA)) - sizeof(OM_MAX_OBJECT_SIZE);
//
// ...and add it to the workset:
//
rc = ObjectAdd(pomPrimary->putTask, pomPrimary, pOMCWSGroup, pOMCWorkset, (POM_OBJECTDATA) pRegObject, updateSize, FIRST, ®ObjectID, ppObjReg); if (rc != 0) { DC_QUIT; }
//
// Done!
//
TRACE_OUT(( "Added reg object for WSG %d to workset %u in OMC " "(handle: 0x%08x, ID: 0x%08x:0x%08x)", pWSGroup->wsg, pOMCWorkset->worksetID, *ppObjReg, regObjectID.creator, regObjectID.sequence));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d adding registration object for WSG %d to " "workset %u in ObManControl", rc, pWSGroup->wsg, pOMCWorkset->worksetID)); }
DebugExitDWORD(RegAnnounceBegin, rc); return(rc);
}
//
// RegAnnounceComplete(...)
//
UINT RegAnnounceComplete ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_OBJECT pObjReg; POM_WSGROUP_REG_REC pRegObject; POM_WSGROUP_REG_REC pNewRegObject; UINT updateSize; UINT rc = 0;
DebugEntry(RegAnnounceComplete);
TRACE_OUT(("Announcing completion of reg for WSG %d", pWSGroup->wsg));
//
// Set up pointers to the ObManControl workset group and the workset
// within it which holds the reg objects for the workset group we've
// just registered with:
//
pOMCWSGroup = GetOMCWsgroup(pDomain);
if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[pWSGroup->wsGroupID];
//
// Set up pointers to the object record and the object data itself:
//
pObjReg = pWSGroup->pObjReg; ValidateObject(pObjReg);
if ((pObjReg->flags & DELETED) || !pObjReg->pData) { ERROR_OUT(("RegAnnounceComplete: object 0x%08x is deleted or has no data", pObjReg)); rc = OM_RC_OBJECT_DELETED; DC_QUIT; }
pRegObject = (POM_WSGROUP_REG_REC)pObjReg->pData; ValidateObjectDataWSGREGREC(pRegObject);
ASSERT(pRegObject->status == CATCHING_UP);
//
// Allocate some memory for the new object with which we are about to
// replace the old one:
//
updateSize = sizeof(OM_WSGROUP_REG_REC) - sizeof(TSHR_PERSON_DATA);
pNewRegObject = (POM_WSGROUP_REG_REC)UT_MallocRefCount(updateSize, FALSE); if (!pNewRegObject) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// Copy the start of the old object into the new one:
//
memcpy(pNewRegObject, pRegObject, updateSize);
//
// Update the status field and also set the length field to be the
// length of the object we just allocated (since this is the number of
// bytes we are updating):
//
pNewRegObject->length = updateSize - sizeof(OM_MAX_OBJECT_SIZE); pNewRegObject->status = READY_TO_SEND;
//
// Issue the update:
//
rc = ObjectDRU(pomPrimary->putTask, pOMCWSGroup, pOMCWorkset, pObjReg, (POM_OBJECTDATA) pNewRegObject, OMNET_OBJECT_UPDATE); if (rc != 0) { DC_QUIT; }
TRACE_OUT(( "Updated status in own reg object for WSG %d to READY_TO_SEND", pWSGroup->wsg));
//
// Set the workset group state, to ensure that the reg/info objects get
// deleted when we deregister.
//
pWSGroup->state = WSGROUP_READY;
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d updating own reg object for WSG %d", rc, pWSGroup->wsg)); }
DebugExitDWORD(RegAnnounceComplete, rc); return(rc);
}
//
// MaybeRetryCatchUp(...)
//
void MaybeRetryCatchUp ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, NET_UID userID ) { POM_WSGROUP pWSGroup; POM_WSGROUP_REG_CB pRegistrationCB;
DebugEntry(MaybeRetryCatchUp);
//
// This function is called on receipt of a DETACH indication from MCS
// or a SEND_DENY message from another node. We check the workset
// group identified and see if we were trying to catch up from the
// departed node.
//
// If we do find a match (on the helperNode), then what we do depends
// on the state of the workset group:
//
// - PENDING_SEND_MIDWAY : Retry the registration from the top.
//
// - PENDING_SEND_COMPLETE : Just repeat the catchup.
//
//
// Find the workset group:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->wsGroups), (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, wsGroupID), (DWORD)wsGroupID, FIELD_SIZE(OM_WSGROUP, wsGroupID)); if (pWSGroup == NULL) { TRACE_OUT(( "No record found for WSG ID %hu", wsGroupID)); DC_QUIT; }
//
// Compare the helperNode stored in the workset group and the userID of
// the node who has either detached or sent us a SEND_DENY message. If
// they do not match then we have nothing further to do.
//
if (pWSGroup->helperNode != userID) { DC_QUIT; }
TRACE_OUT(( "Node 0x%08x was our helper node for WSG %d, in state %hu", userID, pWSGroup->wsg, pWSGroup->state));
//
// We need to retry the registration - check the current state to find
// out how much we need to do.
//
switch (pWSGroup->state) { case PENDING_SEND_MIDWAY: { //
// First check if this is for ObManControl:
//
if (pWSGroup->wsGroupID == WSGROUPID_OMC) { //
// It is, so we need to retry the domain attach process.
// We do this by grabbing the ObMan token and resetting the
// domain state; when the GRAB_CONFIRM event arrives, we
// will rejoin the domain attach process at the correct
// point.
//
if (MG_TokenGrab(pomPrimary->pmgClient, pDomain->tokenID) != 0) { ERROR_OUT(( "Failed to grab token")); DC_QUIT; }
pDomain->state = PENDING_TOKEN_GRAB; } else { //
// Not ObManControl, so there will be a registration CB -
// find it...
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->pendingRegs), (void**)&pRegistrationCB, FIELD_OFFSET(OM_WSGROUP_REG_CB, chain), FIELD_OFFSET(OM_WSGROUP_REG_CB, pWSGroup), (DWORD_PTR)pWSGroup, FIELD_SIZE(OM_WSGROUP_REG_CB, pWSGroup));
if (pRegistrationCB == NULL) { ERROR_OUT(( "No reg CB found for WSG %d in state %hu!", pWSGroup->wsg, PENDING_SEND_MIDWAY)); DC_QUIT; }
//
// ...and retry the registation:
//
WSGRegisterRetry(pomPrimary, pRegistrationCB); } } break;
case PENDING_SEND_COMPLETE: { //
// Retry the object catchup. There is no point in trying to
// find the registration CB as it will have been disposed of as
// soon as we entered the PENDING_SEND_COMPLETE state.
//
if (WSGCatchUp(pomPrimary, pDomain, pWSGroup) != 0)
//
// If there are no nodes ready to provide us with the catchup
// information then we are in a state where everyone either
// does not have the workset group or is catching up the
// workset group.
//
// MD 21/11/95
//
// For now pretend that all is well (it's not!) and go into the
// READY_TO_SEND state - potentially causing ObMan to become
// inconsistent.
{ RegAnnounceComplete(pomPrimary, pDomain, pWSGroup); } } break; }
DC_EXIT_POINT: DebugExitVOID(MaybeRetryCatchUp); }
//
//
//
// WSGRegisterRetry(...)
//
//
//
void WSGRegisterRetry(POM_PRIMARY pomPrimary, POM_WSGROUP_REG_CB pRegistrationCB) { POM_DOMAIN pDomain; POM_WSGROUP pWSGroup; UINT rc = 0;
DebugEntry(WSGRegisterRetry);
//
// Set up pointers
//
pWSGroup = pRegistrationCB->pWSGroup; pDomain = pRegistrationCB->pDomain;
//
// If we've got ObManControl locked for THIS registration, unlock it:
//
MaybeUnlockObManControl(pomPrimary, pRegistrationCB);
//
// If we have joined a channel (so the channelID is non-zero) then
// leave it.
//
if (pWSGroup->channelID != 0) { TRACE_OUT(( "Leaving channel %hu", pWSGroup->channelID));
MG_ChannelLeave(pomPrimary->pmgClient, pWSGroup->channelID);
PurgeReceiveCBs(pRegistrationCB->pDomain, pWSGroup->channelID);
//
// Set the channelID to zero now that we have left it.
//
pWSGroup->channelID = 0; }
//
// Set the workset group state to INITIAL.
//
pWSGroup->state = INITIAL;
//
// We examine the retry count. If it's zero, we call WSGRegisterResult
// to indicate failure. Otherwise, we repost the event with a delay
// and a decremented retry value.
//
if (pRegistrationCB->retryCount == 0) { WARNING_OUT(( "Aborting registration for WSG %d", pRegistrationCB->wsg));
WSGRegisterResult(pomPrimary, pRegistrationCB, OM_RC_TIMED_OUT); } else { //
// Since we're about to post a message referencing the Reg CB, bump
// the use count:
//
UT_BumpUpRefCount(pRegistrationCB);
TRACE_OUT(( "Retrying %d for WSG %d; retries left: %u", pRegistrationCB->type, pRegistrationCB->wsg, pRegistrationCB->retryCount));
pRegistrationCB->retryCount--;
UT_PostEvent(pomPrimary->putTask, pomPrimary->putTask, OM_REGISTER_RETRY_DELAY_DFLT, OMINT_EVENT_WSGROUP_REGISTER_CONT, 0, (UINT_PTR) pRegistrationCB); }
DebugExitVOID(WSGRegisterRetry); }
//
//
//
// WSGRegisterResult(...)
//
//
//
void WSGRegisterResult(POM_PRIMARY pomPrimary, POM_WSGROUP_REG_CB pRegistrationCB, UINT result) { POM_WSGROUP pWSGroup; POM_DOMAIN pDomain; POM_WORKSET pOMCWorkset; OM_EVENT_DATA16 eventData16; OM_EVENT_DATA32 eventData32; UINT type; UINT event = 0;
DebugEntry(WSGRegisterResult);
//
// Assert that this is a valid registration CB (which it DC_ABSolutely
// MUST be, since this function gets called synchronously by some other
// function which should have validated the CB):
//
ASSERT(pRegistrationCB->valid);
//
// If we've still got ObManControl locked for THIS registration, unlock
// it:
//
MaybeUnlockObManControl(pomPrimary, pRegistrationCB);
//
// Determine whether we're doing a REGISTER or a MOVE (we use the
// string values for tracing):
//
type = pRegistrationCB->type;
switch (type) { case WSGROUP_REGISTER: event = OM_WSGROUP_REGISTER_CON; break;
case WSGROUP_MOVE: event = OM_WSGROUP_MOVE_CON; break;
default: ERROR_OUT(("Reached default case in switch statement (value: %hu)", event)); }
//
// Here, we set up pointer to workset group.
//
// NOTE: This field in the structure might be NULL, if we have had to
// abort the registration very early. Therefore, do not use
// pWSGroup without checking it first!!!
//
pWSGroup = pRegistrationCB->pWSGroup; if (pWSGroup) { ValidateWSGroup(pWSGroup); }
//
// Trace if this registration has failed:
//
if (result != 0) { //
// pWSGroup might be NULL if we aborted the registration before we
// got around to creating it in ProcessWSGRegister (pre-Stage1).
// So, do a quick check and use a -1 value for the state if it's
// NULL. In either case pick up the name from the reg CB:
//
WARNING_OUT(( "%d failed for WSG %d (reason: 0x%08x, WSG state: %u)", type, pRegistrationCB->wsg, result, pWSGroup == NULL ? -1 : (UINT)pWSGroup->state));
//
// If a MOVE fails, then the workset group continues to exist in
// the old domain - so set the state back to WSGROUP_READY:
//
if ((type == WSGROUP_MOVE) && (pWSGroup != NULL)) { pWSGroup->state = WSGROUP_READY; } } else { //
// If the registration succeeded, pWSGroup must be OK:
//
ASSERT((pWSGroup != NULL));
ASSERT(((pWSGroup->state == WSGROUP_READY) || (pWSGroup->state == PENDING_SEND_COMPLETE)));
TRACE_OUT(( "%d succeeded for WSG %d (now in state %hu)", type, pRegistrationCB->wsg, pWSGroup->state)); }
//
// Fill in the event parameters and post the result to the Client:
//
eventData16.hWSGroup = pRegistrationCB->hWSGroup; eventData16.worksetID = 0; eventData32.correlator = pRegistrationCB->correlator; eventData32.result = (WORD)result;
UT_PostEvent(pomPrimary->putTask, pRegistrationCB->putTask, 0, event, *(PUINT) &eventData16, *(LPUINT) &eventData32);
//
// If the operation was successful, we also post some more events:
//
if (result == 0) { if (type == WSGROUP_REGISTER) { //
// If this is a REGISTER, we post WORKSET_NEW events to the
// Client for all existing worksets:
//
PostWorksetNewEvents(pomPrimary->putTask, pRegistrationCB->putTask, pWSGroup, pRegistrationCB->hWSGroup);
//
// We also need to generate PERSON_JOINED events - these are
// generated automatically by the ObMan task on receipt of the
// respective OBJECT_ADD events, but only once the registration
// has completed. So, fake ADD events for any objects that may
// exist already:
//
pDomain = pWSGroup->pDomain; pOMCWorkset = GetOMCWorkset(pDomain, pWSGroup->wsGroupID);
PostAddEvents(pomPrimary->putTask, pOMCWorkset, pDomain->omchWSGroup, pomPrimary->putTask); } }
//
// If we mananged to bump up the use counts of the Domain record and
// workset group, free them now:
//
if (pRegistrationCB->flags & BUMPED_CBS) { ASSERT((pWSGroup != NULL));
UT_FreeRefCount((void**)&(pRegistrationCB->pWSGroup), FALSE);
UT_FreeRefCount((void**)&(pRegistrationCB->pDomain), FALSE); }
//
// Dispose of the registration CB - it has served us well!
//
pRegistrationCB->valid = FALSE;
TRACE_OUT(( "Finished %d attempt for WSG %d: result = 0x%08x", type, pRegistrationCB->wsg, result));
COM_BasedListRemove(&(pRegistrationCB->chain)); UT_FreeRefCount((void**)&pRegistrationCB, FALSE);
DebugExitVOID(WSGRegisterResult); }
//
// WSGMove(...)
//
UINT WSGMove ( POM_PRIMARY pomPrimary, POM_DOMAIN pDestDomainRec, POM_WSGROUP pWSGroup ) { UINT rc = 0;
DebugEntry(WSGMove);
//
// Now move the record into the new Domain record (this also removes
// the workset group and its reg object from the old Domain)
//
WSGRecordMove(pomPrimary, pDestDomainRec, pWSGroup);
//
// There is a problem with the way we deal with moving workset groups
// into the local Domain at call-end: if there is already a workset
// group of the same name/FP in the local Domain, we get a name clash,
// which the rest of the ObMan code does not expect. This can cause
// ObMan to get very confused when the workset group is eventually
// discarded from the local Domain, since it tries to throw away the
// wrong WSG_INFO object from workset #0 in ObManControl in the local
// Domain.
//
// In R1.1, this name clash will only ever happen with the ObManControl
// workset group itself, because of the way the apps use workset groups
// (i.e. they never register with one in a call AND one in the local
// Domain simultaneously). Therefore, we make our lives easier by NOT
// fully moving the ObManControl workset group into the local Domain at
// call end.
//
// Note however that it is OK (required, in fact) to move the workset
// group record into the list for the local Domain - the problem arises
// when we try to set it up in the local ObManControl (which we need to
// do for application workset groups so that they can continue to use
// person data objects etc.)
//
// So, if the workset group name matches ObManControl, skip the rest of
// this function:
//
if (pWSGroup->wsg == OMWSG_OM) { TRACE_OUT(("Not registering ObManControl in Domain %u (to avoid clash)", pDestDomainRec->callID)); DC_QUIT; }
//
// Reset the channel ID to zero:
//
pWSGroup->channelID = 0;
//
// Assign a new ID for this workset group:
//
rc = WSGGetNewID(pomPrimary, pDestDomainRec, &(pWSGroup->wsGroupID)); if (rc != 0) { DC_QUIT; }
TRACE_OUT(( "Workset group ID for WSG %d in Domain %u is %hu", pWSGroup->wsg, pDestDomainRec->callID, pWSGroup->wsGroupID));
//
// Now call CreateAnnounce to add a WSG_INFO object to workset #0 in
// ObManControl. There may be a name clash, but we don't mind in this
// case because we've been forced to do the move because of a call end:
//
rc = CreateAnnounce(pomPrimary, pDestDomainRec, pWSGroup); if (rc != 0) { DC_QUIT; }
//
// Now add the reg object:
//
rc = RegAnnounceBegin(pomPrimary, pDestDomainRec, pWSGroup, pDestDomainRec->userID, &(pWSGroup->pObjReg)); if (rc != 0) { DC_QUIT; }
//
// Add the FE data back in:
//
rc = SetPersonData(pomPrimary, pDestDomainRec, pWSGroup); if (rc != 0) { DC_QUIT; }
//
// And update the object, just as if we were registering with it:
//
rc = RegAnnounceComplete(pomPrimary, pDestDomainRec, pWSGroup); if (rc != 0) { DC_QUIT; }
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d moving WSG %d into Domain %u", rc, pWSGroup->wsg, pDestDomainRec->callID)); }
DebugExitDWORD(WSGMove, rc); return(rc);
}
//
// WSGRecordMove(...)
//
void WSGRecordMove ( POM_PRIMARY pomPrimary, POM_DOMAIN pDestDomainRec, POM_WSGROUP pWSGroup ) { POM_DOMAIN pOldDomainRec;
DebugEntry(WSGRecordMove);
//
// Find the record for the Domain the workset group is currently in:
//
pOldDomainRec = pWSGroup->pDomain; ASSERT(pOldDomainRec->valid);
DeregisterLocalClient(pomPrimary, &pOldDomainRec, pWSGroup, FALSE);
//
// Insert it into the destination Domain:
//
TRACE_OUT(("Inserting WSG %d' into list for Domain %u", pWSGroup->wsg, pDestDomainRec->callID));
COM_BasedListInsertBefore(&(pDestDomainRec->wsGroups), &(pWSGroup->chain));
//
// SFR : reset the pending data ack byte counts:
//
WSGResetBytesUnacked(pWSGroup);
//
// The workset group now belongs to this new Domain, so set it so.
//
pWSGroup->pDomain = pDestDomainRec;
//
// Finally, post the MOVE_IND event to all Clients registered with the
// workset group:
//
WSGroupEventPost(pomPrimary->putTask, pWSGroup, PRIMARY | SECONDARY, OM_WSGROUP_MOVE_IND, 0, // no workset
pDestDomainRec->callID);
DebugExitVOID(WSGRecordMove); }
//
// WSGResetBytesUnacked(...)
//
void WSGResetBytesUnacked ( POM_WSGROUP pWSGroup ) { OM_WORKSET_ID worksetID; POM_WORKSET pWorkset;
DebugEntry(WSGResetBytesUnacked);
//
// Reset workset group's unacked byte count:
//
pWSGroup->bytesUnacked = 0;
//
// Now do it for each workset in the workset group:
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { pWorkset = pWSGroup->apWorksets[worksetID]; if (pWorkset != NULL) { pWorkset->bytesUnacked = 0; } }
DebugExitVOID(WSGResetBytesUnacked); }
//
//
//
// ProcessWSGDiscard(...)
//
//
//
void ProcessWSGDiscard ( POM_PRIMARY pomPrimary, POM_WSGROUP pWSGroup ) { POM_DOMAIN pDomain;
DebugEntry(ProcessWSGDiscard);
ASSERT(!pWSGroup->valid);
//
// Now get pointer to Domain record:
//
pDomain = pWSGroup->pDomain;
//
// If the TO_BE_DISCARDED flag has been cleared since the DISCARD event
// was posted, we abort the discard process (this will happen when
// someone local has registered with the workset since it was marked
// TO_BE_DISCARDED).
//
if (!pWSGroup->toBeDiscarded) { WARNING_OUT(( "Throwing away DISCARD event since WSG %d no longer TO_BE_DISCARDED", pWSGroup->wsg)); DC_QUIT; }
//
// Otherwise, we can go ahead and discard it:
//
WSGDiscard(pomPrimary, pDomain, pWSGroup, FALSE);
DC_EXIT_POINT: DebugExitVOID(ProcessWSGDiscard); }
//
// WSGDiscard(...)
//
void WSGDiscard ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup, BOOL fExit ) { POM_WORKSET pWorkset; OM_WORKSET_ID worksetID;
DebugEntry(WSGDiscard);
TRACE_OUT(( "Discarding WSG %d from Domain %u", pWSGroup->wsg, pDomain->callID));
//
// We only ever discard a workset group when nobody's registered with
// it, so check:
//
ASSERT(COM_BasedListFirst(&(pWSGroup->clients), FIELD_OFFSET(OM_CLIENT_LIST, chain)) == NULL);
//
// "Discarding" a workset group involves
//
// - calling DeregisterLocalClient to remove our person object, leave
// the channel, remove the workset group from our domain list etc.
//
// - discarding each of the worksets in the workset group
//
// - freeing the workset group record (which will have been removed
// from the list hung off the Domain record by
// DeregisterLocalClient).
//
DeregisterLocalClient(pomPrimary, &pDomain, pWSGroup, fExit);
//
// Now discard each workset in use:
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { pWorkset = pWSGroup->apWorksets[worksetID]; if (pWorkset != NULL) { WorksetDiscard(pWSGroup, &pWorkset, fExit); } }
//
// Discard the checkpointing dummy workset:
//
pWorkset = pWSGroup->apWorksets[OM_CHECKPOINT_WORKSET]; ASSERT((pWorkset != NULL));
WorksetDiscard(pWSGroup, &pWorkset, fExit);
//
// Free the workset group record (it will have been removed from the
// domain's list by DeregisterLocalClient, above):
//
UT_FreeRefCount((void**)&pWSGroup, FALSE);
DebugExitVOID(WSGDiscard); }
//
// DeregisterLocalClient(...)
//
void DeregisterLocalClient ( POM_PRIMARY pomPrimary, POM_DOMAIN* ppDomain, POM_WSGROUP pWSGroup, BOOL fExit ) { POM_DOMAIN pDomain; UINT callID;
DebugEntry(DeregisterLocalClient);
pDomain = *ppDomain; callID = pDomain->callID;
TRACE_OUT(("Removing WSG %d from Domain %u - state is currently %hu", pWSGroup->wsg, callID, pWSGroup->state));
//
// Removing a workset group from a Domain involves
//
// - deleting the registration object from the relevant registration
// workset in ObManControl, if we put one there earlier
//
// - calling WSGDiscard if there is no one left in the Domain who
// is registered with the workset group
//
// - leaving the relevant channel
//
// - removing the workset group from the list hung off the Domain
// record
//
// We will skip some of these unwinding stages, depending on how far we
// got in the registration process. We use a switch statement with NO
// BREAKS to determine our "entry point" into the unwinding.
//
// When we've done all that, we check to see if we are now no longer
// registered with any workset groups in this Domain. If not, we
// detach from the Domain.
//
switch (pWSGroup->state) { case WSGROUP_READY: case PENDING_SEND_COMPLETE: case PENDING_SEND_MIDWAY: { //
// SFR 5913: Purge any outstanding lock requests for the
// workset group.
//
PurgeLockRequests(pDomain, pWSGroup);
//
// Search for and remove our person object, if we have one:
//
RemovePersonObject(pomPrimary, pDomain, pWSGroup->wsGroupID, pDomain->userID);
pWSGroup->pObjReg = NULL;
//
// If we joined a channel for this workset group, leave it:
//
if (pWSGroup->channelID != 0) { TRACE_OUT(( "Leaving channel %hu", pWSGroup->channelID));
if (!fExit) { MG_ChannelLeave(pomPrimary->pmgClient, pWSGroup->channelID); }
//
// Purge any outstanding receives on this channel:
//
PurgeReceiveCBs(pDomain, pWSGroup->channelID); } } // NO BREAK - fall through to next case
case PENDING_JOIN: case LOCKING_OMC: case INITIAL: { //
// If we didn't get as far as PENDING_SEND_MIDWAY then there's
// very little unwinding to do. This bit removes the workset
// group from the Domain's list:
//
TRACE_OUT(( "Removing workset group record from list"));
COM_BasedListRemove(&(pWSGroup->chain));
//
// We set the channel ID to zero here because even if we never
// succeeded in joining the channel, the field will contain the
// channel CORRELATOR returned to us by MG_ChannelJoin
//
pWSGroup->channelID = 0;
//
// Since the workset group is no longer associated with any
// Domain, NULL it out.
//
pWSGroup->pDomain = NULL; } break;
default: { ERROR_OUT(( "Default case in switch (value: %hu)", pWSGroup->state)); } }
//
// If this was the last workset group in the domain...
//
if (COM_BasedListIsEmpty(&(pDomain->wsGroups))) { //
// ...we should detach:
//
// Note: this will only happen when the workset group we have just
// removed is the ObManControl workset group, so assert:
//
if (!fExit) { ASSERT(pWSGroup->wsg == OMWSG_OM); }
//
// Since ObMan no longer needs this workset group, we remove it
// from the list of registered Clients:
//
RemoveClientFromWSGList(pomPrimary->putTask, pomPrimary->putTask, pWSGroup);
TRACE_OUT(( "No longer using any wsGroups in domain %u - detaching", callID));
//
// This will NULL the caller's pointer:
//
DomainDetach(pomPrimary, ppDomain, fExit); }
DebugExitVOID(DeregisterLocalClient); }
//
// WorksetDiscard(...)
//
void WorksetDiscard ( POM_WSGROUP pWSGroup, POM_WORKSET * ppWorkset, BOOL fExit ) { POM_OBJECT pObj; POM_OBJECT pObjTemp; POM_WORKSET pWorkset; POM_CLIENT_LIST pClient;
DebugEntry(WorksetDiscard);
//
// Set up local pointer:
//
pWorkset = *ppWorkset;
//
// The code here is similar to that in WorksetDoClear, but in this case
// we discard ALL objects, irrespective of the sequence stamps.
//
// In addition, WorksetDoClear doesn't cause the object records to be
// freed - it only marks them as deleted - whereas we actually free them
// up.
//
TRACE_OUT(( "Discarding all objects in workset %u in WSG %d", pWorkset->worksetID, pWSGroup->wsg));
CheckObjectCount(pWSGroup, pWorkset);
pObj = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObj != NULL) { ValidateObject(pObj);
pObjTemp = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain));
//
// If the object (data) hasn't yet been deleted, do it now:
//
if (!(pObj->flags & DELETED)) { if (!pObj->pData) { ERROR_OUT(("WorksetDiscard: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pObj->pData); UT_FreeRefCount((void**)&pObj->pData, FALSE); }
pWorkset->numObjects--; }
//
// Now remove the object record itself from the list and free it:
//
TRACE_OUT(( "Freeing pObj at 0x%08x", pObj));
// NULL this out to catch stale references
COM_BasedListRemove(&(pObj->chain)); UT_FreeRefCount((void**)&pObj, FALSE);
pObj = pObjTemp; }
CheckObjectCount(pWSGroup, pWorkset);
ASSERT(pWorkset->numObjects == 0);
//
// Mark the slot in workset offset array (hung off the workset group
// record) as empty:
//
pWSGroup->apWorksets[pWorkset->worksetID] = NULL;
//
// Free the clients
//
while (pClient = (POM_CLIENT_LIST)COM_BasedListFirst(&(pWorkset->clients), FIELD_OFFSET(OM_CLIENT_LIST, chain))) { TRACE_OUT(("WorksetDiscard: Freeing client 0x%08x workset 0x%08x", pClient, pWorkset));
COM_BasedListRemove(&(pClient->chain)); UT_FreeRefCount((void**)&pClient, FALSE); }
//
// Now discard the chunk holding the workset, setting the caller's
// pointer to NULL:
//
TRACE_OUT(( "Discarded workset %u in WSG %d", pWorkset->worksetID, pWSGroup->wsg));
UT_FreeRefCount((void**)ppWorkset, FALSE);
DebugExitVOID(WorksetDiscard); }
//
// ProcessOMCObjectEvents(...)
//
void ProcessOMCObjectEvents ( POM_PRIMARY pomPrimary, UINT event, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { POM_DOMAIN pDomain; POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_WSGROUP pWSGroup; POM_OBJECT pObjOld; POM_WSGROUP_REG_REC pPersonObject;
DebugEntry(ProcessOMCObjectEvents);
//
// In this function, we do the following:
//
// - find the domain and workset group this event belongs to
//
// - if we have a local client to whom we might be interested in
// posting a person data event, call GeneratePersonEvents
//
// - if this is an object add for a person data object which has our
// user ID in it, store the handle in the workset group record unless
// we're not expecting the person object, in which case delete it
//
// - if this is an object deleted indication for a person data object
// then we count the number of remaining person objects for the
// workset group. If it is zero then we remove the info object.
//
//
// To find the domain, we search the list of active domains, looking up
// the hWSGroup parameter against the omchWSGroup field:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, omchWSGroup), (DWORD)hWSGroup, FIELD_SIZE(OM_DOMAIN, omchWSGroup)); if (pDomain == NULL) { //
// This should only happen at call end time.
//
TRACE_OUT(( "No domain with omchWSGroup %u - has call just ended?", hWSGroup)); DC_QUIT; }
//
// To find the workset group, we use the fact that the ID of the
// control workset (for which we have just received the event) is the
// same as the ID of the workset group to which it relates. So, do a
// lookup on this ID:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->wsGroups), (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, wsGroupID), (DWORD)worksetID, FIELD_SIZE(OM_WSGROUP, wsGroupID));
//
// SFR 5593: Changed comparison to PENDING_SEND_MIDWAY from
// WSGROUP_READY to ensure that late joiners get the person add events.
//
if ((pWSGroup != NULL) && (pWSGroup->state > PENDING_SEND_MIDWAY)) { //
// This means that a local client has fully registered with the
// workset group, so we're in a position maybe translate the event
// to a person event:
//
TRACE_OUT(( "Recd event 0x%08x for person object 0x%08x (for WSG %d in state %hu)", event, pObj, pWSGroup->wsg, pWSGroup->state)); GeneratePersonEvents(pomPrimary, event, pWSGroup, pObj); }
//
// Now, if this event is an ADD event for an object which
//
// - has not been deleted
// - is a person object (i.e. has an OM_WSGREGREC_ID_STAMP stamp)
// - contains our user ID (i.e. is _our_ person object)
//
// then we do one of the following:
//
// - if the workset group exists get a handle to the old person object
// and delete it. Then store the handle of the new person object in
// the workset group record.
// - if the workset group does not exist then delete the person object.
//
// This fixes SFRs 2745 and 2592 which are caused by person objects
// getting left hanging around in some start/stop race scenarios.
//
ValidateObject(pObj);
if ((event == OM_OBJECT_ADD_IND) && !(pObj->flags & DELETED)) { pPersonObject = (POM_WSGROUP_REG_REC)pObj->pData;
if (!pPersonObject) { ERROR_OUT(("ProcessOMCObjectEvents: object 0x%08x has no data", pObj)); }
if (pPersonObject && (pPersonObject->idStamp == OM_WSGREGREC_ID_STAMP) && (pPersonObject->userID == pDomain->userID)) { ValidateObjectData(pObj->pData);
pOMCWSGroup = GetOMCWsgroup(pDomain); if (pOMCWSGroup == NULL) { // lonchanc: ingore left-over events due to race condition
DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[worksetID];
if (pWSGroup != NULL) { if ((pWSGroup->pObjReg != NULL) && (pWSGroup->pObjReg != pObj)) { //
// This object replaces an earlier one we had, so...
//
WARNING_OUT(( "Deleting old person object 0x%08x for WSG %d, " "since person object 0x%08x has just arrived", pWSGroup->pObjReg, pWSGroup->wsg, pObj));
//
// ...set up a pointer to the _old_ object record...
//
pObjOld = pWSGroup->pObjReg;
//
// ...and delete it:
//
ObjectDRU(pomPrimary->putTask, pOMCWSGroup, pOMCWorkset, pObjOld, NULL, OMNET_OBJECT_DELETE); }
pWSGroup->pObjReg = pObj; } else { //
// We've deregistered from the workset group - delete the
// object:
//
TRACE_OUT(( "Deleting reg object 0x%08x since WSG ID %hu not found", pObj, worksetID));
ObjectDRU(pomPrimary->putTask, pOMCWSGroup, pOMCWorkset, pObj, NULL, OMNET_OBJECT_DELETE); } } else { //
// Not our person object - do nothing.
//
}
//
// Finished so quit out.
//
DC_QUIT; }
//
// Now, if this event is a DELETED event then we check to see if anyone
// is still using the workset group. If not then we remove the info
// object.
//
if (event == OM_OBJECT_DELETED_IND) { //
// We need to check the number of person objects left in this
// ObMan control workset if it is not workset zero. If there are
// no person objects left then remove any orphaned INFO objects.
//
pOMCWSGroup = GetOMCWsgroup(pDomain); if (pOMCWSGroup == NULL) { // lonchanc: ingore left-over events due to race condition
DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[worksetID]; if (pOMCWorkset == NULL) { // lonchanc: ingore left-over events due to race condition
DC_QUIT; }
if ((pOMCWorkset->numObjects == 0) && (worksetID != 0)) { TRACE_OUT(( "Workset %hu has no person objects - deleting INFO object", worksetID));
RemoveInfoObject(pomPrimary, pDomain, worksetID); }
//
// A person object has been removed and as we are potentially in
// the middle of a workset group catchup from this person we may
// need to retry the catchup.
//
// We search through all the workset groups looking for WSGs that
// are in the PENDING_SEND_MIDWAY or PENDING_SEND_COMPLETE state
// (i.e. in catchup state). If they are we then search to ensure
// that the person object for them still exists. If it doesn't
// then we need to retry the catchup.
//
pOMCWSGroup = GetOMCWsgroup(pDomain); if (pOMCWSGroup == NULL) { // lonchanc: ingore left-over events due to race condition
DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[worksetID]; if (pOMCWorkset == NULL) { // lonchanc: ingore left-over events due to race condition
DC_QUIT; }
pWSGroup = (POM_WSGROUP)COM_BasedListFirst(&(pDomain->wsGroups), FIELD_OFFSET(OM_WSGROUP, chain)); while (pWSGroup != NULL) { //
// Check the WSG state to see if we are in the middle of a
// catchup.
//
if ((PENDING_SEND_MIDWAY == pWSGroup->state) || (PENDING_SEND_COMPLETE == pWSGroup->state)) { //
// We are in the middle of a catchup so we need to check
// to see that the person object for the person that we
// are catching up from has not been deleted.
//
FindPersonObject(pOMCWorkset, pWSGroup->helperNode, FIND_THIS, &pObj);
//
// Check the person handle.
//
if (NULL == pObj) { TRACE_OUT(("Person object removed for WSG %d - retrying" " catchup", pWSGroup->wsg));
//
// Force MaybeRetryCatchUp to retry the catchup by
// passing the helper node ID that is stored in the
// workset.
//
MaybeRetryCatchUp(pomPrimary, pDomain, pWSGroup->wsGroupID, pWSGroup->helperNode); } else { UT_BumpUpRefCount(pObj); } }
//
// Get the next WSG.
//
pWSGroup = (POM_WSGROUP)COM_BasedListNext(&(pDomain->wsGroups), pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain)); } }
DC_EXIT_POINT: if (pObj) { UT_FreeRefCount((void**)&pObj, FALSE); }
DebugExitVOID(ProcessOMCObjectEvents); }
//
// GeneratePersonEvents(...)
//
void GeneratePersonEvents ( POM_PRIMARY pomPrimary, UINT event, POM_WSGROUP pWSGroup, POM_OBJECT pObj ) { POM_WSGROUP_REG_REC pPersonObject; UINT newEvent = 0;
DebugEntry(GeneratePersonEvents);
//
// OK, to get here we must have determined that a local client has
// registered with the workset group. Now proceed to examine the event
// and generate an appropriate person event for the client:
//
switch (event) { case OM_OBJECT_ADD_IND: case OM_OBJECT_UPDATED_IND: { ValidateObject(pObj); if (pObj->flags & DELETED) { //
// The object has been deleted already! We can't check its
// state so just quit:
//
DC_QUIT; } if (!pObj->pData) { ERROR_OUT(("GeneratePersonEvents: object 0x%08x has no data", pObj)); DC_QUIT; }
//
// We're only interested in person objects, so if it's anything
// else, quit:
//
ValidateObjectData(pObj->pData); pPersonObject = (POM_WSGROUP_REG_REC)pObj->pData;
if (pPersonObject->idStamp != OM_WSGREGREC_ID_STAMP) { DC_QUIT; }
//
// Translate to a PERSON_JOINED event, provided the person data
// has actually arrived. We determine this by reading the
// object and checking the <status> in it:
//
if (pPersonObject->status == READY_TO_SEND) { newEvent = OM_PERSON_JOINED_IND; } } break;
case OM_OBJECT_DELETED_IND: { //
// This means that someone has left the call
//
newEvent = OM_PERSON_LEFT_IND; } break;
case OM_OBJECT_REPLACED_IND: { //
// This means someone has done a SetPersonData:
//
newEvent = OM_PERSON_DATA_CHANGED_IND; } break; }
//
// If there is any translating to be done, newEvent will now be
// non-zero:
//
if (newEvent != 0) { WSGroupEventPost(pomPrimary->putTask, pWSGroup, PRIMARY, newEvent, 0, (UINT_PTR)pObj); }
DC_EXIT_POINT: DebugExitVOID(GeneratePersonEvents); }
//
// ProcessOMCWorksetNew(...)
//
void ProcessOMCWorksetNew ( POM_PRIMARY pomPrimary, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID ) { POM_DOMAIN pDomain; POM_WORKSET pOMCWorkset; POM_CLIENT_LIST pClientListEntry;
DebugEntry(ProcessOMCWorksetNew);
//
// The ObMan task generates person data events for its clients when the
// contents of the relevant control workset changes. We therefore add
// ObMan to this new control workset's list of "clients" and post it
// events for any objects already there:
//
// NOTE: We specify that ObMan should be considered a SECONDARY "client"
// of this workset so that it is not required to confirm delete
// events etc.
//
TRACE_OUT(( "Recd WORKSET_NEW for workset %u, WSG %u", worksetID, hWSGroup));
//
// Look up the domain record based on the workset group handle:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, omchWSGroup), (DWORD)hWSGroup, FIELD_SIZE(OM_DOMAIN, omchWSGroup));
if (pDomain == NULL) { WARNING_OUT(( "No domain record found with omchWSGroup %d", hWSGroup)); DC_QUIT; }
pOMCWorkset = GetOMCWorkset(pDomain, worksetID);
ASSERT((pOMCWorkset != NULL));
if (AddClientToWsetList(pomPrimary->putTask, pOMCWorkset, hWSGroup, SECONDARY, &pClientListEntry) != 0) { DC_QUIT; } TRACE_OUT(( "Added ObMan as secondary client for workset"));
PostAddEvents(pomPrimary->putTask, pOMCWorkset, hWSGroup, pomPrimary->putTask);
DC_EXIT_POINT: DebugExitVOID(ProcessOMCWorksetNew); }
//
// ProcessSendQueue()
//
void ProcessSendQueue ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, BOOL domainRecBumped ) { POM_SEND_INST pSendInst; NET_PRIORITY priority;
DebugEntry(ProcessSendQueue);
//
// Check the Domain record is still valid:
//
if (!pDomain->valid) { TRACE_OUT(( "Got OMINT_EVENT_SEND_QUEUE too late for discarded Domain %u", pDomain->callID)); DC_QUIT; }
//
// Check that there is supposed to be a send event outstanding:
//
if (pDomain->sendEventOutstanding) { //
// Although there might still be a send event outstanding (e.g. a
// FEEDBACK event) we can't be sure (unless we count them as we
// generate them). It's vital that we never leave the send queue
// unprocessed, so to be safe we clear the flag so that QueueMessage
// will post an event next time it's called:
//
pDomain->sendEventOutstanding = FALSE; } else { //
// This will happen
//
// - when we get a FEEDBACK event after we've cleared the queue, OR
//
// - when we get a SEND_QUEUE event which was posted because there
// were none outstanding but a FEEDBACK event arrived in the
// meantime to clear the queue.
//
// NOTE: this flag means that there MIGHT not be a send EVENT
// outstanding (see above). It does not mean that there's
// nothing on the send queue, so we go ahead and check the
// queue.
//
}
//
// The strategy for processing the send queue is to process the highest
// priority operation first, whether or not a transfer is in progress
// at another priority.
//
// So, for each priority, we check if there's anything in the queue:
//
TRACE_OUT(("Searching send queues for Domain %u",pDomain->callID));
for (priority = NET_TOP_PRIORITY; priority <= NET_LOW_PRIORITY; priority++) { TRACE_OUT(("Processing queue at priority %u", priority));
while (pSendInst = (POM_SEND_INST)COM_BasedListFirst(&(pDomain->sendQueue[priority]), FIELD_OFFSET(OM_SEND_INST, chain))) { TRACE_OUT(("Found send instruction for priority %u", priority));
if (SendMessagePkt(pomPrimary, pDomain, pSendInst) != 0) { DC_QUIT; } } }
DC_EXIT_POINT:
if (domainRecBumped) { //
// If our caller has told us that the use count of the Domain
// record has been bumped, free it now:
//
UT_FreeRefCount((void**)&pDomain, FALSE); }
DebugExitVOID(ProcessSendQueue); }
//
// SendMessagePkt(...)
//
UINT SendMessagePkt ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_SEND_INST pSendInst ) { void * pNetBuffer = NULL; void * pAnotherNetBuffer = NULL; UINT transferSize; UINT dataTransferSize; BOOL compressed; BOOL tryToCompress; BOOL spoiled = FALSE; BOOL allSent = FALSE; NET_PRIORITY queuePriority; BOOL fSendExtra; POMNET_PKT_HEADER pMessage; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; UINT rc = 0;
DebugEntry(SendMessagePkt);
//
// We check here if we can spoil this message:
//
rc = TryToSpoilOp(pSendInst);
//
// If so, quit:
//
if (rc == OM_RC_SPOILED) { spoiled = TRUE; rc = 0; DC_QUIT; }
//
// Any other error is more serious:
//
if (rc != 0) { DC_QUIT; }
//
// Now decide how many bytes we're going to ask the network layer for
// this time and how many data bytes we're going to transfer:
//
DecideTransferSize(pSendInst, &transferSize, &dataTransferSize);
ASSERT(dataTransferSize <= pSendInst->dataLeftToGo);
//
// Add 1 byte to the transfer size for the <compressionType> byte:
//
TRACE_OUT(("Asking MG_GetBuffer for 0x%08x bytes for operation type 0x%08x", transferSize + 1, pSendInst->messageType));
rc = MG_GetBuffer(pomPrimary->pmgClient, transferSize + 1, pSendInst->priority, pSendInst->channel, &pNetBuffer); if (rc != 0) { //
// Possible errors include
// - NET_NOT_CONNECTED, when a backlevel call ends
// - NET_INVALID_USER_HANDLE, when an MCS call ends
// - NET_TOO_MUCH_IN_USE, when we hit back pressure (flow control)
//
// In all cases, just quit.
//
TRACE_OUT(("MG_GetBuffer failed; not sending OM message")); DC_QUIT; }
//
// OK so far, so now copy the header of the message into the first part
// of the compress buffer:
//
pMessage = pSendInst->pMessage; ASSERT(pMessage); memcpy(pomPrimary->compressBuffer, pMessage, pSendInst->messageSize);
//
// ...and now copy the data into the rest of the buffer:
//
// This must be a HUGE copy because although the compress buffer is not
// HUGE, the data is and the bit to be copied may span segments.
//
if (dataTransferSize != 0) { memcpy((LPBYTE)pomPrimary->compressBuffer + pSendInst->messageSize, pSendInst->pDataNext, dataTransferSize); }
//
// Determine whether to compress:
//
compressed = FALSE; tryToCompress = FALSE;
if ((pDomain->compressionCaps & OM_CAPS_PKW_COMPRESSION) && (pSendInst->compressOrNot) && (transferSize > DCS_MIN_COMPRESSABLE_PACKET) && (pomPrimary->pgdcWorkBuf != NULL)) { tryToCompress = TRUE; }
//
// If we passed those tests, compress the packet into the network
// buffer.
//
// This will not use the whole network buffer we have allocated, but it
// saves us having to have two buffers and doing a second data copy.
// The network layer can handle a partially used buffer
//
if (tryToCompress) { TRACE_OUT(("OM Compressing %04d bytes", transferSize)); compressed = GDC_Compress(NULL, GDCCO_MAXSPEED, pomPrimary->pgdcWorkBuf, pomPrimary->compressBuffer, transferSize, (LPBYTE)pNetBuffer + 1, &transferSize); }
if (compressed) { TRACE_OUT(("OM Compressed to %04d bytes", transferSize));
*((LPBYTE)pNetBuffer) = OM_PROT_PKW_COMPRESSED; } else { TRACE_OUT(("OM Uncompressed %04d bytes", transferSize));
memcpy((LPBYTE)pNetBuffer + 1, pomPrimary->compressBuffer, transferSize);
*((LPBYTE)pNetBuffer) = OM_PROT_NOT_COMPRESSED; }
//
// If we're in a T.120 call and sending on all priorities, we need to
// do some work to ensure compatibility with NetMeeting 1.0.
//
fSendExtra = ((pSendInst->priority & NET_SEND_ALL_PRIORITIES) != 0); if ( fSendExtra ) { //
// T.120 reserves MCS Top Priority for use by GCC. Sending on all
// priorities used to include Top, but no longer does, to ensure
// compliance. However, ObMan expects to receive 4 responses when
// sending on all priorities whereas the MCS glue now uses only
// 3 priorities. To ensure backward compatibility, whenever ObMan
// sends on all priorities, it has to add an extra send by making
// an extra call to the network here.
// First allocate another net buffer and copy the data to it (we
// have to do before calling MG_SendData as the other buffer is
// invalid after this).
//
TRACE_OUT(( "SEND_ALL: get extra NET buffer")); rc = MG_GetBuffer(pomPrimary->pmgClient, transferSize + 1, (NET_PRIORITY)(pSendInst->priority & ~NET_SEND_ALL_PRIORITIES), pSendInst->channel, &pAnotherNetBuffer); if (rc != 0) { WARNING_OUT(("MG_GetBuffer failed; not sending OM packet")); } else { memcpy(pAnotherNetBuffer, pNetBuffer, transferSize + 1); }
}
//
// Now send the packet, adding 1 byte to the length for the
// <compressionType> byte:
//
TRACE_OUT(( "Sending 0x%08x bytes on channel 0x%08x at priority %hu", transferSize + 1, pSendInst->channel, pSendInst->priority));
if (rc == 0) { TRACE_OUT(("SendMessagePkt: sending packet size %d", transferSize+1));
rc = MG_SendData(pomPrimary->pmgClient, pSendInst->priority, pSendInst->channel, (transferSize + 1), &pNetBuffer); }
if ( fSendExtra && (rc == 0) ) { TRACE_OUT(("SendMessagePkt: sending extra packet size %d", transferSize+1));
rc = MG_SendData(pomPrimary->pmgClient, (NET_PRIORITY)(pSendInst->priority & ~NET_SEND_ALL_PRIORITIES), pSendInst->channel, (transferSize + 1), &pAnotherNetBuffer); }
if (rc != 0) { //
// Network API says free the buffer on error:
//
MG_FreeBuffer(pomPrimary->pmgClient, &pNetBuffer); if ( pAnotherNetBuffer != NULL ) { MG_FreeBuffer(pomPrimary->pmgClient, &pAnotherNetBuffer); }
switch (rc) { case NET_RC_MGC_NOT_CONNECTED: case NET_RC_MGC_INVALID_USER_HANDLE: //
// These are the errors the Network layer returns when we're in
// a singleton Domain or when an MCS domain has just
// terminated. We ignore them.
//
TRACE_OUT(("No data sent since call %u doesn't exist", pDomain->callID)); rc = 0; break;
default: //
// Any other error is more serious, so quit and pass it back:
//
DC_QUIT; } } else { //
// We've sent a message and will therefore get a FEEDBACK event
// sometime later. This qualifies as a SEND_EVENT since it will
// prompt us to examine our send queue, so we set the
// SEND_EVENT_OUTSTANDING flag:
//
TRACE_OUT(("Sent msg in Domain %u (type: 0x%08x) with %hu data bytes", pDomain->callID, pSendInst->messageType, dataTransferSize));
pDomain->sendEventOutstanding = TRUE; }
//
// Here, we decrement the <bytesUnacked> fields for the workset and
// workset group:
//
if (dataTransferSize != 0) { pWorkset = pSendInst->pWorkset; pWorkset->bytesUnacked -= dataTransferSize;
pWSGroup = pSendInst->pWSGroup; pWSGroup->bytesUnacked -= dataTransferSize; }
//
// Now update the send instruction and decide whether we've sent all
// the data for this operation:
//
pSendInst->dataLeftToGo -= dataTransferSize; pSendInst->pDataNext = (POM_OBJECTDATA)((LPBYTE)pSendInst->pDataNext + dataTransferSize);
if (pSendInst->dataLeftToGo == 0) { //
// If so, we
//
// - clear the transfer-in-progress flag for this queue -
// remember that the NET_SEND_ALL_PRIORITIES flag may be set so
// we need to clear it
//
// - free our copy of the message packet and the data, if any (we
// bumped up the use count of the data chunk when the message was
// put on the queue so we won't really be getting rid of it
// unless it's been freed elsewhere already, which is fine)
//
// - pop the instruction off the send queue and free it.
//
TRACE_OUT(( "Sent last packet for operation (type: 0x%08x)", pSendInst->messageType));
queuePriority = pSendInst->priority; queuePriority &= ~NET_SEND_ALL_PRIORITIES; pDomain->sendInProgress[queuePriority] = FALSE; allSent = TRUE; } else { //
// If not, we
//
// - set the transfer-in-progress flag for this queue -
// remember that the NET_SEND_ALL_PRIORITIES flag may be set so
// we need to clear it
//
// - set the <messageSize> field of the send instruction to the
// size of a MORE_DATA header, so that only that many bytes are
// picked out of the message next time
//
// - set the <messageType> field of the message to MORE_DATA
//
// - leave the operation on the queue.
//
TRACE_OUT(("Data left to transfer: %u bytes (starting at 0x%08x)", pSendInst->dataLeftToGo, pSendInst->pDataNext));
queuePriority = pSendInst->priority; queuePriority &= ~NET_SEND_ALL_PRIORITIES; pDomain->sendInProgress[queuePriority] = TRUE;
pSendInst->messageSize = OMNET_MORE_DATA_SIZE;
pMessage->messageType = OMNET_MORE_DATA; }
DC_EXIT_POINT:
//
// If we're finished with the message (either because we've sent it all
// or because it was spoiled) we free it (plus any associated data):
//
if (spoiled || allSent) { FreeSendInst(pSendInst); }
DebugExitDWORD(SendMessagePkt, rc); return(rc); }
//
// TryToSpoilOp
//
UINT TryToSpoilOp ( POM_SEND_INST pSendInst ) { POMNET_OPERATION_PKT pMessage; POM_OBJECT pObj; POM_WORKSET pWorkset; POM_WSGROUP pWSGroup; BOOL spoilable = FALSE; UINT rc = 0;
DebugEntry(TryToSpoilOp);
pMessage = (POMNET_OPERATION_PKT)pSendInst->pMessage; pObj = pSendInst->pObj; pWorkset = pSendInst->pWorkset; pWSGroup = pSendInst->pWSGroup;
//
// The rules for spoiling state that
//
// - any operation is spoiled by a later operation of the same type
//
// - in addition, an Update is spoiled by a later Replace.
//
// Since we never have two Adds or two Deletes for the same object,
// these rules reduce to the following:
//
// - a Clear is spoiled by a later Clear
//
// - a Move is spoiled by a later Move
//
// - a Replace is spoiled by a later Replace
//
// - an Update is spoiled by a later Update or a later Replace.
//
// So, switch according to the operation type:
//
switch (pSendInst->messageType) { case OMNET_WORKSET_CLEAR: if (STAMP_IS_LOWER(pMessage->seqStamp, pWorkset->clearStamp)) { spoilable = TRUE; } break;
case OMNET_OBJECT_UPDATE: if ((STAMP_IS_LOWER(pMessage->seqStamp, pObj->replaceStamp)) || (STAMP_IS_LOWER(pMessage->seqStamp, pObj->updateStamp))) { spoilable = TRUE; } break;
case OMNET_OBJECT_REPLACE: if (STAMP_IS_LOWER(pMessage->seqStamp, pObj->replaceStamp)) { spoilable = TRUE; } break;
case OMNET_OBJECT_MOVE: if (STAMP_IS_LOWER(pMessage->seqStamp, pObj->positionStamp)) { spoilable = TRUE; } break;
case OMNET_HELLO: case OMNET_WELCOME: case OMNET_LOCK_REQ: case OMNET_LOCK_GRANT: case OMNET_LOCK_DENY: case OMNET_LOCK_NOTIFY: case OMNET_UNLOCK: case OMNET_WSGROUP_SEND_REQ: case OMNET_WSGROUP_SEND_MIDWAY: case OMNET_WSGROUP_SEND_COMPLETE: case OMNET_WSGROUP_SEND_DENY: case OMNET_WORKSET_NEW: case OMNET_WORKSET_CATCHUP: case OMNET_OBJECT_ADD: case OMNET_OBJECT_DELETE: case OMNET_OBJECT_CATCHUP: //
// Do nothing
//
break;
default: ERROR_OUT(("Reached default case in switch statement (value: %hu)", pSendInst->messageType)); break; }
if (spoilable) { //
// To spoil the message, we remove it from the send queue and free
// the memory (also NULL the caller's pointer):
//
//
// However, if we spoil the message, the data (if any) will never be
// acknowledged, so we must decrement the relevant <bytesUnacked>
// fields now:
//
TRACE_OUT(( "Spoiling from send queue for workset %u", pWorkset->worksetID));
if (pSendInst->dataLeftToGo != 0) { pWorkset->bytesUnacked -= pSendInst->dataLeftToGo; pWSGroup->bytesUnacked -= pSendInst->dataLeftToGo; }
rc = OM_RC_SPOILED; }
DebugExitDWORD(TryToSpoilOp, rc); return(rc); }
//
// DecideTransferSize(...)
//
void DecideTransferSize ( POM_SEND_INST pSendInst, UINT * pTransferSize, UINT * pDataTransferSize ) { UINT transferSize;
DebugEntry(DecideTransferSize);
//
// Ideally, we'd like to transfer everything in one go, where
// "everything" is the message header plus all the data to go with it
// (if any):
//
transferSize = pSendInst->messageSize + pSendInst->dataLeftToGo;
TRACE_OUT(("Desired transfer size for this portion: %u", transferSize));
//
// However, we never ask for more than half the send pool size, so take
// the minimum of the two:
//
// (we subtract 1 byte to allow for the <compressionType> byte at the
// start of the packet)
//
transferSize = min(transferSize, ((OM_NET_SEND_POOL_SIZE / 2) - 1));
TRACE_OUT(("Feasible transfer size for this portion: %u", transferSize));
//
// The logic of the send queue processing requires that the message
// header is sent completely in the first packet, so assert:
//
ASSERT((transferSize >= pSendInst->messageSize));
//
// As a sanity check, we ensure we're not trying to transfer more than
// the biggest buffer allowed:
//
ASSERT(transferSize <= OM_NET_MAX_TRANSFER_SIZE);
//
// The amount of data to be sent is the transfer size less the size of
// the header we're sending:
//
*pDataTransferSize = ((UINT) transferSize) - pSendInst->messageSize; *pTransferSize = (UINT) transferSize;
TRACE_OUT(("Total transfer size for this packet: %u - data transfer size: %u", (UINT) *pTransferSize, (UINT) *pDataTransferSize));
DebugExitVOID(DecideTransferSize); }
//
// ProcessNetData(...)
//
void ProcessNetData ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, PNET_SEND_IND_EVENT pNetSendInd ) { POMNET_PKT_HEADER pHeader; UINT dataSize; OMNET_MESSAGE_TYPE messageType = 0; UINT rc = 0;
DebugEntry(ProcessNetData);
//
// Decompress the packet and set pHeader to point to the start of
// wherever the data ends up:
//
ASSERT((pNetSendInd->lengthOfData < 0xFFFF));
if (NULL != pNetSendInd->data_ptr) { switch (*(pNetSendInd->data_ptr)) { case OM_PROT_NOT_COMPRESSED: { TRACE_OUT(("Buffer not compressed - taking it as it stands")); memcpy(pomPrimary->compressBuffer, pNetSendInd->data_ptr + 1, pNetSendInd->lengthOfData--); } break;
case OM_PROT_PKW_COMPRESSED: { TRACE_OUT(("Buffer was PKW compressed - size 0x%08x bytes", pNetSendInd->lengthOfData));
dataSize = sizeof(pomPrimary->compressBuffer);
ASSERT(pomPrimary->pgdcWorkBuf != NULL); if (!GDC_Decompress(NULL, pomPrimary->pgdcWorkBuf, pNetSendInd->data_ptr + 1, (WORD) pNetSendInd->lengthOfData - 1, pomPrimary->compressBuffer, &dataSize)) { ERROR_OUT(("Failed to decompress OM data!")); }
pNetSendInd->lengthOfData = dataSize;
TRACE_OUT(("Decompressed to 0x%08x bytes", pNetSendInd->lengthOfData)); } break;
default: { ERROR_OUT(( "Ignoring packet with unknown compression (0x%08x)", *(pNetSendInd->data_ptr))); DC_QUIT; } } pHeader = (POMNET_PKT_HEADER) pomPrimary->compressBuffer;
//
// Now switch accorindg to the message type:
//
messageType = pHeader->messageType;
TRACE_OUT((" Packet contains OMNET message type 0x%08x", messageType));
switch (messageType) { case OMNET_HELLO: { rc = ProcessHello(pomPrimary, pDomain, (POMNET_JOINER_PKT) pHeader, pNetSendInd->lengthOfData);
} break;
case OMNET_WELCOME: { rc = ProcessWelcome(pomPrimary, pDomain, (POMNET_JOINER_PKT) pHeader, pNetSendInd->lengthOfData); } break;
case OMNET_LOCK_DENY: case OMNET_LOCK_GRANT: { ProcessLockReply(pomPrimary, pDomain, pHeader->sender, ((POMNET_LOCK_PKT) pHeader)->data1, pHeader->messageType); } break;
case OMNET_LOCK_REQ: { ProcessLockRequest(pomPrimary, pDomain, (POMNET_LOCK_PKT) pHeader); } break;
case OMNET_WSGROUP_SEND_REQ: { ProcessSendReq(pomPrimary, pDomain, (POMNET_WSGROUP_SEND_PKT) pHeader); } break;
case OMNET_WSGROUP_SEND_MIDWAY: { ProcessSendMidway(pomPrimary, pDomain, (POMNET_WSGROUP_SEND_PKT) pHeader); } break;
case OMNET_WSGROUP_SEND_COMPLETE: { rc = ProcessSendComplete(pomPrimary, pDomain, (POMNET_WSGROUP_SEND_PKT) pHeader); } break;
case OMNET_WSGROUP_SEND_DENY: { MaybeRetryCatchUp(pomPrimary, pDomain, ((POMNET_WSGROUP_SEND_PKT) pHeader)->wsGroupID, pHeader->sender); } break;
//
// We use the special ReceiveData function for any messages which
//
// - might need to be bounced, or
//
// - might fill more than one packet.
//
case OMNET_LOCK_NOTIFY: case OMNET_UNLOCK:
case OMNET_WORKSET_NEW: case OMNET_WORKSET_CLEAR: case OMNET_WORKSET_CATCHUP:
case OMNET_OBJECT_ADD: case OMNET_OBJECT_MOVE: case OMNET_OBJECT_UPDATE: case OMNET_OBJECT_REPLACE: case OMNET_OBJECT_DELETE: case OMNET_OBJECT_CATCHUP:
case OMNET_MORE_DATA: { rc = ReceiveData(pomPrimary, pDomain, pNetSendInd, (POMNET_OPERATION_PKT) pHeader); } break;
default: { ERROR_OUT(( "Unexpected messageType 0x%08x", messageType)); } }
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(( "Error %d processing OMNET message 0x%08x", rc, messageType)); } }
DebugExitVOID(ProcessNetData);
}
//
// ReceiveData(...)
//
UINT ReceiveData ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, PNET_SEND_IND_EVENT pNetSendInd, POMNET_OPERATION_PKT pNetMessage ) { POM_RECEIVE_CB pReceiveCB = NULL; UINT thisHeaderSize; UINT thisDataSize; OMNET_MESSAGE_TYPE messageType; long bytesStillExpected = 0; UINT rc = 0;
DebugEntry(ReceiveData);
//
// Set up some local variables:
//
messageType = pNetMessage->header.messageType;
//
// The amount of data included in this message is the size of the
// network buffer less the size of our message header at the front of
// it:
//
// Note: <thisHeaderSize> is the size of the header IN THIS PACKET,
// rather than the size of the header in the first packet of a
// multi-packet send.
//
thisHeaderSize = GetMessageSize(pNetMessage->header.messageType); thisDataSize = pNetSendInd->lengthOfData - thisHeaderSize;
//
// If this is a MORE_DATA packet, then there should already be a
// receive CB set up for the transfer. If not, we need to create one:
//
if (messageType == OMNET_MORE_DATA) { rc = FindReceiveCB(pDomain, pNetSendInd, pNetMessage, &pReceiveCB);
//
// If no receive CB, we swallow the return code and quit. This will
// happen when we join a channel midway through a large data
// transfer.
//
if (rc == OM_RC_RECEIVE_CB_NOT_FOUND) { WARNING_OUT(("Discarding unexpected packet from 0x%08x", pNetMessage->header.sender)); rc = 0; DC_QUIT; } } else { // lonchanc: added the following block of code
if (messageType == OMNET_OBJECT_REPLACE) { POM_RECEIVE_CB p; // lonchanc: This packet does not contain all the data.
// More data will come in another packets; however,
// in this case, bytesStillExpected will be greater than zero
// after substracting from thisDataSize, as a result,
// this receiveCB will be appended to the ReceiveList.
// However, FindReceiveCB will find the first one matched.
// As a result, the one we just appended to the ReceiveList will
// not be found.
// Even worse, if there is receiveCB (of same sender, priority, and
// channel), the first-matched receiveCB will be totally confused
// when more data come in. This is bug #578.
TRACE_OUT(("Removing receiveCB {")); while (FindReceiveCB(pDomain, pNetSendInd, pNetMessage, &p) == 0) { //
// Remove the message from the list it's in (either the pending
// receives list if this message was never bounced or the bounce
// list if it has been bounced):
//
COM_BasedListRemove(&(p->chain));
//
// Now free the message and the receive control block (NOT THE
// DATA! If there was any, it's just been used for an object
// add/update etc.)
//
UT_FreeRefCount((void**)&(p->pHeader), FALSE);
UT_FreeRefCount((void**)&p, FALSE); } }
rc = CreateReceiveCB(pDomain, pNetSendInd, pNetMessage, &pReceiveCB); }
if (rc != 0) { ERROR_OUT(("%s failed, rc=0x0x%08x", (messageType == OMNET_MORE_DATA) ? "FindReceiveCB" : "CreateReceiveCB", rc)); DC_QUIT; }
TRACE_OUT(("%s ok, pRecvCB=0x0x%p", (messageType == OMNET_MORE_DATA) ? "FindReceiveCB" : "CreateReceiveCB", pReceiveCB)); //
// Now we copy the data, if any, from the network buffer into the chunk
// we allocated when we called CreateReceiveCB.
//
if (thisDataSize != 0) { //
// We copy the data across using memcpy.
//
bytesStillExpected = ((long) (pReceiveCB->pHeader->totalSize) - (long) (pReceiveCB->bytesRecd));
TRACE_OUT(("thisDataSize=0x0x%08x, bytesStillExpected=0x0x%08x, totalSize=0x0x%08x, bytesRecd=0x0x%08x", (long) thisDataSize, (long) bytesStillExpected, (long) pReceiveCB->pHeader->totalSize, (long) pReceiveCB->bytesRecd));
ASSERT((long) thisDataSize <= bytesStillExpected);
memcpy(pReceiveCB->pCurrentPosition, ((LPBYTE) pNetMessage) + thisHeaderSize, thisDataSize);
pReceiveCB->bytesRecd += thisDataSize; pReceiveCB->pCurrentPosition += thisDataSize; bytesStillExpected -= thisDataSize;
TRACE_OUT((" Still expecting %u bytes", bytesStillExpected)); }
//
// If we are expecting no more data for this transfer, process it:
//
if (bytesStillExpected <= 0) { rc = ProcessMessage(pomPrimary, pReceiveCB, OK_TO_RETRY_BOUNCE_LIST); if (rc == OM_RC_BOUNCED) { //
// If ProcessMessage can't deal with the message immediately
// (because e.g. it's an update for an object we don't yet
// have), it will have added it to the bounce list so it will
// be tried again later.
//
// We special case this return code as it's not a problem for
// us here (it exists because other parts of the code need it):
//
WARNING_OUT(("Bounced message type 0x%08x", messageType)); rc = 0; }
if (rc != 0) { //
// Any other non-zero return code is more serious:
//
DC_QUIT; } }
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(("Error %d from message type 0x%08x", rc, messageType));
if (rc == OM_RC_OUT_OF_RESOURCES) { //
// If we couldn't allocate memory for the data to be recd, we
// act as if we've been kicked out of the channel:
//
ERROR_OUT(( "Leaving chann 0x%08x, simulating expulsion", pNetSendInd->channel));
MG_ChannelLeave(pomPrimary->pmgClient, pNetSendInd->channel);
ProcessNetLeaveChannel(pomPrimary, pDomain, pNetSendInd->channel); } }
DebugExitDWORD(ReceiveData, rc); return(rc);
}
//
// CreateReceiveCB(...)
//
UINT CreateReceiveCB ( POM_DOMAIN pDomain, PNET_SEND_IND_EVENT pNetSendInd, POMNET_OPERATION_PKT pNetMessage, POM_RECEIVE_CB * ppReceiveCB ) { POM_RECEIVE_CB pReceiveCB = NULL; POMNET_OPERATION_PKT pHeader = NULL; UINT headerSize; UINT totalDataSize; UINT rc = 0;
DebugEntry(CreateReceiveCB);
//
// We get here when the first packet of a message arrives . What we
// need to do is to set up a "receive" structure and add it to the list
// of receives-in-progress for the Domain. Then, when the ensuing data
// packets (if any) arrive, they will be correlated and concatenated.
// When all the data has arrived, the receive CB will be passed to
// ProcessMessage.
//
//
// Allocate some memory for the receive CB:
//
pReceiveCB = (POM_RECEIVE_CB)UT_MallocRefCount(sizeof(OM_RECEIVE_CB), TRUE); if (!pReceiveCB) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pReceiveCB, RCVCB);
pReceiveCB->pDomain = pDomain; pReceiveCB->priority = pNetSendInd->priority; pReceiveCB->channel = pNetSendInd->channel;
//
// Allocate some memory for the message header and copy the packet into
// it from the network buffer (note: we must copy the header since at
// the moment it is in a network buffer which we can't hang on to for
// the entire duration of the transfer):
//
headerSize = GetMessageSize(pNetMessage->header.messageType);
pHeader = (POMNET_OPERATION_PKT)UT_MallocRefCount(sizeof(OMNET_OPERATION_PKT), TRUE); if (!pHeader) { rc = UT_RC_NO_MEM; DC_QUIT; }
memcpy(pHeader, pNetMessage, headerSize);
pReceiveCB->pHeader = pHeader;
//
// Not all messages sent over the network have a totalSize field, but
// our subsequent processing requires one. So, if the message we've
// just received didn't have one, we set the value (our local copy of
// the header has room since we alloacated enough memory for the
// largest type of header):
//
if (headerSize >= (offsetof(OMNET_OPERATION_PKT, totalSize) + (sizeof(pNetMessage->totalSize)))) { TRACE_OUT(("Header contains <totalSize> field (value: %u)", pNetMessage->totalSize)); } else { TRACE_OUT(("Header doesn't contain <totalSize> field"));
pReceiveCB->pHeader->totalSize = headerSize; }
//
// Now determine the total number of data bytes involved in this
// operation:
//
totalDataSize = pReceiveCB->pHeader->totalSize - ((UINT) headerSize);
//
// If there is any data, allocate some memory to receive it and set the
// <pData> pointer to point to it (otherwise NULL it):
//
if (totalDataSize != 0) { TRACE_OUT(( "Allocating %u bytes for data for this transfer", totalDataSize));
pReceiveCB->pData = UT_MallocRefCount(totalDataSize, FALSE); if (!pReceiveCB->pData) { ERROR_OUT(( "Failed to allocate %u bytes for object to be recd " "from node 0x%08x - will remove WSG from Domain", totalDataSize, pNetMessage->header.sender)); rc = OM_RC_OUT_OF_RESOURCES; DC_QUIT; } } else { pReceiveCB->pData = NULL; }
pReceiveCB->pCurrentPosition = (LPBYTE)pReceiveCB->pData;
//
// Set <bytesRecd> to the size of the header. We may have recd some
// data bytes as well, but they'll be added to the header size in
// ReceiveData.
//
pReceiveCB->bytesRecd = headerSize;
//
// Now insert in the list hung off the Domain record:
//
COM_BasedListInsertBefore(&(pDomain->receiveList), &(pReceiveCB->chain));
//
// Set caller's pointer:
//
*ppReceiveCB = pReceiveCB;
DC_EXIT_POINT:
if (rc != 0) { if(pHeader != NULL) { ERROR_OUT(( "Error %d receiving first packet of message type %u from node 0x%08x", rc, pHeader->header.messageType, pHeader->header.sender)); }
if (pReceiveCB != NULL) { if (pReceiveCB->pData != NULL) { UT_FreeRefCount((void**)&(pReceiveCB->pData), FALSE); }
UT_FreeRefCount((void**)&pReceiveCB, FALSE); }
if (pHeader != NULL) { UT_FreeRefCount((void**)&pHeader, FALSE); } }
DebugExitDWORD(CreateReceiveCB, rc); return(rc);
}
//
//
//
// FindReceiveCB(...)
//
//
//
UINT FindReceiveCB(POM_DOMAIN pDomain, PNET_SEND_IND_EVENT pNetSendInd, POMNET_OPERATION_PKT pPacket, POM_RECEIVE_CB * ppReceiveCB) { POM_RECEIVE_CB pReceiveCB; NET_PRIORITY priority; NET_CHANNEL_ID channel; NET_UID sender; POMNET_OPERATION_PKT pHeader;
UINT rc = 0;
DebugEntry(FindReceiveCB);
//
// First thing to do is to find the receive control block for the
// transfer. It should be in the list hung off the Domain record:
//
sender = pPacket->header.sender; priority = pNetSendInd->priority; channel = pNetSendInd->channel;
pReceiveCB = (POM_RECEIVE_CB)COM_BasedListFirst(&(pDomain->receiveList), FIELD_OFFSET(OM_RECEIVE_CB, chain)); while (pReceiveCB != NULL) { //
// We check for a match on sender's user ID, channel and priority.
//
// We assume that, for a given channel, MCS does not reorder packets
// sent by the same user at the same priority.
//
pHeader = pReceiveCB->pHeader;
if ((pHeader->header.sender == sender) && (pReceiveCB->priority == priority) && (pReceiveCB->channel == channel)) { //
// Found!
//
TRACE_OUT(("Found receive CB for user %hu, chann 0x%08x, pri %hu, at pRecvCB=0x0x%p", sender, channel, priority, pReceiveCB)); break; }
pReceiveCB = (POM_RECEIVE_CB)COM_BasedListNext(&(pDomain->receiveList), pReceiveCB, FIELD_OFFSET(OM_RECEIVE_CB, chain)); }
if (pReceiveCB == NULL) { rc = OM_RC_RECEIVE_CB_NOT_FOUND; DC_QUIT; } else { *ppReceiveCB = pReceiveCB; }
DC_EXIT_POINT:
DebugExitDWORD(FindReceiveCB, rc); return(rc); }
//
// PurgeReceiveCBs(...)
//
void PurgeReceiveCBs ( POM_DOMAIN pDomain, NET_CHANNEL_ID channel ) { POM_RECEIVE_CB pReceiveCB; POM_RECEIVE_CB pNextReceiveCB;
DebugEntry(PurgeReceiveCBs);
pReceiveCB = (POM_RECEIVE_CB)COM_BasedListFirst(&(pDomain->receiveList), FIELD_OFFSET(OM_RECEIVE_CB, chain)); while (pReceiveCB != NULL) { //
// Need to chain here since we may remove pReceiveCB from the list:
//
pNextReceiveCB = (POM_RECEIVE_CB)COM_BasedListNext(&(pDomain->receiveList), pReceiveCB, FIELD_OFFSET(OM_RECEIVE_CB, chain));
if (pReceiveCB->channel == channel) { //
// This receive CB is for the channel being purged - remove it
// from the list and free the memory.
//
WARNING_OUT(( "Purging receive CB from user %hu", pReceiveCB->pHeader->header.sender));
COM_BasedListRemove(&(pReceiveCB->chain));
//
// Free the data memory.
//
if (pReceiveCB->pData != NULL) { UT_FreeRefCount(&pReceiveCB->pData, FALSE); }
//
// Free the header memory.
//
if (pReceiveCB->pHeader != NULL) { UT_FreeRefCount((void**)&pReceiveCB->pHeader, FALSE); }
//
// Finally free the control block.
//
UT_FreeRefCount((void**)&pReceiveCB, FALSE); }
pReceiveCB = pNextReceiveCB; }
DebugExitVOID(PurgeReceiveCBs); }
//
// ProcessMessage(...)
//
UINT ProcessMessage ( POM_PRIMARY pomPrimary, POM_RECEIVE_CB pReceiveCB, UINT whatNext ) { POM_DOMAIN pDomain; POMNET_OPERATION_PKT pHeader; void * pData; NET_PRIORITY priority; OMNET_MESSAGE_TYPE messageType; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_OBJECT pObj; BOOL bounced = FALSE; BOOL retryBounceList = FALSE; BOOL freeMemory = FALSE; UINT rc = 0;
DebugEntry(ProcessMessage);
//
// Set up local variables:
//
pDomain = pReceiveCB->pDomain; pHeader = pReceiveCB->pHeader; priority = pReceiveCB->priority; pData = pReceiveCB->pData;
messageType = pHeader->header.messageType;
//
// Extract pointers to workset group, workset and object record from
// the packet:
//
rc = PreProcessMessage(pDomain, pHeader->wsGroupID, pHeader->worksetID, &pHeader->objectID, pHeader->header.messageType, &pWSGroup, &pWorkset, &pObj);
//
// PreProcess will have told us if it didn't find the relevant workset
// group, workset or object. Whether or not this is an error depends
// on the operation in question. We use a series of IF statements to
// detect and handle the following conditions:
//
//
// 1. Unknown workset group Discard the operation
//
// 2. Existing workset, WORKSET_NEW/CATCHUP Discard the operation
// 3. Unknown workset, any other operation Bounce the operation
//
// 4. Deleted object, any operation Discard the operation
// 5. Existing object, OBJECT_ADD/CATCHUP Discard the operation
// 6. Unknown object, any other operation Bounce the operation
//
//
//
// Test 1.:
//
if (rc == OM_RC_WSGROUP_NOT_FOUND) { //
// If we didn't even find the workset group, we just quit:
//
WARNING_OUT(( "Message is for unknown WSG (ID: %hu) in Domain %u", pHeader->wsGroupID, pDomain->callID)); rc = 0;
//
// Mark the data memory allocated for this object to be freed.
//
freeMemory = TRUE;
DC_QUIT; }
//
// Test 2.:
//
if (rc != OM_RC_WORKSET_NOT_FOUND) // i.e. existing workset
{ if ((messageType == OMNET_WORKSET_NEW) || (messageType == OMNET_WORKSET_CATCHUP)) { //
// We've got a WORKSET_NEW or WORKSET_CATCHUP message, but the
// workset already exists. This is not a problem - we throw the
// message away - but check the priority and persistence fields
// are set to the right values.
//
// (They might be wrong if we created the workset on receipt of
// a lock request for a workset we didn't already have).
//
TRACE_OUT(( "Recd WORKSET_NEW/CATCHUP for extant workset %u in WSG %d", pWorkset->worksetID, pWSGroup->wsg));
pWorkset->priority = *((NET_PRIORITY *) &(pHeader->position)); pWorkset->fTemp = *((BOOL *) &(pHeader->objectID));
rc = 0; DC_QUIT; } }
//
// Test 3.:
//
else // rc == OM_RC_WORKSET_NOT_FOUND
{ if ((messageType != OMNET_WORKSET_NEW) && (messageType != OMNET_WORKSET_CATCHUP)) { //
// Packet is for unknown workset and it's not a
// WORKSET_NEW/CATCHUP, so bounce it:
//
TRACE_OUT(( "Bouncing message for unknown workset %d WSG %d", pHeader->worksetID, pWSGroup->wsg));
BounceMessage(pDomain, pReceiveCB); bounced = TRUE; rc = 0; DC_QUIT; } }
//
// Test 4:.
//
if ((rc == OM_RC_OBJECT_DELETED) || (rc == OM_RC_OBJECT_PENDING_DELETE)) { //
// Packet is for object which has been deleted, so we just throw it
// away (done for us by our caller):
//
TRACE_OUT(("Message 0x%08x for deleted obj 0x%08x:0x%08x in WSG %d:%hu", messageType, pHeader->objectID.creator, pHeader->objectID.sequence, pWSGroup->wsg, pWorkset->worksetID)); rc = 0;
//
// Mark the data memory allocated for this object to be freed.
//
freeMemory = TRUE;
DC_QUIT; }
//
// Test 5.:
//
if (rc != OM_RC_BAD_OBJECT_ID) // i.e. existing object
{ if ((messageType == OMNET_OBJECT_ADD) || (messageType == OMNET_OBJECT_CATCHUP)) { //
// In this case, we DO have an OBEJCT_ADD/CATCHUP, but the
// object was found anyway! This must be a duplicate Add, so
// we just throw it away:
//
TRACE_OUT(( "Add for existing object 0x%08x:0x%08x in WSG %d:%hu", pHeader->objectID.creator, pHeader->objectID.sequence, pWSGroup->wsg, pWorkset->worksetID)); rc = 0;
//
// Mark the data memory allocated for this object to be freed.
//
freeMemory = TRUE;
DC_QUIT; } }
//
// Test 6.:
//
else // rc == OM_RC_BAD_OBJECT_ID
{ if ((messageType != OMNET_OBJECT_ADD) && (messageType != OMNET_OBJECT_CATCHUP)) { //
// Packet is for unknown object, but it's not an
// OBJECT_ADD/CATCHUP, so bounce it:
//
TRACE_OUT(( "Message 0x%08x for unknown obj 0x%08x:0x%08x in WSG %d:%hu", messageType, pHeader->objectID.creator, pHeader->objectID.sequence, pWSGroup->wsg, pWorkset->worksetID));
BounceMessage(pDomain, pReceiveCB); bounced = TRUE; rc = 0; DC_QUIT; } }
//
// OK, we've passed all the tests above, so we must be in a position to
// process the operation. Switch on the message type and invoke the
// appropriate function:
//
switch (messageType) { case OMNET_LOCK_NOTIFY: { ProcessLockNotify(pomPrimary, pDomain, pWSGroup, pWorkset, ((POMNET_LOCK_PKT)pHeader)->data1); } break;
case OMNET_UNLOCK: { ProcessUnlock(pomPrimary, pWorkset, pHeader->header.sender); } break;
case OMNET_WORKSET_CATCHUP: case OMNET_WORKSET_NEW: { rc = ProcessWorksetNew(pomPrimary->putTask, pHeader, pWSGroup);
//
// We will want to see if any bouncing messages can be
// processed because of this new workset, so set the reprocess
// flag:
//
retryBounceList = TRUE; } break;
case OMNET_WORKSET_CLEAR: { rc = ProcessWorksetClear(pomPrimary->putTask, pomPrimary, pHeader, pWSGroup, pWorkset); } break;
case OMNET_OBJECT_CATCHUP: case OMNET_OBJECT_ADD: { rc = ProcessObjectAdd(pomPrimary->putTask, pHeader, pWSGroup, pWorkset, (POM_OBJECTDATA) pData, &pObj);
retryBounceList = TRUE; } break;
case OMNET_OBJECT_MOVE: { ProcessObjectMove(pomPrimary->putTask, pHeader, pWorkset, pObj); } break;
case OMNET_OBJECT_DELETE: case OMNET_OBJECT_REPLACE: case OMNET_OBJECT_UPDATE: { rc = ProcessObjectDRU(pomPrimary->putTask, pHeader, pWSGroup, pWorkset, pObj, (POM_OBJECTDATA) pData); } break;
default: { ERROR_OUT(( "Default case in switch (message type: 0x%08x)", messageType)); } }
if (rc != 0) { ERROR_OUT(( "Error %d processing operation (type: 0x%08x)", rc, messageType)); DC_QUIT; }
TRACE_OUT(("Processed message type 0x%08x", messageType));
DC_EXIT_POINT:
//
// Unless we bounced the message, do some cleanup:
//
// Note: This must be after DC_EXIT_POINT because we want to do it
// even if we didn't process the message (unless we bounced it).
//
// If we haven't bounced the message then we may be able to free
// the data depending on the results of the above tests.
//
if (bounced == FALSE) { //
// Remove the message from the list it's in (either the pending
// receives list if this message was never bounced or the bounce
// list if it has been bounced):
//
COM_BasedListRemove(&(pReceiveCB->chain));
//
// Now free the message and the receive control block (NOT THE
// DATA! If there was any, it's just been used for an object
// add/update etc.)
//
UT_FreeRefCount((void**)&pHeader, FALSE); UT_FreeRefCount((void**)&pReceiveCB, FALSE);
//
// ...unless of course we indicated that we should free the data:
//
if (freeMemory) { if (pData != NULL) { TRACE_OUT(("Freeing object data at 0x%08x", pData)); UT_FreeRefCount(&pData, FALSE); } } } else { rc = OM_RC_BOUNCED; }
//
// If we're not already processing bounced messages, and this message
// is an "enabling" message (i.e. a WORKSET_NEW or OBJECT_ADD), then
// retry the bounce list:
//
if ((whatNext == OK_TO_RETRY_BOUNCE_LIST) && (retryBounceList)) { ProcessBouncedMessages(pomPrimary, pDomain); }
DebugExitDWORD(ProcessMessage, rc); return(rc);
}
//
// BounceMessage()
//
void BounceMessage ( POM_DOMAIN pDomain, POM_RECEIVE_CB pReceiveCB ) { UINT count;
DebugEntry(BounceMessage);
TRACE_OUT(( "Bouncing message type 0x%08x (CB at 0x%08x)", pReceiveCB->pHeader->header.messageType, pReceiveCB));
//
// Remove this receive CB from whichever list its currently in (either
// the list of pending receives if this is the first time it's been
// bounced or the bounce list if not) and insert it at the START of the
// bounce list for the Domain:
//
// Note: the reason why we insert at the start is because
// ProcessBouncedMessages may be chaining through the list and
// we don't want to put this one back in the list at a later
// point or else we might go infinite.
//
COM_BasedListRemove(&(pReceiveCB->chain)); COM_BasedListInsertAfter(&(pDomain->bounceList), &(pReceiveCB->chain));
DebugExitVOID(BounceMessage); }
//
//
//
// ProcessBouncedMessages(...)
//
//
//
void ProcessBouncedMessages(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain) { UINT count; POM_RECEIVE_CB pReceiveCB; POM_RECEIVE_CB pTempReceiveCB; BOOL listGettingShorter; UINT numPasses; UINT rc;
DebugEntry(ProcessBouncedMessages);
TRACE_OUT(( "Processing bounced messages"));
//
// It is important that we process bounced messages as soon as we are
// able. Since processing one may enable others to be processed, we
// must go through the list several times, until we can't do any more
// work on it. So, we keep track of whether the list is getting shorter
// - if it is, we must have processed something so it's worth going
// through again.
//
// Note: an alternative would be do do exactly three passes through the
// list: one to do all the WORKSET_NEWs, then one to do all the
// OBJECT_ADDs and then one to do any remaining operations. This
// is slightly less generic code and is tied in to the current
// dependencies between operations so is not ideal but it may
// prove to be a good performance improvement if the average
// number of passes we do now exceeds three.
//
listGettingShorter = TRUE; numPasses = 0;
pReceiveCB = (POM_RECEIVE_CB)COM_BasedListFirst(&(pDomain->bounceList), FIELD_OFFSET(OM_RECEIVE_CB, chain)); while (listGettingShorter) { numPasses++; listGettingShorter = FALSE;
while (pReceiveCB != NULL) { //
// We want to chain through the list of bounced messages and try
// to process each one. However, trying to process a message
// could cause it to be removed from the list (if processed) or
// added back in at the start (if bounced again).
//
// So, we chain NOW to the next one in the list:
//
pTempReceiveCB = (POM_RECEIVE_CB)COM_BasedListNext(&(pDomain->bounceList), pReceiveCB, FIELD_OFFSET(OM_RECEIVE_CB, chain));
TRACE_OUT(( "Retrying message type 0x%08x (CB at 0x%08x)", pReceiveCB->pHeader->header.messageType, pReceiveCB));
rc = ProcessMessage(pomPrimary, pReceiveCB, DONT_RETRY_BOUNCE_LIST); if (rc != OM_RC_BOUNCED) { //
// We processed a message, so set the flag for another run
// through the list:
//
TRACE_OUT(( "Successfully processed bounced message"));
listGettingShorter = TRUE; }
//
// Now "chain" on to the next one, using the link we've already
// set up:
//
pReceiveCB = pTempReceiveCB; } }
TRACE_OUT(( "Processed as much of bounce list as possible in %hu passes", numPasses));
DebugExitVOID(ProcessBouncedMessages); }
//
// FreeSendInst(...)
//
void FreeSendInst ( POM_SEND_INST pSendInst ) { DebugEntry(FreeSendInst);
if (pSendInst->pMessage != NULL) { UT_FreeRefCount((void**)&(pSendInst->pMessage), FALSE); }
if (pSendInst->pWSGroup != NULL) { UT_FreeRefCount((void**)&(pSendInst->pWSGroup), FALSE); }
if (pSendInst->pWorkset != NULL) { UT_FreeRefCount((void**)&(pSendInst->pWorkset), FALSE); }
if (pSendInst->pObj != NULL) { UT_FreeRefCount((void**)&(pSendInst->pObj), FALSE); }
if (pSendInst->pDataStart != NULL) { UT_FreeRefCount((void**)&(pSendInst->pDataStart), FALSE); }
//
// Now free the send instruction itself:
//
COM_BasedListRemove(&(pSendInst->chain)); UT_FreeRefCount((void**)&pSendInst, FALSE);
DebugExitVOID(FreeSendInst); }
//
// PreProcessMessage(...)
//
UINT PreProcessMessage ( POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, OM_WORKSET_ID worksetID, POM_OBJECT_ID pObjectID, OMNET_MESSAGE_TYPE messageType, POM_WSGROUP * ppWSGroup, POM_WORKSET * ppWorkset, POM_OBJECT * ppObj ) { POM_WSGROUP pWSGroup = NULL; POM_WORKSET pWorkset = NULL; POM_OBJECT pObj; UINT rc = 0;
DebugEntry(PreProcessMessage);
//
// OK, we've got some sort of operation message: let's find the workset
// group it relates to:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pDomain->wsGroups), (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, wsGroupID), (DWORD)wsGroupID, FIELD_SIZE(OM_WSGROUP, wsGroupID));
if (pWSGroup == NULL) { //
// This is a message for a workset group which we are not/no longer
// registered with, so quit (our caller will throw it away):
//
rc = OM_RC_WSGROUP_NOT_FOUND; DC_QUIT; }
ValidateWSGroup(pWSGroup);
pWorkset = pWSGroup->apWorksets[worksetID];
//
// Check that this set up a valid workset pointer:
//
if (pWorkset == NULL) { rc = OM_RC_WORKSET_NOT_FOUND; DC_QUIT; }
ValidateWorkset(pWorkset);
//
// Search for the object ID, locking workset group mutex while we do
// so.
//
// Note: if the <pObjectID> parameter is NULL, it means that the caller
// doesn't want us to search for the object ID, so we skip this
// step
//
switch (messageType) { case OMNET_OBJECT_ADD: case OMNET_OBJECT_CATCHUP: case OMNET_OBJECT_REPLACE: case OMNET_OBJECT_UPDATE: case OMNET_OBJECT_DELETE: case OMNET_OBJECT_MOVE: { rc = ObjectIDToPtr(pWorkset, *pObjectID, &pObj); if (rc != 0) { //
// No object found with this ID (rc is BAD_ID, DELETED or
// PENDING_DELETE):
//
*ppObj = NULL; } else { ValidateObject(pObj); *ppObj = pObj; } } break;
default: { //
// Do nothing for other messages.
//
} }
DC_EXIT_POINT: *ppWorkset = pWorkset; *ppWSGroup = pWSGroup; TRACE_OUT(("Pre-processed message for Domain %u", pDomain->callID));
DebugExitDWORD(PreProcessMessage, rc); return(rc); }
//
// PurgeNonPersistent(...)
//
void PurgeNonPersistent ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, NET_UID userID ) { POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; OM_WORKSET_ID worksetID; POM_OBJECT pObj;
DebugEntry(PurgeNonPersistent);
//
// Find the workset group which has the specified ID:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &pDomain->wsGroups, (void**)&pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain), FIELD_OFFSET(OM_WSGROUP, wsGroupID), (DWORD)wsGroupID, FIELD_SIZE(OM_WSGROUP, wsGroupID));
if (pWSGroup == NULL) { //
// SFR5794: Not an error if wsgroup not found - this just means
// someone has detached who was using a workset group which we were
// not using.
//
TRACE_OUT(("WSGroup %hu not found in domain %u", wsGroupID, pDomain->callID)); DC_QUIT; }
//
// Chain through each workset in the group - for those that are
// non-persistent, then chain through each object looking for a match
// on the user ID of the departed node:
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { pWorkset = pWSGroup->apWorksets[worksetID]; if (pWorkset == NULL) { //
// Workset with this ID doesn't exist - continue
//
continue; }
if (!pWorkset->fTemp) { //
// A persistent workset - we don't need to purge it of objects
//
continue; }
pObj = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObj != NULL) { ValidateObject(pObj);
//
// SFR6353: Don't try to delete the object if it's already
// pending delete.
//
if (!(pObj->flags & DELETED) && !(pObj->flags & PENDING_DELETE)) { //
// If this object was added by the departed node, OR if
// ALL_REMOTES have gone and it was not added by us...
//
if ((pObj->objectID.creator == userID) || ((userID == NET_ALL_REMOTES) && (pObj->objectID.creator != pDomain->userID))) { //
// ...delete it:
//
ObjectDRU(pomPrimary->putTask, pWSGroup, pWorkset, pObj, NULL, OMNET_OBJECT_DELETE); } }
pObj = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); } }
DC_EXIT_POINT: DebugExitVOID(PurgeNonPersistent); }
//
// SetPersonData(...)
//
UINT SetPersonData ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, POM_WSGROUP pWSGroup ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_OBJECT pObjReg; POM_WSGROUP_REG_REC pRegObject; POM_WSGROUP_REG_REC pNewRegObject; UINT rc = 0;
DebugEntry(SetPersonData);
//
// Set up pointers to the ObManControl workset group and the workset
// which contains the object to be replaced:
//
pOMCWSGroup = GetOMCWsgroup(pDomain);
if( pOMCWSGroup == NULL) { TRACE_OUT(("pOMCWSGroup not found")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[pWSGroup->wsGroupID];
//
// Set up pointers to the object record and the object data itself:
//
pObjReg = pWSGroup->pObjReg; ValidateObject(pObjReg);
pRegObject = (POM_WSGROUP_REG_REC)pObjReg->pData; if (!pRegObject) { ERROR_OUT(("SetPersonData: object 0x%08x has no data", pObjReg)); rc = UT_RC_NO_MEM; DC_QUIT; } ValidateObjectDataWSGREGREC(pRegObject);
//
// Allocate some memory for the new object with which we are about to
// replace the old one:
//
pNewRegObject = (POM_WSGROUP_REG_REC)UT_MallocRefCount(sizeof(OM_WSGROUP_REG_REC), TRUE); if (!pNewRegObject) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// Set the fields in the new object to have the same data as the old:
//
pNewRegObject->length = pRegObject->length; pNewRegObject->idStamp = pRegObject->idStamp; pNewRegObject->userID = pRegObject->userID; pNewRegObject->status = pRegObject->status;
//
// Fill in the person data fields and issue the replace:
//
COM_GetSiteName(pNewRegObject->personData.personName, sizeof(pNewRegObject->personData.personName));
rc = ObjectDRU(pomPrimary->putTask, pOMCWSGroup, pOMCWorkset, pObjReg, (POM_OBJECTDATA) pNewRegObject, OMNET_OBJECT_REPLACE); if (rc != 0) { DC_QUIT; }
TRACE_OUT((" Set person data for WSG %d", pWSGroup->wsg));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(("Error %d updating own reg object for WSG %d", rc, pWSGroup->wsg)); }
DebugExitDWORD(SetPersonData, rc); return(rc); }
//
// RemoveInfoObject(...)
//
void RemoveInfoObject ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_OBJECT pObj;
DebugEntry(RemoveInfoObject);
//
// OK, we've got to delete the identification object in workset #0 in
// ObManControl which identified the workset group.
//
pOMCWSGroup = GetOMCWsgroup(pDomain); pOMCWorkset = GetOMCWorkset(pDomain, 0);
//
// ...search for the WSGROUP_INFO object (by wsGroupID - we don't know
// the name or function profile so leave them blank):
//
FindInfoObject(pDomain, wsGroupID, OMWSG_MAX, OMFP_MAX, &pObj);
if (pObj == NULL) { //
// This should happen only for the local Domain:
//
// SFR 2208 : No: This will also happen in a regular call when
// the call ends almost as soon as it has begun. The
// sequence of events is as follows:
//
// - on callee, ObMan sends WSG_SEND_REQ to caller
// - caller sends REG_REC object, then WORKSET_CATCHUP
// then the INFO object we can't find
// - callee receives REG_REC then WORKSET_CATHCUP
// - call ends and callee enters WSGRemoveFromDomain
// which finds the REG_REC then calls us here
//
// Therefore the DC_ABSence of the INFO object is valid
// and we just trace an alert:
//
// NOTE: It will also happen when we receive a DELETE from
// someone else who is doing the same purge process
// as us.
//
WARNING_OUT(("No INFO object found for wsGroup %hu", wsGroupID)); DC_QUIT; } else { ValidateObject(pObj); }
//
// We found an object, so delete it from the workset:
//
TRACE_OUT(("Deleting INFO object for wsGroup %hu from domain %u", wsGroupID, pDomain->callID));
ObjectDRU(pomPrimary->putTask, pOMCWSGroup, pOMCWorkset, pObj, NULL, OMNET_OBJECT_DELETE);
DC_EXIT_POINT: DebugExitVOID(RemoveInfoObject); }
//
// RemovePersonObject(...)
//
void RemovePersonObject ( POM_PRIMARY pomPrimary, POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, NET_UID detachedUserID ) { POM_WSGROUP pOMCWSGroup; POM_WORKSET pOMCWorkset; POM_OBJECT pObjReg; NET_UID userIDRemoved; POM_WSGROUP_REG_REC pRegObject;
DebugEntry(RemovePersonObject);
//
// Set up pointers to the ObManControl workset group and the relevant
// workset within it:
//
pOMCWSGroup = GetOMCWsgroup(pDomain);
if( pOMCWSGroup == NULL) { TRACE_OUT(("OMC Workset Group not found - no person objects to remove")); DC_QUIT; }
pOMCWorkset = pOMCWSGroup->apWorksets[wsGroupID];
//
// If there is no such workset, it could be because the workset group
// has been moved into the local Domain on call end etc. In this case,
// just quit out.
//
if (pOMCWorkset == NULL) { TRACE_OUT(("OMC Workset not found - no person objects to remove")); DC_QUIT; }
//
// If detachedUserID is NET_ALL_REMOTES, we've a lot of work to do and
// we'll do this loop many times - otherwise we'll just do it for a
// single person object.
//
for (;;) { if (detachedUserID == NET_ALL_REMOTES) { //
// This will find ANY person object that's NOT OURS:
//
FindPersonObject(pOMCWorkset, pDomain->userID, FIND_OTHERS, &pObjReg); } else { //
// This will find a specific node's person object:
//
FindPersonObject(pOMCWorkset, detachedUserID, FIND_THIS, &pObjReg); }
//
// If we don't find one, get out of the loop:
//
if (pObjReg == NULL) { break; }
ValidateObject(pObjReg);
//
// If detachedUserID was NET_ALL_REMOTES, the user ID in the object
// we're deleting will obviously be different. So, find out the
// real user ID from the object we're deleting:
//
pRegObject = (POM_WSGROUP_REG_REC)pObjReg->pData; if (!pRegObject) { ERROR_OUT(("RemovePersonObject: object 0x%08x has no data", pObjReg)); } else { ValidateObjectDataWSGREGREC(pRegObject);
userIDRemoved = pRegObject->userID;
//
// Now delete the object. If the return code is bad, don't quit -
// we may still want to delete the info object.
//
TRACE_OUT(("Deleting person object for node 0x%08x, wsGroup %hu", userIDRemoved, wsGroupID));
if (ObjectDRU(pomPrimary->putTask, pOMCWSGroup, pOMCWorkset, pObjReg, NULL, OMNET_OBJECT_DELETE) != 0) { ERROR_OUT(("Error from ObjectDRU - leaving loop")); break; } } }
DC_EXIT_POINT: DebugExitVOID(RemovePersonObject); }
//
// WSGRecordFind(...)
//
void WSGRecordFind ( POM_DOMAIN pDomain, OMWSG wsg, OMFP fpHandler, POM_WSGROUP * ppWSGroup ) { POM_WSGROUP pWSGroup = NULL;
DebugEntry(WSGRecordFind);
//
// Search for workset group record:
//
TRACE_OUT(("Searching WSG list for Domain %u for match on WSG %d FP %d", pDomain->callID, wsg, fpHandler));
pWSGroup = (POM_WSGROUP)COM_BasedListFirst(&(pDomain->wsGroups), FIELD_OFFSET(OM_WSGROUP, chain)); while (pWSGroup != NULL) { if ((pWSGroup->wsg == wsg) && (pWSGroup->fpHandler == fpHandler)) { break; }
pWSGroup = (POM_WSGROUP)COM_BasedListNext(&(pDomain->wsGroups), pWSGroup, FIELD_OFFSET(OM_WSGROUP, chain)); }
//
// Set up caller's pointer:
//
*ppWSGroup = pWSGroup;
DebugExitVOID(WSGRecordFind); }
//
// AddClientToWSGList(...)
//
UINT AddClientToWSGList ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, OM_WSGROUP_HANDLE hWSGroup, UINT mode ) { POM_CLIENT_LIST pClientListEntry; UINT count; UINT rc = 0;
DebugEntry(AddClientToWSGList);
//
// Count the number of local primaries registered with the workset
// group:
//
count = 0;
pClientListEntry = (POM_CLIENT_LIST)COM_BasedListFirst(&(pWSGroup->clients), FIELD_OFFSET(OM_CLIENT_LIST, chain)); while (pClientListEntry != NULL) { if (pClientListEntry->mode == PRIMARY) { count++; }
pClientListEntry = (POM_CLIENT_LIST)COM_BasedListNext(&(pWSGroup->clients), pClientListEntry, FIELD_OFFSET(OM_CLIENT_LIST, chain)); }
//
// What we do now depends on whether this is a primary or a secondary
// registration:
//
if (mode == PRIMARY) { //
// If a primary, check that no other primaries are present:
//
if (count > 0) { ERROR_OUT(("Can't register TASK 0x%08x with WSG %d as primary: " "another primary is already registered", putTask, pWSGroup->wsg)); rc = OM_RC_TOO_MANY_CLIENTS; DC_QUIT; } else { TRACE_OUT(("%hu primary Clients already registered with WSG %d", count, pWSGroup->wsg)); } } else // mode == SECONDARY
{ if (count == 0) { WARNING_OUT(("Can't register TASK 0x%08x with WSG %d as secondary: " "no primary registered", putTask, pWSGroup->wsg)); rc = OM_RC_NO_PRIMARY; DC_QUIT; } }
//
// OK, allocate some memory for the Client's entry in the list:
//
pClientListEntry = (POM_CLIENT_LIST)UT_MallocRefCount(sizeof(OM_CLIENT_LIST), TRUE); if (!pClientListEntry) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pClientListEntry, CLIENTLIST);
pClientListEntry->putTask = putTask; pClientListEntry->hWSGroup = hWSGroup; pClientListEntry->mode = (WORD)mode;
COM_BasedListInsertBefore(&(pWSGroup->clients), &(pClientListEntry->chain));
TRACE_OUT(("Added TASK 0x%08x to Client list for WSG %d as %s", putTask, pWSGroup->wsg, mode == PRIMARY ? "primary" : "secondary"));
DC_EXIT_POINT: DebugExitDWORD(AddClientToWSGList, rc); return(rc); }
//
// FindPersonObject(...)
//
void FindPersonObject ( POM_WORKSET pOMCWorkset, NET_UID userID, UINT searchType, POM_OBJECT * ppObjReg ) { BOOL found = FALSE; POM_OBJECT pObj; POM_WSGROUP_REG_REC pRegObject; UINT rc = 0;
DebugEntry(FindPersonObject);
TRACE_OUT(("Searching OMC workset %u for reg obj %sowned by node 0x%08x", pOMCWorkset->worksetID, searchType == FIND_THIS ? "" : "not ", userID));
pObj = (POM_OBJECT)COM_BasedListFirst(&(pOMCWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObj != NULL) { ValidateObject(pObj);
if (pObj->flags & DELETED) { // Do nothing
} else if (!pObj->pData) { ERROR_OUT(("FindPersonObject: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pObj->pData); pRegObject = (POM_WSGROUP_REG_REC)pObj->pData;
if (pRegObject->idStamp == OM_WSGREGREC_ID_STAMP) { if (((searchType == FIND_THIS) && (pRegObject->userID == userID)) || ((searchType == FIND_OTHERS) && (pRegObject->userID != userID))) { //
// Got it:
//
found = TRUE; break; } } }
pObj = (POM_OBJECT)COM_BasedListNext(&(pOMCWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); }
if (found == TRUE) { *ppObjReg = pObj; } else { if (searchType == FIND_THIS) { TRACE_OUT(("No reg object found for node 0x%08x in workset %u", userID, pOMCWorkset->worksetID)); }
*ppObjReg = NULL; }
DebugExitVOID(FindPersonObject); }
//
// PostWorksetNewEvents(...)
//
UINT PostWorksetNewEvents ( PUT_CLIENT putFrom, PUT_CLIENT putTo, POM_WSGROUP pWSGroup, OM_WSGROUP_HANDLE hWSGroup ) { OM_WORKSET_ID worksetID; OM_EVENT_DATA16 eventData16; POM_WORKSET pWorkset; UINT count; UINT rc = 0;
DebugEntry(PostWorksetNewEvents);
TRACE_OUT(("Posting WORKSET_NEW events to Client TASK 0x%08x for WSG %d", putTo, pWSGroup->wsg));
count = 0; for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { pWorkset = pWSGroup->apWorksets[worksetID];
if (pWorkset != NULL) { eventData16.hWSGroup = hWSGroup; eventData16.worksetID = worksetID;
UT_PostEvent(putFrom, putTo, 0, OM_WORKSET_NEW_IND, *(PUINT) &eventData16, 0);
count++; } }
TRACE_OUT(("Posted %hu WORKSET_NEW events (hWSGroup: %hu)", count, hWSGroup));
DebugExitDWORD(PostWorksetNewEvents, rc); return(rc); }
//
// OM_Register(...)
//
UINT OM_Register ( PUT_CLIENT putTask, OMCLI omType, POM_CLIENT * ppomClient ) { POM_CLIENT pomClient = NULL; UINT rc = 0;
DebugEntry(OM_Register);
UT_Lock(UTLOCK_OM);
if (!g_pomPrimary) { ERROR_OUT(("OM_Register failed; primary doesn't exist")); DC_QUIT; }
ValidateOMP(g_pomPrimary); ASSERT(omType >= OMCLI_FIRST); ASSERT(omType < OMCLI_MAX);
//
// Make sure this task isn't registered as an OM client
//
pomClient = &(g_pomPrimary->clients[omType]); if (pomClient->putTask) { ERROR_OUT(("OM secondary %d already exists", omType)); pomClient = NULL; rc = OM_RC_ALREADY_REGISTERED; DC_QUIT; }
// Bump up ref count on OM primary
UT_BumpUpRefCount(g_pomPrimary);
//
// Fill in the client info
//
ZeroMemory(pomClient, sizeof(*pomClient));
SET_STAMP(pomClient, OCLIENT); pomClient->putTask = putTask;
COM_BasedListInit(&(pomClient->locks));
//
// Register an exit procedure for cleanup
//
UT_RegisterExit(putTask, OMSExitProc, pomClient); pomClient->exitProcReg = TRUE;
//
// Register our hidden event handler for the Client (the parameter to be
// passed to the event handler is the pointer to the Client record):
//
UT_RegisterEvent(putTask, OMSEventHandler, pomClient, UT_PRIORITY_OBMAN); pomClient->hiddenHandlerReg = TRUE;
DC_EXIT_POINT: *ppomClient = pomClient;
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_Register, rc); return(rc); }
//
// OM_Deregister()
//
void OM_Deregister(POM_CLIENT * ppomClient) { DebugEntry(OM_Deregister);
ASSERT(ppomClient); OMSExitProc(*ppomClient); *ppomClient = NULL;
DebugExitVOID(OM_Deregister); }
//
// OMSExitProc(...)
//
void CALLBACK OMSExitProc(LPVOID uData) { POM_CLIENT pomClient = (POM_CLIENT)uData; OM_WSGROUP_HANDLE hWSGroup; OM_WSGROUP_HANDLE hWSGroupTemp;
DebugEntry(OMSecExitProc);
UT_Lock(UTLOCK_OM);
ValidateOMS(pomClient);
// Deregister the event handler and exit procedure (we do this early and
// clear the flags since we want to avoid recursive abends):
//
if (pomClient->hiddenHandlerReg) { UT_DeregisterEvent(pomClient->putTask, OMSEventHandler, pomClient); pomClient->hiddenHandlerReg = FALSE; }
if (pomClient->exitProcReg) { UT_DeregisterExit(pomClient->putTask, OMSExitProc, pomClient); pomClient->exitProcReg = FALSE; }
//
// Deregister the Client from any workset groups with which it is still
// registered.
//
// The code works as follows:
//
// FOR each record in the apUsageRecs array
// IF there is a valid offset there it refers to a registered
// workset group so deregister it.
//
TRACE_OUT(("Checking Client record for active workset group handles"));
for (hWSGroup = 0; hWSGroup < OMWSG_MAXPERCLIENT; hWSGroup++) { if ((pomClient->apUsageRecs[hWSGroup] != NULL) && (pomClient->apUsageRecs[hWSGroup] != (POM_USAGE_REC)-1)) { //
// Need to copy hWSGroup into a temporary variable, since
// OM_WSGroupDeregister will set it to zero and that would
// mess up our for-loop otherwise:
//
hWSGroupTemp = hWSGroup; OM_WSGroupDeregister(pomClient, &hWSGroupTemp); } }
//
// NULL out the task; that's how the OM primary knows the task is
// present or not.
//
pomClient->putTask = NULL;
UT_FreeRefCount((void**)&g_pomPrimary, TRUE);
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OMSExitProc); }
//
// OMSEventHandler(...)
//
BOOL CALLBACK OMSEventHandler ( LPVOID uData, UINT event, UINT_PTR eventParam1, UINT_PTR eventParam2 ) { POM_CLIENT pomClient = (POM_CLIENT)uData; OM_WSGROUP_HANDLE hWSGroup; OM_WORKSET_ID worksetID; POM_OBJECT pObj; UINT correlator; POM_PENDING_OP pPendingOp = NULL; POM_LOCK pLock; POM_WORKSET pWorkset; UINT result; POM_USAGE_REC pUsageRec; OM_OPERATION_TYPE type = NULL_OP; BOOL ObjectEvent = FALSE; BOOL processed = FALSE;
DebugEntry(OMSEventHandler);
UT_Lock(UTLOCK_OM);
ValidateOMS(pomClient);
//
// First check if this is an ObMan event:
//
if ((event < OM_BASE_EVENT) || (event > OM_LAST_EVENT)) { DC_QUIT; }
TRACE_OUT(("Processing ObMan event %d (param1: 0x%08x, param2: 0x%08x)", event, eventParam1, eventParam2));
//
// Extract the fields from the event parameters (some or all of these
// will be unused, depending on which event this is):
//
hWSGroup = (*(POM_EVENT_DATA16)&eventParam1).hWSGroup; worksetID = (*(POM_EVENT_DATA16)&eventParam1).worksetID;
correlator = (*(POM_EVENT_DATA32)&eventParam2).correlator; result = (*(POM_EVENT_DATA32)&eventParam2).result;
pObj = (POM_OBJECT) eventParam2;
//
// ObMan guarantees not to deliver out of date events to client e.g.
// workset open events for aworkset it has since closed, or object add
// events for a workset group from which it has deregistered.
//
// Filtering these events is the main purpose of this hidden handler
// function; we check each event and if the workset group handle or
// object handle are invalid or if the workset is closed, we swallow the
// event.
//
switch (event) { case OM_OUT_OF_RESOURCES_IND: { //
// Do nothing.
//
} break;
case OM_WSGROUP_REGISTER_CON: { //
// Mark this workset group as valid for our client.
//
pomClient->wsgValid[hWSGroup] = TRUE;
ASSERT(ValidWSGroupHandle(pomClient, hWSGroup));
pUsageRec = pomClient->apUsageRecs[hWSGroup];
TRACE_OUT(("REGISTER_CON arrived for wsg %d (result %u, hWSGroup %u)", pUsageRec->pWSGroup->wsg, result, hWSGroup));
if (result != 0) { //
// The registration has failed, so call WSGroupDeregister to
// free up all the resources, then quit:
//
WARNING_OUT(("Registration failed for wsg %d, deregistering", pUsageRec->pWSGroup->wsg));
OM_WSGroupDeregister(pomClient, &hWSGroup); DC_QUIT; } } break;
case OMINT_EVENT_WSGROUP_DEREGISTER: { //
// This event is designed to flush the Client's message queue of
// all events relating to a particular workset group handle.
//
// Because this event has arrived, we know there are no more
// events containing this workset group handle in the queue, so
// we can safely mark the handle for re-use:
//
// So, do a quick sanity check then reset the slot in the array
// of usage record offsets:
//
ASSERT(!pomClient->wsgValid[hWSGroup]);
TRACE_OUT(("Got WSGROUP_DEREGISTER back marker event for " "hWSGroup %u, marking handle as ready for re-use", hWSGroup));
pomClient->apUsageRecs[hWSGroup] = NULL;
//
// ...and swallow the event:
//
processed = TRUE; } break;
case OM_WSGROUP_MOVE_CON: case OM_WSGROUP_MOVE_IND: case OM_WORKSET_NEW_IND: { if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; } } break;
case OM_WORKSET_OPEN_CON: { if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; }
//
// Else mark the workset as open:
//
pUsageRec = pomClient->apUsageRecs[hWSGroup];
TRACE_OUT(("Marking workset %u in wsg %d open for Client 0x%08x", worksetID, pUsageRec->pWSGroup->wsg, pomClient));
WORKSET_SET_OPEN(pUsageRec, worksetID); } break;
case OM_WORKSET_UNLOCK_IND: { if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; }
pUsageRec = pomClient->apUsageRecs[hWSGroup]; if (!WORKSET_IS_OPEN(pUsageRec, worksetID)) { TRACE_OUT(("Workset %u in wsg %d no longer open; ignoring event %d", worksetID, pUsageRec->pWSGroup->wsg, event)); processed = TRUE; DC_QUIT; } } break;
case OM_WORKSET_CLEAR_IND: { if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; }
pUsageRec = pomClient->apUsageRecs[hWSGroup]; if (!WORKSET_IS_OPEN(pUsageRec, worksetID)) { TRACE_OUT(("Workset %u in wsg %d no longer open; ignoring event %d", worksetID, pUsageRec->pWSGroup->wsg, event)); processed = TRUE; DC_QUIT; }
//
// Check if Clear still pending; quit if not:
//
pWorkset = pUsageRec->pWSGroup->apWorksets[worksetID]; ASSERT((pWorkset != NULL));
FindPendingOp(pWorkset, pObj, WORKSET_CLEAR, &pPendingOp);
if (pPendingOp == NULL) { TRACE_OUT(("Clear already confirmed for workset %hu", worksetID)); processed = TRUE; DC_QUIT; } } break;
case OM_WORKSET_LOCK_CON: { if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; }
pUsageRec = pomClient->apUsageRecs[hWSGroup]; if (!WORKSET_IS_OPEN(pUsageRec, worksetID)) { TRACE_OUT(("Workset %u in wsg %d no longer open; ignoring event %d", worksetID, pUsageRec->pWSGroup->wsg, event)); processed = TRUE; DC_QUIT; }
//
// Search for the lock on the lock stack:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pomClient->locks), (void**)&pLock, FIELD_OFFSET(OM_LOCK, chain), FIELD_OFFSET(OM_LOCK, worksetID), (DWORD)worksetID, FIELD_SIZE(OM_LOCK, worksetID));
//
// If the lock is not present on the lock stack, then the Client
// must have called Unlock since it called LockReq. So, we
// swallow the event:
//
if (pLock == NULL) { TRACE_OUT(("Lock already cancelled for workset %hu", worksetID)); processed = TRUE; DC_QUIT; }
//
// When object locking supported, the first lock which matches
// on worksetID might not be the workset lock, so more code will
// be needed here then. In the meantime, just assert:
//
ASSERT((OBJECT_ID_IS_NULL(pLock->objectID)));
//
// If lock request failed, remove the lock from the Client's
// lock stack:
//
if (result != 0) { TRACE_OUT(("Lock failed; removing lock from Client's lock stack"));
COM_BasedListRemove(&pLock->chain); UT_FreeRefCount((void**)&pLock, FALSE); } } break;
case OM_OBJECT_ADD_IND: case OM_OBJECT_MOVE_IND: { ObjectEvent = TRUE;
if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; }
pUsageRec = pomClient->apUsageRecs[hWSGroup]; if (!WORKSET_IS_OPEN(pUsageRec, worksetID)) { TRACE_OUT(("Workset %u in wsg %d no longer open; ignoring event %d", worksetID, pUsageRec->pWSGroup->wsg, event)); processed = TRUE; DC_QUIT; }
if (!ValidObject(pObj) || (pObj->flags & DELETED)) { processed = TRUE; DC_QUIT; }
pUsageRec = pomClient->apUsageRecs[hWSGroup]; pWorkset = pUsageRec->pWSGroup->apWorksets[worksetID]; ASSERT((pWorkset != NULL));
if (WorksetClearPending(pWorkset, pObj)) { TRACE_OUT(("Event %hu for object 0x%08x will be swallowed since " "object about to be cleared from the workset", event, pObj)); processed = TRUE; DC_QUIT; } } break;
case OM_OBJECT_DELETE_IND: case OM_OBJECT_REPLACE_IND: case OM_OBJECT_UPDATE_IND: { ObjectEvent = TRUE;
switch (event) { case OM_OBJECT_DELETE_IND: type = OBJECT_DELETE; break;
case OM_OBJECT_REPLACE_IND: type = OBJECT_REPLACE; break;
case OM_OBJECT_UPDATE_IND: type = OBJECT_UPDATE; break;
default: ERROR_OUT(("Reached default case in switch")); }
//
// Check workset group handle is still valid, workset is still
// open and object handle is still valid; if not, swallow event:
//
if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; }
pUsageRec = pomClient->apUsageRecs[hWSGroup]; if (!WORKSET_IS_OPEN(pUsageRec, worksetID)) { TRACE_OUT(("Workset %u in wsg %d no longer open; ignoring event %d", worksetID, pUsageRec->pWSGroup->wsg, event)); processed = TRUE; DC_QUIT; }
//
// We also want to quit if the object is no longer valid or if
// there is a clear pending (just as for ADD/MOVE) but if we do
// so, we will also need to remove the pending op from the list.
// So, find the op now; if we quit and swallow the event, the
// function exit code will do the remove (this saves having to
// break up the QUIT_IF... macros for this special case).
//
// So, check the pending op list:
//
pWorkset = pUsageRec->pWSGroup->apWorksets[worksetID]; ASSERT((pWorkset != NULL));
FindPendingOp(pWorkset, pObj, type, &pPendingOp); if (pPendingOp == NULL) { TRACE_OUT(("Operation type %hu already confirmed for object 0x%08x", type, pObj)); processed = TRUE; DC_QUIT; }
if (!ValidObject(pObj) || (pObj->flags & DELETED)) { processed = TRUE; DC_QUIT; }
if (WorksetClearPending(pWorkset, pObj)) { TRACE_OUT(("Event %hu for object 0x%08x will be swallowed since " "object about to be cleared from the workset", event, pObj)); processed = TRUE; DC_QUIT; } } break;
case OM_WORKSET_CLEARED_IND: case OM_OBJECT_DELETED_IND: case OM_OBJECT_UPDATED_IND: case OM_OBJECT_REPLACED_IND: { //
// All of these except the CLEARED_IND are object events:
//
if (event != OM_WORKSET_CLEARED_IND) { ObjectEvent = TRUE; }
//
// These are secondary API events. Swallow them if the workset
// is closed, but DO NOT swallow if object handle invalid (since
// we don't make guarantees about validity of handles passed in
// these events):
//
if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; }
pUsageRec = pomClient->apUsageRecs[hWSGroup]; if (!WORKSET_IS_OPEN(pUsageRec, worksetID)) { TRACE_OUT(("Workset %u in WSG %d no longer open; ignoring event %d", worksetID, pUsageRec->pWSGroup->wsg, event)); processed = TRUE; DC_QUIT; } } break;
case OM_PERSON_JOINED_IND: case OM_PERSON_LEFT_IND: case OM_PERSON_DATA_CHANGED_IND: { if (!ValidWSGroupHandle(pomClient, hWSGroup)) { TRACE_OUT(("hWSGroup %d is not valid; ignoring event %d", hWSGroup, event)); processed = TRUE; DC_QUIT; } } break;
default: { ERROR_OUT(("Unrecognised ObMan event 0x%08x", event)); } }
DC_EXIT_POINT:
//
// Whenever an event containing an object handle is posted, the use
// count of the object record is bumped, so we free it now:
//
if (ObjectEvent) { ValidateObject(pObj); UT_FreeRefCount((void**)&pObj, FALSE); }
UT_Unlock(UTLOCK_OM);
DebugExitBOOL(OMSEventHandler, processed); return(processed); }
//
// OM_WSGroupRegisterS(...)
//
UINT OM_WSGroupRegisterS ( POM_CLIENT pomClient, UINT callID, OMFP fpHandler, OMWSG wsg, OM_WSGROUP_HANDLE * phWSGroup ) { POM_DOMAIN pDomain; POM_WSGROUP pWSGroup; POM_USAGE_REC pUsageRec; POM_CLIENT_LIST pClientListEntry; BOOL setUpUsageRec = FALSE; UINT rc = 0;
DebugEntry(OM_WSGroupRegisterS);
UT_Lock(UTLOCK_OM);
//
// Validate params:
//
ValidateOMS(pomClient);
//
// Search for this Domain and workset group:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(g_pomPrimary->domains), (void**)&pDomain, FIELD_OFFSET(OM_DOMAIN, chain), FIELD_OFFSET(OM_DOMAIN, callID), (DWORD)callID, FIELD_SIZE(OM_DOMAIN, callID));
if (pDomain == NULL) { //
// We don't have a record for this Domain so there can be no primary
// registered with the workset group:
//
TRACE_OUT(("Not attached to Domain %u", callID)); rc = OM_RC_NO_PRIMARY; DC_QUIT; }
WSGRecordFind(pDomain, wsg, fpHandler, &pWSGroup); if (pWSGroup == NULL) { rc = OM_RC_NO_PRIMARY; DC_QUIT; }
//
// If we get here, then the workset group exists locally so see if the
// Client is already registered with it:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pWSGroup->clients), (void**)&pClientListEntry, FIELD_OFFSET(OM_CLIENT_LIST, chain), FIELD_OFFSET(OM_CLIENT_LIST, putTask), (DWORD_PTR)pomClient->putTask, FIELD_SIZE(OM_CLIENT_LIST, putTask));
if (pClientListEntry != NULL) { rc = OM_RC_ALREADY_REGISTERED; ERROR_OUT(("Can't register Client 0x%08x with WSG %d - already registered", pomClient, wsg)); DC_QUIT; }
//
// OK, Client is not already registered so register it now:
//
rc = SetUpUsageRecord(pomClient, SECONDARY, &pUsageRec, phWSGroup); if (rc != 0) { DC_QUIT; }
//
// SetUpUsageRecord doesn't put the workset group pointer in the CB
// (since it's not known yet in the case of a PRIMARY registration), so
// we do this now ourselves:
//
pUsageRec->pWSGroup = pWSGroup;
setUpUsageRec = TRUE;
//
// add this Client to the workset group's Client list:
//
rc = AddClientToWSGList(pomClient->putTask, pWSGroup, *phWSGroup, SECONDARY); if (rc != 0) { DC_QUIT; }
pUsageRec->flags |= ADDED_TO_WSGROUP_LIST;
pomClient->wsgValid[*phWSGroup] = TRUE;
//
// Post WORKSET_NEW events to the Client for the worksets in the group,
// if any:
//
PostWorksetNewEvents(pomClient->putTask, pomClient->putTask, pWSGroup, *phWSGroup);
TRACE_OUT(("Registered 0x%08x as secondary Client for WSG %d (hWSGroup: %hu)", pomClient, wsg, *phWSGroup));
DC_EXIT_POINT:
if (rc != 0) { if (rc == OM_RC_NO_PRIMARY) { //
// We do a regular trace here rather than an error because this
// happens normally:
//
TRACE_OUT(("No primary Client for WSG %d in Domain %u " "- can't register secondary", wsg, callID)); } else { ERROR_OUT(("Error %d registering Client 0x%08x as secondary" "for WSG %d in Domain %u", rc, pomClient, wsg, callID)); }
if (setUpUsageRec == TRUE) { pomClient->apUsageRecs[*phWSGroup] = NULL;
if (pUsageRec->flags & ADDED_TO_WSGROUP_LIST) { RemoveClientFromWSGList(pomClient->putTask, pomClient->putTask, pWSGroup); }
UT_FreeRefCount((void**)&pUsageRec, FALSE); }
pomClient->wsgValid[*phWSGroup] = FALSE; }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_WSGroupRegisterS, rc); return(rc); }
//
// OM_WorksetOpenS(...)
//
UINT OM_WorksetOpenS ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID ) { POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_USAGE_REC pUsageRec; POM_CLIENT_LIST pClientListEntry = NULL; UINT rc = 0;
DebugEntry(OM_WorksetOpenS);
UT_Lock(UTLOCK_OM);
//
// Validate params:
//
ValidateParams2(pomClient, hWSGroup, SECONDARY, &pUsageRec, &pWSGroup);
TRACE_OUT(("Secondary Client 0x%08x requesting to open workset %u in WSG %d", pomClient, worksetID, pWSGroup->wsg));
//
// If the Client already has this workset open then return a (non-error)
// return code:
//
if (WORKSET_IS_OPEN(pUsageRec, worksetID) == TRUE) { TRACE_OUT(("Client 0x%08x already has workset %u in WSG %d open", pomClient, worksetID, pWSGroup->wsg)); rc = OM_RC_WORKSET_ALREADY_OPEN; DC_QUIT; }
//
// Check workset group record to see if workset exists:
//
if (pWSGroup->apWorksets[worksetID] == NULL) { //
// Workset doesn't exist so return bad rc:
//
WARNING_OUT(("Workset %hu doesn't exist in WSG %d", worksetID, pWSGroup->wsg)); rc = OM_RC_WORKSET_DOESNT_EXIST; DC_QUIT; } else { //
// Workset already exists, so we don't need to do anything.
//
TRACE_OUT((" Workset %hu in WSG %d already exists", worksetID, pWSGroup->wsg)); }
//
// If the workset didn't already exist, queueing the send instruction
// will have caused the workset to be created syncrhonously. So, either
// way the workset exists at this point.
//
//
// Get a pointer to the workset:
//
pWorkset = pWSGroup->apWorksets[worksetID];
ASSERT((pWorkset != NULL));
//
// Mark this workset as open in the Client's usage record:
//
WORKSET_SET_OPEN(pUsageRec, worksetID);
//
// Add this Client to the list kept in the workset record:
//
rc = AddClientToWsetList(pomClient->putTask, pWorkset, hWSGroup, pUsageRec->mode, &pClientListEntry); if (rc != 0) { DC_QUIT; }
rc = PostAddEvents(pomClient->putTask, pWorkset, hWSGroup, pomClient->putTask); if (rc != 0) { DC_QUIT; }
TRACE_OUT(("Opened workset %u in WSG %d for secondary Client 0x%08x", worksetID, pWSGroup->wsg, pomClient));
DC_EXIT_POINT:
if ((rc != 0) && (rc != OM_RC_WORKSET_ALREADY_OPEN)) { //
// Cleanup:
//
ERROR_OUT(("Error %d opening workset %u in WSG %d for Client 0x%08x", rc, worksetID, pWSGroup->wsg, pomClient));
WORKSET_SET_CLOSED(pUsageRec, worksetID);
if (pClientListEntry != NULL) { COM_BasedListRemove(&(pClientListEntry->chain)); UT_FreeRefCount((void**)&pClientListEntry, FALSE); } }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_WorksetOpenS, rc); return(rc); }
//
// OM_WSGroupRegisterPReq(...)
//
UINT OM_WSGroupRegisterPReq ( POM_CLIENT pomClient, UINT callID, OMFP fpHandler, OMWSG wsg, OM_CORRELATOR * pCorrelator ) { POM_WSGROUP_REG_CB pRegistrationCB = NULL; POM_USAGE_REC pUsageRec; OM_WSGROUP_HANDLE hWSGroup; BOOL setUpUsageRec = FALSE; UINT rc = 0;
DebugEntry(OM_WSGroupRegisterPReq);
UT_Lock(UTLOCK_OM);
ValidateOMS(pomClient);
//
// Set up a usage record and workset group handle for the Client:
//
rc = SetUpUsageRecord(pomClient, PRIMARY, &pUsageRec, &hWSGroup); if (rc != 0) { DC_QUIT; } setUpUsageRec = TRUE;
//
// Create a new correlator for the Client and put it in the Client's
// variable:
//
*pCorrelator = NextCorrelator(g_pomPrimary);
//
// Sub alloc a chunk of memory for the registration control block, in
// which we will pass the registration request parameters to the ObMan
// task:
//
pRegistrationCB = (POM_WSGROUP_REG_CB)UT_MallocRefCount(sizeof(OM_WSGROUP_REG_CB), TRUE); if (!pRegistrationCB) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pRegistrationCB, REGCB);
//
// Fill in the fields, but note that we don't yet know the Domain record
// or workset group, so we leave those ones blank:
//
pRegistrationCB->putTask = pomClient->putTask; pRegistrationCB->callID = callID; pRegistrationCB->correlator = *pCorrelator; pRegistrationCB->hWSGroup = hWSGroup; pRegistrationCB->wsg = wsg; pRegistrationCB->fpHandler = fpHandler; pRegistrationCB->retryCount = OM_REGISTER_RETRY_COUNT_DFLT; pRegistrationCB->valid = TRUE; pRegistrationCB->type = WSGROUP_REGISTER; pRegistrationCB->mode = PRIMARY; pRegistrationCB->pUsageRec = pUsageRec;
//
// Now put a pointer to the registration CB in the usage record, as
// described above, and set a flag so we know what we've done:
//
pUsageRec->pWSGroup = (POM_WSGROUP) pRegistrationCB; pUsageRec->flags |= PWSGROUP_IS_PREGCB;
//
// Post an event to the ObMan task telling it to process this CB.
//
// The first parameter is the retry value for the event.
//
// The second parameter is the offset of the control block in the OMMISC
// memory block.
//
UT_PostEvent(pomClient->putTask, // Client's putTask
g_pomPrimary->putTask, // ObMan's putTask
0, OMINT_EVENT_WSGROUP_REGISTER, 0, (UINT_PTR)pRegistrationCB);
TRACE_OUT(("Requested to register Client 0x%08x with WSG %d", pomClient, wsg));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(("Error 0x%08x registering Client 0x%08x with WSG %d", rc, pomClient, wsg));
if (pRegistrationCB != NULL) { //
// We can free the reg CB safely since we know that if we hit an
// error, we never got around to inserting the item in the list or
// posting its offset to the ObMan task:
//
UT_FreeRefCount((void**)&pRegistrationCB, FALSE); }
if (setUpUsageRec) { UT_FreeRefCount((void**)&pUsageRec, FALSE); pomClient->apUsageRecs[hWSGroup] = NULL; } }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_WSGroupRegisterPReq, rc); return(rc); }
//
// OM_WSGroupMoveReq(...)
//
UINT OM_WSGroupMoveReq ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, UINT callID, OM_CORRELATOR * pCorrelator ) { POM_USAGE_REC pUsageRec; POM_WSGROUP pWSGroup; POM_DOMAIN pDomain; POM_WSGROUP_REG_CB pRegistrationCB = NULL; UINT rc = 0;
DebugEntry(OM_WSGroupMoveReq);
UT_Lock(UTLOCK_OM);
ValidateParams2(pomClient, hWSGroup, PRIMARY, &pUsageRec, &pWSGroup);
TRACE_OUT(("Client 0x%08x requesting to move WSG %d into Domain %u", pomClient, hWSGroup, callID));
//
// Check workset group is not already in a Call: (this may be relaxed)
//
pDomain = pWSGroup->pDomain;
if (pDomain->callID != OM_NO_CALL) { ERROR_OUT(("Client 0x%08x attempted to move WSG %d out of a call " "(Domain %u)", pomClient, hWSGroup, pDomain->callID)); rc = OM_RC_ALREADY_IN_CALL; DC_QUIT; }
//
// Create a correlator, to correlate the MOVE_CON event:
//
*pCorrelator = NextCorrelator(g_pomPrimary);
//
// Create a control block to pass the relevant info to ObMan:
//
pRegistrationCB = (POM_WSGROUP_REG_CB)UT_MallocRefCount(sizeof(OM_WSGROUP_REG_CB), TRUE); if (!pRegistrationCB) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pRegistrationCB, REGCB);
//
// Fill in the fields:
//
pRegistrationCB->putTask = pomClient->putTask; pRegistrationCB->callID = callID; // DESTINATION Domain!
pRegistrationCB->correlator = *pCorrelator; pRegistrationCB->hWSGroup = hWSGroup; pRegistrationCB->wsg = pWSGroup->wsg; pRegistrationCB->fpHandler = pWSGroup->fpHandler; pRegistrationCB->retryCount = OM_REGISTER_RETRY_COUNT_DFLT; pRegistrationCB->valid = TRUE; pRegistrationCB->type = WSGROUP_MOVE; pRegistrationCB->mode = pUsageRec->mode; pRegistrationCB->pWSGroup = pWSGroup;
//
// Post an event to ObMan requesting it to process the CB:
//
UT_PostEvent(pomClient->putTask, g_pomPrimary->putTask, 0, // no delay
OMINT_EVENT_WSGROUP_MOVE, 0, (UINT_PTR)pRegistrationCB);
TRACE_OUT(("Requested to move WSG %d into Domain %u for Client 0x%08x", hWSGroup, callID, pomClient));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(("Error 0x%08x requesting to move WSG %d into Domain %u", rc, hWSGroup, callID));
if (pRegistrationCB != NULL) { UT_FreeRefCount((void**)&pRegistrationCB, FALSE); } }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_WSGroupMoveReq, rc); return(rc); }
//
// OM_WSGroupDeregister(...)
//
void OM_WSGroupDeregister ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE * phWSGroup ) { POM_WSGROUP pWSGroup; POM_USAGE_REC pUsageRec; OM_WORKSET_ID worksetID; OM_EVENT_DATA16 eventData16; OM_WSGROUP_HANDLE hWSGroup;
DebugEntry(OM_WSGroupDeregister);
UT_Lock(UTLOCK_OM);
ValidateOMS(pomClient);
hWSGroup = *phWSGroup;
//
// If this function has been called because of an abortive
// WSGroupRegister, or from OM_Deregister, the wsg might not yet be
// marked as VALID, so we check here and set it to VALID.
//
if (!pomClient->wsgValid[hWSGroup]) { TRACE_OUT(("Deregistering Client before registration completed")); pomClient->wsgValid[hWSGroup] = TRUE; }
// lonchanc: bug #1986, make sure we have a valid wsg.
// pWSGroup can be invalid in a race condition that we hang up
// before Whiteboard initializes.
pUsageRec = NULL; // make sure this local is reset in case we bail out from here.
if (!ValidWSGroupHandle(pomClient, hWSGroup) || (pomClient->apUsageRecs[hWSGroup] == (POM_USAGE_REC)-1)) { ERROR_OUT(("OM_WSGroupDeregister: Invalid wsg=0x0x%08x", hWSGroup)); DC_QUIT; }
//
// Get a pointer to the associated usage record:
//
pUsageRec = pomClient->apUsageRecs[hWSGroup];
//
// Extract a Client pointer to the workset group from the usage record:
//
pWSGroup = pUsageRec->pWSGroup;
//
// Test the flag in the usage record to see whether the <pWSGroup> field
// is actually pointing to the registration CB (which will be the case
// if we are deregistering immediately after registering):
//
if (pUsageRec->flags & PWSGROUP_IS_PREGCB) { //
// Mark the registration CB as invalid in order to abort the
// registration (ObMan will test for this in ProcessWSGRegister):
//
// Note: the pWSGroup field of the usage record is actually a pointer
// to a registration CB in this case
//
TRACE_OUT(("Client deregistering before registration even started - aborting")); ((POM_WSGROUP_REG_CB)pUsageRec->pWSGroup)->valid = FALSE; DC_QUIT; }
//
// Check the workset group record is valid:
//
ValidateWSGroup(pWSGroup);
//
// If it is valid, we continue with the deregistration process:
//
TRACE_OUT(("Deregistering Client 0x%08x from WSG %d", pomClient, hWSGroup));
//
// Close all the worksets in the group that the Client has open:
//
for (worksetID = 0; worksetID < OM_MAX_WORKSETS_PER_WSGROUP; worksetID++) { if (WORKSET_IS_OPEN(pUsageRec, worksetID)) { OM_WorksetClose(pomClient, hWSGroup, worksetID); } }
//
// If we added this Client to the workset group's Client list, find it
// again and remove it:
//
if (pUsageRec->flags & ADDED_TO_WSGROUP_LIST) { TRACE_OUT(("Removing Client from workset group list")); RemoveClientFromWSGList(pomClient->putTask, pomClient->putTask, pWSGroup); pUsageRec->flags &= ~ADDED_TO_WSGROUP_LIST; } else { TRACE_OUT(("Client not added to wsGroup list, not removing")); }
TRACE_OUT(("Deregistered Client 0x%08x from WSG %d", pomClient, hWSGroup));
DC_EXIT_POINT: //
// Free the usage record (we put this after the DC_QUIT since we want to
// do this even if the workset group pointer was found to be invalid
// above):
//
UT_FreeRefCount((void**)&pUsageRec, FALSE);
//
// Mark the workset group handle as invalid, so that any events which
// the Client gets will be swallowed:
//
pomClient->wsgValid[hWSGroup] = FALSE;
//
// Note: we don't set the slot in the usage record offset array to zero,
// since we don't want the workset group handle to be reused yet.
// When the DEREGISTER events arrives (after flushing the Client's
// event queue), we will set the offset to zero.
//
// However, if we leave the offset as it is, OM_Deregister might
// call us again because it thinks we haven't yet deregistered
// from the workset group. So, we set it to -1, which ensures
// that
//
// a) it is seen as in use by FindUnusedWSGHandle, since that
// function checks for 0
//
// b) it is seen as not in use by OM_Deregister, since that
// function checks for 0 or -1.
//
pomClient->apUsageRecs[hWSGroup] = (POM_USAGE_REC)-1;
//
// Send an OMINT_EVENT_WSGROUP_DEREGISTER event to the hidden handler (which
// will swallow it) to flush the Client's message queue:
//
TRACE_OUT(("Posting WSGROUP_DEREGISTER event to Client's hidden handler"));
eventData16.hWSGroup = hWSGroup; eventData16.worksetID = 0;
UT_PostEvent(pomClient->putTask, pomClient->putTask, 0, OMINT_EVENT_WSGROUP_DEREGISTER, *(PUINT) &eventData16, 0);
*phWSGroup = 0;
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_WSGroupDeregister); }
//
// OM_WorksetOpenPReq(...)
//
UINT OM_WorksetOpenPReq ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, NET_PRIORITY priority, BOOL fTemp, OM_CORRELATOR * pCorrelator ) { POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_USAGE_REC pUsageRec; OM_EVENT_DATA16 eventData16; OM_EVENT_DATA32 eventData32; POM_CLIENT_LIST pClientListEntry = NULL; UINT rc = 0;
DebugEntry(OM_WorksetOpenPReq);
UT_Lock(UTLOCK_OM);
//
// Validate params:
//
ValidateParams2(pomClient, hWSGroup, PRIMARY, &pUsageRec, &pWSGroup);
TRACE_OUT(("Client 0x%08x opening workset %u in WSG %d at priority 0x%08x", pomClient, worksetID, hWSGroup, priority));
//
// If the Client already has this workset open then return a (non-error)
// return code:
//
if (WORKSET_IS_OPEN(pUsageRec, worksetID) == TRUE) { TRACE_OUT(("Client 0x%08x already has workset %hu in WSG %d open", pomClient, worksetID, hWSGroup)); rc = OM_RC_WORKSET_ALREADY_OPEN; DC_QUIT; }
//
// Check the Client has supplied a valid value for <priority>:
//
if ((priority < NET_HIGH_PRIORITY) || (priority > NET_LOW_PRIORITY)) { ASSERT((priority == OM_OBMAN_CHOOSES_PRIORITY)); }
//
// Check workset group record to see if workset exists:
//
// Note: this check looks to see if the offset to the workset is zero,
// since workset records never reside at the start of the OMWORKSETS
// block.
//
if (pWSGroup->apWorksets[worksetID] == NULL) { rc = WorksetCreate(pomClient->putTask, pWSGroup, worksetID, fTemp, priority); if (rc != 0) { DC_QUIT; } } else { //
// Workset already exists, so we don't need to do anything.
//
TRACE_OUT((" Workset %hu in WSG %d already exists", worksetID, hWSGroup)); }
//
// If the workset didn't already exist, queueing the send instruction
// will have caused the workset to be created syncrhonously. So, either
// way the workset exists at this point.
//
//
// Get a pointer to the workset:
//
pWorkset = pWSGroup->apWorksets[worksetID];
ASSERT((pWorkset != NULL));
//
// Set the persistence field for the workset - we might not have done
// this as part of the WorksetCreate above if someone else had created
// the workset already. However, we set our local copy to have the
// appropriate persistence value.
//
pWorkset->fTemp = fTemp;
//
// We need to mark this workset as open in the Client's usage record.
// However, we don't do this yet - we do it in our hidden handler when
// the OPEN_CON event is received.
//
// The reason for this is that a Client shouldn't start using a workset
// until it has received the event, so we want the workset to remain
// closed until then.
//
// Note that whether we do it this way or mark the workset as open here
// and now doesn't make much difference from ObMan's point of view but
// it will help detect applications which are badly behaved.
//
//
// Add this Client to the list kept in the workset record:
//
rc = AddClientToWsetList(pomClient->putTask, pWorkset, hWSGroup, pUsageRec->mode, &pClientListEntry); if (rc != 0) { pClientListEntry = NULL; DC_QUIT; }
//
// Create correlator:
//
*pCorrelator = NextCorrelator(g_pomPrimary);
//
// Post WORKSET_OPEN_CON event to Client:
//
eventData16.hWSGroup = hWSGroup; eventData16.worksetID = worksetID;
eventData32.result = 0; eventData32.correlator = *pCorrelator;
TRACE_OUT((" Posting WORKSET_OPEN_CON to Client 0x%08x (task 0x%08x)"));
UT_PostEvent(pomClient->putTask, pomClient->putTask, 0, // no delay
OM_WORKSET_OPEN_CON, *(UINT *) &eventData16, *(UINT *) &eventData32);
//
// Now post OBJECT_ADD_IND events for each of the objects in the
// workset:
//
rc = PostAddEvents(pomClient->putTask, pWorkset, hWSGroup, pomClient->putTask); if (rc != 0) { DC_QUIT; }
TRACE_OUT(("Opened workset %hu in WSG %d for Client 0x%08x", worksetID, hWSGroup, pomClient));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(("Error 0x%08x opening workset %u in WSG %d for Client 0x%08x", rc, worksetID, hWSGroup, pomClient));
if (pClientListEntry != NULL) { COM_BasedListRemove(&(pClientListEntry->chain)); UT_FreeRefCount((void**)&pClientListEntry, FALSE); } }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_WorksetOpenPReq, rc); return(rc); }
//
// OM_WorksetClose(...)
//
void OM_WorksetClose ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID ) { POM_WORKSET pWorkset; POM_USAGE_REC pUsageRec; POM_CLIENT_LIST pClientListEntry;
DebugEntry(OM_WorksetClose);
UT_Lock(UTLOCK_OM);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY | SECONDARY, &pUsageRec, &pWorkset);
//
// Mark the workset as closed in the Client's usage record:
//
TRACE_OUT(("Closing workset %u in WSG %d for Client 0x%08x", worksetID, hWSGroup, pomClient));
WORKSET_SET_CLOSED(pUsageRec, worksetID);
//
// Now we release all the resources the Client is using which concern
// this workset. We
//
// - release all the locks the Client has for this workset
//
// - confirm any outstanding operations such as Deletes, etc.
//
// - release all the objects it is currently reading
//
// - discard any objects allocated but not yet used.
//
TRACE_OUT(("Releasing all resources in use by Client..."));
ReleaseAllLocks(pomClient, pUsageRec, pWorkset); ReleaseAllObjects(pUsageRec, pWorkset); ConfirmAll(pomClient, pUsageRec, pWorkset); DiscardAllObjects(pUsageRec, pWorkset);
//
// Remove the Client from the list of Clients stored in the workset
// record:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pWorkset->clients), (void**)&pClientListEntry, FIELD_OFFSET(OM_CLIENT_LIST, chain), FIELD_OFFSET(OM_CLIENT_LIST, putTask), (DWORD_PTR)pomClient->putTask, FIELD_SIZE(OM_CLIENT_LIST, putTask));
//
// If we've got this far, the Client has the workset open, so it must be
// listed in the workset's list of Clients:
//
ASSERT((pClientListEntry != NULL));
COM_BasedListRemove(&(pClientListEntry->chain)); UT_FreeRefCount((void**)&pClientListEntry, FALSE);
TRACE_OUT(("Closed workset %u in WSG %d for Client 0x%08x", worksetID, hWSGroup, pomClient));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_WorksetClose); }
//
// OM_WorksetLockReq(...)
//
UINT OM_WorksetLockReq ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, OM_CORRELATOR * pCorrelator ) { POM_USAGE_REC pUsageRec; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_LOCK pLastLock; POM_LOCK pThisLock = NULL; BOOL inserted = FALSE; UINT rc = 0;
DebugEntry(OM_WorksetLockReq);
UT_Lock(UTLOCK_OM);
//
// Validate params:
//
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY, &pUsageRec, &pWorkset);
//
// Set up workset group pointer:
//
pWSGroup = pUsageRec->pWSGroup;
TRACE_OUT(("Client 0x%08x requesting to lock workset %u in WSG %d", pomClient, worksetID, hWSGroup));
//
// Create a lock record which we will (eventually) put in the Client's
// lock stack:
//
pThisLock = (POM_LOCK)UT_MallocRefCount(sizeof(OM_LOCK), TRUE); if (!pThisLock) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pThisLock, LOCK);
//
// Fill in the fields:
//
pThisLock->pWSGroup = pWSGroup; pThisLock->worksetID = worksetID; ZeroMemory(&(pThisLock->objectID), sizeof(OM_OBJECT_ID));
//
// Check that granting this lock won't result in a lock order violation:
// (it will if this lock is earlier than or equal to the last lock
// acquired).
//
TRACE_OUT(("Checking for lock order violation..."));
pLastLock = (POM_LOCK)COM_BasedListFirst(&(pomClient->locks), FIELD_OFFSET(OM_LOCK, chain));
if (pLastLock != NULL) { ASSERT(CompareLocks(pLastLock, pThisLock) < 0);
TRACE_OUT(("Last lock acquired by Client 0x%08x was workset %u in WSG %d", pomClient, pLastLock->worksetID, pLastLock->pWSGroup->wsg)); } else { //
// If there aren't any locks on the lock stack then there can't be
// any lock violation, so do nothing.
//
TRACE_OUT(("No locks on Client's lock stack")); }
//
// Put a record of this lock in the Client's lock stack (we don't need
// to surround this with a mutex since a Client's lock stack is only
// accessed from that Client's task):
//
// Note: since this is a stack, we insert the item at the head of the
// list.
//
COM_BasedListInsertAfter(&(pomClient->locks), &(pThisLock->chain));
//
// Now start the process of requesting the lock from the ObMan task:
//
WorksetLockReq(pomClient->putTask, g_pomPrimary, pWSGroup, pWorkset, hWSGroup, pCorrelator);
TRACE_OUT(("Requested lock for workset %u in WSG %d for Client 0x%08x", worksetID, pWSGroup->wsg, pomClient));
DC_EXIT_POINT:
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_WorksetLockReq, rc); return(rc); }
//
// OM_WorksetUnlock(...)
//
void OM_WorksetUnlock ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID ) { POM_USAGE_REC pUsageRec; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_LOCK pLastLock; OM_LOCK thisLock; UINT rc = 0;
DebugEntry(OM_WorksetUnlock);
UT_Lock(UTLOCK_OM);
//
// Validate params:
//
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY, &pUsageRec, &pWorkset);
pWSGroup = pUsageRec->pWSGroup;
TRACE_OUT(("Client 0x%08x requesting to unlock workset %u in WSG %d", pomClient, worksetID, hWSGroup));
//
// Find the lock uppermost on the Client's lock stack:
//
pLastLock = (POM_LOCK)COM_BasedListFirst(&(pomClient->locks), FIELD_OFFSET(OM_LOCK, chain));
ASSERT((pLastLock != NULL));
//
// Assert that the lock uppermost on the lock stack is the one the
// Client is trying to release (i.e. that the workset IDs are the same
// and that the object ID of the lock on the stack is NULL):
//
thisLock.pWSGroup = pWSGroup; thisLock.worksetID = worksetID; ZeroMemory(&(thisLock.objectID), sizeof(OM_OBJECT_ID));
ASSERT(CompareLocks(pLastLock, &thisLock) == 0);
//
// Now call the common function to do the unlock:
//
WorksetUnlock(pomClient->putTask, pWSGroup, pWorkset);
//
// Remove the lock from the lock stack and free the memory:
//
COM_BasedListRemove(&(pLastLock->chain)); UT_FreeRefCount((void**)&pLastLock, FALSE);
TRACE_OUT(("Unlocked workset %u in WSG %d for Client 0x%08x", worksetID, hWSGroup, pomClient));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_WorksetUnlock); }
//
// OM_WorksetCountObjects(...)
//
void OM_WorksetCountObjects ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, UINT * pCount ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset;
DebugEntry(OM_WorksetCountObjects);
UT_Lock(UTLOCK_OM);
//
// Validate params:
//
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY | SECONDARY, &pUsageRec, &pWorkset);
//
// Extract <numObjects> field and put in *pCount:
//
*pCount = pWorkset->numObjects;
//
// Debug-only check:
//
CheckObjectCount(pUsageRec->pWSGroup, pWorkset);
TRACE_OUT(("Number of objects in workset %u in WSG %d = %u", worksetID, hWSGroup, *pCount));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_WorksetCountObjects); }
//
// OM_WorksetClear(...)
//
UINT OM_WorksetClear ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID ) { POM_USAGE_REC pUsageRec; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POMNET_OPERATION_PKT pPacket; UINT rc = 0;
DebugEntry(OM_WorksetClear);
UT_Lock(UTLOCK_OM);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY, &pUsageRec, &pWorkset);
pWSGroup = pUsageRec->pWSGroup;
TRACE_OUT(("Client 0x%08x requesting to clear workset %u in WSG %d", pomClient, worksetID, hWSGroup));
//
// Check workset isn't locked by somebody else (OK if locked by us):
//
CHECK_WORKSET_NOT_LOCKED(pWorkset);
//
// Check workset is not exhausted:
//
CHECK_WORKSET_NOT_EXHAUSTED(pWorkset);
//
// Generate, process and queue the WORKSET_NEW message:
//
rc = GenerateOpMessage(pWSGroup, worksetID, NULL, // no object ID
NULL, // no object data
OMNET_WORKSET_CLEAR, &pPacket); if (rc != 0) { DC_QUIT; }
rc = ProcessWorksetClear(pomClient->putTask, g_pomPrimary, pPacket, pWSGroup, pWorkset); if (rc != 0) { DC_QUIT; }
rc = QueueMessage(pomClient->putTask, pWSGroup->pDomain, pWSGroup->channelID, NET_HIGH_PRIORITY, pWSGroup, pWorkset, NULL, // no object record
(POMNET_PKT_HEADER) pPacket, NULL, // no object data
TRUE); if (rc != 0) { DC_QUIT; }
TRACE_OUT(("Issued WorksetClear for workset %u in WSG %d for Client 0x%08x", worksetID, hWSGroup, pomClient));
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(("Error 0x%08x clearing workset %u in WSG %d for Client 0x%08x", rc, worksetID, hWSGroup, pomClient)); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_WorksetClear, rc); return(rc); }
//
// OM_WorksetClearConfirm(...)
//
void OM_WorksetClearConfirm ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID ) { POM_USAGE_REC pUsageRec; POM_PENDING_OP pPendingOp; POM_WORKSET pWorkset; UINT rc = 0;
DebugEntry(OM_WorksetClearConfirm);
UT_Lock(UTLOCK_OM);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY, &pUsageRec, &pWorkset);
TRACE_OUT(("Client 0x%08x confirming WorksetClear for workest %u in WSG %d", pomClient, worksetID, hWSGroup));
//
// Find the pending clear that we've been asked to confirm (assume it is
// first clear we find in the pending operation queue):
//
FindPendingOp(pWorkset, 0, WORKSET_CLEAR, &pPendingOp);
//
// We assert that a relevant pending op was found:
//
ASSERT(pPendingOp != NULL);
//
// In versions which support object locking, we will need to unlock any
// objects that are both
//
// - locked, and
//
// - deleted by this Clear (remember that a Clear doesn't delete ALL
// objects but only those that were added before the Clear was
// issued).
//
//
// We also need to release any objects
//
// - that the Client was using and
//
// - which are to be deleted.
//
// Since it's rather a lot of effort to ensure both conditions, we just
// release all the objects the Client was using i.e. invoking
// ClearConfirm invalidates ALL object pointers obtained via ObjectRead,
// as specified in the API:
//
ReleaseAllObjects(pUsageRec, pWorkset);
//
// If an object which is to be deleted because of the clear has an
// operation pending on it, the IND event will be swallowed by the
// HiddenHandler.
//
// Note that we cannot call ConfirmAll (to confirm any pending
// operations on objects in the workset) at this point for the following
// reasons:
//
// - this Clear might not affect the objects on which we were confirming
// operations
//
// - the Client might have received the IND events and try to call a
// Confirm function in the future, which would cause an assertion
// failure
//
// - if the Client hasn't yet got the IND events it will never get them
// because the hidden handler will swallow them if this DoClear causes
// them to be deleted.
//
//
// Here we actually perform the clear:
//
// (with multiple local access to workset groups as we may have in R2.0,
// we can't necessarily clear a workset when just one Client has
// confirmed; exactly what we will do depends on the design on R2.0).
//
WorksetDoClear(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pPendingOp);
TRACE_OUT(("Confirmed Clear for workset %u in WSG %d for Client 0x%08x", worksetID, hWSGroup, pomClient));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_WorksetClearConfirm); }
//
// OM_ObjectAdd()
//
UINT OM_ObjectAdd ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECTDATA * ppData, UINT updateSize, POM_OBJECT * ppObj, OM_POSITION position ) { POM_USAGE_REC pUsageRec; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POM_OBJECTDATA pData; OM_OBJECT_ID newObjectID; UINT rc = 0;
DebugEntry(OM_ObjectAdd);
UT_Lock(UTLOCK_OM);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY, &pUsageRec, &pWorkset);
pData = *ppData; ValidateObjectData(pData);
TRACE_OUT(("Client 0x%08x adding object to workset %u in WSG %d", pomClient, worksetID, hWSGroup));
TRACE_OUT((" object data is at 0x%08x - size: %u", pData, pData->length));
ASSERT((updateSize < OM_MAX_UPDATE_SIZE));
//
// Set up workset group pointer:
//
pWSGroup = pUsageRec->pWSGroup;
//
// Check workset isn't locked by somebody else (OK if locked by us):
//
CHECK_WORKSET_NOT_LOCKED(pWorkset);
//
// Check workset is not exhausted:
//
CHECK_WORKSET_NOT_EXHAUSTED(pWorkset);
//
// Call the internal function to add the object:
//
rc = ObjectAdd(pomClient->putTask, g_pomPrimary, pWSGroup, pWorkset, pData, updateSize, position, &newObjectID, ppObj); if (rc != 0) { DC_QUIT; }
//
// Remove the object from the unused objects list:
//
RemoveFromUnusedList(pUsageRec, pData);
//
// If all has gone well, we NULL the Client's pointer to the object
// data, since we now own the object and the Client is not supposed to
// refer to it again (unless, of course, it does an OM_ObjectRead).
//
*ppData = NULL;
DC_EXIT_POINT:
if (rc != 0) { ERROR_OUT(("ERROR %d adding object to workset %u in WSG %d for Client 0x%08x", rc, pWorkset->worksetID, hWSGroup, pomClient)); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectAdd, rc); return(rc); }
//
// OM_ObjectMove()
//
UINT OM_ObjectMove ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj, OM_POSITION position ) { POM_USAGE_REC pUsageRec; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; POMNET_OPERATION_PKT pPacket = NULL; UINT rc = 0;
DebugEntry(OM_ObjectMove);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY, &pUsageRec, &pWorkset);
TRACE_OUT(("Client 0x%08x moving object 0x%08x in workset %u in WSG %d (position: %s)...", pomClient, pObj, worksetID, hWSGroup, position == LAST ? "LAST" : "FIRST"));
//
// Set up workset group pointer:
//
pWSGroup = pUsageRec->pWSGroup;
//
// Check workset isn't locked by somebody else (OK if locked by us):
//
CHECK_WORKSET_NOT_LOCKED(pWorkset);
//
// Check workset is not exhausted:
//
CHECK_WORKSET_NOT_EXHAUSTED(pWorkset);
//
// Here we generate, process and queue an OBJECT_MOVE message:
//
rc = GenerateOpMessage(pWSGroup, pWorkset->worksetID, &(pObj->objectID), NULL, // no object data
OMNET_OBJECT_MOVE, &pPacket); if (rc != 0) { pPacket = NULL; DC_QUIT; }
//
// Generate message doesn't put the position in the <misc1> field, so we
// do it here:
//
pPacket->position = position;
//
// QueueMessage may free the packet (if we're not in a call) but we need
// to process it in a minute so bump the use count:
//
UT_BumpUpRefCount(pPacket);
rc = QueueMessage(pomClient->putTask, pWSGroup->pDomain, pWSGroup->channelID, NET_HIGH_PRIORITY, pWSGroup, pWorkset, pObj, (POMNET_PKT_HEADER) pPacket, NULL, // no object data for a MOVE
TRUE); if (rc != 0) { DC_QUIT; }
ProcessObjectMove(pomClient->putTask, pPacket, pWorkset, pObj);
DC_EXIT_POINT:
if (pPacket != NULL) { //
// Do this on success OR error since we bumped up the ref count above.
//
UT_FreeRefCount((void**)&pPacket, FALSE); }
if (rc != 0) { ERROR_OUT(("ERROR %d moving object 0x%08x in workset %u in WSG %d", rc, pObj, worksetID, hWSGroup)); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectMove, rc); return(rc); }
//
// OM_ObjectDelete(...)
//
UINT OM_ObjectDelete ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; UINT rc = 0;
DebugEntry(OM_ObjectDelete);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY, &pUsageRec, &pWorkset);
TRACE_OUT(("Client 0x%08x requesting to delete object 0x%08x from workset %u in WSG %d", pomClient, pObj, worksetID, hWSGroup));
//
// Check workset isn't locked by somebody else (OK if locked by us):
//
CHECK_WORKSET_NOT_LOCKED(pWorkset);
//
// Check workset is not exhausted:
//
CHECK_WORKSET_NOT_EXHAUSTED(pWorkset);
//
// If there is already a Delete pending for the object, we return an
// error and do not post the delete indication event.
//
// If we returned success, we would then have to post another event,
// since the Client may wait for it. If we post the event, the Client
// will probably invoke DeleteConfirm a second time when it is
// unexpected, thereby causing an assertion failure.
//
// Note that we cannot rely on the hidden handler to get us out of this
// one, since the Client might receive the second event before
// processing the first one, so the handler would have no way of knowing
// to trap the event.
//
//
// So, to find out if there's a delete pending, check the flag in the
// object record:
//
if (pObj->flags & PENDING_DELETE) { TRACE_OUT(("Client tried to delete object already being deleted (0x%08x)", pObj)); rc = OM_RC_OBJECT_DELETED; DC_QUIT; }
//
// Here we call the ObjectDelete function to generate, process and queue
// an OBJECT_DELETE message:
//
rc = ObjectDRU(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, NULL, OMNET_OBJECT_DELETE); if (rc != 0) { DC_QUIT; }
//
// Remember, the delete doesn't actually happen until the local
// Client(s) have invoked DeleteConfirm().
//
DC_EXIT_POINT:
//
// SFR5843: Don't trace an error if the object has been deleted - this
// is just safe race condition.
//
if ((rc != 0) && (rc != OM_RC_OBJECT_DELETED)) { ERROR_OUT(("ERROR %d issuing delete for object 0x%08x in WSG %d:%hu", rc, pObj, hWSGroup, worksetID)); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectDelete, rc); return(rc); }
//
// OM_ObjectDeleteConfirm
//
void OM_ObjectDeleteConfirm ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { POM_WORKSET pWorkset; POM_USAGE_REC pUsageRec; POM_PENDING_OP pPendingOp; POM_PENDING_OP pOtherPendingOp; UINT rc = 0;
DebugEntry(OM_ObjectDeleteConfirm);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY, &pUsageRec, &pWorkset);
//
// To check that there is indeed a Delete pending for the object, we
// look in the workset's pending operation list.
//
FindPendingOp(pWorkset, pObj, OBJECT_DELETE, &pPendingOp);
//
// We assert that a relevant pending op was found:
//
ASSERT((pPendingOp != NULL));
//
// Call ObjectRelease, to release the object (will be a no-op and return
// NOT_FOUND if the Client hasn't done a Read on it):
//
rc = ObjectRelease(pUsageRec, worksetID, pObj);
ASSERT(((rc == 0) || (rc == OM_RC_OBJECT_NOT_FOUND)));
//
// If we are going to confirm the delete, then we must ensure that any
// pending update or replace is carried out too. There can be only one
// of each, so check as follows (ther order we do them in is not
// relevant):
//
FindPendingOp(pWorkset, pObj, OBJECT_REPLACE, &pOtherPendingOp); if (pOtherPendingOp != NULL) { ObjectDoReplace(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pOtherPendingOp); }
FindPendingOp(pWorkset, pObj, OBJECT_UPDATE, &pOtherPendingOp); if (pOtherPendingOp != NULL) { ObjectDoUpdate(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pOtherPendingOp); }
//
// Perform the Delete:
//
ObjectDoDelete(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pPendingOp);
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_ObjectDeleteConfirm); }
//
// OM_ObjectReplace(...)
//
UINT OM_ObjectReplace ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj, POM_OBJECTDATA * ppData ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; POM_OBJECTDATA pData; UINT rc = 0;
DebugEntry(OM_ObjectReplace);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY, &pUsageRec, &pWorkset);
pData = *ppData; ValidateObjectData(pData);
//
// Check that the Client is not attempting to replace the object with
// one smaller that the object's update size (which is the minimum size
// for a replace):
//
ASSERT((pData->length >= pObj->updateSize));
//
// Check workset isn't locked by somebody else (OK if locked by us):
//
CHECK_WORKSET_NOT_LOCKED(pWorkset);
//
// Check workset is not exhausted:
//
CHECK_WORKSET_NOT_EXHAUSTED(pWorkset);
//
// If the object is in the process of being deleted, we prevent the
// Replace. This is because if we don't, the Client will get a
// REPLACE_IND event after it has got (and processed) a DELETE event for
// the object.
//
if (pObj->flags & PENDING_DELETE) { TRACE_OUT(("Client 0x%08x tried to replace object being deleted (0x%08x)", pomClient, pObj)); rc = OM_RC_OBJECT_DELETED; DC_QUIT; }
//
// When object locking supported, need to prevent object replace when
// object is locked.
//
//
// Generate, process and queue an OBJECT_REPLACE message:
//
rc = ObjectDRU(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pData, OMNET_OBJECT_REPLACE); if (rc != 0) { DC_QUIT; }
//
// Remove the object from the unused objects list:
//
RemoveFromUnusedList(pUsageRec, pData);
//
// NULL the Client's pointer to the object:
//
*ppData = NULL;
TRACE_OUT(("Queued replace for object 0x%08x in workset %u for Client 0x%08x", pObj, worksetID, pomClient));
DC_EXIT_POINT:
//
// SFR5843: Don't trace an error if the object has been deleted - this
// is just safe race condition.
//
if ((rc != 0) && (rc != OM_RC_OBJECT_DELETED)) { ERROR_OUT(("ERROR %d issuing replace for object 0x%08x in WSG %d:%hu", rc, pObj, hWSGroup, worksetID)); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectReplace, rc); return(rc); }
//
// OM_ObjectUpdate
//
UINT OM_ObjectUpdate ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj, POM_OBJECTDATA * ppData ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; POM_OBJECTDATA pData; UINT rc = 0;
DebugEntry(OM_ObjectUpdate);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY, &pUsageRec, &pWorkset);
pData = *ppData; ValidateObjectData(pData);
//
// Check size of update equals the update size for the object:
//
ASSERT((pData->length == pObj->updateSize));
TRACE_OUT(("Update request is for first 0x%08x bytes, starting at 0x%08x", pData->length, pData->data));
//
// Check workset isn't locked by somebody else (OK if locked by us):
//
CHECK_WORKSET_NOT_LOCKED(pWorkset);
//
// Check workset is not exhausted:
//
CHECK_WORKSET_NOT_EXHAUSTED(pWorkset);
//
// If the object is in the process of being deleted, we prevent the
// Update. This is because if we don't, the Client will get a
// UPDATE_IND event after it has got (and processed) a DELETE event for
// the object.
//
if (pObj->flags & PENDING_DELETE) { TRACE_OUT(("Client 0x%08x tried to update object being deleted (0x%08x)", pomClient, pObj)); rc = OM_RC_OBJECT_DELETED; DC_QUIT; }
//
// When object locking supported, need to prevent object update/replace
// when object is locked.
//
//
// Generate, process and queue an OBJECT_UPDATE message:
//
rc = ObjectDRU(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pData, OMNET_OBJECT_UPDATE); if (rc != 0) { DC_QUIT; }
//
// Remove the object from the unused objects list:
//
RemoveFromUnusedList(pUsageRec, pData);
//
// NULL the Client's pointer to the object:
//
*ppData = NULL;
TRACE_OUT(("Queued update for object 0x%08x in workset %u for Client 0x%08x", pObj, worksetID, pomClient));
DC_EXIT_POINT:
//
// SFR5843: Don't trace an error if the object has been deleted - this
// is just safe race condition.
//
if ((rc != 0) && (rc != OM_RC_OBJECT_DELETED)) { ERROR_OUT(("ERROR %d issuing update for object 0x%08x in WSG %d:%hu", rc, pObj, hWSGroup, worksetID)); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectUpdate, rc); return(rc); }
//
// OM_ObjectReplaceConfirm(...)
//
void OM_ObjectReplaceConfirm ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { POM_WORKSET pWorkset; POM_USAGE_REC pUsageRec; POM_PENDING_OP pPendingOp; UINT rc = 0;
DebugEntry(OM_ObjectReplaceConfirm);
UT_Lock(UTLOCK_OM);
//
// Here, we do our usual parameter validation, but we don't want to
// assert if the object has been delete-confirmed already, so we modify
// the code from ValidateParams4 a bit:
//
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY, &pUsageRec, &pWorkset);
//
// Retrieve the Replace operation from the object's pending op queue (we
// want the first REPLACE operation on the queue, so we start from the
// head):
//
FindPendingOp(pWorkset, pObj, OBJECT_REPLACE, &pPendingOp);
ASSERT((pPendingOp != NULL));
//
// Call ObjectRelease, to release the object (will be a no-op if the
// Client hasn't done a Read on it):
//
rc = ObjectRelease(pUsageRec, worksetID, pObj); ASSERT(((rc == 0) || (rc == OM_RC_OBJECT_NOT_FOUND)));
//
// Call the internal function to perform the actual Replace:
//
ObjectDoReplace(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pPendingOp);
TRACE_OUT(("Confirmed Replace for object 0x%08x in workset %u for Client 0x%08x", pObj, worksetID, pomClient));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_ObjectReplaceConfirm); }
//
// OM_ObjectUpdateConfirm(...)
//
void OM_ObjectUpdateConfirm ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; POM_PENDING_OP pPendingOp; UINT rc = 0;
DebugEntry(OM_ObjectUpdateConfirm);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY, &pUsageRec, &pWorkset);
//
// Retrieve the Update operation from the object's pending op queue (we
// want the first UPDATE operation on the queue, so we start from the
// head):
//
FindPendingOp(pWorkset, pObj, OBJECT_UPDATE, &pPendingOp);
ASSERT((pPendingOp != NULL));
//
// Call ObjectRelease, to release the object (will be a no-op if the
// Client hasn't done a Read on it):
//
rc = ObjectRelease(pUsageRec, worksetID, pObj); ASSERT(((rc == 0) || (rc == OM_RC_OBJECT_NOT_FOUND)));
//
// Call the internal function to perform the actual Update:
//
ObjectDoUpdate(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pPendingOp);
TRACE_OUT(("Confirmed Update for object 0x%08x in workset %u for Client 0x%08x", pObj, worksetID, pomClient));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_ObjectUpdateConfirm); }
//
// OM_ObjectH()
// Gets a ptr to the first/next/previous/last object
//
UINT OM_ObjectH ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObjOther, POM_OBJECT * ppObj, OM_POSITION omPos ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; UINT rc = 0;
DebugEntry(OM_ObjectH);
UT_Lock(UTLOCK_OM);
//
// Validate params. If no hOtherObject (like in first/last), don't validate hOtherObject
//
if ((omPos == FIRST) || (omPos == LAST)) { ASSERT(pObjOther == NULL);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY | SECONDARY, &pUsageRec, &pWorkset);
if (omPos == FIRST) omPos = AFTER; else omPos = BEFORE; } else { ValidateParams4(pomClient, hWSGroup, worksetID, pObjOther, PRIMARY | SECONDARY, &pUsageRec, &pWorkset); }
//
// Get the object pointer
//
//
// Here we derive a pointer to what is "probably" the object record
// we're looking for:
//
if (pObjOther == NULL) { //
// Remember, if *ppObj == 0, then we're looking for the first or
// last object in the workset:
//
if (omPos == AFTER) { TRACE_OUT(("Getting first object in workset %u", worksetID)); *ppObj = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); } else { TRACE_OUT(("Getting last object in workset %u", worksetID)); *ppObj = (POM_OBJECT)COM_BasedListLast(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); } } else { *ppObj = pObjOther;
if (omPos == AFTER) { TRACE_OUT(("Getting object after 0x%08x in workset %u", pObjOther, worksetID)); *ppObj = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObjOther, FIELD_OFFSET(OM_OBJECT, chain)); } else { TRACE_OUT(("Getting object before 0x%08x in workset %u", pObjOther, worksetID)); *ppObj = (POM_OBJECT)COM_BasedListPrev(&(pWorkset->objects), pObjOther, FIELD_OFFSET(OM_OBJECT, chain)); } }
//
// ppObj now has "probably" a pointer to the object we're looking for,
// but now we need to skip deleted objects.
//
while ((*ppObj != NULL) && ((*ppObj)->flags & DELETED)) { ValidateObject(*ppObj);
if (omPos == AFTER) { *ppObj = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), *ppObj, FIELD_OFFSET(OM_OBJECT, chain)); } else { *ppObj = (POM_OBJECT)COM_BasedListPrev(&(pWorkset->objects), *ppObj, FIELD_OFFSET(OM_OBJECT, chain)); } }
if (*ppObj == NULL) { rc = OM_RC_NO_SUCH_OBJECT; }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectH, rc); return(rc); }
//
// OM_ObjectIDToPtr(...)
//
UINT OM_ObjectIDToPtr ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, OM_OBJECT_ID objectID, POM_OBJECT * ppObj ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; UINT rc = 0;
DebugEntry(OM_ObjectIDToPtr);
UT_Lock(UTLOCK_OM);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY | SECONDARY, &pUsageRec, &pWorkset);
//
// Now call the internal function to do the search for the ID:
//
rc = ObjectIDToPtr(pWorkset, objectID, ppObj);
if (rc == OM_RC_OBJECT_DELETED) { //
// This internal function returns OBJECT_DELETED if the object record
// was found but is marked as deleted. We map this to BAD_OBJECT_ID
// since that's all we externalise to Clients:
//
rc = OM_RC_BAD_OBJECT_ID; } else if (rc == OM_RC_OBJECT_PENDING_DELETE) { //
// If we get back PENDING_DELETE, then we map this to OK, since as
// far as the Client is concerned, the object still exists:
//
rc = 0; }
if (rc == OM_RC_BAD_OBJECT_ID) { WARNING_OUT(("No object found in workset with ID 0x%08x:0x%08x", objectID.creator, objectID.sequence)); } else if (rc != 0) { ERROR_OUT(("ERROR %d converting object ID (0x%08x:0x%08x) to handle", rc, objectID.creator, objectID.sequence)); } else { TRACE_OUT(("Converted object ID (0x%08x:0x%08x) to handle (0x%08x)", objectID.creator, objectID.sequence, *ppObj)); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectIDToPtr, rc); return(rc); }
//
// OM_ObjectPtrToID(...)
//
void OM_ObjectPtrToID ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj, POM_OBJECT_ID pObjectID ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; UINT rc = 0;
DebugEntry(OM_ObjectPtrToID);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY | SECONDARY, &pUsageRec, &pWorkset);
//
// Extract ID from object record:
//
memcpy(pObjectID, &pObj->objectID, sizeof(OM_OBJECT_ID));
TRACE_OUT(("Retrieved object ID 0x%08x:0x%08x for object 0x%08x in workset %u", pObjectID->creator, pObjectID->sequence, pObj, worksetID));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_ObjectHandleToID); }
//
// OM_ObjectRead(...)
//
UINT OM_ObjectRead ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj, POM_OBJECTDATA * ppData ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; POM_OBJECT_LIST pListEntry; UINT rc = 0;
DebugEntry(OM_ObjectRead);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY | SECONDARY, &pUsageRec, &pWorkset);
//
// Check the Client hasn't already read this object without releasing
// it:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pUsageRec->objectsInUse), (void**)&pListEntry, FIELD_OFFSET(OM_OBJECT_LIST, chain), FIELD_OFFSET(OM_OBJECT_LIST, pObj), (DWORD_PTR)pObj, FIELD_SIZE(OM_OBJECT_LIST, pObj)); ASSERT(pListEntry == NULL);
//
// Convert object handle to a pointer to the object data:
//
*ppData = pObj->pData; if (!*ppData) { ERROR_OUT(("OM_ObjectRead: Object 0x%08x has no data", pObj)); rc = UT_RC_NO_MEM; DC_QUIT; }
//
// Bump up the use count of the chunk so it won't be freed until the
// Client calls OM_ObjectRelease (explicitly or implicitly via e.g
// DeleteConfirm)
//
UT_BumpUpRefCount(*ppData);
//
// We need to add this object's handle to the Client's list of
// objects-in-use, so allocate some memory for the object...
//
pListEntry = (POM_OBJECT_LIST)UT_MallocRefCount(sizeof(OM_OBJECT_LIST), TRUE); if (!pListEntry) { rc = UT_RC_NO_MEM; DC_QUIT; }
SET_STAMP(pListEntry, OLIST);
//
// ...fill in the fields...
//
pListEntry->pObj = pObj; pListEntry->worksetID = worksetID;
//
// ...and insert into the list:
//
COM_BasedListInsertBefore(&(pUsageRec->objectsInUse), &(pListEntry->chain));
TRACE_OUT(("Read object at 0x%08x (handle: 0x%08x) for Client 0x%08x", *ppData, pObj, pomClient));
DC_EXIT_POINT:
if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(("ERROR %d reading object 0x%08x in workset %u in WSG %d", rc, pObj, worksetID, hWSGroup));
if (pListEntry != NULL) { UT_FreeRefCount((void**)&pListEntry, FALSE); }
if (*ppData) UT_FreeRefCount((void**)ppData, FALSE); }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectRead, rc); return(rc); }
//
// OM_ObjectRelease()
//
void OM_ObjectRelease ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT pObj, POM_OBJECTDATA * ppData ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; UINT rc = 0;
DebugEntry(OM_ObjectRelease);
UT_Lock(UTLOCK_OM);
ValidateParams4(pomClient, hWSGroup, worksetID, pObj, PRIMARY | SECONDARY, &pUsageRec, &pWorkset);
//
// Check that the object pointer and object handle match:
//
ASSERT(pObj->pData == *ppData);
//
// Now try to release the object from the objects-in-use list:
//
rc = ObjectRelease(pUsageRec, worksetID, pObj);
//
// ObjectRelease will return an error if the object handle wasn't found
// in the objects-in-use list. As far as we're concerned, this is an
// assert-level error:
//
ASSERT((rc == 0));
//
// NULL the Client's pointer:
//
*ppData = NULL;
TRACE_OUT(("Released Client 0x%08x's hold on object 0x%08x in workset %u in WSG %d", pomClient, pObj, worksetID, hWSGroup));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_ObjectRelease); }
//
// OM_ObjectAlloc(...)
//
UINT OM_ObjectAlloc ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, UINT size, POM_OBJECTDATA * ppData ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; POM_OBJECTDATA_LIST pListEntry = NULL; UINT rc = 0;
DebugEntry(OM_ObjectAlloc);
UT_Lock(UTLOCK_OM);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY, &pUsageRec, &pWorkset);
TRACE_OUT(("Client 0x%08x requesting to allocate 0x%08x bytes " "for object for workset %u in WSG %d", pomClient, size, worksetID, hWSGroup));
//
// Check request not too big:
//
ASSERT((size < OM_MAX_OBJECT_SIZE - sizeof(OM_MAX_OBJECT_SIZE)));
//
// Check request not too small:
//
ASSERT((size > 0));
//
// Allocate a chunk of memory for the object (note that we add 4 bytes
// to the size the Client asked for (i.e. the <size> parameter) since
// the API stipulates that this does not include the <size> field which
// is at the start of the object.
//
*ppData = (POM_OBJECTDATA)UT_MallocRefCount(size + sizeof(OM_MAX_OBJECT_SIZE), FALSE); if (! *ppData) { rc = UT_RC_NO_MEM; DC_QUIT; }
ZeroMemory(*ppData, min(size, OM_ZERO_OBJECT_SIZE));
//
// Now insert a reference to this chunk in the Client's unused-objects
// list (will be removed by Add, Replace, Update or Discard functions).
//
pListEntry = (POM_OBJECTDATA_LIST)UT_MallocRefCount(sizeof(OM_OBJECTDATA_LIST), TRUE); if (!pListEntry) { rc = UT_RC_NO_MEM; DC_QUIT; }
SET_STAMP(pListEntry, ODLIST);
pListEntry->pData = *ppData; pListEntry->size = size; pListEntry->worksetID = worksetID;
COM_BasedListInsertBefore(&(pUsageRec->unusedObjects), &(pListEntry->chain));
TRACE_OUT(("Allocated object starting at 0x%08x", *ppData));
DC_EXIT_POINT:
if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(("ERROR %d allocating object (size: 0x%08x) for Client 0x%08x", rc, size + sizeof(OM_MAX_OBJECT_SIZE), pomClient));
if (pListEntry != NULL) { UT_FreeRefCount((void**)&pListEntry, FALSE); }
if (*ppData != NULL) { UT_FreeRefCount((void**)ppData, FALSE); } }
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_ObjectAlloc, rc); return(rc); }
//
// OM_ObjectDiscard(...)
//
void OM_ObjectDiscard ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, OM_WORKSET_ID worksetID, POM_OBJECTDATA * ppData ) { POM_USAGE_REC pUsageRec; POM_WORKSET pWorkset; POM_OBJECTDATA pData; UINT rc = 0;
DebugEntry(OM_ObjectDiscard);
UT_Lock(UTLOCK_OM);
ValidateParams3(pomClient, hWSGroup, worksetID, PRIMARY, &pUsageRec, &pWorkset);
pData = *ppData;
//
// Remove the object from the unused objects list:
//
RemoveFromUnusedList(pUsageRec, pData);
//
// Free the chunk containing the object, NULLing the caller's pointer at
// the same time:
//
UT_FreeRefCount((void**)ppData, FALSE);
TRACE_OUT(("Discarded object at 0x%08x in workset %u in WSG %d for Client 0x%08x", pData, worksetID, hWSGroup, pomClient));
UT_Unlock(UTLOCK_OM);
DebugExitVOID(OM_ObjectDiscard); }
//
// OM_GetNetworkUserID
//
UINT OM_GetNetworkUserID ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup, NET_UID * pNetUserID ) { POM_DOMAIN pDomain; POM_USAGE_REC pUsageRec; POM_WSGROUP pWSGroup; UINT rc = 0;
DebugEntry(OM_GetNetworkUserID);
UT_Lock(UTLOCK_OM);
ValidateParams2(pomClient, hWSGroup, PRIMARY | SECONDARY, &pUsageRec, &pWSGroup);
//
// Get a pointer to the relevant Domain:
//
pDomain = pWSGroup->pDomain;
if (pDomain->callID == OM_NO_CALL) { rc = OM_RC_LOCAL_WSGROUP; DC_QUIT; }
//
// Otherwise, everything's OK, so we fill in the caller's pointer and
// return:
//
if (pDomain->userID == 0) { WARNING_OUT(("Client requesting userID for Domain %u before we've attached", pDomain->callID)); rc = OM_RC_NOT_ATTACHED; DC_QUIT; }
*pNetUserID = pDomain->userID;
TRACE_OUT(("Returned Network user ID (0x%08x) to Client 0x%08x for '0x%08x'", *pNetUserID, pomClient, hWSGroup));
DC_EXIT_POINT:
UT_Unlock(UTLOCK_OM);
DebugExitDWORD(OM_GetNetworkUserID, rc); return(rc); }
//
// SetUpUsageRecord(...)
//
UINT SetUpUsageRecord ( POM_CLIENT pomClient, UINT mode, POM_USAGE_REC * ppUsageRec, OM_WSGROUP_HANDLE * phWSGroup ) { UINT rc = 0;
DebugEntry(SetUpUsageRecord);
ValidateOMS(pomClient);
//
// Find an unused workset group handle for the Client:
//
rc = FindUnusedWSGHandle(pomClient, phWSGroup); if (rc != 0) { DC_QUIT; }
//
// Client has a spare handle so create a new usage record for this
// Client's use of the workset group:
//
*ppUsageRec = (POM_USAGE_REC)UT_MallocRefCount(sizeof(OM_USAGE_REC), TRUE); if (! *ppUsageRec) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP((*ppUsageRec), USAGEREC);
//
// Next, fill in the fields, but note that:
//
// - until the registration gets to pre-Stage1, the only way to abort it
// from the Client context is to mark the registration CB as invalid.
// To do this (e.g. in WSGroupDeregister) we need access to the
// registration CB, so we will put a pointer to it in the usage record
// below.
//
// - the <worksetOpenFlags> field is zero initially (it will be changed
// when the Client does a WorksetOpen), so we do nothing
//
// - the <wsGroupMutex> field also needs to be zero initially (the
// correct value is inserted by the hidden handler), so we leave this
// blank too.
//
(*ppUsageRec)->mode = (BYTE)mode;
COM_BasedListInit(&((*ppUsageRec)->unusedObjects)); COM_BasedListInit(&((*ppUsageRec)->objectsInUse));
//
// Put the offset to the usage record in the array of offsets:
//
pomClient->apUsageRecs[*phWSGroup] = *ppUsageRec;
TRACE_OUT(("Set up usage record for Client 0x%08x at 0x%08x (hWSGroup: %hu)", pomClient, *ppUsageRec, *phWSGroup));
DC_EXIT_POINT: DebugExitDWORD(SetUpUsageRecord, rc); return(rc); }
//
// FindUnusedWSGHandle(...)
//
UINT FindUnusedWSGHandle ( POM_CLIENT pomClient, OM_WSGROUP_HANDLE * phWSGroup ) { BOOL found; OM_WSGROUP_HANDLE hWSGroup; UINT rc = 0;
DebugEntry(FindUnusedWSGHandle);
ValidateOMS(pomClient);
//
// Workset group handles are indexes into an array of offsets to usage
// records. When one of these offsets is 0, the slot is available for
// use.
//
// We start our loop at 1 because 0 is never used as a workset group
// handle. Because we start at 1, we end at MAX + 1 to ensure that we
// use MAX handles.
//
found = FALSE;
for (hWSGroup = 1; hWSGroup < OMWSG_MAXPERCLIENT; hWSGroup++) { if (pomClient->apUsageRecs[hWSGroup] == NULL) { found = TRUE; TRACE_OUT(("Found unused workset group handle %hu for Client 0x%08x", hWSGroup, pomClient));
ASSERT(!pomClient->wsgValid[hWSGroup]);
break; } }
//
// If there aren't any, quit with an error:
//
if (!found) { WARNING_OUT(("Client 0x%08x has no more workset group handles", pomClient)); rc = OM_RC_NO_MORE_HANDLES; DC_QUIT; } else { *phWSGroup = hWSGroup; }
DC_EXIT_POINT: DebugExitDWORD(FindUnusedWSGHandle, rc); return(rc); }
//
// RemoveFromUnusedList()
//
void RemoveFromUnusedList ( POM_USAGE_REC pUsageRec, POM_OBJECTDATA pData ) { POM_OBJECTDATA_LIST pListEntry;
DebugEntry(RemoveFromUnusedList);
//
// Search in the unused-objects list hung off the usage record for an
// entry whose field is the same as the offset of this object:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pUsageRec->unusedObjects), (void**)&pListEntry, FIELD_OFFSET(OM_OBJECTDATA_LIST, chain), FIELD_OFFSET(OM_OBJECTDATA_LIST, pData), (DWORD_PTR)pData, FIELD_SIZE(OM_OBJECTDATA_LIST, pData));
//
// This object must have been previously allocated, so it must be in the
// list. Assert failure if not:
//
ASSERT((pListEntry != NULL));
//
// Also, we check to make sure the Client hasn't set the <size> field to
// more memory than we originally allocated for the object:
//
if (pData->length != pListEntry->size) { ASSERT((pData->length < pListEntry->size));
TRACE_OUT(("Client has shrunk object from %u to %u bytes", pListEntry->size, pData->length)); }
COM_BasedListRemove(&(pListEntry->chain)); UT_FreeRefCount((void**)&pListEntry, FALSE);
DebugExitVOID(RemoveFromUnusedList); }
//
// ReleaseAllObjects(...)
//
void ReleaseAllObjects ( POM_USAGE_REC pUsageRec, POM_WORKSET pWorkset ) { DebugEntry(ReleaseAllObjects);
while (ObjectRelease(pUsageRec, pWorkset->worksetID, 0) == 0) { //
// Calling ObjectRelease with pObj set to NULL will cause the
// first object in the objects-in-use list which is in this workset
// to be released. When there are no more, rc will be set to
// OM_RC_OBJECT_NOT_FOUND and we will break out of our loop:
//
}
DebugExitVOID(ReleaseAllObjects); }
//
// ReleaseAllLocks(...)
//
void ReleaseAllLocks ( POM_CLIENT pomClient, POM_USAGE_REC pUsageRec, POM_WORKSET pWorkset ) { POM_LOCK pThisLock; POM_LOCK pTempLock;
DebugEntry(ReleaseAllLocks);
ValidateOMS(pomClient);
//
// Here we chain through the Client's lock stack and unlock any locks
// that relate to this workset.
//
// Note that, since object locking is not currently supported, the if
// statement in the loop will succeed at most once (i.e. if the workset
// itself is locked). The code is nonetheless implemented as a loop for
// forward compatibility. If this is deemed to be performance critical,
// we could put a break statement in.
//
pThisLock = (POM_LOCK)COM_BasedListFirst(&(pomClient->locks), FIELD_OFFSET(OM_LOCK, chain));
while (pThisLock != NULL) { //
// Since we will remove and free the entry in the lock stack if we
// find a match, we must chain to the next item beforehand:
//
pTempLock = (POM_LOCK)COM_BasedListNext(&(pomClient->locks), pThisLock, FIELD_OFFSET(OM_LOCK, chain));
if ((pThisLock->pWSGroup == pUsageRec->pWSGroup) && (pThisLock->worksetID == pWorkset->worksetID)) { if (OBJECT_ID_IS_NULL(pThisLock->objectID)) // always TRUE in R1.1
{ //
// ...we're dealing with a workset lock:
//
WorksetUnlock(pomClient->putTask, pUsageRec->pWSGroup, pWorkset); } else { //
// ...this is an object lock, so call ObjectUnlock (when it's
// supported!). In the meantime, assert:
//
ERROR_OUT(("Object locking not supported in R1.1!!")); }
COM_BasedListRemove(&(pThisLock->chain)); UT_FreeRefCount((void**)&pThisLock, FALSE);
//
// Could put the break in here for performance improvement.
//
}
pThisLock = pTempLock; }
DebugExitVOID(ReleaseAllLocks); }
//
// ConfirmAll(...)
//
void ConfirmAll ( POM_CLIENT pomClient, POM_USAGE_REC pUsageRec, POM_WORKSET pWorkset ) { POM_PENDING_OP pThisPendingOp; POM_OBJECT pObj; UINT rc = 0;
DebugEntry(ConfirmAll);
ValidateOMS(pomClient);
//
// To confirm all outstanding operations for this workset, we search
// the list of pending ops stored off the workset record:
//
//
// Chain through the workset's list of pending operations and confirm
// them one by one:
//
pThisPendingOp = (POM_PENDING_OP)COM_BasedListFirst(&(pWorkset->pendingOps), FIELD_OFFSET(OM_PENDING_OP, chain)); while (pThisPendingOp != NULL) { pObj = pThisPendingOp->pObj;
switch (pThisPendingOp->type) { case WORKSET_CLEAR: { WorksetDoClear(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pThisPendingOp); break; }
case OBJECT_DELETE: { ObjectDoDelete(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pThisPendingOp); break; }
case OBJECT_UPDATE: { ObjectDoUpdate(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pThisPendingOp); break; }
case OBJECT_REPLACE: { ObjectDoReplace(pomClient->putTask, pUsageRec->pWSGroup, pWorkset, pObj, pThisPendingOp); break; }
default: { ERROR_OUT(("Reached default case in switch statement (value: %hu)", pThisPendingOp->type)); break; } }
//
// The above functions all remove the pending op from the list, so get
// the new first item
//
pThisPendingOp = (POM_PENDING_OP)COM_BasedListFirst(&(pWorkset->pendingOps), FIELD_OFFSET(OM_PENDING_OP, chain)); }
DebugExitVOID(ConfirmAll); }
//
// DiscardAllObjects()
//
void DiscardAllObjects ( POM_USAGE_REC pUsageRec, POM_WORKSET pWorkset ) { POM_OBJECTDATA_LIST pThisEntry; POM_OBJECTDATA_LIST pTempEntry; POM_OBJECTDATA pData;
DebugEntry(DiscardAllObjects);
//
// Chain through the Client's list of unused objects for this workset
// group, free any unused objects which were allocated for this workset
// and remove the entry from the list:
//
pThisEntry = (POM_OBJECTDATA_LIST)COM_BasedListFirst(&(pUsageRec->unusedObjects), FIELD_OFFSET(OM_OBJECTDATA_LIST, chain));
while (pThisEntry != NULL) { //
// Since we may be removing and freeing items from the list, we must
// set up a pointer to the next link in the chain before proceeding:
//
pTempEntry = (POM_OBJECTDATA_LIST)COM_BasedListNext(&(pUsageRec->unusedObjects), pThisEntry, FIELD_OFFSET(OM_OBJECTDATA_LIST, chain));
if (pThisEntry->worksetID == pWorkset->worksetID) { //
// OK, this entry in the list is for an object allocated for this
// workset, so find the object...
//
pData = pThisEntry->pData; if (!pData) { ERROR_OUT(("DiscardAllObjects: object 0x%08x has no data", pThisEntry)); } else { ValidateObjectData(pData);
//
// ...free it...
//
TRACE_OUT(("Discarding object at 0x%08x", pData)); UT_FreeRefCount((void**)&pData, FALSE); }
//
// ...and remove the entry from the list:
//
COM_BasedListRemove(&(pThisEntry->chain)); UT_FreeRefCount((void**)&pThisEntry, FALSE); }
pThisEntry = pTempEntry; }
DebugExitVOID(DiscardAllObjects); }
//
// ObjectRelease(...)
//
UINT ObjectRelease ( POM_USAGE_REC pUsageRec, OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { POM_OBJECT_LIST pListEntry; POM_OBJECTDATA pData; UINT rc = 0;
DebugEntry(ObjectRelease);
if (pObj == NULL) { //
// If <pObj> is NULL, our caller wants us to release the first
// object in the objects-in-use list which is in the specified
// workset:
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pUsageRec->objectsInUse), (void**)&pListEntry, FIELD_OFFSET(OM_OBJECT_LIST, chain), FIELD_OFFSET(OM_OBJECT_LIST, worksetID), (DWORD)worksetID, FIELD_SIZE(OM_OBJECT_LIST, worksetID)); } else { //
// Otherwise, we do the lookup based on the object handle passed in:
//
// Note: since object handles are unique across worksets, we can just
// do a match on the handle. If the implementation of object handles
// changes and they become specific to a workset and not globally
// valid within a machine, we will need to do a double match here.
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pUsageRec->objectsInUse), (void**)&pListEntry, FIELD_OFFSET(OM_OBJECT_LIST, chain), FIELD_OFFSET(OM_OBJECT_LIST, pObj), (DWORD_PTR)pObj, FIELD_SIZE(OM_OBJECT_LIST, pObj)); }
//
// If we didn't find a relevant list entry, set rc and quit:
//
if (pListEntry == NULL) { rc = OM_RC_OBJECT_NOT_FOUND; DC_QUIT; }
//
// Now set pObj (will be a no-op if it wasn't originally NULL):
//
ASSERT((pListEntry->worksetID == worksetID));
pObj = pListEntry->pObj; ValidateObject(pObj);
pData = pObj->pData; if (!pData) { ERROR_OUT(("ObjectRelease: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pData);
//
// Decrement use count of memory chunk holding object:
//
UT_FreeRefCount((void**)&pData, FALSE); }
//
// Remove the entry for this object from the objects-in-use list:
//
COM_BasedListRemove(&(pListEntry->chain)); UT_FreeRefCount((void**)&pListEntry, FALSE);
DC_EXIT_POINT: DebugExitDWORD(ObjectRelease, rc); return(rc); }
//
// WorksetClearPending(...)
//
BOOL WorksetClearPending ( POM_WORKSET pWorkset, POM_OBJECT pObj ) { POM_PENDING_OP pPendingOp; BOOL rc = FALSE;
DebugEntry(WorksetClearPending);
//
// Try to find a pending workset clear for the given workset.
//
// N.B. We can't use FindPendingOp because we may want to check more
// than just the first pending workset clear.
//
pPendingOp = (POM_PENDING_OP)COM_BasedListFirst(&(pWorkset->pendingOps), FIELD_OFFSET(OM_PENDING_OP, chain)); while (pPendingOp != NULL) { if (pPendingOp->type == WORKSET_CLEAR) { ValidateObject(pObj);
//
// Check that this clear affects the given object
//
if (STAMP_IS_LOWER(pObj->addStamp, pPendingOp->seqStamp)) { TRACE_OUT(("Clear pending which affects object 0x%08x", pObj)); rc = TRUE; DC_QUIT; } else { TRACE_OUT(("Clear pending but doesn't affect object 0x%08x", pObj)); } }
//
// On to the next pending op...
//
pPendingOp = (POM_PENDING_OP)COM_BasedListNext(&(pWorkset->pendingOps), pPendingOp, FIELD_OFFSET(OM_PENDING_OP, chain)); }
DC_EXIT_POINT: DebugExitDWORD(WorksetClearPending, rc); return(rc); }
//
// ProcessWorksetNew(...)
//
UINT ProcessWorksetNew ( PUT_CLIENT putClient, POMNET_OPERATION_PKT pPacket, POM_WSGROUP pWSGroup ) { POM_DOMAIN pDomain; POM_WORKSET pWorkset; OM_WORKSET_ID worksetID; UINT rc = 0;
DebugEntry(ProcessWorksetNew);
worksetID = pPacket->worksetID;
TRACE_OUT(("Creating workset %u in WSG %d", worksetID, pWSGroup->wsg));
//
// Allocate some memory for the workset record:
//
pWorkset = (POM_WORKSET)UT_MallocRefCount(sizeof(OM_WORKSET), TRUE); if (!pWorkset) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// Fill in the fields (this chunk is taken from a huge block so we have
// to set it to zero explicitly):
//
// Note: the <position> and <flags> fields of the packet hold a
// two-byte quantity representing the network priority for the workset.
//
SET_STAMP(pWorkset, WORKSET); pWorkset->priority = *((NET_PRIORITY *) &(pPacket->position)); pWorkset->fTemp = *((BOOL *) &(pPacket->objectID)); pWorkset->worksetID = worksetID;
pWorkset->lockState = UNLOCKED; pWorkset->lockedBy = 0; pWorkset->lockCount = 0;
COM_BasedListInit(&(pWorkset->objects)); COM_BasedListInit(&(pWorkset->clients)); COM_BasedListInit(&(pWorkset->pendingOps));
if (pPacket->header.messageType == OMNET_WORKSET_CATCHUP) { //
// For a WORKSET_CATCHUP message, the <userID> field of the
// <seqStamp> field in the message holds the user ID of the node
// which holds the workset lock, if it is locked.
//
if (pPacket->seqStamp.userID != 0) { //
// If the <userID> field is the same as our user ID, then the
// remote node must think that we've got the workset locked -
// but we're just catching up, so something is wrong:
//
pDomain = pWSGroup->pDomain;
ASSERT((pPacket->seqStamp.userID != pDomain->userID));
pWorkset->lockState = LOCK_GRANTED; pWorkset->lockedBy = pPacket->seqStamp.userID; pWorkset->lockCount = 0;
TRACE_OUT(("Catching up with workset %u in WSG %d while locked by %hu", worksetID, pWSGroup->wsg, pWorkset->lockedBy)); }
//
// In addition, the current generation number for the workset is
// held in the <genNumber> field of the <seqStamp> field in the
// message:
//
pWorkset->genNumber = pPacket->seqStamp.genNumber; }
//
// Find the offset within OMWORKSETS of the workset record and put it
// in the array of offsets in the workset group record:
//
pWSGroup->apWorksets[worksetID] = pWorkset;
//
// Post a WORKSET_NEW event to all Clients registered with the workset
// group:
//
WSGroupEventPost(putClient, pWSGroup, PRIMARY | SECONDARY, OM_WORKSET_NEW_IND, worksetID, 0);
TRACE_OUT(("Processed WORKSET_NEW for workset ID %hu in WSG %d", worksetID, pWSGroup->wsg));
DC_EXIT_POINT: if (rc != 0) { ERROR_OUT(("ERROR %d creating workset %u in workset group '%s'", rc, worksetID, pWSGroup->wsg));
if (pWorkset != NULL) { UT_FreeRefCount((void**)&pWorkset, FALSE); }
pWSGroup->apWorksets[worksetID] = NULL; }
DebugExitDWORD(ProcessWorksetNew, rc); return(rc); }
//
// ProcessWorksetClear(...)
//
UINT ProcessWorksetClear ( PUT_CLIENT putClient, POM_PRIMARY pomPrimary, POMNET_OPERATION_PKT pPacket, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset ) { POM_PENDING_OP pPendingOp = NULL; UINT numPosts; UINT rc = 0;
DebugEntry(ProcessWorksetClear);
//
// Update the workset generation number:
//
UpdateWorksetGeneration(pWorkset, pPacket);
//
// See if this Clear operation can be spoiled (it will be spoiled if
// another Clear operation with a later sequence stamp has already been
// issued):
//
if (STAMP_IS_LOWER(pPacket->seqStamp, pWorkset->clearStamp)) { TRACE_OUT(("Spoiling Clear with stamp 0x%08x:0x%08x ('previous': 0x%08x:0x%08x)", pPacket->seqStamp.userID, pPacket->seqStamp.genNumber, pWorkset->clearStamp.userID, pWorkset->clearStamp.genNumber)); DC_QUIT; }
//
// Update the workset clear stamp:
//
COPY_SEQ_STAMP(pWorkset->clearStamp, pPacket->seqStamp);
//
// Now create a pending op CB to add to the list:
//
// Note: even if there is another Clear outstanding for the workset,
// we go ahead and put this one in the list and post another event
// to the Client. If we didn't, then we would expose ourselves
// to the following situation:
//
// 1. Clear issued
// 1a. Clear indication recd
// 2. Object added
// 3. Delete issued
// 3a. Delete indication recd - not filtered because unaffected
// by pending clear
// 4. Clear issued again - "takes over" previous Clear
// 5. Clear confirmed - causes object added in 2 to be deleted
// 6. Delete confirmed - assert because the delete WAS affected
// by the second clear which "took over" earlier one.
//
// A Client can still cause an assert by juggling the events and
// confirms, but we don't care because youo're not supposed to
// reorder ObMan events in any case.
//
pPendingOp = (POM_PENDING_OP)UT_MallocRefCount(sizeof(OM_PENDING_OP), FALSE); if (!pPendingOp) { rc = UT_RC_NO_MEM; DC_QUIT; }
SET_STAMP(pPendingOp, PENDINGOP);
pPendingOp->pObj = 0; pPendingOp->pData = NULL; pPendingOp->type = WORKSET_CLEAR;
COPY_SEQ_STAMP(pPendingOp->seqStamp, pPacket->seqStamp);
COM_BasedListInsertBefore(&(pWorkset->pendingOps), &(pPendingOp->chain));
//
// Post a workset clear indication event to the Client:
//
numPosts = WorksetEventPost(putClient, pWorkset, PRIMARY, OM_WORKSET_CLEAR_IND, 0);
//
// If there are no primaries present, then we won't be getting any
// ClearConfirms, so we do it now:
//
if (numPosts == 0) { TRACE_OUT(("No local primary Client has workset %u in WSG %d open - clearing", pWorkset->worksetID, pWSGroup->wsg));
WorksetDoClear(putClient, pWSGroup, pWorkset, pPendingOp); }
TRACE_OUT(("Processed WORKSET_CLEAR for workset %u in WSG %d", pWorkset->worksetID, pWSGroup->wsg));
DC_EXIT_POINT: if (rc != 0) { ERROR_OUT(("ERROR %d processing clear for workset %u in WSG %d", rc, pWorkset->worksetID, pWSGroup->wsg));
if (pPendingOp != NULL) { UT_FreeRefCount((void**)&pPendingOp, FALSE); } }
DebugExitDWORD(ProcessWorksetClear, rc); return(rc); }
//
// ProcessObjectAdd(...)
//
UINT ProcessObjectAdd ( PUT_CLIENT putTask, POMNET_OPERATION_PKT pPacket, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECTDATA pData, POM_OBJECT * ppObj ) { POM_OBJECT pObj; UINT rc = 0;
DebugEntry(ProcessObjectAdd);
//
// Update the workset generation number:
//
UpdateWorksetGeneration(pWorkset, pPacket);
//
// Create a new record for the object:
//
//
// Allocate memory for the object record:
//
*ppObj = (POM_OBJECT)UT_MallocRefCount(sizeof(OM_OBJECT), FALSE); if (! *ppObj) { rc = UT_RC_NO_MEM; DC_QUIT; }
pObj = *ppObj;
//
// Fill in the fields (remember, pData will be NULL if this is a
// catchup for a deleted object):
//
SET_STAMP(pObj, OBJECT); pObj->updateSize = pPacket->updateSize; pObj->pData = pData;
memcpy(&(pObj->objectID), &(pPacket->objectID), sizeof(OM_OBJECT_ID));
//
// How to set to the <flags> field and the sequence stamps depends on
// whether this is a CATCHUP:
//
if (pPacket->header.messageType == OMNET_OBJECT_CATCHUP) { COPY_SEQ_STAMP(pObj->addStamp, pPacket->seqStamp); COPY_SEQ_STAMP(pObj->positionStamp, pPacket->positionStamp); COPY_SEQ_STAMP(pObj->updateStamp, pPacket->updateStamp); COPY_SEQ_STAMP(pObj->replaceStamp, pPacket->replaceStamp);
pObj->flags = pPacket->flags; } else { COPY_SEQ_STAMP(pObj->addStamp, pPacket->seqStamp); COPY_SEQ_STAMP(pObj->positionStamp, pPacket->seqStamp); COPY_SEQ_STAMP(pObj->updateStamp, pPacket->seqStamp); COPY_SEQ_STAMP(pObj->replaceStamp, pPacket->seqStamp);
pObj->flags = 0; }
//
// The following fields are not filled in since they are handled
// by ObjectInsert, when the object is actually inserted into the
// workset:
//
// - chain
// - position
//
//
// Insert the object into the workset:
//
ObjectInsert(pWorkset, pObj, pPacket->position);
//
// If the object has been deleted (which will only happen for a Catchup
// of a deleted object), we don't need to do anything else, so just
// quit:
if (pObj->flags & DELETED) { ASSERT((pPacket->header.messageType == OMNET_OBJECT_CATCHUP));
TRACE_OUT(("Processing Catchup for deleted object (ID: 0x%08x:0x%08x)", pObj->objectID.creator, pObj->objectID.sequence));
DC_QUIT; }
//
// Otherwise, we continue...
//
// Increment the numObjects field:
//
// (we don't do this inside ObjectInsert since that's called when moving
// objects also)
//
pWorkset->numObjects++;
TRACE_OUT(("Number of objects in workset %u in WSG %d is now %u", pWorkset->worksetID, pWSGroup->wsg, pWorkset->numObjects));
//
// See if this Add can be spoiled (it is spoilable if the workset has
// been cleared since the Add was issued):
//
// Note: even if the Add is to be spoiled, we must create a record for
// it and insert it in the workset, for the same reason that we keep
// records of deleted objects in the workset (i.e. to differentiate
// between operations which are for deleted objects and those which are
// for objects not yet arrived).
//
if (STAMP_IS_LOWER(pPacket->seqStamp, pWorkset->clearStamp)) { TRACE_OUT(("Spoiling Add with stamp 0x%08x:0x%08x (workset cleared at 0x%08x:0x%08x)", pPacket->seqStamp.userID, pPacket->seqStamp.genNumber, pWorkset->clearStamp.userID, pWorkset->clearStamp.genNumber));
//
// We "spoil" an Add by simply deleting it:
//
ObjectDoDelete(putTask, pWSGroup, pWorkset, pObj, NULL);
DC_QUIT; }
//
// Post an add indication to all local Clients with the workset open:
//
WorksetEventPost(putTask, pWorkset, PRIMARY | SECONDARY, OM_OBJECT_ADD_IND, pObj);
TRACE_OUT(("Added object to workset %u in WSG %d (handle: 0x%08x - ID: 0x%08x:0x%08x)", pWorkset->worksetID, pWSGroup->wsg, pObj, pObj->objectID.creator, pObj->objectID.sequence));
TRACE_OUT((" position: %s - data at 0x%08x - size: %u - update size: %u", pPacket->position == LAST ? "LAST" : "FIRST", pData, pData->length, pPacket->updateSize));
DC_EXIT_POINT: if (rc != 0) { ERROR_OUT(("Error 0x%08x processing Add message", rc)); }
DebugExitDWORD(ProcessObjectAdd, rc); return(rc); }
//
// ProcessObjectMove(...)
//
void ProcessObjectMove ( PUT_CLIENT putTask, POMNET_OPERATION_PKT pPacket, POM_WORKSET pWorkset, POM_OBJECT pObj ) { DebugEntry(ProcessObjectMove);
//
// Update the workset generation number:
//
UpdateWorksetGeneration(pWorkset, pPacket);
//
// See if we can spoil this move:
//
if (STAMP_IS_LOWER(pPacket->seqStamp, pObj->positionStamp)) { TRACE_OUT(("Spoiling Move with stamp 0x%08x:0x%08x ('previous': 0x%08x:0x%08x)", pPacket->seqStamp.userID, pPacket->seqStamp.genNumber, pObj->positionStamp.userID, pObj->positionStamp.genNumber)); DC_QUIT; }
//
// Moving an object in a workset involves
//
// 1. removing the object from its current position in the workset,
//
// 2. setting its position stamp to the new value
//
// 3. inserting it at its new position.
//
COM_BasedListRemove(&(pObj->chain));
COPY_SEQ_STAMP(pObj->positionStamp, pPacket->seqStamp);
ObjectInsert(pWorkset, pObj, pPacket->position);
//
// Post an indication to all local Clients with the workset open:
//
WorksetEventPost(putTask, pWorkset, PRIMARY | SECONDARY, OM_OBJECT_MOVE_IND, pObj);
DC_EXIT_POINT: TRACE_OUT(("Moved object 0x%08x to %s of workset %u", pObj, (pPacket->position == LAST ? "end" : "start"), pWorkset->worksetID));
DebugExitVOID(ProcessObjectMove); }
//
// ProcessObjectDRU(...)
//
UINT ProcessObjectDRU ( PUT_CLIENT putTask, POMNET_OPERATION_PKT pPacket, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECT pObj, POM_OBJECTDATA pData ) { UINT numPosts; POM_PENDING_OP pPendingOp = NULL; POM_OBJECTDATA pPrevData; UINT event = 0; // event to post to Client
OM_OPERATION_TYPE type = 0; // type for pendingOp struct
POM_SEQUENCE_STAMP pSeqStamp = NULL; // sequence stamp to update
void (* fnObjectDoAction)(PUT_CLIENT, POM_WSGROUP, POM_WORKSET, POM_OBJECT, POM_PENDING_OP) = NULL; UINT rc = 0;
DebugEntry(ProcessObjectDRU);
//
// Set up the type variables:
//
switch (pPacket->header.messageType) { case OMNET_OBJECT_DELETE: event = OM_OBJECT_DELETE_IND; type = OBJECT_DELETE; pSeqStamp = NULL; fnObjectDoAction = ObjectDoDelete; break;
case OMNET_OBJECT_REPLACE: event = OM_OBJECT_REPLACE_IND; type = OBJECT_REPLACE; pSeqStamp = &(pObj->replaceStamp); fnObjectDoAction = ObjectDoReplace; break;
case OMNET_OBJECT_UPDATE: event = OM_OBJECT_UPDATE_IND; type = OBJECT_UPDATE; pSeqStamp = &(pObj->updateStamp); fnObjectDoAction = ObjectDoUpdate; break;
default: ERROR_OUT(("Reached default case in switch statement (value: %hu)", pPacket->header.messageType)); break; }
//
// Update the workset generation number:
//
UpdateWorksetGeneration(pWorkset, pPacket);
//
// Now do some spoiling checks, unless the object is a Delete (Deletes
// can't be spoiled):
//
if (type != OBJECT_DELETE) { ASSERT(((pSeqStamp != NULL) && (pData != NULL)));
//
// The first check is to see if this operation can be spoiled. It
// will be spoilable if the object has been updated/replaced since
// the operation took place. Since this function is called
// synchronously for a local Update/Replace, this will only event
// happen when a remote Update/Replace arrives "too late".
//
// The way we check is to compare the current stamp for the object
// with the stamp for the operation:
//
if (STAMP_IS_LOWER(pPacket->seqStamp, *pSeqStamp)) { TRACE_OUT(("Spoiling with stamp 0x%08x:0x%08x ('previous': 0x%08x:0x%08x)", pPacket->seqStamp.userID, pPacket->seqStamp.genNumber, (*pSeqStamp).userID, (*pSeqStamp).genNumber));
UT_FreeRefCount((void**)&pData, FALSE); DC_QUIT; }
//
// Update whichever of the object's stamps is involved by copying
// in the stamp from the packet:
//
COPY_SEQ_STAMP(*pSeqStamp, pPacket->seqStamp);
//
// The second check is to see if this operation spoils a previous
// one. This will happen when a Client does two updates or two
// replaces in quick succession i.e. does the second
// update/replace before confirming the first.
//
// In this case, we "spoil" the previous operation by removing the
// previous pending op from the pending op list and inserting this
// one instead. Note that we do NOT post another event, as to do
// so without adding net a new pending op would cause the Client to
// assert on its second call to Confirm().
//
// Note: although in general a Replace will spoil a previous
// Update, it cannot do so in this case because if there is
// an Update outstanding, the Client will call UpdateConfirm
// so we must leave the Update pending and post a Replace
// event also.
//
FindPendingOp(pWorkset, pObj, type, &pPendingOp);
if (pPendingOp != NULL) { //
// OK, there is an operation of this type already outstanding
// for this object. So, we change the entry in the pending op
// list to refer to this operation instead. Before doing so,
// however, we must free up the chunk holding the previous
// (superceded) update/replace:
//
pPrevData = pPendingOp->pData; if (pPrevData != NULL) { UT_FreeRefCount((void**)&pPrevData, FALSE); }
//
// Now put the reference to the new update/replace in the
// pending op:
//
pPendingOp->pData = pData;
COPY_SEQ_STAMP(pPendingOp->seqStamp, pPacket->seqStamp);
//
// The rest of this function inserts the pending op in the
// list, posts an event to local Client and performs the op if
// there are none. We know that
//
// - the op is in the list
//
// - there is an event outstanding because we found a pending
// op in the list
//
// - there are local Clients, for the same reason.
//
// Therefore, just quit:
//
DC_QUIT; } else { //
// No outstanding operation of this type for this object, so do
// nothing here and fall through to the standard processing:
//
} } else { //
// Sanity check:
//
ASSERT((pData == NULL));
pObj->flags |= PENDING_DELETE; }
//
// Add this operation to the workset's pending operation list:
//
pPendingOp = (POM_PENDING_OP)UT_MallocRefCount(sizeof(OM_PENDING_OP), FALSE); if (!pPendingOp) { rc = UT_RC_NO_MEM; DC_QUIT; }
SET_STAMP(pPendingOp, PENDINGOP);
pPendingOp->type = type; pPendingOp->pData = pData; pPendingOp->pObj = pObj;
COPY_SEQ_STAMP(pPendingOp->seqStamp, pPacket->seqStamp);
TRACE_OUT(("Inserting %d in pending op list for workset %u", type, pWorkset->worksetID));
COM_BasedListInsertBefore(&(pWorkset->pendingOps), &(pPendingOp->chain));
//
// Post an indication to all local Clients with the workset open:
//
numPosts = WorksetEventPost(putTask, pWorkset, PRIMARY, event, pObj);
//
// If no one has the workset open, we won't be getting any
// DeleteConfirms, so we'd better do the delete straight away:
//
if (numPosts == 0) { TRACE_OUT(("Workset %hu in WSG %d not open: performing %d immediately", pWorkset->worksetID, pWSGroup->wsg, type));
fnObjectDoAction(putTask, pWSGroup, pWorkset, pObj, pPendingOp); }
TRACE_OUT(("Processed %d message for object 0x%08x in workset %u in WSG %d", type, pObj, pWorkset->worksetID, pWSGroup->wsg));
DC_EXIT_POINT: if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(("ERROR %d processing WSG %d message", rc, pWSGroup->wsg));
if (pPendingOp != NULL) { UT_FreeRefCount((void**)&pPendingOp, FALSE); } }
DebugExitDWORD(ProcessObjectDRU, rc); return(rc); }
//
// ObjectInsert(...)
//
void ObjectInsert ( POM_WORKSET pWorkset, POM_OBJECT pObj, OM_POSITION position ) { POM_OBJECT pObjTemp; PBASEDLIST pChain;
DebugEntry(ObjectInsert);
//
// The algorithm for inserting an object at the start (end) of a workset
// is as follows:
//
// - search forward (back) from the first (last) object until one of the
// following happens:
//
// - we find an object which does not have FIRST (LAST) as a position
// stamp
//
// - we find an object which has a lower (lower) position stamp.
//
// - we reach the root of the list of objects in the workset
//
// - insert the new object before (after) this object.
//
switch (position) { case FIRST: { pObjTemp = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObjTemp != NULL) { ValidateObject(pObjTemp);
if ((pObjTemp->position != position) || (STAMP_IS_LOWER(pObjTemp->positionStamp, pObj->positionStamp))) { break; }
pObjTemp = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObjTemp, FIELD_OFFSET(OM_OBJECT, chain)); } break; }
case LAST: { pObjTemp = (POM_OBJECT)COM_BasedListLast(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObjTemp != NULL) { ValidateObject(pObjTemp);
if ((pObjTemp->position != position) || (STAMP_IS_LOWER(pObjTemp->positionStamp, pObj->positionStamp))) { break; }
pObjTemp = (POM_OBJECT)COM_BasedListPrev(&(pWorkset->objects), pObjTemp, FIELD_OFFSET(OM_OBJECT, chain)); }
break; }
default: { ERROR_OUT(("Reached default case in switch (position: %hu)", position)); break; } }
//
// OK, we've found the correct position for the object. If we reached
// the end (start) of the workset, then we want to insert the object
// before (after) the root, so we set up pChain accordingly:
//
if (pObjTemp == NULL) { pChain = &(pWorkset->objects);
TRACE_OUT(("Inserting object into workset %u as the %s object", pWorkset->worksetID, position == LAST ? "last" : "first")); } else { pChain = &(pObjTemp->chain);
TRACE_OUT(("Inserting object into workset %u %s object " "with record at 0x%08x (position stamp: 0x%08x:0x%08x)", pWorkset->worksetID, (position == LAST ? "after" : "before"), pObjTemp, pObjTemp->objectID.creator, pObjTemp->objectID.sequence)); }
//
// Now insert the object, either before or after the position we
// determined above:
//
if (position == FIRST) { COM_BasedListInsertBefore(pChain, &(pObj->chain)); } else { COM_BasedListInsertAfter(pChain, &(pObj->chain)); }
pObj->position = position;
//
// Now do a debug-only check to ensure correct order of objects:
//
CheckObjectOrder(pWorkset);
DebugExitVOID(ObjectInsert); }
//
// ObjectDoDelete(...)
//
void ObjectDoDelete ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECT pObj, POM_PENDING_OP pPendingOp ) { POM_DOMAIN pDomain;
DebugEntry(ObjectDoDelete);
//
// We should never be called for an object that's already been deleted:
//
ValidateObject(pObj); ASSERT(!(pObj->flags & DELETED));
//
// Derive a pointer to the object itself and then free it:
//
if (!pObj->pData) { ERROR_OUT(("ObjectDoDelete: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pObj->pData); UT_FreeRefCount((void**)&pObj->pData, FALSE); }
//
// Set the deleted flag in the object record:
//
// (note that we don't delete the object record entirely as we need to
// keep track of deleted objects so that when we get operations from the
// network for objects not in the workset, we can differentiate between
// operations on objects
//
// - that haven't yet been added at this node (we keep these operations
// and perform them later) and
//
// - that have been deleted (we throw these operations away).
//
// A slight space optimisation would be to store the IDs of deleted
// objects in a separate list, since we don't need any of the other
// fields in the record.
//
pObj->flags |= DELETED; pObj->flags &= ~PENDING_DELETE;
//
// Remove the pending op from the list, if the pointer passed in is
// valid (it won't be if we're called from WorksetDoClear, since those
// deletes have not been "pending").
//
// In addition, if pPendingOp is not NULL, we post the DELETED event to
// registered secondaries:
//
if (pPendingOp != NULL) { COM_BasedListRemove(&(pPendingOp->chain)); UT_FreeRefCount((void**)&pPendingOp, FALSE);
WorksetEventPost(putTask, pWorkset, SECONDARY, OM_OBJECT_DELETED_IND, pObj); }
//
// If we are in the local domain, we can safely delete the object rec:
//
pDomain = pWSGroup->pDomain; if (pDomain->callID == OM_NO_CALL) { TRACE_OUT(("Freeing pObj at 0x%08x", pObj));
ValidateObject(pObj);
COM_BasedListRemove(&(pObj->chain)); UT_FreeRefCount((void**)&pObj, FALSE); }
//
// Decrement the number of objects in the workset:
//
ASSERT(pWorkset->numObjects > 0); pWorkset->numObjects--;
DebugExitVOID(ObjectDoDelete); }
//
// ObjectDoReplace(...)
//
void ObjectDoReplace ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECT pObj, POM_PENDING_OP pPendingOp ) { POM_OBJECTDATA pDataNew; POM_OBJECTDATA pDataOld; UINT rc = 0;
DebugEntry(ObjectDoReplace);
ValidateObject(pObj);
//
// If the object has already been deleted for whatever reason, quit:
//
if (pObj->flags & DELETED) { WARNING_OUT(("Asked to do replace for deleted object 0x%08x!", pObj)); DC_QUIT; }
//
// Set up some local variables:
//
pDataOld = pObj->pData;
pDataNew = pPendingOp->pData; ValidateObjectData(pDataNew);
pObj->pData = pDataNew;
//
// If this object has been updated since this replace was issued, we
// must ensure that the replace doesn't overwrite the "later" update:
//
// Initial object at t=1 AAAAAA
// Object updated (two bytes) at t=3;
// Object becomes: CCAAAA
//
// Object replaced at t=2: BBBB
// Must now re-enact the update: CCBB
//
// Therefore, if the update stamp for the object is later than the stamp
// of the replace instruction, we copy the first N bytes back over the
// new object, where N is the size of the last update:
//
if (STAMP_IS_LOWER(pPendingOp->seqStamp, pObj->updateStamp)) { ASSERT((pDataNew->length >= pObj->updateSize));
memcpy(&(pDataNew->data), &(pDataOld->data), pObj->updateSize); }
TRACE_OUT(("Replacing object 0x%08x with data at 0x%08x (old data at 0x%08x)", pObj, pDataNew, pDataOld));
//
// We also need to free up the chunk holding the old object:
//
if (!pDataOld) { ERROR_OUT(("ObjectDoReplace: object 0x%08x has no data", pObj)); } else { UT_FreeRefCount((void**)&pDataOld, FALSE); }
//
// Now that we've replaced the object, post a REPLACED event to all
// secondaries:
//
WorksetEventPost(putTask, pWorkset, SECONDARY, OM_OBJECT_REPLACED_IND, pObj);
DC_EXIT_POINT: //
// We've either done the replace or abandoned it because the object has
// been deleted; either way, free up the entry in the pending op list:
//
COM_BasedListRemove(&(pPendingOp->chain)); UT_FreeRefCount((void**)&pPendingOp, FALSE);
DebugExitVOID(ObjectDoReplace); }
//
// ObjectDoUpdate(...)
//
void ObjectDoUpdate ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECT pObj, POM_PENDING_OP pPendingOp ) { POM_OBJECTDATA pDataNew; UINT rc = 0;
DebugEntry(ObjectDoUpdate);
ValidateObject(pObj);
//
// If the object has already been deleted for whatever reason, quit:
//
if (pObj->flags & DELETED) { WARNING_OUT(("Asked to do update for deleted object 0x%08x!", pObj)); DC_QUIT; }
pDataNew = pPendingOp->pData; if (!pObj->pData) { ERROR_OUT(("ObjectDoUpdate: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pObj->pData);
//
// Updating an object involves copying <length> bytes from the <data>
// field of the update over the start of the <data> field of the
// existing object:
//
memcpy(&(pObj->pData->data), &(pDataNew->data), pDataNew->length); }
UT_FreeRefCount((void**)&pDataNew, FALSE);
//
// Now that we've updated the object, post an UPDATED event to all
// secondaries:
//
WorksetEventPost(putTask, pWorkset, SECONDARY, OM_OBJECT_UPDATED_IND, pObj);
DC_EXIT_POINT: //
// We've done the update, so free up the entry in the pending op list:
//
COM_BasedListRemove(&(pPendingOp->chain)); UT_FreeRefCount((void**)&pPendingOp, FALSE);
DebugExitVOID(ObjectDoUpdate); }
//
// ObjectIDToPtr(...)
//
UINT ObjectIDToPtr ( POM_WORKSET pWorkset, OM_OBJECT_ID objectID, POM_OBJECT * ppObj ) { POM_OBJECT pObj; UINT rc = 0;
DebugEntry(ObjectIDToPtr);
//
// To find the handle, we chain through each of the object records in
// the workset and compare the id of each one with the required ID:
//
TRACE_OUT(("About to search object records looking for ID 0x%08x:0x%08x", objectID.creator, objectID.sequence));
ValidateWorkset(pWorkset);
pObj = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObj != NULL) { ValidateObject(pObj);
TRACE_OUT(("Comparing against object at 0x%08x (ID: 0x%08x:0x%08x)", pObj, pObj->objectID.creator, pObj->objectID.sequence));
if (OBJECT_IDS_ARE_EQUAL(pObj->objectID, objectID)) { break; }
pObj = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); }
//
// If object record not found, warn:
//
if (pObj == NULL) { TRACE_OUT(("Object with ID 0x%08x:0x%08x not found", objectID.creator, objectID.sequence));
rc = OM_RC_BAD_OBJECT_ID; DC_QUIT; }
*ppObj = pObj;
//
// If object record found but object deleted or pending delete, warn:
//
if (pObj->flags & DELETED) { TRACE_OUT(("Object record found (handle: 0x%08x) for ID 0x%08x:0x%08x " "but object deleted", *ppObj, objectID.creator, objectID.sequence)); rc = OM_RC_OBJECT_DELETED; DC_QUIT; }
if (pObj->flags & PENDING_DELETE) { TRACE_OUT(("Object record found (handle: 0x%08x) for ID 0x%08x:0x%08x " "but object pending delete", *ppObj, objectID.creator, objectID.sequence)); rc = OM_RC_OBJECT_PENDING_DELETE; DC_QUIT; }
DC_EXIT_POINT: DebugExitDWORD(ObjectIDToPtr, rc); return(rc);
}
//
// GenerateOpMessage(...)
//
UINT GenerateOpMessage ( POM_WSGROUP pWSGroup, OM_WORKSET_ID worksetID, POM_OBJECT_ID pObjectID, POM_OBJECTDATA pData, OMNET_MESSAGE_TYPE messageType, POMNET_OPERATION_PKT * ppPacket ) { POMNET_OPERATION_PKT pPacket; POM_DOMAIN pDomain; POM_WORKSET pWorkset = NULL; UINT rc = 0;
DebugEntry(GenerateOpMessage);
//
// Set up Domain record pointer:
//
pDomain = pWSGroup->pDomain;
TRACE_OUT(("Generating message for operation type 0x%08x", messageType));
//
// Allocate some memory for the packet:
//
pPacket = (POMNET_OPERATION_PKT)UT_MallocRefCount(sizeof(OMNET_OPERATION_PKT), TRUE); if (!pPacket) { rc = UT_RC_NO_MEM; DC_QUIT; }
//
// Here, we fill in the fields common to all types of messages:
//
pPacket->header.sender = pDomain->userID; pPacket->header.messageType = messageType;
//
// The <totalSize> field is the number of bytes in the header packet
// plus the number of associated data bytes, if any. For the moment, we
// set it to the size of the header only; we'll add the size of the data
// later:
//
pPacket->totalSize = GetMessageSize(messageType);
pPacket->wsGroupID = pWSGroup->wsGroupID; pPacket->worksetID = worksetID;
//
// If this is a WorksetNew operation, there will be no workset yet and
// thus no valid sequence stamp, so we use a null sequence stamp.
// Otherwise, we take the value from the workset.
//
if (messageType == OMNET_WORKSET_NEW) { SET_NULL_SEQ_STAMP(pPacket->seqStamp); } else { pWorkset = pWSGroup->apWorksets[worksetID]; ASSERT((pWorkset != NULL)); GET_CURR_SEQ_STAMP(pPacket->seqStamp, pDomain, pWorkset); }
//
// If this is a workset operation, <pObjectID> will be NULL, so we set
// the object ID in the packet to NULL also:
//
if (pObjectID == NULL) { ZeroMemory(&(pPacket->objectID), sizeof(OM_OBJECT_ID)); } else { memcpy(&(pPacket->objectID), pObjectID, sizeof(OM_OBJECT_ID)); }
//
// If this message is associated with object data, we must add the size
// of this data (including the size of the <length> field itself). The
// test for this is if the <pData> parameter is not NULL:
//
if (pData != NULL) { pPacket->totalSize += pData->length + sizeof(pData->length); }
//
// For a WORKSET_CATCHUP message, we need to let the other node know if
// the workset is locked and if so, by whom:
//
if (messageType == OMNET_WORKSET_CATCHUP) { //
// pWorkset should have been set up above:
//
ASSERT((pWorkset != NULL));
//
// Put the ID of the node which owns the workset lock in the <userID>
// field of the <seqStamp> field of the packet:
//
pPacket->seqStamp.userID = pWorkset->lockedBy;
TRACE_OUT(("Set <lockedBy> field in WORKSET_CATCHUP to %hu", pWorkset->lockedBy));
//
// Now we put the current generation number for the workset in the
// <genNumber> field of the <seqStamp> field of the packet:
//
pPacket->seqStamp.genNumber = pWorkset->genNumber;
TRACE_OUT(("Set generation number field in WORKSET_CATCHUP to %u", pPacket->seqStamp.genNumber));
//
// Fill in the priority value for the workset, which goes in the two
// bytes occupied by the <position> and <flags> fields:
//
*((NET_PRIORITY *) &(pPacket->position)) = pWorkset->priority; *((BOOL *) &(pPacket->objectID)) = pWorkset->fTemp; }
//
// We do not fill in the following fields:
//
// position
// flags
// updateSize
//
// This is because these are used only in a minority of messages and to
// add the extra parameters to the GenerateOpMessage function seemed
// undesirable. Messages where these fields are used should be filled
// in by the calling function as appropriate.
//
//
// Set the caller's pointer:
//
*ppPacket = pPacket;
DC_EXIT_POINT: if (rc != 0) { ERROR_OUT(("ERROR %d generating message of type 0x%08x", rc, messageType)); }
DebugExitDWORD(GenerateOpMessage, rc); return(rc); }
//
// QueueMessage(...)
//
UINT QueueMessage ( PUT_CLIENT putTask, POM_DOMAIN pDomain, NET_CHANNEL_ID channelID, NET_PRIORITY priority, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECT pObj, POMNET_PKT_HEADER pMessage, POM_OBJECTDATA pData, BOOL compressOrNot ) { POM_SEND_INST pSendInst; NET_PRIORITY queuePriority; BOOL locked = FALSE; BOOL bumped = FALSE; UINT rc = 0;
DebugEntry(QueueMessage);
//
// If this is the local Domain, we don't put the op on the send queue;
// just free the packet and quit:
//
if (pDomain->callID == NET_INVALID_DOMAIN_ID) { TRACE_OUT(("Not queueing message (it's for the local Domain)")); UT_FreeRefCount((void**)&pMessage, FALSE); DC_QUIT; }
//
// Allocate some memory in OMGLOBAL for the send instruction:
//
pSendInst = (POM_SEND_INST)UT_MallocRefCount(sizeof(OM_SEND_INST), TRUE); if (!pSendInst) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pSendInst, SENDINST);
//
// Fill in the fields of the send instruction:
//
pSendInst->messageSize = (WORD)GetMessageSize(pMessage->messageType);
DeterminePriority(&priority, pData);
pSendInst->priority = priority; pSendInst->callID = pDomain->callID; pSendInst->channel = channelID; pSendInst->messageType = pMessage->messageType; pSendInst->compressOrNot = compressOrNot;
//
// Now calculate the relevant offsets so we can add them to the ObMan
// base pointers:
//
// SFR 2560 { : bump use counts of all non-zero pointers, not just pData
//
if (pMessage != NULL) { pSendInst->pMessage = pMessage;
//
// SFR 5488 { : No! Don't bump use count of pMessage - we're the
// only people using it now so we don't need to. }
//
}
if (pWSGroup != NULL) { UT_BumpUpRefCount(pWSGroup); pSendInst->pWSGroup = pWSGroup; }
if (pWorkset != NULL) { UT_BumpUpRefCount(pWorkset); pSendInst->pWorkset = pWorkset; }
if (pObj != NULL) { UT_BumpUpRefCount(pObj); pSendInst->pObj = pObj; }
if (pData != NULL) { UT_BumpUpRefCount(pData);
pSendInst->pDataStart = pData; pSendInst->pDataNext = pData;
//
// In addition, we set up some send instruction fields which are
// specific to operations which involve object data:
//
pSendInst->dataLeftToGo = pData->length + sizeof(pData->length);
//
// Increment the <bytesUnacked> fields in the workset and workset
// group:
//
pWorkset->bytesUnacked += pSendInst->dataLeftToGo; pWSGroup->bytesUnacked += pSendInst->dataLeftToGo;
TRACE_OUT(("Bytes unacked for workset %u in WSG %d now %u " "(for wsGroup: %u)", pWorkset->worksetID, pWSGroup->wsg, pWorkset->bytesUnacked, pWSGroup->bytesUnacked)); }
//
// Set a flag so we can clean up a bit better on error:
//
bumped = TRUE;
//
// Unless there's a send event outstanding, post an event to the ObMan
// task prompting it to examine the send queue. Providing we have
// received a Net Attach indication.
//
if ( !pDomain->sendEventOutstanding && (pDomain->state > PENDING_ATTACH) ) { TRACE_OUT(("No send event outstanding - posting SEND_QUEUE event"));
//
// Bump up the use count of the Domain record (since we're passing it
// in an event):
//
UT_BumpUpRefCount(pDomain);
//
// NFC - we used to pass the pDomain pointer as param2 in this
// event, but the event may get processed in a different process
// where the pointer is no longer valid, so pass the offset instead.
//
ValidateOMP(g_pomPrimary);
UT_PostEvent(putTask, g_pomPrimary->putTask, 0, // no delay
OMINT_EVENT_SEND_QUEUE, 0, (UINT_PTR)pDomain);
pDomain->sendEventOutstanding = TRUE; } else { TRACE_OUT(("Send event outstanding/state %u: not posting SEND_QUEUE event", pDomain->state)); }
//
// Place the event at the end of the relevant send queue. This depends
// on priority - but remember, the priority value passed in might have
// the NET_SEND_ALL_PRIORITIES flag set - so clear it when determining
// the queue.
//
// NB: Do this after any possible DC-QUIT so we're not left with a
// NULL entry in the list.
//
queuePriority = priority; queuePriority &= ~NET_SEND_ALL_PRIORITIES; COM_BasedListInsertBefore(&(pDomain->sendQueue[queuePriority]), &(pSendInst->chain));
TRACE_OUT((" Queued instruction (type: 0x%08x) at priority %hu " "on channel 0x%08x in Domain %u", pMessage->messageType, priority, channelID, pDomain->callID));
DC_EXIT_POINT:
if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(("ERROR %d queueing send instruction (message type: %hu)", rc, pMessage->messageType));
if (pSendInst != NULL) { UT_FreeRefCount((void**)&pSendInst, FALSE); }
if (bumped == TRUE) { // SFR 2560 { : Free all non-zero pointers not just pData
if (pMessage != NULL) { UT_FreeRefCount((void**)&pMessage, FALSE); }
if (pWSGroup != NULL) { UT_FreeRefCount((void**)&pWSGroup, FALSE); }
if (pWorkset != NULL) { UT_FreeRefCount((void**)&pWorkset, FALSE); }
if (pObj != NULL) { UT_FreeRefCount((void**)&pObj, FALSE); }
if (pData != NULL) { UT_FreeRefCount((void**)&pData, FALSE); } } }
DebugExitDWORD(QueueMessage, rc); return(rc); }
//
// DeterminePriority(...)
//
void DeterminePriority ( NET_PRIORITY * pPriority, POM_OBJECTDATA pData ) {
DebugEntry(DeterminePriority);
if (OM_OBMAN_CHOOSES_PRIORITY == *pPriority) { if (pData != NULL) { if (pData->length < OM_NET_HIGH_PRI_THRESHOLD) { *pPriority = NET_HIGH_PRIORITY; } else if (pData->length < OM_NET_MED_PRI_THRESHOLD) { *pPriority = NET_MEDIUM_PRIORITY; } else { *pPriority = NET_LOW_PRIORITY; }
TRACE_OUT(("Priority chosen: %hu (data size: %u)", *pPriority, pData->length)); } else { *pPriority = NET_HIGH_PRIORITY; } } else { TRACE_OUT(("Priority specified is %hu - not changing", *pPriority)); }
DebugExitVOID(DeterminePriority); }
//
// GetMessageSize(...)
//
UINT GetMessageSize ( OMNET_MESSAGE_TYPE messageType ) { UINT size;
DebugEntry(GetMessageSize);
switch (messageType) { case OMNET_HELLO: case OMNET_WELCOME: size = sizeof(OMNET_JOINER_PKT); break;
case OMNET_LOCK_REQ: case OMNET_LOCK_GRANT: case OMNET_LOCK_DENY: case OMNET_LOCK_NOTIFY: case OMNET_UNLOCK: size = sizeof(OMNET_LOCK_PKT); break;
case OMNET_WSGROUP_SEND_REQ: case OMNET_WSGROUP_SEND_MIDWAY: case OMNET_WSGROUP_SEND_COMPLETE: case OMNET_WSGROUP_SEND_DENY: size = sizeof(OMNET_WSGROUP_SEND_PKT); break;
//
// The remaining messages all use OMNET_OPERATION_PKT packets, but
// each uses different amounts of the generic packet. Therefore, we
// can't use sizeof so we've got some defined constants instead:
//
case OMNET_WORKSET_NEW: size = OMNET_WORKSET_NEW_SIZE; break;
case OMNET_WORKSET_CATCHUP: size = OMNET_WORKSET_CATCHUP_SIZE; break;
case OMNET_WORKSET_CLEAR: size = OMNET_WORKSET_CLEAR_SIZE; break;
case OMNET_OBJECT_MOVE: size = OMNET_OBJECT_MOVE_SIZE; break;
case OMNET_OBJECT_DELETE: size = OMNET_OBJECT_DELETE_SIZE; break;
case OMNET_OBJECT_REPLACE: size = OMNET_OBJECT_REPLACE_SIZE; break;
case OMNET_OBJECT_UPDATE: size = OMNET_OBJECT_UPDATE_SIZE; break;
case OMNET_OBJECT_ADD: size = OMNET_OBJECT_ADD_SIZE; break;
case OMNET_OBJECT_CATCHUP: size = OMNET_OBJECT_CATCHUP_SIZE; break;
case OMNET_MORE_DATA: size = OMNET_MORE_DATA_SIZE; break;
default: ERROR_OUT(("Reached default case in switch statement (type: %hu)", messageType)); size = 0; break; }
DebugExitDWORD(GetMessageSize, size); return(size); }
//
// WorksetEventPost()
//
UINT WorksetEventPost ( PUT_CLIENT putTask, POM_WORKSET pWorkset, BYTE target, UINT event, POM_OBJECT pObj ) { POM_CLIENT_LIST pClientListEntry; OM_EVENT_DATA16 eventData16; UINT numPosts;
DebugEntry(WorksetEventPost);
//
// Need to post the event to each Client which has the workset open, so
// we chain through the list of Clients stored in the workset record:
//
numPosts = 0;
pClientListEntry = (POM_CLIENT_LIST)COM_BasedListFirst(&(pWorkset->clients), FIELD_OFFSET(OM_CLIENT_LIST, chain)); while (pClientListEntry != NULL) { //
// <target> specifies which type of Client we are to post events to
// and is PRIMARY and/or SECONDARY (ORed together if both). Check
// against this Client's registration mode:
//
if (target & pClientListEntry->mode) { //
// If the pObj was not NULL, bump the use count for the object
// record. If this fails, give up:
//
if (pObj != NULL) { ValidateObject(pObj); UT_BumpUpRefCount(pObj); }
//
// Fill in the fields of the event parameter, using the workset
// group handle as found in the Client list and the workset ID as
// found in the workset record:
//
eventData16.hWSGroup = pClientListEntry->hWSGroup; eventData16.worksetID = pWorkset->worksetID;
UT_PostEvent(putTask, pClientListEntry->putTask, 0, event, *(PUINT) &eventData16, (UINT_PTR)pObj);
numPosts++; }
pClientListEntry = (POM_CLIENT_LIST)COM_BasedListNext(&(pWorkset->clients), pClientListEntry, FIELD_OFFSET(OM_CLIENT_LIST, chain)); }
TRACE_OUT(("Posted event 0x%08x to %hu Clients (those with workset %u open)", event, numPosts, pWorkset->worksetID));
DebugExitDWORD(WorksetEventPost, numPosts); return(numPosts); }
//
// WSGroupEventPost(...)
//
UINT WSGroupEventPost ( PUT_CLIENT putFrom, POM_WSGROUP pWSGroup, BYTE target, UINT event, OM_WORKSET_ID worksetID, UINT_PTR param2 ) { POM_CLIENT_LIST pClientListEntry; OM_EVENT_DATA16 eventData16; UINT numPosts; UINT rc = 0;
DebugEntry(WSGroupEventPost);
//
// Need to post the event to each Client which is registered with the
// workset group, so we chain through the list of Clients stored in the
// workset group record:
//
numPosts = 0;
pClientListEntry = (POM_CLIENT_LIST)COM_BasedListFirst(&(pWSGroup->clients), FIELD_OFFSET(OM_CLIENT_LIST, chain)); while (pClientListEntry != NULL) { //
// <target> specifies which type of Client we are to post events to
// and is PRIMARY and/or SECONDARY (ORed together if both). Check
// against this Client's registration mode:
//
if (target & pClientListEntry->mode) { //
// Fill in the fields of the event parameter, using the workset
// group handle as found in the Client list and the workset ID
// passed in:
//
eventData16.hWSGroup = pClientListEntry->hWSGroup; eventData16.worksetID = worksetID;
TRACE_OUT(("Posting event 0x%08x to 0x%08x (hWSGroup: %hu - worksetID: %hu)", event, pClientListEntry->putTask, eventData16.hWSGroup, eventData16.worksetID));
UT_PostEvent(putFrom, pClientListEntry->putTask, 0, event, *(PUINT) &eventData16, param2);
numPosts++; }
pClientListEntry = (POM_CLIENT_LIST)COM_BasedListNext(&(pWSGroup->clients), pClientListEntry, FIELD_OFFSET(OM_CLIENT_LIST, chain)); }
TRACE_OUT(("Posted event 0x%08x to %hu Clients (all registered with '0x%08x')", event, numPosts, pWSGroup->wsg));
DebugExitDWORD(WSGroupEventPost, numPosts); return(numPosts); }
//
// WorksetDoClear(...)
//
void WorksetDoClear ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_PENDING_OP pPendingOp ) { POM_OBJECT pObj; POM_OBJECT pObj2; BOOL locked = FALSE;
DebugEntry(WorksetDoClear);
//
// To clear a workset, we chain through each object in the workset and
// compare its addition stamp to the stamp of the clear operation we're
// performing. If the object was added before the workset clear was
// issued, we delete the object. Otherwise, we ignore it.
//
TRACE_OUT(("Clearing workset %u...", pWorkset->worksetID));
pObj = (POM_OBJECT)COM_BasedListLast(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain));
while (pObj != NULL) { ValidateObject(pObj);
pObj2 = (POM_OBJECT)COM_BasedListPrev(&(pWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain));
if (pObj->flags & DELETED) { //
// Do nothing
//
} else { if (STAMP_IS_LOWER(pObj->addStamp, pPendingOp->seqStamp)) { TRACE_OUT(("Object 0x%08x added before workset cleared, deleting", pObj));
PurgePendingOps(pWorkset, pObj);
ObjectDoDelete(putTask, pWSGroup, pWorkset, pObj, NULL); } }
// restore the previous one
pObj = pObj2; }
//
// This operation isn't pending anymore, so we remove it from the
// pending operation list and free the memory:
//
COM_BasedListRemove(&(pPendingOp->chain)); UT_FreeRefCount((void**)&pPendingOp, FALSE);
//
// Now that we've cleared the workset, post a CLEARED event to all
// secondaries:
//
WorksetEventPost(putTask, pWorkset, SECONDARY, OM_WORKSET_CLEARED_IND, 0);
TRACE_OUT(("Cleared workset %u", pWorkset->worksetID));
DebugExitVOID(WorksetDoClear); }
//
// WorksetCreate(...)
//
UINT WorksetCreate ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, OM_WORKSET_ID worksetID, BOOL fTemp, NET_PRIORITY priority ) { POMNET_OPERATION_PKT pPacket; UINT rc = 0;
DebugEntry(WorksetCreate);
//
// Here we create the new workset by generating the message to be
// broadcast, processing it as if it had just arrived, and then
// queueing it to be sent:
//
rc = GenerateOpMessage(pWSGroup, worksetID, NULL, // no object ID
NULL, // no object
OMNET_WORKSET_NEW, &pPacket); if (rc != 0) { DC_QUIT; }
//
// Fill in the priority value for the workset, which goes in the two
// bytes occupied by the <position> and <flags> fields:
//
*((NET_PRIORITY *) &(pPacket->position)) = priority; *((BOOL *) &(pPacket->objectID)) = fTemp;
rc = ProcessWorksetNew(putTask, pPacket, pWSGroup); if (rc != 0) { DC_QUIT; }
//
// NEW FOR R2.0
//
// In R2.0, the checkpointing mechanism used by a helper to get up to
// date before sending a workset group to a late joiner relies on
// locking a "dummy" workset (#255) in the workset group in question.
// So, if the workset ID is 255, this is the dummy workset. We do not
// broadcast the WORKSET_NEW for this dummy workset, for two reasons:
//
// - it will confuse R1.1 systems
//
// - all other R2.0 systems will create it locally just as we have, so
// there isn't any need.
//
// So, do a check and free up the send packet if necessary; otherwise
// queue the message as normal:
//
if (worksetID == OM_CHECKPOINT_WORKSET) { TRACE_OUT(("WORKSET_NEW for checkpointing dummy workset - not queueing")); UT_FreeRefCount((void**)&pPacket, FALSE); } else { rc = QueueMessage(putTask, pWSGroup->pDomain, pWSGroup->channelID, priority, pWSGroup, NULL, NULL, // no object
(POMNET_PKT_HEADER) pPacket, NULL, // no object data
TRUE); if (rc != 0) { DC_QUIT; } }
TRACE_OUT(("Created workset ID %hu in WSG %d for TASK 0x%08x", worksetID, pWSGroup->wsg, putTask));
DC_EXIT_POINT: if (rc != 0) { //
// Cleanup:
//
ERROR_OUT(("Error 0x%08x creating workset ID %hu in WSG %d for TASK 0x%08x", rc, worksetID, pWSGroup->wsg, putTask)); }
DebugExitDWORD(WorksetCreate, rc); return(rc); }
//
// ObjectAdd(...)
//
UINT ObjectAdd ( PUT_CLIENT putTask, POM_PRIMARY pomPrimary, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECTDATA pData, UINT updateSize, OM_POSITION position, OM_OBJECT_ID * pObjectID, POM_OBJECT * ppObj ) { POM_OBJECT pObj; POMNET_OPERATION_PKT pPacket; POM_DOMAIN pDomain; UINT rc = 0;
DebugEntry(ObjectAdd);
TRACE_OUT(("Adding object to workset %u in WSG %d", pWorkset->worksetID, pWSGroup->wsg));
//
// Allocate a new ID for this object:
//
pDomain = pWSGroup->pDomain; GET_NEXT_OBJECT_ID(*pObjectID, pDomain, pomPrimary);
//
// Generate the OMNET_OBJECT_ADD message:
//
rc = GenerateOpMessage(pWSGroup, pWorkset->worksetID, pObjectID, pData, OMNET_OBJECT_ADD, &pPacket); if (rc != 0) { pPacket = NULL; DC_QUIT; }
//
// Generate message doesn't fill in the <updateSize> or <position>
// fields (as they are specific to ObjectAdd) so we do them here:
//
pPacket->updateSize = updateSize; pPacket->position = position;
//
// This processes the message, as if it has just been received from the
// network (i.e. allocates the record, sets up the object handle,
// inserts the object in the workset, etc.)
//
rc = ProcessObjectAdd(putTask, pPacket, pWSGroup, pWorkset, pData, ppObj); if (rc != 0) { DC_QUIT; }
pObj = *ppObj;
//
// This queues the OMNET_OBJECT_ADD message on the send queue for this
// Domain and priority:
//
rc = QueueMessage(putTask, pWSGroup->pDomain, pWSGroup->channelID, pWorkset->priority, pWSGroup, pWorkset, pObj, (POMNET_PKT_HEADER) pPacket, pData, TRUE); if (rc != 0) { ValidateObject(pObj);
//
// If we failed to queue the message, we must unwind by deleting the
// object and its record from the workset (since otherwise it would
// be present on this node and no another, which we want to avoid):
//
// We don't want to call ObjectDoDelete since that frees the object
// data (which our caller will expect still to be valid if the
// function fails). We could, of course, bump the use count and then
// call ObjectDoDelete but if we fail on the bump, what next?
//
// Instead, we
//
// - set the DELETED flag so the hidden handler will swallow the
// Add event
//
// - decrement the numObjects field in the workset
//
// - free the object record after removing it from the workset.
//
pObj->flags |= DELETED; pWorkset->numObjects--;
TRACE_OUT(("Freeing object record at 0x%08x)", pObj)); COM_BasedListRemove(&(pObj->chain)); UT_FreeRefCount((void**)&pObj, FALSE);
DC_QUIT; }
DC_EXIT_POINT: if (rc != 0) { ERROR_OUT(("ERROR %d adding object to workset %u in WSG %d for TASK 0x%08x", rc, pWorkset->worksetID, pWSGroup->wsg, putTask));
if (pPacket != NULL) { UT_FreeRefCount((void**)&pPacket, FALSE); } }
DebugExitDWORD(ObjectAdd, rc); return(rc); }
//
// ObjectDRU(...)
//
UINT ObjectDRU ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, POM_OBJECT pObj, POM_OBJECTDATA pData, OMNET_MESSAGE_TYPE type ) { POMNET_OPERATION_PKT pPacket; UINT rc = 0;
DebugEntry(ObjectDRU);
TRACE_OUT(("Issuing operation type 0x%08x for object 0x%08x in workset %u in WSG %d", type, pData, pWorkset->worksetID, pWSGroup->wsg));
rc = GenerateOpMessage(pWSGroup, pWorkset->worksetID, &(pObj->objectID), pData, type, &pPacket); if (rc != 0) { pPacket = NULL; DC_QUIT; }
//
// QueueMessage may free the packet (if we're not in a call) but we need
// to process it in a minute so bump the use count:
//
UT_BumpUpRefCount(pPacket);
rc = QueueMessage(putTask, pWSGroup->pDomain, pWSGroup->channelID, pWorkset->priority, pWSGroup, pWorkset, pObj, (POMNET_PKT_HEADER) pPacket, pData, TRUE); if (rc != 0) { DC_QUIT; }
rc = ProcessObjectDRU(putTask, pPacket, pWSGroup, pWorkset, pObj, pData);
DC_EXIT_POINT:
//
// Now free the packet since we bumped its use count above:
//
if (pPacket != NULL) { UT_FreeRefCount((void**)&pPacket, FALSE); }
if (rc != 0) { ERROR_OUT(("ERROR %d issuing D/R/U (type %hu) for object 0x%08x " "in workset %u in WSG %d", rc, type, pData, pWorkset->worksetID, pWSGroup->wsg)); }
DebugExitDWORD(ObjectDRU, rc); return(rc); }
//
// FindPendingOp(...)
//
void FindPendingOp ( POM_WORKSET pWorkset, POM_OBJECT pObj, OM_OPERATION_TYPE type, POM_PENDING_OP * ppPendingOp ) { POM_PENDING_OP pPendingOp;
DebugEntry(FindPendingOp);
pPendingOp = (POM_PENDING_OP)COM_BasedListFirst(&(pWorkset->pendingOps), FIELD_OFFSET(OM_PENDING_OP, chain)); while (pPendingOp != NULL) { if ((pPendingOp->type == type) && (pPendingOp->pObj == pObj)) { break; }
pPendingOp = (POM_PENDING_OP)COM_BasedListNext(&(pWorkset->pendingOps), pPendingOp, FIELD_OFFSET(OM_PENDING_OP, chain)); }
if (pPendingOp == NULL) { TRACE_OUT(("No pending op of type %hu found for object 0x%08x", type, pObj)); }
*ppPendingOp = pPendingOp;
DebugExitVOID(FindPendingOp); }
//
// AddClientToWsetList(...)
//
UINT AddClientToWsetList ( PUT_CLIENT putTask, POM_WORKSET pWorkset, OM_WSGROUP_HANDLE hWSGroup, UINT mode, POM_CLIENT_LIST * ppClientListEntry ) { UINT rc = 0;
DebugEntry(AddClientToWsetList);
//
// Adding a task to a workset's client list means that that task will
// get events relating to that workset.
//
TRACE_OUT((" Adding TASK 0x%08x to workset's client list"));
*ppClientListEntry = (POM_CLIENT_LIST)UT_MallocRefCount(sizeof(OM_CLIENT_LIST), FALSE); if (! *ppClientListEntry) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP((*ppClientListEntry), CLIENTLIST);
(*ppClientListEntry)->putTask = putTask; (*ppClientListEntry)->hWSGroup = hWSGroup; (*ppClientListEntry)->mode = (WORD)mode;
//
// Now insert the entry into the list:
//
COM_BasedListInsertBefore(&(pWorkset->clients), &((*ppClientListEntry)->chain));
TRACE_OUT((" Inserted Client list item into workset's Client list"));
DC_EXIT_POINT: DebugExitDWORD(AddClientToWsetList, rc); return(rc);
}
//
// RemoveClientFromWSGList(...)
//
void RemoveClientFromWSGList ( PUT_CLIENT putUs, PUT_CLIENT putTask, POM_WSGROUP pWSGroup ) { POM_CLIENT_LIST pClientListEntry; BOOL locked = FALSE;
DebugEntry(RemoveClientFromWSGList);
TRACE_OUT(("Searching for Client TASK 0x%08x in WSG %d", putTask, pWSGroup->wsg));
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pWSGroup->clients), (void**)&pClientListEntry, FIELD_OFFSET(OM_CLIENT_LIST, chain), FIELD_OFFSET(OM_CLIENT_LIST, putTask), (DWORD_PTR)putTask, FIELD_SIZE(OM_CLIENT_LIST, putTask));
//
// If it's not there, the Client may have already deregistered itself:
//
if (pClientListEntry == NULL) { WARNING_OUT(("Client TASK 0x%08x not found in list for WSG %d", putTask, pWSGroup->wsg)); DC_QUIT; }
//
// Remove the Client from the list and free the memory:
//
COM_BasedListRemove(&(pClientListEntry->chain)); UT_FreeRefCount((void**)&pClientListEntry, FALSE);
//
// If there are now no local Clients registered with the workset group,
// post an event to ObMan so it can discard the workset group (unless
// the workset group is marked non-discardable e.g the ObManControl
// workset group)
//
// The event parameter is the offset of the workset group record.
//
// Note: this discard is done asynchronously since it may involve
// allocating resources (broadcasting to other nodes that
// we've deregistered), and we want this function to always
// succeed.
//
// However, we clear the <valid> flag synchronously so that
// ObMan will not try to process events etc. which arrive
// for it.
//
if (COM_BasedListIsEmpty(&(pWSGroup->clients))) { pWSGroup->toBeDiscarded = TRUE; pWSGroup->valid = FALSE;
TRACE_OUT(("Last local Client deregistered from WSG %d, " "marking invalid and posting DISCARD event", pWSGroup->wsg));
ValidateOMP(g_pomPrimary);
UT_PostEvent(putUs, g_pomPrimary->putTask, 0, // no delay
OMINT_EVENT_WSGROUP_DISCARD, 0, (UINT_PTR)pWSGroup); } else { TRACE_OUT(("Clients still registered with WSG %d", pWSGroup->wsg)); }
DC_EXIT_POINT: DebugExitVOID(RemoveClientFromWSGList); }
//
// FindInfoObject(...)
//
void FindInfoObject ( POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, OMWSG wsg, OMFP fpHandler, POM_OBJECT * ppObjInfo ) { POM_WORKSET pOMCWorkset; POM_OBJECT pObj; POM_WSGROUP_INFO pInfoObject;
DebugEntry(FindInfoObject);
TRACE_OUT(("FindInfoObject: FP %d WSG %d ID %d, domain %u", fpHandler, wsg, wsGroupID, pDomain->callID));
//
// In this function, we search workset #0 in ObManControl for a
// Function Profile/workset group name combination which matches the
// ones specified
//
// So, we need to start with a pointer to this workset:
//
pOMCWorkset = GetOMCWorkset(pDomain, OM_INFO_WORKSET);
//
// Now chain through each of the objects in the workset to look for a
// match.
//
pObj = (POM_OBJECT)COM_BasedListLast(&(pOMCWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObj != NULL) { ValidateObject(pObj);
//
// If the object has not been deleted...
//
if (pObj->flags & DELETED) {
} else if (!pObj->pData) { ERROR_OUT(("FindInfoObject: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pObj->pData); pInfoObject = (POM_WSGROUP_INFO)pObj->pData;
//
// ...and if it is an INFO object...
//
if (pInfoObject->idStamp == OM_WSGINFO_ID_STAMP) { // If no FP provided, check the group IDs match
if (fpHandler == OMFP_MAX) { //
// ...and the ID matches, we've got what we want:
//
if (wsGroupID == pInfoObject->wsGroupID) { break; } } //
// ...but otherwise, try match on functionProfile...
//
else { if (!lstrcmp(pInfoObject->functionProfile, OMMapFPToName(fpHandler))) { //
// ...and also on WSG unless it is not provided
//
if ((wsg == OMWSG_MAX) || (!lstrcmp(pInfoObject->wsGroupName, OMMapWSGToName(wsg)))) { break; } } } } }
pObj = (POM_OBJECT)COM_BasedListPrev(&(pOMCWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); }
TRACE_OUT(("%s info object in Domain %u", pObj == NULL ? "Didn't find" : "Found", pDomain->callID));
//
// Set up the caller's pointer:
//
*ppObjInfo = pObj;
DebugExitVOID(FindInfoObject); }
//
// PostAddEvents(...)
//
UINT PostAddEvents ( PUT_CLIENT putFrom, POM_WORKSET pWorkset, OM_WSGROUP_HANDLE hWSGroup, PUT_CLIENT putTo ) { OM_EVENT_DATA16 eventData16; POM_OBJECT pObj; UINT rc = 0;
DebugEntry(PostAddEvents);
eventData16.hWSGroup = hWSGroup; eventData16.worksetID = pWorkset->worksetID;
if (pWorkset->numObjects != 0) { TRACE_OUT(("Workset has %u objects - posting OBJECT_ADD events", pWorkset->numObjects));
pObj = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain));
while (pObj != NULL) { ValidateObject(pObj);
//
// Don't post events for DELETED objects:
//
if (!(pObj->flags & DELETED)) { //
// We're posting an event with an pObj in it, so bump the
// use count of the object record it refers to:
//
UT_BumpUpRefCount(pObj);
UT_PostEvent(putFrom, putTo, 0, // no delay
OM_OBJECT_ADD_IND, *(PUINT) &eventData16, (UINT_PTR)pObj); }
pObj = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); } } else { TRACE_OUT(("No objects in workset")); }
DebugExitDWORD(PostAddEvents, rc); return(rc); }
//
// PurgePendingOps(...)
//
void PurgePendingOps ( POM_WORKSET pWorkset, POM_OBJECT pObj ) { POM_PENDING_OP pPendingOp; POM_PENDING_OP pTempPendingOp;
DebugEntry(PurgePendingOps);
//
// Chain through the workset's list of pending operations and confirm
// them one by one:
//
pPendingOp = (POM_PENDING_OP)COM_BasedListFirst(&(pWorkset->pendingOps), FIELD_OFFSET(OM_PENDING_OP, chain)); while (pPendingOp != NULL) { pTempPendingOp = (POM_PENDING_OP)COM_BasedListNext(&(pWorkset->pendingOps), pPendingOp, FIELD_OFFSET(OM_PENDING_OP, chain));
if (pPendingOp->pObj == pObj) { TRACE_OUT(("Purging operation type %hd", pPendingOp->type)); COM_BasedListRemove(&(pPendingOp->chain)); UT_FreeRefCount((void**)&pPendingOp, FALSE); }
pPendingOp = pTempPendingOp; }
DebugExitVOID(PurgePendingOps); }
//
// WorksetLockReq(...)
//
void WorksetLockReq ( PUT_CLIENT putTask, POM_PRIMARY pomPrimary, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset, OM_WSGROUP_HANDLE hWSGroup, OM_CORRELATOR * pCorrelator ) { POM_DOMAIN pDomain; POM_LOCK_REQ pLockReq = NULL; POMNET_LOCK_PKT pLockReqPkt = NULL; UINT rc = 0;
DebugEntry(WorksetLockReq);
TRACE_OUT(("TASK 0x%08x requesting to lock workset %u in WSG %d", putTask, pWorkset->worksetID, hWSGroup));
//
// The caller will need a correlator value to correlate the eventual
// lock success/failure event:
//
*pCorrelator = NextCorrelator(pomPrimary);
//
// Set up a pointer to the Domain record:
//
pDomain = pWSGroup->pDomain;
//
// Allocate some memory for the lock request control block:
//
pLockReq = (POM_LOCK_REQ)UT_MallocRefCount(sizeof(OM_LOCK_REQ), TRUE); if (!pLockReq) { rc = OM_RC_OUT_OF_RESOURCES; DC_QUIT; } SET_STAMP(pLockReq, LREQ);
//
// Set up the fields:
//
pLockReq->putTask = putTask; pLockReq->correlator = *pCorrelator; pLockReq->wsGroupID = pWSGroup->wsGroupID; pLockReq->worksetID = pWorkset->worksetID; pLockReq->hWSGroup = hWSGroup; pLockReq->type = LOCK_PRIMARY; pLockReq->retriesToGo = OM_LOCK_RETRY_COUNT_DFLT;
pLockReq->pWSGroup = pWSGroup;
COM_BasedListInit(&(pLockReq->nodes));
//
// Insert this lock request in the Domain's list of pending lock
// requests:
//
COM_BasedListInsertBefore(&(pDomain->pendingLocks), &(pLockReq->chain));
//
// Now examine the workset lock state to see if we can grant the lock
// immediately:
//
TRACE_OUT(("Lock state for workset %u in WSG %d is %hu", pWorkset->worksetID, hWSGroup, pWorkset->lockState));
switch (pWorkset->lockState) { case LOCKING: case LOCKED: { TRACE_OUT(( "Workset %hu in WSG %d already locked/locking - bumping count", pWorkset->worksetID, hWSGroup));
pLockReq->type = LOCK_SECONDARY; pWorkset->lockCount++;
if (pWorkset->lockState == LOCKED) { //
// If we've already got the lock, post success immediately:
//
WorksetLockResult(putTask, &pLockReq, 0); } else { //
// Otherwise, this request will be handled when the primary
// request completes, so do nothing for now.
//
} } break;
case LOCK_GRANTED: { //
// We've already granted the lock to another node so we fail
// our local client's request for it:
//
WorksetLockResult(putTask, &pLockReq, OM_RC_WORKSET_LOCK_GRANTED);
} break;
case UNLOCKED: { //
// Build up a list of other nodes using the workset group:
//
rc = BuildNodeList(pDomain, pLockReq); if (rc != 0) { DC_QUIT; }
pWorkset->lockState = LOCKING; pWorkset->lockCount++; pWorkset->lockedBy = pDomain->userID;
//
// If the list is empty, we have got the lock:
//
if (COM_BasedListIsEmpty(&pLockReq->nodes)) { TRACE_OUT(("No remote nodes, granting lock immediately"));
pWorkset->lockState = LOCKED; WorksetLockResult(putTask, &pLockReq, 0); } //
// Otherwise, we need to broadcast a lock request CB:
//
else { pLockReqPkt = (POMNET_LOCK_PKT)UT_MallocRefCount(sizeof(OMNET_LOCK_PKT), TRUE); if (!pLockReqPkt) { rc = UT_RC_NO_MEM; DC_QUIT; }
pLockReqPkt->header.messageType = OMNET_LOCK_REQ; pLockReqPkt->header.sender = pDomain->userID;
pLockReqPkt->data1 = pLockReq->correlator; pLockReqPkt->wsGroupID = pLockReq->wsGroupID; pLockReqPkt->worksetID = pLockReq->worksetID;
//
// Lock messages go at the priority of the workset
// involved. If this is OBMAN_CHOOSES_PRIORITY, then
// all bets are off and we send them TOP_PRIORITY.
//
rc = QueueMessage(putTask, pDomain, pWSGroup->channelID, (NET_PRIORITY)((pWorkset->priority == OM_OBMAN_CHOOSES_PRIORITY) ? NET_TOP_PRIORITY : pWorkset->priority), NULL, NULL, NULL, (POMNET_PKT_HEADER) pLockReqPkt, NULL, TRUE); if (rc != 0) { DC_QUIT; }
//
// Post a timeout event to the ObMan task so that we don't hang around
// forever waiting for the lock replies:
//
UT_PostEvent(putTask, pomPrimary->putTask, // ObMan's utH
OM_LOCK_RETRY_DELAY_DFLT, OMINT_EVENT_LOCK_TIMEOUT, pLockReq->correlator, pDomain->callID); } } break; }
DC_EXIT_POINT: //
// For the checkpointing dummy workset, we always "forget" our lock
// state so that subsequent requests to lock it will result in the
// required end-to-end ping:
//
if (pWorkset->worksetID == OM_CHECKPOINT_WORKSET) { TRACE_OUT(("Resetting lock state of checkpoint workset in WSG %d", hWSGroup));
pWorkset->lockState = UNLOCKED; pWorkset->lockCount = 0; pWorkset->lockedBy = 0; }
if (rc != 0) { if (pLockReqPkt != NULL) { UT_FreeRefCount((void**)&pLockReqPkt, FALSE); }
//
// This function never returns an error to its caller directly;
// instead, we call WorksetLockResult which will post a failure
// event to the calling task (this means the caller doesn't have to
// have two error processing paths)
//
if (pLockReq != NULL) { WorksetLockResult(putTask, &pLockReq, rc); } else { WARNING_OUT(("ERROR %d requesting lock for workset %u in WSG %d ", rc, pWorkset->worksetID, hWSGroup)); } }
DebugExitVOID(WorksetLockReq); }
//
// BuildNodeList(...)
//
UINT BuildNodeList ( POM_DOMAIN pDomain, POM_LOCK_REQ pLockReq ) { NET_PRIORITY priority; POM_WORKSET pOMCWorkset; POM_OBJECT pObj; POM_WSGROUP_REG_REC pPersonObject; POM_NODE_LIST pNodeEntry; NET_UID ownUserID; BOOL foundOurRegObject; UINT rc = 0;
DebugEntry(BuildNodeList);
//
// OK, we're about to broadcast a lock request throughout this Domain
// on this workset group's channel. Before we do so, however, we build
// up a list of the nodes we expect to respond to the request. As the
// replies come in we tick them off against this list; when all of them
// have been received, the lock is granted.
//
// SFR 6117: Since the lock replies will come back on all priorities
// (to correctly flush the channel), we add 4 items for each remote
// node - one for each priority.
//
// So, we examine the control workset for this workset group, adding
// items to our list for each person object we find in it (except our
// own, of course).
//
//
// First, get a pointer to the relevant control workset:
//
pOMCWorkset = GetOMCWorkset(pDomain, pLockReq->wsGroupID); ASSERT((pOMCWorkset != NULL));
//
// We want to ignore our own registration object, so make a note of our
// user ID:
//
ownUserID = pDomain->userID;
//
// Now chain through the workset:
//
foundOurRegObject = FALSE;
pObj = (POM_OBJECT)COM_BasedListFirst(&(pOMCWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); while (pObj != NULL) { ValidateObject(pObj);
if (pObj->flags & DELETED) { //
// Do nothing
//
} else if (!pObj->pData) { ERROR_OUT(("BuildNodeList: object 0x%08x has no data", pObj)); } else { ValidateObjectData(pObj->pData); pPersonObject = (POM_WSGROUP_REG_REC)pObj->pData;
if (pPersonObject->idStamp != OM_WSGREGREC_ID_STAMP) { TRACE_OUT(("Not a person object, skipping")); } else { if (pPersonObject->userID == ownUserID) { if (foundOurRegObject) { ERROR_OUT(("Duplicate person object in workset %u", pOMCWorkset->worksetID)); } else { TRACE_OUT(("Found own person object, skipping")); foundOurRegObject = TRUE; } } else { //
// Add an item to our expected respondents list (this
// memory is freed in each case when the remote node
// replies, or the timer expires and we notice that the
// node has disappeared).
//
// SFR 6117: We add one item for each priority value, since
// the lock replies will come back on all priorities.
//
for (priority = NET_TOP_PRIORITY; priority <= NET_LOW_PRIORITY; priority++) { TRACE_OUT(("Adding node 0x%08x to node list at priority %hu", pPersonObject->userID, priority));
pNodeEntry = (POM_NODE_LIST)UT_MallocRefCount(sizeof(OM_NODE_LIST), TRUE); if (!pNodeEntry) { rc = UT_RC_NO_MEM; DC_QUIT; } SET_STAMP(pNodeEntry, NODELIST);
pNodeEntry->userID = pPersonObject->userID;
COM_BasedListInsertAfter(&(pLockReq->nodes), &(pNodeEntry->chain));
//
// BUT! We only do this for R20 and later (i.e.
// anything over real MCS). For R11 calls, just put
// one entry on the list.
//
// ALSO! For ObManControl worksets, we only expect one
// lock reply (at TOP_PRIORITY) - this is to speed up
// processing of registration attempts. So, if this is
// for ObManControl, don't go around this loop again -
// just get out.
//
if (pLockReq->wsGroupID == WSGROUPID_OMC) { break; } } } } }
pObj = (POM_OBJECT)COM_BasedListNext(&(pOMCWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); }
DC_EXIT_POINT: if (rc != 0) { ERROR_OUT(("ERROR %d trying to build node list", rc)); }
DebugExitDWORD(BuildNodeList, rc); return(rc);
}
//
// WorksetLockResult(...)
//
void WorksetLockResult ( PUT_CLIENT putTask, POM_LOCK_REQ * ppLockReq, UINT result ) { POM_LOCK_REQ pLockReq; POM_WSGROUP pWSGroup; POM_WORKSET pWorkset; OM_EVENT_DATA16 eventData16; OM_EVENT_DATA32 eventData32; POM_NODE_LIST pNodeEntry;
DebugEntry(WorksetLockResult);
//
// First some sanity checks:
//
ASSERT((ppLockReq != NULL)); ASSERT((*ppLockReq != NULL));
pLockReq = *ppLockReq;
//
// Set up a local pointer to the workset:
//
pWSGroup = pLockReq->pWSGroup;
pWorkset = pWSGroup->apWorksets[pLockReq->worksetID]; ASSERT((pWorkset != NULL));
TRACE_OUT(("Lock %s: lock state: %hu - locked by: 0x%08x - lock count: %hu", (result == 0) ? "succeded" : "failed", pWorkset->lockState, pWorkset->lockedBy, pWorkset->lockCount));
//
// We merge the LOCKED and LOCK_GRANTED return codes at the API level:
//
if (result == OM_RC_WORKSET_LOCK_GRANTED) { result = OM_RC_WORKSET_LOCKED; }
//
// Fill in fields of event parameter and post the result:
//
eventData16.hWSGroup = pLockReq->hWSGroup; eventData16.worksetID = pLockReq->worksetID;
eventData32.correlator = pLockReq->correlator; eventData32.result = (WORD)result;
UT_PostEvent(putTask, pLockReq->putTask, // task that wants the lock
0, // i.e. ObMan or Client
OM_WORKSET_LOCK_CON, *((PUINT) &eventData16), *((LPUINT) &eventData32));
//
// Remove any node entries left hanging off the lockReqCB:
//
pNodeEntry = (POM_NODE_LIST)COM_BasedListFirst(&(pLockReq->nodes), FIELD_OFFSET(OM_NODE_LIST, chain)); while (pNodeEntry != NULL) { COM_BasedListRemove(&pNodeEntry->chain); UT_FreeRefCount((void**)&pNodeEntry, FALSE);
pNodeEntry = (POM_NODE_LIST)COM_BasedListFirst(&(pLockReq->nodes), FIELD_OFFSET(OM_NODE_LIST, chain)); }
//
// Remove the lock request itself from the list and free the memory:
//
COM_BasedListRemove(&pLockReq->chain); UT_FreeRefCount((void**)&pLockReq, FALSE);
*ppLockReq = NULL;
DebugExitVOID(WorksetLockResult); }
//
// WorksetUnlock(...)
//
void WorksetUnlock ( PUT_CLIENT putTask, POM_WSGROUP pWSGroup, POM_WORKSET pWorkset ) { DebugEntry(WorksetUnlock);
TRACE_OUT(("Unlocking workset %u in WSG %d for TASK 0x%08x", pWorkset->worksetID, pWSGroup->wsg, putTask));
TRACE_OUT((" lock state: %hu - locked by: 0x%08x - lock count: %hu", pWorkset->lockState, pWorkset->lockedBy, pWorkset->lockCount));
//
// Check the workset lock state
//
if ((pWorkset->lockState != LOCKED) && (pWorkset->lockState != LOCKING)) { ERROR_OUT(("Unlock error for workset %u in WSG %d - not locked", pWorkset->worksetID, pWSGroup->wsg)); DC_QUIT; }
//
// If this workset is "multiply locked" (i.e. locked more than one
// time by the same task), then all we want to do is decrement the lock
// count. Otherwise, we want to release the lock.
//
pWorkset->lockCount--;
if (pWorkset->lockCount == 0) { TRACE_OUT(("Lock count now 0 - really unlocking"));
WorksetUnlockLocal(putTask, pWorkset);
QueueUnlock(putTask, pWSGroup->pDomain, pWSGroup->wsGroupID, pWorkset->worksetID, pWSGroup->channelID, pWorkset->priority); }
DC_EXIT_POINT: DebugExitVOID(WorksetUnlock); }
//
// WorksetUnlockLocal(...)
//
void WorksetUnlockLocal ( PUT_CLIENT putTask, POM_WORKSET pWorkset ) { DebugEntry(WorksetUnlockLocal);
//
// To unlock a workset, we
//
// - check that it's not already unlocked
//
// - check that the lock count is zero, so we can now unlock it
//
// - set the lock fields in the workset record
//
// - post an OM_WORKSET_UNLOCK_IND to all Clients with the workset
// open.
//
if (pWorkset->lockState == UNLOCKED) { WARNING_OUT(("Workset %hu is already UNLOCKED!", pWorkset->worksetID)); DC_QUIT; }
ASSERT((pWorkset->lockCount == 0));
pWorkset->lockedBy = 0; pWorkset->lockState = UNLOCKED;
WorksetEventPost(putTask, pWorkset, PRIMARY | SECONDARY, OM_WORKSET_UNLOCK_IND, 0);
DC_EXIT_POINT: DebugExitVOID(WorksetUnlockLocal); }
//
// QueueUnlock(...)
//
UINT QueueUnlock ( PUT_CLIENT putTask, POM_DOMAIN pDomain, OM_WSGROUP_ID wsGroupID, OM_WORKSET_ID worksetID, NET_UID destination, NET_PRIORITY priority ) { POMNET_LOCK_PKT pUnlockPkt; UINT rc = 0;
DebugEntry(QueueUnlock);
//
// Allocate memory for the message, fill in the fields and queue it:
//
pUnlockPkt = (POMNET_LOCK_PKT)UT_MallocRefCount(sizeof(OMNET_LOCK_PKT), TRUE); if (!pUnlockPkt) { rc = UT_RC_NO_MEM; DC_QUIT; }
pUnlockPkt->header.messageType = OMNET_UNLOCK; pUnlockPkt->header.sender = pDomain->userID;
pUnlockPkt->wsGroupID = wsGroupID; pUnlockPkt->worksetID = worksetID;
//
// Unlock messages go at the priority of the workset involved. If this
// is OBMAN_CHOOSES_PRIORITY, then all bets are off and we send them
// TOP_PRIORITY.
//
if (priority == OM_OBMAN_CHOOSES_PRIORITY) { priority = NET_TOP_PRIORITY; }
rc = QueueMessage(putTask, pDomain, destination, priority, NULL, NULL, NULL, // no object
(POMNET_PKT_HEADER) pUnlockPkt, NULL, // no object data
TRUE); if (rc != 0) { DC_QUIT; }
DC_EXIT_POINT: if (rc != 0) { ERROR_OUT(("ERROR %d in FindInfoObject"));
if (pUnlockPkt != NULL) { UT_FreeRefCount((void**)&pUnlockPkt, FALSE); } }
DebugExitDWORD(QueueUnlock, rc); return(rc); }
//
//
// DEBUG ONLY FUNCTIONS
//
// These functions are debug code only - for normal compilations, they are
// #defined to nothing.
//
#ifdef _DEBUG
//
// CheckObjectCount(...)
//
void CheckObjectCount ( POM_WSGROUP pWSGroup, POM_WORKSET pWorkset ) { POM_OBJECT pObj; UINT count;
DebugEntry(CheckObjectCount);
count = 0;
pObj = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain));
while (pObj != NULL) { ValidateObject(pObj);
if (!(pObj->flags & DELETED)) { count++; }
pObj = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObj, FIELD_OFFSET(OM_OBJECT, chain)); }
ASSERT((count == pWorkset->numObjects));
TRACE_OUT(("Counted %u items in workset %u in WSG %d, agrees with numObjects", count, pWorkset->worksetID, pWSGroup->wsg));
DebugExitVOID(CheckObjectCount); }
//
// CheckObjectOrder(...)
//
void CheckObjectOrder ( POM_WORKSET pWorkset ) { POM_OBJECT pObjThis; POM_OBJECT pObjNext; BOOL orderIsGood = TRUE;
DebugEntry(CheckObjectOrder);
//
// This function checks that objects in the specified workset have been
// correctly positioned. The correct order of objects is one where
//
// - all FIRST objects are before all LAST objects
//
// - the position stamps of the FIRST objects decrease monotonically
// from the start of the workset onwards
//
// - the position stamps of the LAST objects decrease monotonically
// from the end of the workset backwards.
//
//
//
// This can be represented grahpically as follows:
//
// * *
// * * * *
// * * * * * *
// * * * * * * * *
// * * * * * * * * * *
// * * * * * * * * * * * *
//
// F F F F F F L L L L L L
//
// ...where taller columns indicate later sequence stamps and 'F' and
// 'L' indicate the FIRST or LAST objects.
//
//
//
// The way we test for correct order is to compare each adjacent pair of
// objects. If the overall order is correct, the for each pair of
// objects where A immediately precedes B, one of the following is true:
//
// - both are FIRST and B has a lower sequence stamp than A
//
// - A is FIRST and B is LAST
//
// - both are LAST and A has a lower sequence stamp than B.
//
pObjThis = (POM_OBJECT)COM_BasedListFirst(&(pWorkset->objects), FIELD_OFFSET(OM_OBJECT, chain)); if (!pObjThis) { //
// Hitting the end of the workset at any stage means order is
// correct, so quit:
//
DC_QUIT; } pObjNext = pObjThis;
orderIsGood = TRUE;
while (orderIsGood) { pObjNext = (POM_OBJECT)COM_BasedListNext(&(pWorkset->objects), pObjNext, FIELD_OFFSET(OM_OBJECT, chain)); if (!pObjNext) { DC_QUIT; }
switch (pObjThis->position) { case FIRST: // condition 3 has failed
if (pObjNext->position == FIRST) // condition 2 has failed
{ if (!STAMP_IS_LOWER(pObjNext->positionStamp, pObjThis->positionStamp)) { ERROR_OUT(("Object order check failed (1)")); orderIsGood = FALSE; // final condition (1) has failed
DC_QUIT; } } break;
case LAST: // conditions 1 and 2 have failed
if ((pObjNext->position != LAST) || (!STAMP_IS_LOWER(pObjThis->positionStamp, pObjNext->positionStamp))) { ERROR_OUT(("Object order check failed (2)")); orderIsGood = FALSE; // final condition (3) has failed
DC_QUIT; } break;
default: ERROR_OUT(("Reached default case in switch statement (value: %hu)", pObjThis->position)); break; }
pObjThis = pObjNext; }
DC_EXIT_POINT: if (!orderIsGood) { ERROR_OUT(("This object (handle: 0x%08x - ID: 0x%08x:0x%08x) " "has position stamp 0x%08x:0x%08x (position %s)", pObjThis, pObjThis->objectID.creator, pObjThis->objectID.sequence, pObjThis->positionStamp.userID, pObjThis->positionStamp.genNumber, (pObjThis->position == LAST) ? "LAST" : "FIRST"));
ERROR_OUT(("This object (handle: 0x%08x - ID: 0x%08x:0x%08x) " "has position stamp 0x%08x:0x%08x (position %s)", pObjNext, pObjNext->objectID.creator, pObjNext->objectID.sequence, pObjNext->positionStamp.userID, pObjNext->positionStamp.genNumber, (pObjNext->position == LAST) ? "LAST" : "FIRST"));
ERROR_OUT(("Object order check failed for workset %u. " "See trace for more details", pWorkset->worksetID)); }
TRACE_OUT(("Object order in workset %u is correct", pWorkset->worksetID));
DebugExitVOID(CheckObjectOrder); }
#endif // _DEBUG
//
// OMMapNameToFP()
//
OMFP OMMapNameToFP(LPCSTR szFunctionProfile) { int fp;
DebugEntry(OMMapNameToFP);
for (fp = OMFP_FIRST; fp < OMFP_MAX; fp++) { if (!lstrcmp(szFunctionProfile, c_aFpMap[fp].szName)) { // Found it
break; } }
//
// Note that OMFP_MAX means "not found"
//
DebugExitDWORD(OMMapNameToFP, fp); return((OMFP)fp); }
//
// OMMapFPToName()
//
// This returns a data pointer of the FP name to the caller. The caller
// can only copy it or compare it; it may not write into or otherwise
// modify/hang on to the pointer.
//
LPCSTR OMMapFPToName(OMFP fp) { LPCSTR szFunctionProfile;
DebugEntry(OMMapFPToName);
ASSERT(fp >= OMFP_FIRST); ASSERT(fp < OMFP_MAX);
szFunctionProfile = c_aFpMap[fp].szName;
DebugExitPVOID(OMMapFPToName, (PVOID)szFunctionProfile); return(szFunctionProfile); }
//
// OMMapNameToWSG()
//
OMWSG OMMapNameToWSG(LPCSTR szWSGName) { int wsg;
DebugEntry(OMMapNameToWSG);
for (wsg = OMWSG_FIRST; wsg < OMWSG_MAX; wsg++) { if (!lstrcmp(szWSGName, c_aWsgMap[wsg].szName)) { // Found it
break; } }
//
// Note that OMWSG_MAX means "not found"
//
DebugExitDWORD(OMMapNameToWSG, wsg); return((OMWSG)wsg); }
//
// OMMapWSGToName()
//
LPCSTR OMMapWSGToName(OMWSG wsg) { LPCSTR szWSGName;
DebugEntry(OMMapWSGToName);
ASSERT(wsg >= OMWSG_FIRST); ASSERT(wsg < OMWSG_MAX);
szWSGName = c_aWsgMap[wsg].szName;
DebugExitPVOID(OMMapWSGToName, (PVOID)szWSGName); return(szWSGName); }
|