2017-04-26 17:20:15 +02:00
|
|
|
/*************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* Copyright (c) 1994 Stanford University */
|
|
|
|
/* */
|
|
|
|
/* All rights reserved. */
|
|
|
|
/* */
|
|
|
|
/* Permission is given to use, copy, and modify this software for any */
|
|
|
|
/* non-commercial purpose as long as this copyright notice is not */
|
|
|
|
/* removed. All other uses, including redistribution in whole or in */
|
|
|
|
/* part, are forbidden without prior written permission. */
|
|
|
|
/* */
|
|
|
|
/* This software is provided with absolutely no warranty and no */
|
|
|
|
/* support. */
|
|
|
|
/* */
|
|
|
|
/*************************************************************************/
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
*
|
|
|
|
* Task management.
|
|
|
|
*
|
|
|
|
* This module has the following functions.
|
|
|
|
* (1) Allocate/free a task object.
|
|
|
|
* (2) Enqueue/decuque a task object.
|
|
|
|
*
|
|
|
|
*************************************************************************/
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
#include <stdio.h>
|
2017-04-26 17:20:15 +02:00
|
|
|
|
|
|
|
EXTERN_ENV;
|
|
|
|
|
|
|
|
include(radiosity.h)
|
|
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
char pad1[PAGE_SIZE]; /* padding to avoid false-sharing
|
|
|
|
and allow page-placement */
|
|
|
|
long n_local_free_task ;
|
|
|
|
Task *local_free_task ;
|
|
|
|
long crnt_taskq_id ;
|
|
|
|
char pad2[PAGE_SIZE]; /* padding to avoid false-sharing
|
|
|
|
and allow page-placement */
|
|
|
|
} task_struct[MAX_PROCESSORS];
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
****************************************************************************
|
|
|
|
*
|
|
|
|
* Methods for Task object
|
|
|
|
*
|
|
|
|
****************************************************************************
|
|
|
|
****************************************************************************/
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* process_tasks()
|
|
|
|
*
|
|
|
|
* Process tasks in the task queue. Task type is specified by the mask.
|
|
|
|
* Multiple task types may be specified by bit-oring the task type.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#define QUEUES_VISITED (n_taskqueues)
|
|
|
|
#define DEQUEUE_TASK(q,v,p) (dequeue_task((q),(v),p))
|
|
|
|
|
|
|
|
void process_tasks(long process_id)
|
|
|
|
{
|
|
|
|
Task *t ;
|
|
|
|
|
|
|
|
t = DEQUEUE_TASK( taskqueue_id[process_id], QUEUES_VISITED, process_id ) ;
|
|
|
|
|
|
|
|
retry_entry:
|
2017-04-26 18:03:02 +02:00
|
|
|
while( t )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
switch( t->task_type )
|
|
|
|
{
|
|
|
|
case TASK_MODELING:
|
|
|
|
process_model( t->task.model.model, t->task.model.type, process_id ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_BSP:
|
|
|
|
define_patch( t->task.bsp.patch, t->task.bsp.parent, process_id ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_FF_REFINEMENT:
|
|
|
|
ff_refine_elements( t->task.ref.e1, t->task.ref.e2, 0, process_id ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_RAY:
|
|
|
|
process_rays( t->task.ray.e, process_id ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_VISIBILITY:
|
|
|
|
visibility_task( t->task.vis.e, t->task.vis.inter,
|
|
|
|
t->task.vis.n_inter, t->task.vis.k, process_id ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_RAD_AVERAGE:
|
|
|
|
radiosity_averaging( t->task.rad.e, t->task.rad.mode, process_id ) ;
|
|
|
|
break ;
|
|
|
|
default:
|
|
|
|
fprintf( stderr, "Panic:process_tasks:Illegal task type\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free the task */
|
|
|
|
free_task( t, process_id ) ;
|
|
|
|
|
|
|
|
/* Get next task */
|
|
|
|
t = DEQUEUE_TASK( taskqueue_id[process_id], QUEUES_VISITED, process_id ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Barrier. While waiting for other processors to finish, poll the task
|
|
|
|
queues and resume processing if there is any task */
|
|
|
|
|
|
|
|
LOCK(global->pbar_lock);
|
|
|
|
/* Reset the barrier counter if not initialized */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( global->pbar_count >= n_processors )
|
2017-04-26 17:20:15 +02:00
|
|
|
global->pbar_count = 0 ;
|
|
|
|
|
|
|
|
/* Increment the counter */
|
|
|
|
global->pbar_count++ ;
|
|
|
|
UNLOCK(global->pbar_lock);
|
|
|
|
|
|
|
|
/* barrier spin-wait loop */
|
2017-04-26 18:03:02 +02:00
|
|
|
while( global->pbar_count < n_processors )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Wait for a while and then retry dequeue */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( _process_task_wait_loop() )
|
2017-04-26 17:20:15 +02:00
|
|
|
break ;
|
|
|
|
|
|
|
|
/* Waited for a while but other processors are still running.
|
|
|
|
Poll the task queue again */
|
|
|
|
t = DEQUEUE_TASK( taskqueue_id[process_id], QUEUES_VISITED, process_id ) ;
|
2017-04-26 18:03:02 +02:00
|
|
|
if( t )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Task found. Exit the barrier and work on it */
|
|
|
|
LOCK(global->pbar_lock);
|
|
|
|
global->pbar_count-- ;
|
|
|
|
UNLOCK(global->pbar_lock);
|
|
|
|
goto retry_entry ;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
BARRIER(global->barrier, n_processors);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
long _process_task_wait_loop()
|
|
|
|
{
|
|
|
|
long i ;
|
|
|
|
long finished = 0 ;
|
|
|
|
|
|
|
|
/* Wait for a while and then retry */
|
2017-04-26 18:03:02 +02:00
|
|
|
for( i = 0 ; i < 1000 && ! finished ; i++ )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
2017-04-26 18:03:02 +02:00
|
|
|
if( ((i & 0xff) == 0) && ((volatile long)global->pbar_count >= n_processors) )
|
2017-04-26 17:20:15 +02:00
|
|
|
|
|
|
|
finished = 1 ;
|
|
|
|
}
|
|
|
|
|
|
|
|
return( finished ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* create_modeling_task()
|
|
|
|
* create_bsp_task()
|
|
|
|
* create_ff_refine_task()
|
|
|
|
* create_ray_task()
|
|
|
|
* create_visibility_task()
|
|
|
|
* create_radavg_task()
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
void create_modeling_task(Model *model, long type, long process_id)
|
|
|
|
{
|
|
|
|
/* Implemented this way (routine just calls another routine)
|
|
|
|
for historical reasons */
|
|
|
|
|
|
|
|
process_model( model, type, process_id ) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void create_bsp_task(Patch *patch, Patch *parent, long process_id)
|
|
|
|
{
|
|
|
|
/* Implemented this way (routine just calls another routine) for historical reasons */
|
|
|
|
define_patch( patch, parent, process_id ) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create_ff_refine_task(Element *e1, Element *e2, long level, long process_id)
|
|
|
|
{
|
|
|
|
Task *t ;
|
|
|
|
|
|
|
|
/* Check existing parallelism */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Task queue is too long. Solve it immediately */
|
|
|
|
ff_refine_elements( e1, e2, level, process_id ) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a task */
|
|
|
|
t = get_task(process_id) ;
|
|
|
|
t->task_type = TASK_FF_REFINEMENT ;
|
|
|
|
t->task.ref.e1 = e1 ;
|
|
|
|
t->task.ref.e2 = e2 ;
|
|
|
|
t->task.ref.level = level ;
|
|
|
|
|
|
|
|
/* Put in the queue */
|
|
|
|
enqueue_task( taskqueue_id[process_id], t, TASK_INSERT ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create_ray_task(Element *e, long process_id)
|
|
|
|
{
|
|
|
|
/* Check existing parallelism */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( ((e->n_interactions + e->n_vis_undef_inter)
|
2017-04-26 17:20:15 +02:00
|
|
|
< N_inter_parallel_bf_refine)
|
|
|
|
|| taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
|
|
|
|
{
|
|
|
|
/* Task size is small, or the queue is too long.
|
|
|
|
Solve it immediately */
|
|
|
|
process_rays( e, process_id ) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Put in the queue */
|
|
|
|
enqueue_ray_task( taskqueue_id[process_id], e, TASK_INSERT, process_id ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void enqueue_ray_task(long qid, Element *e, long mode, long process_id)
|
|
|
|
{
|
|
|
|
Task *t ;
|
|
|
|
|
|
|
|
/* Create task object */
|
|
|
|
t = get_task(process_id) ;
|
|
|
|
t->task_type = TASK_RAY ;
|
|
|
|
t->task.ray.e = e ;
|
|
|
|
|
|
|
|
/* Put in the queue */
|
|
|
|
enqueue_task( qid, t, mode ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void create_visibility_tasks(Element *e, void (*k)(), long process_id)
|
|
|
|
{
|
|
|
|
long n_tasks ;
|
|
|
|
long remainder ; /* Residue of MOD(total_undefs)*/
|
|
|
|
long i_cnt ;
|
|
|
|
Interaction *top, *tail ;
|
|
|
|
Task *t ;
|
|
|
|
long total_undefs = 0 ;
|
|
|
|
long tasks_created = 0 ;
|
|
|
|
|
|
|
|
/* Check number of hard problems */
|
2017-04-26 18:03:02 +02:00
|
|
|
for( top = e->vis_undef_inter ; top ; top = top->next )
|
|
|
|
if( top->visibility == VISIBILITY_UNDEF )
|
2017-04-26 17:20:15 +02:00
|
|
|
total_undefs++ ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( total_undefs == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* No process needs to be created. Call the continuation
|
|
|
|
immediately */
|
|
|
|
(*k)( e, process_id ) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check existing parallelism */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( (total_undefs < N_visibility_per_task)
|
2017-04-26 17:20:15 +02:00
|
|
|
|| taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
|
|
|
|
{
|
|
|
|
/* Task size is small, or the queue is too long.
|
|
|
|
Solve it immediately. */
|
|
|
|
visibility_task( e, e->vis_undef_inter,
|
|
|
|
e->n_vis_undef_inter, k, process_id ) ;
|
|
|
|
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create multiple tasks. Hard problems (i.e. where visibility comp is
|
|
|
|
really necessary) are divided into 'n_tasks' groups by residue
|
|
|
|
number division (or Bresenham's DDA) */
|
|
|
|
/* Note: once the first task is enqueued, the vis-undef list may be
|
|
|
|
modified while other tasks are being created. So, any information
|
|
|
|
that is necessary in the for-loop must be read from the element
|
|
|
|
and saved locally */
|
|
|
|
|
|
|
|
n_tasks = (total_undefs + N_visibility_per_task - 1)
|
|
|
|
/ N_visibility_per_task ;
|
|
|
|
remainder = 0 ;
|
|
|
|
i_cnt = 0 ;
|
2017-04-26 18:03:02 +02:00
|
|
|
for( top = e->vis_undef_inter, tail = top ; tail ; tail = tail->next )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
i_cnt++ ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tail->visibility != VISIBILITY_UNDEF )
|
2017-04-26 17:20:15 +02:00
|
|
|
continue ;
|
|
|
|
|
|
|
|
remainder += n_tasks ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( remainder >= total_undefs )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Create a task */
|
|
|
|
|
|
|
|
/* For the last task, append following (easy) interactions
|
|
|
|
if there is any */
|
|
|
|
tasks_created++ ;
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tasks_created >= n_tasks )
|
|
|
|
for( ; tail->next ; tail = tail->next, i_cnt++ ) ;
|
2017-04-26 17:20:15 +02:00
|
|
|
|
|
|
|
/* Set task descriptor */
|
|
|
|
t = get_task(process_id) ;
|
|
|
|
t->task_type = TASK_VISIBILITY ;
|
|
|
|
t->task.vis.e = e ;
|
|
|
|
t->task.vis.inter = top ;
|
|
|
|
t->task.vis.n_inter = i_cnt ;
|
|
|
|
t->task.vis.k = k ;
|
|
|
|
|
|
|
|
/* Enqueue */
|
|
|
|
enqueue_task( taskqueue_id[process_id], t, TASK_INSERT ) ;
|
|
|
|
|
|
|
|
/* Update pointer and the residue variable */
|
|
|
|
top = tail->next ;
|
|
|
|
remainder -= total_undefs ;
|
|
|
|
i_cnt = 0 ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create_radavg_task(Element *e, long mode, long process_id)
|
|
|
|
{
|
|
|
|
/* Check existing parallelism */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( (e->n_interactions < N_inter_parallel_bf_refine)
|
2017-04-26 17:20:15 +02:00
|
|
|
|| taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
|
|
|
|
{
|
|
|
|
/* Task size is too small or queue is too long.
|
|
|
|
Solve it immediately */
|
|
|
|
radiosity_averaging( e, mode, process_id ) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Put in the queue */
|
|
|
|
enqueue_radavg_task( taskqueue_id[process_id], e, mode, process_id ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void enqueue_radavg_task(long qid, Element *e, long mode, long process_id)
|
|
|
|
{
|
|
|
|
Task *t ;
|
|
|
|
|
|
|
|
/* Create task object */
|
|
|
|
t = get_task(process_id) ;
|
|
|
|
t->task_type = TASK_RAD_AVERAGE ;
|
|
|
|
t->task.rad.e = e ;
|
|
|
|
t->task.rad.mode = mode ;
|
|
|
|
|
|
|
|
/* Put in the queue */
|
|
|
|
enqueue_task( qid, t, TASK_INSERT ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* enqueue_task()
|
|
|
|
* dequeue_task()
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
void enqueue_task(long qid, Task *task, long mode)
|
|
|
|
{
|
|
|
|
Task_Queue *tq ;
|
|
|
|
|
|
|
|
|
|
|
|
tq = &global->task_queue[ qid ] ;
|
|
|
|
|
|
|
|
/* Lock the task queue */
|
|
|
|
LOCK(tq->q_lock);
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tq->tail == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* The first task in the queue */
|
|
|
|
tq->tail = task ;
|
|
|
|
tq->top = task ;
|
|
|
|
tq->n_tasks = 1 ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Usual case */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( mode == TASK_APPEND )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
tq->tail->next = task ;
|
|
|
|
tq->tail = task ;
|
|
|
|
tq->n_tasks++ ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
task->next = tq->top ;
|
|
|
|
tq->top = task ;
|
|
|
|
tq->n_tasks++ ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock the task queue */
|
|
|
|
UNLOCK(tq->q_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Task *dequeue_task(long qid, long max_visit, long process_id)
|
|
|
|
/*
|
|
|
|
* Attempts to dequeue first from the specified queue (qid), but if no
|
|
|
|
* task is found the routine searches max_visit other queues and returns
|
|
|
|
* a task. If a task is taken from another queue, the task is taken from
|
|
|
|
* the tail of the queue (usually, larger amount of work is involved than
|
|
|
|
* the task at the top of the queue and more locality can be exploited
|
|
|
|
* within the stolen task).
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
Task_Queue *tq ;
|
|
|
|
Task *t = 0 ;
|
|
|
|
Task *prev ;
|
|
|
|
long visit_count = 0 ;
|
|
|
|
long sign = -1 ; /* The first retry will go backward */
|
|
|
|
long offset ;
|
|
|
|
|
|
|
|
/* Check number of queues to be visited */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( max_visit > n_taskqueues )
|
2017-04-26 17:20:15 +02:00
|
|
|
max_visit = n_taskqueues ;
|
|
|
|
|
|
|
|
/* Get next task */
|
2017-04-26 18:03:02 +02:00
|
|
|
while( visit_count < max_visit )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Select a task queue */
|
|
|
|
tq = &global->task_queue[ qid ] ;
|
|
|
|
|
|
|
|
/* Check the length (test-test&set) */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tq->n_tasks > 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Lock the task queue */
|
|
|
|
LOCK(tq->q_lock);
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tq->top )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
2017-04-26 18:03:02 +02:00
|
|
|
if( qid == taskqueue_id[process_id] )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
t = tq->top ;
|
|
|
|
tq->top = t->next ;
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tq->top == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
tq->tail = 0 ;
|
|
|
|
tq->n_tasks-- ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Get tail */
|
2017-04-26 18:03:02 +02:00
|
|
|
for( prev = 0, t = tq->top ; t->next ;
|
2017-04-26 17:20:15 +02:00
|
|
|
prev = t, t = t->next ) ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( prev == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
tq->top = 0 ;
|
|
|
|
else
|
|
|
|
prev->next = 0 ;
|
|
|
|
tq->tail = prev ;
|
|
|
|
tq->n_tasks-- ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Unlock the task queue */
|
|
|
|
UNLOCK(tq->q_lock);
|
|
|
|
break ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update visit count */
|
|
|
|
visit_count++ ;
|
|
|
|
|
|
|
|
/* Compute next taskqueue ID */
|
|
|
|
offset = (sign > 0)? visit_count : -visit_count ;
|
|
|
|
sign = -sign ;
|
|
|
|
|
|
|
|
qid += offset ;
|
2017-04-26 18:03:02 +02:00
|
|
|
if( qid < 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
qid += n_taskqueues ;
|
2017-04-26 18:03:02 +02:00
|
|
|
else if( qid >= n_taskqueues )
|
2017-04-26 17:20:15 +02:00
|
|
|
qid -= n_taskqueues ;
|
|
|
|
}
|
|
|
|
|
|
|
|
return( t ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* get_task() Create a new instance of Task
|
|
|
|
* free_task() Free a Task object
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
Task *get_task(long process_id)
|
|
|
|
{
|
|
|
|
Task *p ;
|
|
|
|
Task_Queue *tq ;
|
|
|
|
long i ;
|
|
|
|
long q_id ;
|
|
|
|
long retry_count = 0 ;
|
|
|
|
|
|
|
|
/* First, check local task queue */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( task_struct[process_id].local_free_task == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* If empty, allocate task objects from the shared list */
|
|
|
|
q_id = taskqueue_id[process_id] ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
while( task_struct[process_id].local_free_task == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
tq = &global->task_queue[ q_id ] ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tq->n_free > 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
LOCK(tq->f_lock);
|
2017-04-26 18:03:02 +02:00
|
|
|
if( tq->free )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Scan the free list */
|
2017-04-26 18:03:02 +02:00
|
|
|
for( i = 1, p = tq->free ;
|
2017-04-26 17:20:15 +02:00
|
|
|
(i < N_ALLOCATE_LOCAL_TASK) && p->next ;
|
|
|
|
i++, p = p->next ) ;
|
|
|
|
|
|
|
|
task_struct[process_id].local_free_task = tq->free ;
|
|
|
|
task_struct[process_id].n_local_free_task = i ;
|
|
|
|
tq->free = p->next ;
|
|
|
|
tq->n_free -= i ;
|
|
|
|
p->next = 0 ;
|
|
|
|
UNLOCK(tq->f_lock);
|
|
|
|
break ;
|
|
|
|
}
|
|
|
|
UNLOCK(tq->f_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try next task queue */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( ++q_id >= n_taskqueues )
|
2017-04-26 17:20:15 +02:00
|
|
|
q_id = 0 ;
|
|
|
|
|
|
|
|
/* Check retry count */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( ++retry_count > MAX_TASKGET_RETRY )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
fprintf( stderr, "Panic(P%ld):No free task\n",
|
|
|
|
process_id ) ;
|
|
|
|
fprintf( stderr, " Local %ld\n", task_struct[process_id].n_local_free_task ) ;
|
|
|
|
fprintf( stderr, " Q0 free %ld\n",
|
|
|
|
global->task_queue[0].n_free ) ;
|
|
|
|
fprintf( stderr, " Q0 task %ld\n",
|
|
|
|
global->task_queue[0].n_tasks ) ;
|
|
|
|
exit(1) ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Delete from the queue */
|
|
|
|
p = task_struct[process_id].local_free_task ;
|
|
|
|
task_struct[process_id].local_free_task = p->next ;
|
|
|
|
task_struct[process_id].n_local_free_task-- ;
|
|
|
|
|
|
|
|
/* Clear pointer just in case.. */
|
|
|
|
p->next = 0 ;
|
|
|
|
|
|
|
|
|
|
|
|
return( p ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void free_task(Task *task, long process_id)
|
|
|
|
{
|
|
|
|
Task_Queue *tq ;
|
|
|
|
Task *p, *top ;
|
|
|
|
long i ;
|
|
|
|
|
|
|
|
/* Insert to the local queue */
|
|
|
|
task->next = task_struct[process_id].local_free_task ;
|
|
|
|
task_struct[process_id].local_free_task = task ;
|
|
|
|
task_struct[process_id].n_local_free_task++ ;
|
|
|
|
|
|
|
|
/* If local list is too long, export some tasks */
|
2017-04-26 18:03:02 +02:00
|
|
|
if( task_struct[process_id].n_local_free_task >= (N_ALLOCATE_LOCAL_TASK * 2) )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
tq = &global->task_queue[ taskqueue_id[process_id] ] ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
for( i = 1, p = task_struct[process_id].local_free_task ;
|
2017-04-26 17:20:15 +02:00
|
|
|
i < N_ALLOCATE_LOCAL_TASK ; i++, p = p->next ) ;
|
|
|
|
|
|
|
|
/* Update local list */
|
|
|
|
top = task_struct[process_id].local_free_task ;
|
|
|
|
task_struct[process_id].local_free_task = p->next ;
|
|
|
|
task_struct[process_id].n_local_free_task -= i ;
|
|
|
|
|
|
|
|
/* Insert in the shared list */
|
|
|
|
LOCK(tq->f_lock);
|
|
|
|
p->next = tq->free ;
|
|
|
|
tq->free = top ;
|
|
|
|
tq->n_free += i ;
|
|
|
|
UNLOCK(tq->f_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* init_taskq()
|
|
|
|
*
|
|
|
|
* Initialize task free list and the task queue.
|
|
|
|
* This routine must be called when only one process is active.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
void init_taskq(long process_id)
|
|
|
|
{
|
|
|
|
long i ;
|
|
|
|
long qid ;
|
|
|
|
long task_index = 0 ;
|
|
|
|
long task_per_queue ;
|
|
|
|
long n_tasks ;
|
|
|
|
|
|
|
|
/* Reset task assignment index */
|
|
|
|
task_struct[process_id].crnt_taskq_id = 0 ;
|
|
|
|
|
|
|
|
/* Initialize task queues */
|
|
|
|
task_per_queue = (MAX_TASKS + n_taskqueues - 1) / n_taskqueues ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
for( qid = 0 ; qid < n_taskqueues ; qid++ )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
/* Initialize free list */
|
|
|
|
if (task_index + task_per_queue > MAX_TASKS )
|
|
|
|
n_tasks = MAX_TASKS - task_index ;
|
|
|
|
else
|
|
|
|
n_tasks = task_per_queue ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
for( i = task_index ; i < task_index + n_tasks - 1 ; i++ )
|
2017-04-26 17:20:15 +02:00
|
|
|
global->task_buf[i].next = &global->task_buf[i+1] ;
|
|
|
|
global->task_buf[ i ].next = 0 ;
|
|
|
|
|
|
|
|
global->task_queue[ qid ].free = &global->task_buf[ task_index ] ;
|
|
|
|
global->task_queue[ qid ].n_free = n_tasks ;
|
|
|
|
|
|
|
|
/* Initialize task queue */
|
|
|
|
global->task_queue[ qid ].top = 0 ;
|
|
|
|
global->task_queue[ qid ].tail = 0 ;
|
|
|
|
global->task_queue[ qid ].n_tasks = 0 ;
|
|
|
|
|
|
|
|
/* Initialize locks */
|
|
|
|
LOCKINIT(global->task_queue[ qid ].q_lock);
|
|
|
|
LOCKINIT(global->task_queue[ qid ].f_lock);
|
|
|
|
|
|
|
|
/* Update index for next queue */
|
|
|
|
task_index += n_tasks ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize local free lists */
|
|
|
|
task_struct[process_id].n_local_free_task = 0 ;
|
|
|
|
task_struct[process_id].local_free_task = 0 ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* check_task_counter()
|
|
|
|
*
|
|
|
|
* Check task counter and return TRUE if this is the first task.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
long check_task_counter()
|
|
|
|
{
|
|
|
|
long flag = 0 ;
|
|
|
|
|
|
|
|
|
|
|
|
LOCK(global->task_counter_lock);
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( global->task_counter == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
/* First processor */
|
|
|
|
flag = 1 ;
|
|
|
|
|
|
|
|
global->task_counter++ ;
|
2017-04-26 18:03:02 +02:00
|
|
|
if( global->task_counter >= n_processors )
|
2017-04-26 17:20:15 +02:00
|
|
|
global->task_counter = 0 ;
|
|
|
|
|
|
|
|
UNLOCK(global->task_counter_lock);
|
|
|
|
|
|
|
|
return( flag ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* assign_taskq()
|
|
|
|
*
|
|
|
|
* Assign process its task queue.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
long assign_taskq(long process_id)
|
|
|
|
{
|
|
|
|
long qid ;
|
|
|
|
|
|
|
|
qid = task_struct[process_id].crnt_taskq_id++ ;
|
|
|
|
|
2017-04-26 18:03:02 +02:00
|
|
|
if( task_struct[process_id].crnt_taskq_id >= n_taskqueues )
|
2017-04-26 17:20:15 +02:00
|
|
|
task_struct[process_id].crnt_taskq_id = 0 ;
|
|
|
|
|
|
|
|
return( qid ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* print_task()
|
|
|
|
* print_taskq()
|
|
|
|
*
|
|
|
|
* Print contents of a task.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
void print_task(Task *task)
|
|
|
|
{
|
2017-04-26 18:03:02 +02:00
|
|
|
if( task == 0 )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
printf( "Task (NULL)\n" ) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch( task->task_type )
|
|
|
|
{
|
|
|
|
case TASK_MODELING:
|
|
|
|
printf( "Task (Model)\n" ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_BSP:
|
|
|
|
printf( "Task (BSP)\n" ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_FF_REFINEMENT:
|
|
|
|
printf( "Task (FF Refinement)\n" ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_RAY:
|
|
|
|
printf( "Task (Ray) (patch ID %ld)\n",
|
|
|
|
task->task.ray.e->patch->seq_no ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_VISIBILITY:
|
|
|
|
printf( "Task (Visibility) (patch ID %ld)\n",
|
|
|
|
task->task.vis.e->patch->seq_no ) ;
|
|
|
|
break ;
|
|
|
|
case TASK_RAD_AVERAGE:
|
|
|
|
printf( "Task (RadAvg)\n" ) ;
|
|
|
|
break ;
|
|
|
|
default:
|
|
|
|
fprintf( stderr, "Task(Illegal task type %ld)\n", task->task_type );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void print_taskq(Task_Queue *tq)
|
|
|
|
{
|
|
|
|
Task *t ;
|
|
|
|
|
|
|
|
printf( "TaskQ: %ld tasks in the queue\n", taskq_length(tq) ) ;
|
2017-04-26 18:03:02 +02:00
|
|
|
for( t = taskq_top(tq) ; t ; t = t->next )
|
2017-04-26 17:20:15 +02:00
|
|
|
{
|
|
|
|
printf( " " ) ;
|
|
|
|
print_task( t ) ;
|
|
|
|
}
|
|
|
|
}
|