;***************************************************** ;* ;* Queue Routines ;* ;***************************************************** ; Each queue system entry point is mutually exclusive ; of the other queue system entry points. ; All the routines called in QUE2.RTM are ; subroutines of the entry points in QUE1.RTM and ; thus are called only when the process has ownership ; of the q system. ; If a process is already in the queue system another process ; will wait until the first process calls Q_UNSYNC. ; This allows us to keep interrupts on while using shared variables. ; To protect against terminating and potentially losing a QD, ; a message or neglecting to wake up a DQing or NQing ; process; a "no abort region" is entered whenever ; we obtain the queue system. In addition if we assign ; the queue system to a DQing or NQing process, the new owner ; of the q system is forced into a "no abort region". This ; makes the environment of the waking NQing or DQing process appear ; as if it never went to sleep. ; A note for the uninitiated: DQing is short for "Decoding from a Queue" ; or reading from a queue. NQing is short for "eNcoding to a Queue" ; or writing to a queue. ;=========== ======================= makeq_entry: ; Create a System Queue ;=========== ======================= ; input : U_WRKSEG:DX = address of QD to create ; output: BX = 0 if okay , 0ffffh if error ; CX = error Code call q_sync ;obtain q system, BX,DX preserved call getqdaddr ;get QD and q buffer jcxz qm_gotqd jmp qm_err qm_gotqd: ;DI->QD in SYSDAT ;make sure this queue doesn't ;already exist qm_chk: push es ;save UDA mov es,sysdat mov si,(offset qlr)-q_link qm_nxt: mov si,q_link[si] ;go down QLR to see if QD already test si,si ! jz qm_go ;exists push di ! push si ;save new QD and QLR ptr mov cx,qnamsiz/2 add di,q_name ;ES:DI->QPB.NAME add si,q_name ;DS:SI->QPB.NAME repe cmpsw pop si ! pop di ;restore QLR ptr and new QD jne qm_nxt ;no match try next QD pop es ;names match, restore UDA call remqd ;QD pointed to by DS:DI mov cx,e_q_inuse jmps qm_err qm_go: ;no match - alright to make this q pop es ;ES=UDA xor dx,dx ;initialize the QD mov q_dq[di],dx mov q_nq[di],dx mov q_msgcnt[di],dx mov q_msgout[di],dx mov ax,qlr ;put QD on QLR mov q_link[di],ax mov qlr,di xor bx,bx ;return success jmps qm_ret qm_err: mov bx,0ffffh qm_ret: jmp q_unsync ;release q system; BX,CX preserved ;=========== ====================== openq_entry: ; Find an active Queue ;=========== ====================== ; input: U_WRKSEG:DX = address of QPB ; ; output: sets QPB.QADDR to QD offset in SYSDAT ; BX = 0 if okay, 0ffffh if not ; CX = error Code call q_sync ;obtain q system; BX,DX preserved push es ;save UDA mov es,u_wrkseg mov si,(offset qlr)-q_link mov di,dx ;ES:DI->QPB qo_nqd: mov si,q_link[si] ;go down QLR until QD name test si,si ! jnz qo_cmp ;matches QPB name or end of QLR pop es ;ES=UDA mov cx,e_no_queue ;found end of QLR - can't open jmps qo_err qo_cmp: push di ! push si ;compare names mov cx,qnamsiz/2 add si,q_name ;DS:SI->QD.NAME add di,qpb_name ;ES:DI->QPB.NAME repe cmpsw pop si ! pop di ;restore QD,QPB jne qo_nqd ;try next QD if no match test q_flags[si],qf_hide ;names match jz noprot ;check for protection: mov bx,rlr ;must be system process to test p_flag[bx],pf_sys ;open q with QF_HIDE set jnz noprot pop es ;ES=UDA mov cx,e_q_protected ;QF_HIDE and not SYS PD jmps qo_err noprot: mov es:qpb_qaddr[di],si ;write the QD offset into pop es ;ES=UDA xor bx,bx ;the QPB_QADDR field jmps qo_ret ;to open the q qo_err: ;CX=error code mov bx,0ffffh qo_ret: call q_unsync ;release q system; BX,CX preserved ret ;============= ======================= deleteq_entry: ; Delete a System Queue ;============= ======================= ; Takes QD off the QLR and place it in the QUL ; input: U_WRKSEG:DX = offset of QPB ; output: BX = 0 if ok, 0ffffh if error ; CX = error code mov ax,es ;save UDA mov es,u_wrkseg ;get QD address from user's mov di,dx ;QPB mov di,es:qpb_qaddr[di] ;DS:DI->QD (unverified) mov es,ax ;ES=UDA ;Check for KEEP, SYS Flags ;DS:DI->QD test q_flags[di],qf_keep jnz qd_err1 test q_flags[di],qf_hide jz qd_ok1 mov bx,rlr ;if hide flag then test p_flag[bx],pf_sys ;SYS flag must be on in PD jnz qd_ok1 qd_err1: mov cx,e_q_protected mov bx,0ffffh ret qd_ok1: push di ;DI->QD call q_sync ;obtain q system; BX,DX preserved pop di mov ax,q_dq[di] ;if any process is NQing or mov dx,q_nq[di] ;DQing we can't delete or dx,ax ! mov cx,e_q_inuse jnz qd_err2 mov bx,(offset qlr)-q_link qd_nqd: ;look for QD on QLR mov si,q_link[bx] ;DI=QD to delete test si,si ! jz qd_noq cmp si,di ! je qd_found mov bx,si ! jmps qd_nqd ;try next queue qd_found: mov ax,q_link[di] ;found the queue, remove mov q_link[bx],ax ;it from QLR call remqd xor bx,bx ;return success jmps qd_ret qd_noq: mov cx,e_no_queue qd_err2: mov bx,0ffffh qd_ret: jmp q_unsync ;release q system ;ret ;BX,CX preserved ;=========== ============ readq_entry: ; Read Queue ;=========== ============ xor al,al ! jmps readq ;============ creadq_entry: ; Conditional Read Queue ;============ mov al,0ffh ; jmps readq readq: ; Read message from queue ;----- ------------------------- ; If no buffer is available the process ; making an unconditional READQ is placed into the DQ list. ; ; input: U_WRKSEG:DX = QPB ; AL = 0 if unconditional ; <> 0 if not ; output: BX = 0 if okay ; 0ffffh if error ; CX = Error Code push ax ;save cond code call q_sync ;get q system; BX,DX preserved call queverify ;is the QPB valid ? pop ax ;AL=cond code mov si,dx ;U_WRKSEG:SI->QPB jcxz qr_ver ;CX=0 QPB is ok jmp qr_err ;CX=error code from qr_ver: ;queverify push es ;save UDA mov es,u_wrkseg ;ES:SI->QPB mov bx,es:qpb_qaddr[si] ;BX->QD cmp q_msgcnt[bx],0 ;is there a msg to read ? jne qr_readit qr_wait: pop es ;ES=UDA test al,al ! jz qr_wait1 ;conditional read if AL <> 0 mov cx,e_q_empty jmps qr_err qr_wait1: ;ES=UDA push bx ! push si ;QD, QPB lea dx,q_dq[bx] ;DX=addr of DQ List mov bl,ps_dq ;sleep status=DQ call q_wait ;wait for a DQ ;we now own the q system ;and cannot be aborted pop si ! pop bx ;QPB, QD push es ;save UDA mov es,u_wrkseg qr_readit: mov di,es:qpb_buffptr[si] ;ES:[DI]->user's queue buffer mov cx,q_msglen[bx] ;ES=U_WRKSEG test cx,cx ! jnz qr_lmsg ;check message length xor ax,ax test q_flags[bx],qf_mx ;msglen=0, check for MX q jz qr_end mov ax,rlr ;its a MX queue mov q_buf[bx],ax ;BUF = PD addr of owner xor ax,ax jmps qr_end qr_lmsg: ;msglen > 0 mov ax,q_msgout[bx] ! push ax ;MSGOUT is # of message to read mul cx ! add ax,q_buf[bx] ;compute its start in buffer mov si,ax rep movsb ;move to QPB buffer pop ax ! inc ax ;set MSGOUT to next message cmp ax,q_nmsgs[bx] ;circular buffers, so jne qr_end ;check for wrap around xor ax,ax qr_end: pop es ;ES=UDA mov q_msgout[bx],ax dec q_msgcnt[bx] lea dx,q_nq[bx] call q_assign_sync ;give the queue system to ;first NQing process if any, call q_unsync ;release q system if we still ;own it, exit no abort region xor bx,bx ;indicate success ret qr_err: call q_unsync ;release q system, exit no abort ;region, BX,CX preserved mov bx,0ffffh ;indicate error ret ;============ ============= writeq_entry: ; Write Queue ;============ ============= xor al,al ! jmps writeq ;============= ========================= cwriteq_entry: ; conditional Write Queue ;============= ========================= mov al,0ffh ;jmps writeq writeq: ; Write message to queue ;------ ------------------------ ; If no buffer is available when making an unconditional ; WRITEQ call, the calling process is placed into the NQ list. ; ; input: U_WRKSEG:DX = QPB ; AL = 0 if unconditional ; <> 0 if not ; output: BX = 0 if okay ; 0ffffh if error ; CX = Error Code push ax ;save cond code call q_sync ;get queue system ;BX,DX preserved call queverify ;is the QPB valid ? pop ax ;AL = cond code mov di,dx ;U_WRKSEG:DI->QPB jcxz qw_ver ;CX=0 QPB is ok jmp qw_err ;CX=error code from qw_ver: ;queverify push es ;save UDA mov es,u_wrkseg ;ES:DI->QPB mov bx,es:qpb_qaddr[di] ;BX->QD mov cx,q_msgcnt[bx] cmp cx,q_nmsgs[bx] ;is there a buffer to write ? jne qw_writeit qw_wait: pop es ;ES=UDA test al,al ! jz qw_wait1 ;conditional read if AL <> 0 mov cx,e_q_full jmps qw_err qw_wait1: ;ES=UDA push bx ! push di ;save QD, QPB lea dx,q_nq[bx] ;DX=addr of NQ List mov bl,ps_nq ;sleep status=NQ call q_wait ;sleep on NQ List ;q system is assigned to us, ;we are in no abort region ;by DQing process that woke us pop di ! pop bx ;QPB,QD push es ;save UDA mov es,u_wrkseg qw_writeit: mov si,es:qpb_buffptr[di] ;U_WRKSEG:[SI]->user's queue buffer mov cx,q_msglen[bx] ;ES=U_WRKSEG test cx,cx ! jnz qw_lmsg ;check message length test q_flags[bx],qf_mx ;msglen=0, check for MX q jz qw_end xor ax,ax mov q_buf[bx],ax ;its a MX queue jmps qw_end ;BUF = 0 qw_lmsg: ;msglen > 0 mov ax,q_msgout[bx] add ax,q_msgcnt[bx] ;AX = # of message to write cmp ax,q_nmsgs[bx] ;check for wrap around jb qw_move sub ax,q_nmsgs[bx] qw_move: mul cx ! add ax,q_buf[bx] ;compute its start in buffer mov di,ax ;DI offset of new msg in buffer mov ax,ds ! mov dx,es mov es,ax ! mov ds,dx ;ES=SYSDAT, DS=U_WRKSEG rep movsb ;move to QPB buffer mov ax,es ! mov ds,ax ;DS=SYSDAT qw_end: pop es ;ES=UDA inc q_msgcnt[bx] ;one more message in the queue lea dx,q_dq[bx] ;wake DQing process if any call q_assign_sync call q_unsync ;release q system if we still xor bx,bx ;own it, exit no abort region ret ;BX=0: success qw_err: call q_unsync ;release q system; exit no abort ;region BX,CX preserved mov bx,0ffffh ;indicate error ret q_wait: ;wait on DQ or NQ list ;------ ; entry: DX = list to wait on ; BL = sleep status ; calling process owns queue system through ; a call to q_sync ; Queue message or buffer space is not available. ; Give up queue system and sleep on DQ or NQ list, but ; do not allow any other process to read or write to a queue ; until we get on the sleep list. pushf ! cli ;keep abort spec from running ... push bx ! push dx ;save sleep status, list address call q_unsync ;does not go to dispatcher, we can be pop dx ! pop bx ;aborted once on sleep list call sleep_entry ;go to dispatcher popf ;come back after q_assign call ret ;Q_ASSIGN: wakes us up when resource ;is ready q_sync: ;obtain ownership of q system ;------ ; entry: interrupts should be on ; ES=UDA ; exit: we own the queue system for make,open,read,write,delete ; queue operations, interrupts unchanged ; BX,DX preserved - usually entry parameters push bx ! push dx call no_abort_entry ;we cannot abort while in the queue system mov bx,offset q_spb call sync_entry pop dx ! pop bx ret q_unsync: ;release the queue system ;-------- ;to other processes ; entry: interrupts can be or off ; exit: interrupts unchanged, ; have not gone to dispatcher, ; BX,CX preserved - usually return codes push bx ! push cx mov bx,offset q_spb call unsync_entry ;wait for queue system call ok_abort_entry ;allow calling PD to be terminated pop cx ! pop bx ret q_assign_sync: ;------------- ; entry: DX = address of DQ or NQ list to give the queue ; exit: none ; Assign is used so a process is forced to read or write ; after DQing or NQing. Otherwise an awakening DQing or NQing ; process could be deleted, leaving a message or buffer space ; available with other processes still DQing or NQing. ; This message or buffer space would never be reclaimed. ; To prevent this situation, the waking DQing or NQing process ; is kept from aborting and assigned the QSPB. Note ; it is ok for a process to be aborted while it is on the NQ or ; DQ list before this routine is called. pushf ! cli ;keep abort spec from terminating mov bx,dx ;waking process mov dx,[bx] ;first process on list test dx,dx ;is there a process to wake? jz qa_ret push bx ;save list root call no_abort_spec_entry ;can not abort while in queue pop dx ;system popf ;interrupts are ok now - mov bx,offset q_spb ;present owner and next owner jmp assign_sync_entry ;have TEMPKEEP on qa_ret: popf ret