/*static char *SCCSID = "@(#)subq2.c    6.2 90/12/05";*/
/****************************************************************************/
/*                                                                          */
/*                       IBM/Microsoft Confidential                         */
/*                                                                          */
/*                 Copyright (c) IBM Corporation  1987, 1990                */
/*                 Copyright (c) Microsoft Corp.  1987, 1990                */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/

#include        <dos.h>
#include        <doscalls.h>
#include        <error.h>
#include        <error2.h>
#include        <ipcqueue.h>

/********************** START OF SPECIFICATIONS **********************/
/*                                                                   */
/* SOURCE FILE NAME: subq2.c                                         */
/*                                                                   */
/* DESCRIPTIVE NAME: Common Subroutines for IPC Queues which are     */
/*                   frequently used                                 */
/*                                                                   */
/* COPYRIGHT: (TOP MODULE ONLY)                                      */
/*                                                                   */
/* STATUS: RELEASE 1 LEVEL 0                                         */
/*                                                                   */
/* FUNCTION:                                                         */
/*                                                                   */
/* NOTES:                                                            */
/*    DEPENDENCIES:                                                  */
/*    RESTRICTIONS:                                                  */
/*    PATCH LABEL: (TOP MODULE ONLY)                                 */
/*                                                                   */
/* ENTRY POINTS:                                                     */
/*             ScanQname                                             */
/*             Q_Delete                                              */
/*             L_Find                                                */
/*             Q_Empty                                               */
/*             Q_Sleep                                               */
/*                                                                   */
/* INTERNAL REFERENCES:                                              */
/*    ROUTINES:                                                      */
/*                                                                   */
/* EXTERNAL REFERENCES:                                              */
/*             strcmp:near                                           */
/*             DOSSUBFREE:FAR                                        */
/*             DOSSEMSET:FAR                                         */
/*             RESET16EVENTSEM:FAR                                   */
/*             DOSSEMWAIT:FAR                                        */
/*             FuncFree:near                                         */
/*             FuncInit:near                                         */
/*                                                                   */
/*********************** END OF SPECIFICATIONS ***********************/

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME:    ScanQname                                    */
/*                                                                  */
/* DESCRIPTIVE NAME:   ScanQname                                    */
/*                                                                  */
/* FUNCTION:   Searches the chain of queues for the desired q name  */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT:        ScanQname                                    */
/*    LINKAGE:         near at pl 2                                 */
/*                                                                  */
/* INPUT:                                                           */
/*             DD       address queue name                          */
/*             DD       address of queue address                    */
/*             DD       address of prev queue address               */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*             @offset         desired queue                        */
/*             @back_chn       previous queue                       */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             offset = 0 (no such name)                            */
/*             'QUEUE NAME DOES NOT EXIST'                          */
/*                                                                  */
/* EFFECTS:                                                         */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*              strcmp:near                                         */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/*      ScanQname:                                                      */
/*                                                                      */
/*      if (static.fst_q != NULL) then                                  */
/*        offset = static.fst_q                                         */
/*        while (offset.q_name != queue name & offset.q_chain != 0) do  */
/*          back_chn = offset                                           */
/*          offset = offset.q_chain                                     */
/*        endwhile                                                      */
/*        qoffset = offset                                              */
/*        qprev = prev                                                  */
/*      endif                                                           */
/*      if (offset = NULL) then                                         */
/*        set error code        'QUEUE NAME DOES NOT EXIST'             */
/*      else                                                            */
/*        set good return code                                          */
/*      endif                                                           */
/*      return                                                          */

unsigned int
ScanQname(Qnam,qoffset,qprev)
char    far     *Qnam;
struct  Q_ENTRY far * far *qoffset;
struct  Q_ENTRY far * far *qprev;
{
        extern  struct  STATDATA        statseg;
        struct  Q_ENTRY far *off;
        struct  Q_ENTRY far *prev;
        register unsigned int    rc = 0xffff;

