home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ddjmag
/
ddj8712.arc
/
HOLUB.ARC
/
QUEUE.C
< prev
next >
Wrap
Text File
|
1987-12-21
|
25KB
|
262 lines
#include "kernel.h"
/*------------------------------------------------------------*/
T_QUEUE *t_makequeue( size )
int size;
{
/* Make a queue for intertask communication and link it
* into the the queue chain. Return values are pointer
* to queue on suceess or TE_NOMEM if Insufficient memory.
* Queues are searched in order of creation so it's best
* to create the most active queues first. Be sure that
* multitasking is blocked when tailp is modified (because
* it's static).
*/
static T_QUEUE *tailp;
T_QUEUE *p;
void *malloc();
t_block();
p = (T_QUEUE *) malloc( sizeof(T_QUEUE)
+ ((size-1) * sizeof(void *)) );
t_release();
if( !p )
return (T_QUEUE *) TE_NOMEM;
p->signature = TQ_SIG;
p->next = NULL;
p->task_h = NULL;
p->task_t = NULL;
p->numele = 0;
p->q_size = size;
p->headp = p->queue;
p->tailp = p->queue;
t_block();
if( !T_queues )
T_queues = tailp = p ;
else
{
tailp->next = p;
tailp = p;
}
t_release();
return p;
}
/*------------------------------------------------------------*/
int t_send( q, msg )
T_QUEUE *q; /* Pointer to queue */
void *msg; /* Pointer to message to enqueue */
{
/* Send a message and reschedule if necessary.
*
* Return Values:
* TE_NOERR No error;
* TE_BADARG Bad q argument.
* TE_QFULL Queue is full
*
* The message is always enqueued in the indicated queue.
* Then, if a task is waiting, The message at the head of
* the queue is dequeued and attached to the task, which
* is put back into the active list. Finally, if a task
* was activated, the current process yields. Note,
* however, that the current task will still be the
* active task if it's higher priority than the one to
* which you send a message. The sending task should wait()
* somewhere to make room for the lower-priority task.
*/
TCB *task;
if( q->signature != TQ_SIG )
return( TE_BADARG );
t_block();
if( q->numele == q->q_size ) /* Queue is full */
{
t_release();
return( TE_QFULL );
}
/*
* Enqueue the message.
*/
++ q->numele;
if( ++q->tailp >= q->queue + q->q_size )
q->tailp = q->queue ;
*(q->tailp) = msg ;
if( q->task_h )
{
/* A task is waiting, dequeue both it and the message,
* attach the message to the task, and reschedule
*/
task = q->task_h;
q->task_h = task->next;
--q->numele;
if( ++q->headp >= q->queue + q->q_size )
q->headp = q->queue;
task->msg = *(q->headp);
task->status = TS_WAIT ;
pq_ins( T_tasks, &task ) ;
t_yield();
}
t_release();
return TE_NOERR;
}
/*------------------------------------------------------------*/
void *t_wait( q, timeout )
T_QUEUE *q;
int timeout;
{
/* Wait for a message to arrive at the queue. If several
* tasks are waiting at the same queue, the first task
* in the queue gets the message. Return if no message
* arrives within timeout system clock ticks. Maximum
* timeout is 32,767 ticks. A 0 timeout value
* means that the subroutine returns immediately
* (without a reschedule) if no message is waiting
* in the queue.
*
* Message requests are queued up in order recieved,
* without reguard to priority. I've done this both
* because it's easy and because, in most applications,
* tasks with different priorities will not be pending
* on the same queue.
*
* If a message is present, the routine returns it
* imediately without yielding, otherwise the current
* task is removed from the active list and yield()
* is called.
*
* Hints: Use this routine to suspend a task for a
* limited amount of time (as compared to deleteing
* the task). Just pend on a queue that will never have
* a message sent to it.
*
* Normally, a pointer to the message is returned, other
* return values are:
*
* TE_TIMEOUT on a timeout or if the input value of
* timeout is 0 and no message is waiting
*
* TE_NOTASKS There current task is the only one in
* existance. This is a guaranteed deadlock.
*/
TCB *new;
if( q->signature != TQ_SIG )
return( TE_BADARG );
t_block();
if( q->numele )
{
/* There's a message waiting in the queue. Dequeue
* the message and return it immediately. Strictly
* speaking, we don't have to attach the message
* to the task, but it's convenient to do it for
* debugging reasons.
*/
-- q->numele;
if( ++q->headp >= q->queue + q->q_size )
q->headp = q->queue ;
T_active->msg = *(q->headp);
T_active->status = TS_WAIT ;
t_release();
return T_active->msg;
}
else /* No messages waiting */
{
if( timeout == 0 ) /* Immediate time out */
{
t_release();
return TE_TIMEOUT;
}
else
{
/* Enqueue the current task to wait for an
* incomming message. The pq_del call
* gets a task to preempt the current one.
*/
if( !pq_del( T_tasks, &new) )
{
t_release(); /* No tasks to activate */
return TE_NOTASKS;
}
else
{
T_active->wait = timeout;
T_active->next = NULL;
T_active->timestamp = T_clock;
if( !q->task_h )
q->task_h = T_active;
else
q->task_t->next = T_active;
q->task_t = T_active;
_t_swap_in( new ); /* Returns on either a message being */
/* sent to this queue or a timeout. */
return ( T_active->msg ? T_active->msg : TE_TIMEOUT );
}
}
}
}
/*------------------------------------------------------------*/
t_yield()
{
/* Yield to the highest priority task (if there is one).
* You can't yield to tasks waiting at queues, only to
* active ones.
*
* Returns:
* TE_NOERR successfull yield
* TE_NOTASKS Current task is the only active task
*/
TCB *new, *old ;
t_block();
if( ! pq_del( T_tasks, &new ) )
{
t_release();
return TE_NOTASKS;
}
old = T_active;
old->timestamp = T_clock ;
pq_ins( T_tasks, &old );
_t_swap_in( new ); /* _t_swap_in() changes T_active to new; */
return TE_NOERR ;
}