/*----------------------------------------------------------+ | SHARE Multi-tasking Example - 1998 Winter conference | | session 5957 | +----------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include /* /* not implemented till 6.50 */ #include #include "shrtyp.h" #include "shrpro.h" #include "shrgbl.h" /*-------------------------------------------------------------------+ | | FUNCTION NAME: Log | | DESCRIPTIVE NAME: Send a print line to the log | +-------------------------------------------------------------------*/ void Log(PSZ pText) { DCB_t *pstLogDCB; /* points to print log dcb */ time_t dTime; struct tm *pTime; UCHAR abLine[133]; LOCK stLogLockElement; memset(abLine, 0, 133); time(&dTime); pTime = localtime(&dTime); format(abLine, " %04u%03u %02u:%02u:%02u ", pTime->tm_year + 1900, pTime->tm_yday + 1, pTime->tm_hour, pTime->tm_min, pTime->tm_sec); strncat(abLine, pText, 132); /* get the "Print Log Lock" before opening the file */ Lock(&stLogLockElement, &pAnchor->ulSemLog); pstLogDCB = osdcb("log", "lrecl=133,dsorg=ps,recfm=va,bufno=1,ncp=1", 0, 0); osopen(pstLogDCB, "output", 0); /* WTP(abLine); /* /* when all else fails, go back to the trusty WTO (not implemented till 6.50) */ osput(pstLogDCB, abLine, strlen(abLine)); osclose(pstLogDCB, "disp"); /* release the lock. If other subtasks are waiting, the last one in will be posted to continue */ Unlock(&pAnchor->ulSemLog); } /*-------------------------------------------------------------------+ | | FUNCTION NAME: WriteQueue | | DESCRIPTIVE NAME: Add a message to specified queue | +-------------------------------------------------------------------*/ void WriteQueue(PQUEUE pQueue, UINT uiMsgLen, PUCHAR pMsgIn) { PMSGBLOCK pMsg; ULONG ulCount; if (uiMsgLen > sizeof(pMsg->stMsg)) { exit(20); } /* get msg storage from the free pool */ retry0: _ldregs(R1 | R2 | R3 | R15, offsetof(MSGBLOCK, pMsgPrev), pAnchor->uCDS.stCDS.pMsgFirst, pAnchor->uCDS.stCDS.ulRefNum, &pAnchor->uCDS.stCDS); LR(14, 2); /* load next msg. on chain address */ AR(14, 1); /* (same) */ L(14, 0, 0+b(14)); /* (same) */ CLI(0+b(14), 0xff); /* did we get the next to last message? */ BC(7, 0, _flabel(1)); /* no - continue */ Log("Message Pool exhausted"); return; _label(1); _ldregs(0); /* Tell the compiler we're starting an assembler sequence */ LR(1, 15); /* save CDS doubleword address */ LR(15,3); /* copy for CDS */ CDS(2, 14, 0+b(1)); /* update the message pool header */ _stregs(R2, &pMsg); if (_cc() != 0) { goto retry0; } _ldregs(R1, &pAnchor->ulFree); L(2, 0, 0+b(1)); /* load free msg count */ BASR(14, 0); /* save next inst. address */ LR(3, 2); /* calc new free msg count */ BCTR(3, 0); /* (same) */ CS(2, 3, 0+b(1)); /* decrement the free message counter */ BCR(7, 14); /* if fail, try again */ /* indicate last message of the FIFO queue */ pMsg->pMsgNext = 0; /* copy the message */ memcpy(&pMsg->stMsg, pMsgIn, uiMsgLen); /* put the message on the queue */ retry: _ldregs(R1 | R3 | R15, &pQueue->pMsgLast, offsetof(MSGBLOCK, pMsgPrev), pMsg); L(2, 0, 0+b(1)); /* load old end pointer */ AR(3, 15); /* point to pMsgPrev of pMsg */ ST(2, 0, 0+b(3)); /* pMsg->pMsgPrev = pQueue->pMsgLast */ CS(2, 15, 0+b(1)); /* pQueue->pMsgLast = pMsg */ if (_cc() != 0) { goto retry; } /* wake up the receiving task */ _ldregs(R1 | R2, pQueue->uCDS.stCDS.ulCount, &pQueue->uCDS.stCDS.ulCount); BASR(14, 0); /* set retry address */ LA(3, 0, 1+b(1)); /* increment the waiting message counter */ CS(1, 3, 0+b(2)); /* update counter */ BCR(7, 14); /* if failed CS, try again */ _stregs(R3, &ulCount); /* save the number of messages on the queue*/ if (ulCount == 1) /* if first message on queue, wake up receiving task */ { POST(&pQueue->uCDS.stCDS.ulQECB, 0); } } /*-------------------------------------------------------------------+ | | FUNCTION NAME: ReadQueue | | DESCRIPTIVE NAME: Removes a message from the specified queue | +-------------------------------------------------------------------*/ void ReadQueue(PQUEUE pQueue, PUINT puiMsgLen, PUCHAR pMsgOut) { PMSGBLOCK pMsg, pMsgNext, pMsgFirst; UINT uiMsgLen; int iMaxLen; iMaxLen = sizeof(pMsg->stMsg); if (!(pQueue->uCDS.stCDS.ulQECB & POSTED)) /* if not already posted, wait */ { WAIT1(&pQueue->uCDS.stCDS.ulQECB); } retry1: if (pQueue->pMsgFirst != 0) /* if fifo list already built... */ { pMsgFirst = pQueue->pMsgFirst; /* save first msg pointer */ pMsgNext = pMsgFirst->pMsgNext; } else /* build fifo list */ { pMsg = pQueue->pMsgLast; /* point to most recently added msg */ pMsgNext = 0; /* used to mark end of chain */ while (pMsg->pMsgPrev != 0) { pMsgNext = pMsg; /* save current as prev */ pMsg = pMsg->pMsgPrev;/* bump current pointer */ pMsg->pMsgNext = pMsgNext; /* save previous message pointer */ } pMsgFirst = pMsg; /* save first fifo msg pointer */ } pQueue->pMsgFirst = pMsgNext; /* save next in fifo list as start */ if (pQueue->pMsgFirst != 0) /* if more msgs waiting */ { pQueue->pMsgFirst->pMsgPrev = 0; /* mark new end of fifo list */ } else /* we've taken off all the messages, zero out the start of the queue */ { _ldregs(R1 | R2 | R3, &pQueue->pMsgLast, pMsgFirst, 0); CS(2, 3, 0+b(1)); /* zero out the head of queue */ if (_cc() != 0) { goto retry1; } } /* reset the ecb and decrement the message count */ retry2: _ldregs(R1 | R2 | R3, &pQueue->uCDS.stCDS.ulQECB, pQueue->uCDS.stCDS.ulQECB, pQueue->uCDS.stCDS.ulCount); LR(15, 3); /* load ulCount */ LA(0, 0, 0x40+b(0)); /* set POSTED flag */ SLL(0, 24); /* (same) */ BCT(15, 0, _flabel(1)); /* if not last message, don't clear post flag*/ XR(0, 0); /* clear post flag */ _label(1); LR(14, 0); /* load ecb contents */ CDS(2, 14, 0+b(1)); /* reset ecb and decrement counter */ if (_cc() != 0) { goto retry2; } /* copy the message */ uiMsgLen = *puiMsgLen; pMsg = pMsgFirst; memcpy(pMsgOut, &pMsg->stMsg, uiMsgLen); /* return message units to the free pool */ pMsg = pMsgFirst; retry3: _ldregs(R0 | R1 | R2 | R14 | R15, pAnchor->uCDS.stCDS.ulRefNum, &pAnchor->uCDS.stCDS, pAnchor->uCDS.stCDS.pMsgFirst, pMsg, offsetof(MSGBLOCK, pMsgPrev)); LR(3, 0); /* copy access number */ ST(2, 15, 0+b(14)); /* pMsg->pMsgPrev = uCDS.stCDS.pMsgFirst */ LA(15, 0, 1+b(3)); /* update access number */ CDS(2, 14, 0+b(1)); /* update the free pool head */ if (_cc() != 0) { goto retry3; } _ldregs(R1, &pAnchor->ulFree); L(2, 0, 0+b(1)); /* load free msg count */ BASR(14, 0); /* save next inst. address */ LA(3, 0, 1+b(2)); /* calc new free msg count */ CS(2, 3, 0+b(1)); /* increment the free message counter */ BCR(7, 14); /* if fail, try again */ } /*-------------------------------------------------------------------+ | | FUNCTION NAME: Lock | | DESCRIPTIVE NAME: Enqueue a mutex semaphore | | See IBM - ESA/390 Principles of Operation | Document Number SA22-7201 | Section - | A.6.4.1 Lock/Unlock with LIFO Queuing for Contentions | +-------------------------------------------------------------------*/ void Lock(PLOCK pLock, PULONG pQueue) { pLock->ulLockECB = 0; /* Clear our lock element ECB */ _ldregs(R1 | R2, /* Load element and header address */ pLock, pQueue); LNR(0, 1); /* Force R0 negative as a flag */ XR(3, 3); /* Clear R3 for use in the CS instruction*/ BASR(14, 0); /* set address for retry */ CS(3, 0, 0+b(2)); /* Set the header to a negative value if the current header is 0 */ if (_cc() == 0) { return; /* If successful, exit */ } _ldregs(0); /* Tell the compiler we're starting an assembler sequence */ ST(3, 0, 4+b(1)); /* Save the address of the prior locker in my lock element */ CS(3, 1, 0+b(2)); /* Store our element address into the header (this time it's not negative)*/ LA(3, 0, 0+b(0)); /* Clear R3 in case we need to try the first CS again. (note that this will not reset the condition code and mess up the following comparison) */ BCR(7, 14); /* If the store was interrupted, go back to the first CS */ WAIT1(&pLock->ulLockECB); } /*-------------------------------------------------------------------+ | | FUNCTION NAME: Unlock | | DESCRIPTIVE NAME: Dequeue a mutex semaphore | | See IBM - ESA/390 Principles of Operation | Document Number SA22-7201 | Section - | A.6.4.1 Lock/Unlock with LIFO Queuing for Contentions | +-------------------------------------------------------------------*/ void Unlock(PULONG pQueue) { PECB pEcb = 0; _ldregs(R2, pQueue); /* Load queue header address */ L(1, 0, 0+b(2)); /* Load the contents of the header */ BASR(14, 0); /* set address for retry */ LTR(1, 1); /* Does the header contain a negative value? */ BC(4, 0, _flabel(1)); /* Yes, free the header and continue */ L(0, 0, 4+b(1)); /* Load the "Last in" element address */ CS(1,0,0+b(2)); /* Save the "Last in" element address in the header */ BCR(7, 14); /* If the store was interrupted, try again */ _stregs(R1, &pEcb); /* Set ECB of next waiter on queue */ POST(pEcb, 0); return; _label(1); _ldregs(0); /* Tell the compiler we're starting an assembler sequence */ XR(0, 0); /* Clear for CS */ CS(1, 0, 0+b(2)); /* Set header to 0 */ BCR(7, 14); /* If the store was interrupted, start over */ return; }