        if (statseg.fst_q != NULL)
        {
                off = statseg.fst_q;
                while (((rc = strcmp((char far *) (*off).q_name,Qnam)) != NULL) && ((*off).q_chain != NULL))
                {
                        prev = off;
                        off = (*off).q_chain;
                }
                *qoffset = off;
                *qprev = prev;
        }
        if (rc != NULL)
                rc = ERROR_QUE_NAME_NOT_EXIST;
        return(rc);
}

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME:    Q_Delete                                     */
/*                                                                  */
/* DESCRIPTIVE NAME:   Q_Delete                                     */
/*                                                                  */
/* FUNCTION:   Deletes a queue                                      */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT:        Q_Delete                                     */
/*    LINKAGE:         near  at pl 2                                */
/*                                                                  */
/* INPUT:                                                           */
/*             DD      offset of queue entry                        */
/*             DD      offset of previous queue entry               */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'SYS ERROR'                                          */
/*                                                                  */
/* EFFECTS:    Deletes all elements belonging to the queue,         */
/*             adjusts any links to this queue, adjusts the         */
/*             queue, and if no links are present, removes          */
/*             the queue.                                           */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             El_Delete:NEAR                                       */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             DOSSUBFREE:FAR                                       */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/*      Q_Delete:                                                   */
/*                                                                  */
/*      if (offset.q_fst != NULL) then                              */
/*        call El_Delete                                            */
/*        if (El_Delete returns in error) then                      */
/*          return error            'SYS ERROR'                     */
/*        endif                                                     */
/*      endif                                                       */
/*      set offset = static.fst_lnk                                 */
/*      while (offset != NULL)                                      */
/*        if (offset.l_qhndl = qu_entry.q_hndl) then                */
/*          set l_closed in offset.l_flags                          */
/*          set links in qu_entry.q_flags                           */
/*        endif                                                     */
/*        offset = offset.l_chain                                   */
/*      endwhile                                                    */
/*      if (error) then                                             */
/*        return with error                                         */
/*      endif                                                       */
/*      initialize exitlist action variables                        */
/*      set exitlist action flag = ACT_DEL_QUE                      */
/*      if (static.fst_q != qu_entry) then                          */
/*        back_chn.q_chain = qu_entry.q_chain                       */
/*      else                                                        */
/*        static.fst_q = qu_entry.q_chain                           */
/*      endif                                                       */
/*      free memory block located at qu_entry via DosSubFree        */
/*      if (DosSubFree returns in error) then                       */
/*        set error           'SYS ERROR'                           */
/*      endif                                                       */
/*      set exitlist action flag off                                */
/*      return                                                      */

unsigned int
Q_Delete(qoffset,qprev)
struct  Q_ENTRY far *qoffset;
struct  Q_ENTRY far *qprev;
{
        extern  struct  STATDATA        statseg;
        extern  unsigned char   action;
        extern  struct  Q_ENTRY far *prev_fst_q;
        extern  struct  Q_ENTRY far *que1;
        extern  struct  Q_ENTRY far *que2;
        extern  struct  Q_ENTRY far *que3;

        struct  Q_ENTRY far     *qrec;
        struct  L_ENTRY far     *lprev;
        struct  L_ENTRY far     *lrec;
        struct  L_ENTRY far     *lnext;
        register unsigned int   qhandl;
        register unsigned int   rc;

        if ((*qoffset).q_fst != NULL)
        {
                if ((El_Delete(qoffset)) != NO_ERROR)
                        return(ERROR_SYS_INTERNAL);
        }
        lrec = statseg.fst_link;
        lprev = NULL;
        rc = NO_ERROR;
        while ((lrec != NULL) && (rc == NO_ERROR))
        {
                qhandl = (*qoffset).q_hndl;
                if ((*lrec).l_qhndl == qhandl)
                {
                        lnext = (*lrec).l_chain;
                        rc = L_Remove(lrec,lprev,qhandl);
                        lrec = lnext;
                }
                else
                {
                        lprev = lrec;
                        lrec = (*lrec).l_chain;
                }
        }
        if (rc != NO_ERROR)
                return(rc);
        prev_fst_q = statseg.fst_q;
        que1 = qoffset;
        que2 = qprev;
        que3 = (*qoffset).q_chain;
        action = ACT_DEL_QUE;
        if (statseg.fst_q != qoffset)
                (*qprev).q_chain = (*qoffset).q_chain;
        else
                statseg.fst_q = (*qoffset).q_chain;
        if ((DOSSUBFREE(FP_SEG(qoffset),FP_OFF(qoffset),(sizeof(struct Q_ENTRY)+farstrlen((*qoffset).q_name)))) != NO_ERROR)
                rc = ERROR_SYS_INTERNAL;
        action = 0;
        return(rc);
}

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME:    L_Find                                       */
/*                                                                  */
/* DESCRIPTIVE NAME:   L_Find                                       */
/*                                                                  */
/* FUNCTION:   Find the appropriate link for the requesting process */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT:        L_Find                                       */
/*    LINKAGE:         near at pl 2                                 */
/*                                                                  */
/* INPUT:                                                           */
/*             DW      queue handle                                 */
/*             DD      offset                                       */
/*             DD      back_chn                                     */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*             offset                                               */
/*             back_chn                                             */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'LINK NOT FOUND'                                     */
/*                                                                  */
/* EFFECTS:                                                         */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/*      L_Find:                                                     */
/*                                                                  */
/*      set offset = static.fst_lnk                                 */
/*      set back_chn = 0                                            */
/*      while (offset != 0 and                                      */
/*            (offset.l_pid != static.st_pid or                     */
/*            (offset.l_qhndl != requested queue handle)) do        */
/*        back_chn = offset                                         */
/*        offset = offset.l_chain                                   */
/*      endwhile                                                    */
/*      if (offset = 0) then                                        */
/*        return with         'LINK NOT FOUND'                      */
/*      else                                                        */
/*        loff = loffset                                            */
/*        lprev = prev                                              */
/*        return with         'NO ERROR'                            */
/*      endif                                                       */

