/*************************************************************************
* *
* System Call Trap Handler *
* *
* realizzato dal Gruppo 17 di Lab2 Anno Accademico 1995/96 *
* *
* Lorenzo Claudio Valerio Riccardo Emiliano *
* Coronati Lanconelli Paolini Solmi Trentini *
* *
**************************************************************************/
#include "../h/supconst.h"
#include "../h/tproc.h"
#include "../h/syscallt.h"
#include "../h/vqueue.e"
#include "../h/services.e"
#include "../h/supglobals.e"
/***
formato dei messaggi ricevuti dal sys trap handler:
A) invocazione di servizio (msg inviato dalla passup)
m_sender: caller
m_message: sate_t *
B) VSend da un altro T-process
m_sender: gestore del thread che fa la VSend
m_message: vmsg_t *
C) VReply da un altro T-process
m_sender: gestore del thread che fa la VReply
m_message: vmsg_t *
il gestore mantiene 3 code (di tipo vqueue_t):
- wait_recv: coda dei thread che hanno fatto una VSend (in attesa di una VRecv)
- wait_reply: coda dei thread che hanno fatto una VSend (in attesa di una VReply)
- wait_send: coda dei thread che hanno fatto una VRecv (in attesa di una VSend)
Ora analizziamo uno ad uno tutti i casi partendo dallo scambio di
Vmessaggi all'interno dello stesso T-process.
1A) Il gestore riceve un messaggio di tipo (A) che richiede una VSend.
Il gestore guarda nella coda wait_send se vi e` il thread destinatario
della VSend; se e` presente lo estrae dalla coda, copia il messaggio,
e lo risveglia (facendo una msgSend), infine mette il chiamante
nella coda wait_reply;
se invece non e` presente mette il thread chiamante nella coda
wait_recv.
2A) Il gestore riceve un messaggio di tipo (A) che richiede una VRecv.
Il gestore guarda nella coda wait_recv se vi e` almeno un thread
se e` stata richiesta una VRecvALL, altrimenti se vi e` il thread
indicato in R4. Nel caso vi fosse lo estrae dalla coda, copia il
messaggio, lo inserisce nella coda wait_reply e risveglia il thread
chiamante; in caso contrario mette il thread chiamante nella coda
wait_send.
3A) Il gestore riceve un messaggio di tipo (A) che richiede una VReply.
Il gestore guarda nella coda wait_reply se vi e` il thread indicato
in R4; se e` presente lo estrae dalla coda e lo risveglia, inoltre
risveglia anche il chiamante.
4A) Il gestore riceve un messaggio di tipo (A) che richiede una VSend e
si accorge che il thread destinatario e` "esterno".
Il gestore invia un messaggio al gestore del thread destinatario con
il seguente formato:
v_msgtype: SENDTAG
v_msgfrom: thread chiamante
v_msgto: thread destinarario
v_msglength: lunghezza del Vmessaggio
v_msgbuffer: vi viene copiato il Vmessaggio da mandare
5A) Il gestore riceve un messaggio di tipo (A) che richiede una VReply.
Il gestore guarda nella coda wait_reply se vi e` il thread indicato
in R4; se e` presente ed e` un thread "esterno" lo estrae dalla coda
e spedisce al suo gestore un messaggio dal seguente formato:
v_msgtype: REPLYTAG
v_msgfrom: thread chiamante
v_msgto: thread destinatario (della reply)
v_msglenth: N.U.
v_msgbuffer: N.U.
infine risveglia il chiamante.
6B) Il gestore riceve un messaggio di tipo (B) che richiede una VSend.
Il gestore guarda nella coda wait_send se vi e` il thread destinatario
(indicato in v_msgdsest) della VSend; se e` presente lo estrae dalla
coda, copia il messaggio
(dal v_msgbuffer), e lo risveglia (facendo una msgSend), infine mette
il mittente nella coda wait_reply;
se invece non e` presente mette il thread mittente nella coda
wait_recv.
7C) Il gestore riceve un messaggio di tipo (C) che richiede una VReply.
Il gestore risveglia il thread destinatario della reply (indicato
in v_msgto) facendogli una msgSend.
***/
void
syscallt()
{
register thr_t *sender; /* R4 */
register union {state_t *st; vmsg_t *vm} msg; /* R3 */
register tproc_t *tproc;
int term_no;
int j;
/* inizializzazione dati del gestore */
term_no = (int)tproc;
tproc = tprocTable + term_no;
/* marca tutti i buffer come liberi */
for (j = 0; j < VBUFNO; j++)
tproc->T_vbuffers[j].vmsg_type = FREETAG;
/* inizializza le code */
tproc->T_waitsend = tproc->T_waitrecv = tproc->T_waitrply = NULL;
initVQueue(tproc);
while(TRUE)
{
sender = ANYSENDER;
DO_MSGRECV();
/* controlla se il messaggio proviene dallo stesso T-process */
if (INTERNAL(tproc, sender))
{
switch(msg.st->s_ps2.p2_sys.sys_no)
{
case V_MSGSEND:
/* controllo parametri */
if (msg.st->s_r[2] > MAXMSGLEN ||
!EVEN(msg.st->s_r[4]) )
{
do_terminate(term_no);
}
else if (INTERNAL(tproc, msg.st->s_r[4])) /* (1A) */
{
msgVSend(tproc, sender, msg.st->s_r[3], msg.st);
}
else /* (4A) */
{
state_t *sts = msg.st;
thr_t *from = sender;
if ( (sender = get_sct(msg.st->s_r[4])) == NULL )
{
/* significa che il destinatario non esiste!! */
/* manda un messaggio e termina il T-process */
do_terminate(term_no);
}
else
{
/* spedisce al gestore del destinatario un messaggio
* con la copia del messaggio in memoria fisica */
msg.vm = NEXTVMSG(tproc);
msg.vm->vmsg_type = SENDTAG;
msg.vm->vmsg_from = from;
msg.vm->vmsg_to = sts->s_r[4];
msg.vm->vmsg_state = sts;
if (sts->s_r[2] > 0)
MOVBCK(sts->s_r[3], msg.vm->vmsg_buffer, sts->s_r[2]);
}
DO_MSGSEND();
}
break;
case V_MSGRECV: /* (2A) */
/* controllo parametri */
if (msg.st->s_r[2] < 0 ||
!EVEN(msg.st->s_r[4]) )
{
do_terminate(term_no);
}
else
{
msgVRecv(tproc, sender, msg.st);
}
break;
case V_MSGREPLY:
/* controllo parametri */
if ( !EVEN(msg.st->s_r[4]) )
{
do_terminate(term_no);
}
else
{
msgVReply(tproc, sender, msg.st);
}
break;
case V_MSGRR:
/* controllo parametri */
if (msg.st->s_r[2] < 0 ||
!EVEN(msg.st->s_r[4]) )
{
do_terminate(term_no);
}
else
{
msgVR_R(tproc, sender, msg.st);
}
break;
default:
do_terminate(term_no);
break;
} /* switch */
}
else
{
switch(msg.vm->vmsg_type)
{
case SENDTAG: /* (6B) */
msgVSend(tproc, msg.vm->vmsg_from, msg.vm->vmsg_buffer, msg.vm->vmsg_state);
break;
case REPLYTAG: /* (7C) */
/* risveglia il destinatario della reply */
sender = msg.vm->vmsg_to;
DO_MSGSEND();
/* marca il buffer come libero */
msg.vm->vmsg_type = FREETAG;
break;
default:
break;
}
} /* else */
} /* while(TRUE) */
}
#if VBUFNO > 1
HIDDEN vmsg_t *
nextVmsg(tproc)
register tproc_t *tproc;
{
register int i;
for (i = 0; tproc->T_vbuffers[i].vmsg_type != FREETAG && i < VBUFNO; i++)
;
return (i < VBUFNO) ? tproc->T_vbuffers + i : NULL;
}
#endif
/* Dato un thread, restituisce il suo system-call thread */
HIDDEN thr_t *
get_sct(thr)
register thr_t *thr;
{
register int term_no = 0;
while (tprocTable[term_no].T_process != thr->t_process && term_no < MAXTPROC)
term_no++;
return (term_no < MAXTPROC) ? tprocTable[term_no].T_syscallt : NULL;
}
HIDDEN void
msgVSend(tproc, from, address, st)
tproc_t *tproc;
thr_t *from;
void *address;
state_t *st;
{
register int r4;
register vqueue_t *p;
register state_t *state = st;
vmsg_t *vmsg;
/* guarda nella coda wait_send se vi e` il destinatario della
* VSend che attende il mittente o qualsiasi thread */
if ( (p = keyVSend(tproc, from, state->s_r[4])) != NULL )
{
if (state->s_r[2] > 0)
MOVBCK(address, p->vq_address,
MIN(state->s_r[2], p->vq_state->s_r[2]));
/* memorizza il risultato nei registri del destinatario */
p->vq_state->s_r[2] = state->s_r[2];
p->vq_state->s_r[4] = from;
/* Se il destinatario aveva effettuato una VRecv (e non una
* VRecv&Reply) mette il chiamante nella coda wait_reply.
* I nuovi mitt. e dest. sono relativi alla reply
*/
if (p->vq_type == VQ_RECV)
{
p->vq_from = state->s_r[4];
p->vq_to = from;
insVQueue(&tproc->T_waitrply, p);
}
else /* type == VQ_RR */
{
freeVQueue(tproc, p);
/* risveglia il chiamante */
if (INTERNAL(tproc, from))
{
r4 = from;
DO_MSGSEND();
}
else
{
/* spedisce al suo gestore un messaggio */
vmsg = address;
vmsg->vmsg_type = REPLYTAG;
vmsg->vmsg_to = from;
if ( (r4 = get_sct(from)) != NULL )
DO_MSGSEND();
else
{
/* Panico!!! probabile errore nella get_sct() */
HALT();
}
}
}
/* risveglia il destinatario */
r4 = state->s_r[4];
DO_MSGSEND();
}
else
{
/* mette il chiamante nella coda wait_recv */
if ( (p = allocVQueue(tproc)) == NULL )
{
/* Panico!!! Aumentare VQUEUENO */
HALT();
}
p->vq_type = VQ_SEND;
p->vq_from = from;
p->vq_to = state->s_r[4];
p->vq_address = address;
p->vq_state = state;
insVQueue(&tproc->T_waitrecv, p);
}
}
HIDDEN void
msgVRecv(tproc, sender, st)
tproc_t *tproc;
thr_t *sender;
state_t *st;
{
register int r4;
register vqueue_t *p;
register state_t *state = st;
/* controlla se la VSend relativa e` gia` stata fatta */
if ( (p = keyVRecv(tproc, state->s_r[4], sender)) != NULL )
{
if (p->vq_state->s_r[2] > 0)
MOVBCK(p->vq_address, state->s_r[3],
MIN(p->vq_state->s_r[2], state->s_r[2]));
/* memorizza il risultato nei registri del chiamante */
state->s_r[2] = p->vq_state->s_r[2];
state->s_r[4] = p->vq_from;
/* inserisce il mittente nella coda wait_reply */
p->vq_type = VQ_RECV;
p->vq_from = sender;
p->vq_to = state->s_r[4];
insVQueue(&tproc->T_waitrply, p);
/* e risveglia il chiamante */
r4 = sender;
DO_MSGSEND();
}
else
{
/* mette il chiamante nella coda wait_send in attesa della
* VSend relativa */
if ( (p = allocVQueue(tproc)) == NULL )
{
/* Panico!!! Aumentare VQUEUENO */
HALT();
}
p->vq_type = VQ_RECV;
p->vq_from = state->s_r[4];
p->vq_to = sender;
p->vq_address = state->s_r[3];
p->vq_state = state;
insVQueue(&tproc->T_waitsend, p);
}
}
HIDDEN void
msgVReply(tproc, sender, state)
tproc_t *tproc;
thr_t *sender;
state_t *state;
{
register int r4;
register vmsg_t *vmsg;
register vqueue_t *p;
/* guarda nella coda wait_reply se vi e` il thread indicato */
if ( (p = keyVReply(tproc, sender, state->s_r[4])) != NULL )
{
if (INTERNAL(tproc, state->s_r[4])) /* (3A) */
{
freeVQueue(tproc, p);
r4 = state->s_r[4];
DO_MSGSEND();
}
else /* (5A) */
{
vmsg = p->vq_address;
freeVQueue(tproc, p);
/* spedisce al suo gestore un messaggio */
vmsg->vmsg_type = REPLYTAG;
vmsg->vmsg_to = state->s_r[4];
if ( (r4 = get_sct(state->s_r[4])) != NULL )
DO_MSGSEND();
else
{
/* Panico!!! probabile errore nella get_sct() */
HALT();
}
}
}
/* else --> significa che viene fatta la VReply ad un thread
* che non ci ha fatto la VSend: la ignoriamo */
/* risveglia il chiamante */
r4 = sender;
DO_MSGSEND();
}
HIDDEN void
msgVR_R(tproc, sender, state)
tproc_t *tproc;
thr_t *sender;
state_t *state;
{
register int r4;
register vqueue_t *p;
register vmsg_t *vmsg;
/* controlla se la VSend relativa e` gia` stata fatta */
if ( (p = keyVRecv(tproc, state->s_r[4], sender)) != NULL )
{
if (p->vq_state->s_r[2] > 0)
MOVBCK(p->vq_address, state->s_r[3],
MIN(p->vq_state->s_r[2], state->s_r[2]));
/* memorizza il risultato nei registri del chiamante */
state->s_r[2] = p->vq_state->s_r[2];
state->s_r[4] = p->vq_from;
vmsg = p->vq_address;
freeVQueue(tproc, p);
/* controlla se il mittente e` esterno o interno al T-process */
if (INTERNAL(tproc, state->s_r[4])) /* (3A) */
{
/* risveglia il mittente (colui che ha fatto la VSend) */
r4 = state->s_r[4];
DO_MSGSEND();
}
else /* (5A) */
{
/* spedisce al suo gestore un messaggio */
vmsg->vmsg_type = REPLYTAG;
vmsg->vmsg_to = state->s_r[4];
if ( (r4 = get_sct(state->s_r[4])) != NULL )
DO_MSGSEND();
else
{
/* Panico!!! probabile errore nella get_sct() */
HALT();
}
}
/* e risveglia il chiamante */
r4 = sender;
DO_MSGSEND();
}
else
{
/* mette il chiamante nella coda wait_send in attesa della
* VSend relativa. A differenza della VRecv, all'arrivo della
* VSend relativa il mittente non verra` bloccato per attendere la reply
*/
if ( (p = allocVQueue(tproc)) == NULL )
{
/* Panico!!! Aumentare VQUEUENO */
HALT();
}
p->vq_type = VQ_RR;
p->vq_from = state->s_r[4];
p->vq_to = sender;
p->vq_address = state->s_r[3];
p->vq_state = state;
insVQueue(&tproc->T_waitsend, p);
}
}