unsigned int
L_Find(qhndl,loff,lprev)
register unsigned int qhndl;
struct  L_ENTRY far * far *loff;
struct  L_ENTRY far * far *lprev;
{
        extern  struct  STATDATA        statseg;

        struct  L_ENTRY far     *ptroff;
        struct  L_ENTRY far     *ptrprev;
        register unsigned int rc;

        ptroff = statseg.fst_link;
        ptrprev = NULL;
        while ((ptroff != NULL) &&
                (((*ptroff).l_pid != statseg.st_pid) ||
                ((*ptroff).l_qhndl != qhndl)))
        {
                ptrprev = ptroff;
                ptroff = (*ptroff).l_chain;
        }
        if (ptroff != NULL)
        {
                *loff = ptroff;
                *lprev = ptrprev;
                return(NO_ERROR);
        }
        else
                return(ERROR_QUE_LINK_NOT_FOUND);
}

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME:    Q_Empty                                      */
/*                                                                  */
/* DESCRIPTIVE NAME:   Q_Empty                                      */
/*                                                                  */
/* FUNCTION:   Performs semaphore setting, and waiting, necessarry  */
/*             when a queue is empty during a readq or peekq        */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT:        Q_Empty                                      */
/*    LINKAGE:         near  at pl 2                                */
/*                                                                  */
/* INPUT:                                                           */
/*             DW      No_Wait parameter                            */
/*             DW      Semaphore parameter                          */
/*             DD      Address of Queue Entry                       */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'SYS ERROR'                                          */
/*             'QUEUE EMPTY'                                        */
/*                                                                  */
/* EFFECTS:    Sets q_semhnd in queue structure if system semaphore */
/*             supplied by 16-bit call, may put thread to sleep,    */
/*             thereby freeing the queue package semaphore and      */
/*             other entries in statseg data structure.             */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             Q_Sleep:NEAR                                         */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             DOSSEMSET:FAR                                        */
/*             RESET16EVENTSEM:FAR                                  */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/*      Q_Empty:                                                    */
/*                                                                  */
/*      initialize return code to no error                          */
/*      if (no wait) then                                           */
/*        use the supplied system semaphore                         */
/*      else                                                        */
/*        use the local ram semaphore                               */
/*      endif                                                       */
/*      if (no wait & no system semaphore supplied) then            */
/*        set error code        'ERROR_QUE_EMPTY'                   */
/*      else                                                        */
/*        if (no wait)                                              */
/*          if (called from 16-bit code) then                       */
/*            call DosSemSet                                        */
/*          else if (called from 32-bit code) then                  */
/*            call Reset16EventSem to perform Dos32ResetEventSem on */
/*                      the wait semaphore                          */
/*        if (Reset16EventSem or DosSemSet rets ok and no wait) then*/
/*          set error code      'ERROR_QUE_EMPTY'                   */
/*        else                                                      */
/*          if (error on Reset16EventSem or DosSemSet) then         */
/*            set error code    'ERROR_SYS_INTERNAL'                */
/*          endif                                                   */
/*        endif                                                     */
/*      endif                                                       */
/*      if (wait & no error) then                                   */
/*        while (queue count = 0 & no error)                        */
/*          call DosSemSet                                          */
/*          call Q_Sleep                                            */
/*          if (Q_Sleep returns in error) then                      */
/*            set error code    'ERROR_SYS_INTERNAL'                */
/*          endif                                                   */
/*        endwhile                                                  */
/*      endif                                                       */
/*      return                                                      */

extern unsigned far RESET16EVENTSEM(unsigned long);

unsigned int
Q_Empty(nowait,semhand,q_off)
register unsigned char   nowait;
unsigned long   semhand;
struct  Q_ENTRY far     *q_off;
{
        unsigned long   wait_sem;
        register unsigned int    rc;


        rc = NO_ERROR;                          /* initialize return value */
        if (nowait == NO_WAIT)
        {
                if (((*q_off).q_flags & Q_BIT32) == 0)
                  (*q_off).q_semhnd = semhand;
                wait_sem = semhand;
        }
        else
                wait_sem = (unsigned long) &((*q_off).q_ramsem);
        if ((nowait == NO_WAIT) && (semhand == NULL))
                rc = ERROR_QUE_EMPTY;
        else
        {
          if (nowait == NO_WAIT)                 /* if no wait            */
          {
            if (((*q_off).q_flags & Q_BIT32)==0) /* if 16-bit code        */
            {
              rc = DOSSEMSET(wait_sem);          /* set semaphore         */
            }
            else                                 /* else if 32-bit code   */
            {
              rc = RESET16EVENTSEM(wait_sem);    /* reset event sem       */
              if (rc == ERROR_ALREADY_RESET)    /* if the sem was not set*/
              {
                rc = NO_ERROR;                   /* it is not an error    */
              } /* endif */
            } /* endif */
          }
//
// Moved setting of queue's RAM semaphore into Q_Sleep loop to avoid
// following scenario:
//     Thread 1 (not time critical) reads synchronously from queue
//     Thread 2 (time critical) reads synchronously from queue
//     Thread 3 writes to queue
//     Thread 1 is made ready, thread 2 is run because it is time critical
//     Thread 2 removes element from queue
//     Thread 1 loops because semaphore is clear and count of elements in
//         queue is zero
//
//        else                                   /* else if wait set      */
//        {
//          rc = DOSSEMSET(wait_sem);            /* set semaphore         */
//        }
//
                                                 /* if set was successful */
          if ((rc == NO_ERROR) && (nowait == NO_WAIT))
                  rc = ERROR_QUE_EMPTY;          /* return value saying   */
                                                 /*   queue is empty      */
          else if (rc != NO_ERROR)               /* otherwise             */
                  rc = ERROR_SYS_INTERNAL;       /*   return error        */
        }
        if ((nowait == WAIT) && (rc == NO_ERROR))
        {
                while ((*q_off).q_cnt == NULL)
                {
                        DOSSEMSET(wait_sem);     /* set semaphore again   */
                        if ((Q_Sleep(wait_sem)) != NO_ERROR)
                        {
                                rc = ERROR_SYS_INTERNAL;
                                break;
                        }
                }
        }
        return(rc);
}

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME: Q_Sleep                                         */
/*                                                                  */
/* DESCRIPTIVE NAME: Q_Sleep                                        */
/*                                                                  */
/* FUNCTION: Put this thread to sleep                               */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT: Q_Sleep                                             */
/*    LINKAGE:  near at pl 2                                        */
/*                                                                  */
/* INPUT:                                                           */
/*             DD       Semaphore                                   */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'SYS ERROR'                                          */
/*                                                                  */
/* EFFECTS:    Clears & Resets static semaphore, uses sem. parm.    */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             DOSSEMWAIT:FAR                                       */
/*             FuncFree:near                                        */
/*             FuncInit:near                                        */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/*                                                                  */
/*      Q_Sleep                                                     */
/*                                                                  */
/*                                                                  */
/*      call FuncFree                                               */
/*      if (FuncFree returns ok) then                               */
/*        repeat                                                    */
/*          call DosSemWait on queue entry ram semaphore            */
/*        until (return !=      'ERROR_INTERRUPT')                  */
/*        if (DosSemWait returns ok or interrupt) then              */
/*          call FuncInit                                           */
/*        endif                                                     */
/*      endif                                                       */
/*      if (error code) then                                        */
/*        set error code        'SYS ERROR'                         */
/*      endif                                                       */
/*      return                                                      */

unsigned int
Q_Sleep(sleep_sem)
unsigned long   sleep_sem;
{
        extern  struct  STATDATA        statseg;
        register unsigned int    rc;

        if ((rc = FuncFree()) == NO_ERROR)
        {
                do
                        rc = DOSSEMWAIT(sleep_sem,((unsigned long) -1));
                while (rc == ERROR_INTERRUPT);
                if (rc == NO_ERROR)
                        rc = FuncInit();
        }
        if (rc != NO_ERROR)
                rc = ERROR_SYS_INTERNAL;
        return(rc);
}
