Commit a4597f03 authored by Pedro Gonnet's avatar Pedro Gonnet
Browse files

minor bug fixes and make space_rebuild parallel (kind of).


Former-commit-id: 00d92b2b60c7d1041482fb0eea49b5c98831c4de
parent 9505d699
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
/* Error macro. */ /* Error macro. */
#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } #define error(s) { fprintf( stderr , "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); }
/* The timers. */ /* The timers. */
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#include "runner_iact.h" #include "runner_iact.h"
/* Error macro. */ /* Error macro. */
#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } #define error(s) { fprintf( stderr , "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); }
/* Convert cell location to ID. */ /* Convert cell location to ID. */
#define cell_getid( cdim , i , j , k ) ( (int)(k) + (cdim)[2]*( (int)(j) + (cdim)[1]*(int)(i) ) ) #define cell_getid( cdim , i , j , k ) ( (int)(k) + (cdim)[2]*( (int)(j) + (cdim)[1]*(int)(i) ) )
...@@ -206,7 +206,6 @@ void engine_run ( struct engine *e , int sort_queues ) { ...@@ -206,7 +206,6 @@ void engine_run ( struct engine *e , int sort_queues ) {
/* Run throught the tasks and get all the waits right. */ /* Run throught the tasks and get all the waits right. */
for ( k = 0 ; k < s->nr_tasks ; k++ ) { for ( k = 0 ; k < s->nr_tasks ; k++ ) {
s->tasks[k].done = 0;
for ( j = 0 ; j < s->tasks[k].nr_unlock_tasks ; j++ ) for ( j = 0 ; j < s->tasks[k].nr_unlock_tasks ; j++ )
s->tasks[k].unlock_tasks[j]->wait += 1; s->tasks[k].unlock_tasks[j]->wait += 1;
for ( j = 0 ; j < s->tasks[k].nr_unlock_cells ; j++ ) for ( j = 0 ; j < s->tasks[k].nr_unlock_cells ; j++ )
......
...@@ -32,13 +32,13 @@ ...@@ -32,13 +32,13 @@
#include <hdf5.h> #include <hdf5.h>
#include "task.h"
#include "lock.h" #include "lock.h"
#include "task.h"
#include "part.h" #include "part.h"
#include "space.h" #include "space.h"
/* Error macro. */ /* Error macro. */
#define error(s) { printf( "%s:%s():%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } #define error(s) { fprintf( stderr , "%s:%s():%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); }
/** /**
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#define lock_lock( l ) ( pthread_spin_lock( l ) != 0 ) #define lock_lock( l ) ( pthread_spin_lock( l ) != 0 )
#define lock_trylock( l ) ( pthread_spin_lock( l ) != 0 ) #define lock_trylock( l ) ( pthread_spin_lock( l ) != 0 )
#define lock_unlock( l ) ( pthread_spin_unlock( l ) != 0 ) #define lock_unlock( l ) ( pthread_spin_unlock( l ) != 0 )
#define lock_unlock_blind( l ) pthread_spin_unlock( l )
#else #else
#define lock_type volatile int #define lock_type volatile int
#define lock_init( l ) ( *l = 0 ) #define lock_init( l ) ( *l = 0 )
...@@ -46,4 +47,5 @@ ...@@ -46,4 +47,5 @@
} }
#define lock_trylock( l ) ( ( *(l) ) ? 1 : __sync_val_compare_and_swap( l , 0 , 1 ) ) #define lock_trylock( l ) ( ( *(l) ) ? 1 : __sync_val_compare_and_swap( l , 0 , 1 ) )
#define lock_unlock( l ) ( __sync_val_compare_and_swap( l , 1 , 0 ) != 1 ) #define lock_unlock( l ) ( __sync_val_compare_and_swap( l , 1 , 0 ) != 1 )
#define lock_unlock_blind( l ) __sync_val_compare_and_swap( l , 1 , 0 )
#endif #endif
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#include "queue.h" #include "queue.h"
/* Error macro. */ /* Error macro. */
#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } #define error(s) { fprintf( stderr , "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); }
/* Define the timer macros. */ /* Define the timer macros. */
#ifdef TIMER_VERBOSE #ifdef TIMER_VERBOSE
...@@ -147,7 +147,7 @@ void queue_init ( struct queue *q , int size , struct task *tasks ) { ...@@ -147,7 +147,7 @@ void queue_init ( struct queue *q , int size , struct task *tasks ) {
* @param keep Remove the returned task from this queue. * @param keep Remove the returned task from this queue.
*/ */
struct task *queue_gettask ( struct queue *q , int blocking , int keep ) { struct task *queue_gettask_old ( struct queue *q , int blocking , int keep ) {
int k, tid = -1, qcount, *qtid = q->tid; int k, tid = -1, qcount, *qtid = q->tid;
lock_type *qlock = &q->lock; lock_type *qlock = &q->lock;
...@@ -267,7 +267,7 @@ struct task *queue_gettask ( struct queue *q , int blocking , int keep ) { ...@@ -267,7 +267,7 @@ struct task *queue_gettask ( struct queue *q , int blocking , int keep ) {
} }
struct task *queue_gettask_new ( struct queue *q , int rid , int blocking , int keep ) { struct task *queue_gettask ( struct queue *q , int rid , int blocking , int keep ) {
int k, tid = -1, qcount, *qtid = q->tid; int k, tid = -1, qcount, *qtid = q->tid;
lock_type *qlock = &q->lock; lock_type *qlock = &q->lock;
......
...@@ -59,8 +59,8 @@ struct queue { ...@@ -59,8 +59,8 @@ struct queue {
/* Function prototypes. */ /* Function prototypes. */
struct task *queue_gettask ( struct queue *q , int blocking , int keep ); struct task *queue_gettask_old ( struct queue *q , int blocking , int keep );
struct task *queue_gettask_new ( struct queue *q , int rid , int blocking , int keep ); struct task *queue_gettask ( struct queue *q , int rid , int blocking , int keep );
void queue_init ( struct queue *q , int size , struct task *tasks ); void queue_init ( struct queue *q , int size , struct task *tasks );
void queue_insert ( struct queue *q , struct task *t ); void queue_insert ( struct queue *q , struct task *t );
void queue_sort ( struct queue *q ); void queue_sort ( struct queue *q );
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#include "runner_iact.h" #include "runner_iact.h"
/* Error macro. */ /* Error macro. */
#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } #define error(s) { fprintf( stderr , "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); }
/* Convert cell location to ID. */ /* Convert cell location to ID. */
#define cell_getid( cdim , i , j , k ) ( (int)(k) + (cdim)[2]*( (int)(j) + (cdim)[1]*(int)(i) ) ) #define cell_getid( cdim , i , j , k ) ( (int)(k) + (cdim)[2]*( (int)(j) + (cdim)[1]*(int)(i) ) )
...@@ -508,11 +508,11 @@ void *runner_main ( void *data ) { ...@@ -508,11 +508,11 @@ void *runner_main ( void *data ) {
TIMER_TIC TIMER_TIC
t = NULL; t = NULL;
if ( e->nr_queues == 1 ) { if ( e->nr_queues == 1 ) {
t = queue_gettask( &e->queues[0] , 1 , 0 ); t = queue_gettask_old( &e->queues[0] , 1 , 0 );
} }
else if ( e->policy & engine_policy_steal ) { else if ( e->policy & engine_policy_steal ) {
if ( ( myq->next == myq->count ) || if ( ( myq->next == myq->count ) ||
( t = queue_gettask_new( myq , r->id , 0 , 0 ) ) == NULL ) { ( t = queue_gettask( myq , r->id , 0 , 0 ) ) == NULL ) {
TIMER_TIC2 TIMER_TIC2
qid = rand_r( &myseed ) % naq; qid = rand_r( &myseed ) % naq;
keep = ( e->policy & engine_policy_keep ) && keep = ( e->policy & engine_policy_keep ) &&
...@@ -521,7 +521,7 @@ void *runner_main ( void *data ) { ...@@ -521,7 +521,7 @@ void *runner_main ( void *data ) {
COUNT(runner_counter_steal_empty); COUNT(runner_counter_steal_empty);
else else
COUNT(runner_counter_steal_stall); COUNT(runner_counter_steal_stall);
t = queue_gettask_new( queues[qid] , r->id , 0 , keep ); t = queue_gettask( queues[qid] , r->id , 0 , keep );
if ( t != NULL && keep ) if ( t != NULL && keep )
queue_insert( myq , t ); queue_insert( myq , t );
TIMER_TOC2(runner_timer_steal); TIMER_TOC2(runner_timer_steal);
...@@ -529,10 +529,10 @@ void *runner_main ( void *data ) { ...@@ -529,10 +529,10 @@ void *runner_main ( void *data ) {
} }
else if ( e->policy & engine_policy_rand ) { else if ( e->policy & engine_policy_rand ) {
qid = rand_r( &myseed ) % naq; qid = rand_r( &myseed ) % naq;
t = queue_gettask( queues[qid] , e->policy & engine_policy_block , 0 ); t = queue_gettask( queues[qid] , r->id , e->policy & engine_policy_block , 0 );
} }
else { else {
t = queue_gettask( &e->queues[threadID] , e->policy & engine_policy_block , 0 ); t = queue_gettask( &e->queues[threadID] , r->id , e->policy & engine_policy_block , 0 );
} }
TIMER_TOC(runner_timer_getpair); TIMER_TOC(runner_timer_getpair);
...@@ -602,8 +602,6 @@ void *runner_main ( void *data ) { ...@@ -602,8 +602,6 @@ void *runner_main ( void *data ) {
error( "Unknown task type." ); error( "Unknown task type." );
} }
t->done = 1;
/* Resolve any dependencies. */ /* Resolve any dependencies. */
for ( k = 0 ; k < t->nr_unlock_tasks ; k++ ) for ( k = 0 ; k < t->nr_unlock_tasks ; k++ )
if ( __sync_fetch_and_sub( &t->unlock_tasks[k]->wait , 1 ) == 0 ) if ( __sync_fetch_and_sub( &t->unlock_tasks[k]->wait , 1 ) == 0 )
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#include "runner.h" #include "runner.h"
/* Error macro. */ /* Error macro. */
#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } #define error(s) { fprintf( stderr , "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); }
/* Split size. */ /* Split size. */
int space_splitsize = space_splitsize_default; int space_splitsize = space_splitsize_default;
...@@ -288,6 +288,7 @@ int space_rebuild ( struct space *s , int force , double cell_max ) { ...@@ -288,6 +288,7 @@ int space_rebuild ( struct space *s , int force , double cell_max ) {
/* At this point, we have the upper-level cells, old or new. Now make /* At this point, we have the upper-level cells, old or new. Now make
sure that the parts in each cell are ok. */ sure that the parts in each cell are ok. */
#pragma omp parallel for shared(s) reduction(+:changes)
for ( k = 0 ; k < s->nr_cells ; k++ ) for ( k = 0 ; k < s->nr_cells ; k++ )
changes += space_rebuild_recurse( s , &s->cells[k] ); changes += space_rebuild_recurse( s , &s->cells[k] );
...@@ -344,14 +345,35 @@ void parts_sort ( struct part *parts , int *ind , int N , int min , int max ) { ...@@ -344,14 +345,35 @@ void parts_sort ( struct part *parts , int *ind , int N , int min , int max ) {
error( "Sorting failed (>pivot)." ); error( "Sorting failed (>pivot)." );
} }
/* Recurse on the left? */ /* Try to recurse in parallel. */
if ( j > 0 && pivot > min ) if ( N < 100 ) {
parts_sort( parts , ind , j+1 , min , pivot );
/* Recurse on the left? */
if ( j > 0 && pivot > min )
parts_sort( parts , ind , j+1 , min , pivot );
/* Recurse on the right? */
if ( i < N && pivot+1 < max )
parts_sort( &parts[i], &ind[i], N-i , pivot+1 , max );
}
/* Recurse on the right? */ else
if ( i < N && pivot+1 < max ) #pragma omp parallel sections
parts_sort( &parts[i], &ind[i], N-i , pivot+1 , max ); {
/* Recurse on the left? */
#pragma omp section
if ( j > 0 && pivot > min )
parts_sort( parts , ind , j+1 , min , pivot );
/* Recurse on the right? */
#pragma omp section
if ( i < N && pivot+1 < max )
parts_sort( &parts[i], &ind[i], N-i , pivot+1 , max );
}
} }
...@@ -413,101 +435,6 @@ void space_map_clearnrtasks ( struct cell *c , void *data ) { ...@@ -413,101 +435,6 @@ void space_map_clearnrtasks ( struct cell *c , void *data ) {
} }
/**
* @brief Get a task free of dependencies and conflicts.
*
* @param s The #space.
*/
struct task *space_gettask ( struct space *s ) {
int k, tid = -1;
struct task *res = NULL;
struct cell *c;
/* Main loop, while there are tasks... */
while ( s->next_task < s->nr_tasks ) {
/* Grab the task lock. */
if ( lock_lock( &s->task_lock ) != 0 )
error( "Locking the task_lock failed.\n" );
/* Loop over the remaining task IDs. */
for ( k = s->next_task ; k < s->nr_tasks ; k++ ) {
/* Put a finger on the task. */
res = &s->tasks[ s->tasks_ind[k] ];
/* Is this task blocked? */
if ( res->wait )
continue;
/* Different criteria for different types. */
switch ( res->type ) {
case task_type_self:
if ( res->ci->lock || res->ci->wait > 0 )
continue;
break;
case task_type_pair:
if ( res->ci->lock || res->cj->lock || res->ci->wait || res->cj->wait )
continue;
break;
case task_type_sort:
if ( res->ci->lock )
continue;
break;
}
/* If we made it this far, we're safe. */
break;
} /* loop over the task IDs. */
/* Did we get a task? */
if ( k < s->nr_tasks ) {
// /* Swap to front. */
// tid = s->tasks_ind[k];
// s->tasks_ind[k] = s->tasks_ind[ s->next_task ];
// s->tasks_ind[ s->next_task ] = tid;
/* Bubble-down the task. */
tid = s->tasks_ind[k];
while ( k > s->next_task ) {
s->tasks_ind[ k ] = s->tasks_ind[ k-1 ];
k -= 1;
}
s->tasks_ind[ s->next_task ] = tid;
/* Lock the cells, if needed. */
if ( s->tasks[tid].type != task_type_sort ) {
for ( c = res->ci ; c != NULL ; c = c->parent )
__sync_fetch_and_add( &c->lock , 1 );
for ( c = res->cj ; c != NULL ; c = c->parent )
__sync_fetch_and_add( &c->lock , 1 );
}
/* up the counter. */
s->next_task += 1;
}
/* Release the task lock. */
if ( lock_unlock( &s->task_lock ) != 0 )
error( "Locking the task_lock failed.\n" );
/* Leave? */
if ( tid >= 0 )
return &s->tasks[tid];
} /* while there are tasks. */
/* No beef. */
return NULL;
}
/** /**
* @brief Map a function to all particles in a aspace. * @brief Map a function to all particles in a aspace.
* *
...@@ -584,7 +511,13 @@ void space_map_cells ( struct space *s , int full , void (*fun)( struct cell *c ...@@ -584,7 +511,13 @@ void space_map_cells ( struct space *s , int full , void (*fun)( struct cell *c
struct task *space_addtask ( struct space *s , int type , int subtype , int flags , int wait , struct cell *ci , struct cell *cj , struct task *unlock_tasks[] , int nr_unlock_tasks , struct cell *unlock_cells[] , int nr_unlock_cells ) { struct task *space_addtask ( struct space *s , int type , int subtype , int flags , int wait , struct cell *ci , struct cell *cj , struct task *unlock_tasks[] , int nr_unlock_tasks , struct cell *unlock_cells[] , int nr_unlock_cells ) {
struct task *t = &s->tasks[ s->nr_tasks ]; struct task *t;
/* Lock the space. */
lock_lock( &s->lock );
/* Get the next free task. */
t = &s->tasks[ s->nr_tasks ];
/* Copy the data. */ /* Copy the data. */
t->type = type; t->type = type;
...@@ -603,6 +536,9 @@ struct task *space_addtask ( struct space *s , int type , int subtype , int flag ...@@ -603,6 +536,9 @@ struct task *space_addtask ( struct space *s , int type , int subtype , int flag
/* Increase the task counter. */ /* Increase the task counter. */
s->nr_tasks += 1; s->nr_tasks += 1;
/* Unock the space. */
lock_unlock_blind( &s->lock );
/* Return a pointer to the new task. */ /* Return a pointer to the new task. */
return t; return t;
...@@ -1344,9 +1280,6 @@ void space_maketasks ( struct space *s , int do_sort ) { ...@@ -1344,9 +1280,6 @@ void space_maketasks ( struct space *s , int do_sort ) {
printf( " %s=%i" , taskID_names[k] , counts[k] ); printf( " %s=%i" , taskID_names[k] , counts[k] );
printf( " ]\n" ); printf( " ]\n" );
/* Re-set the next task pointer. */
s->next_task = 0;
} }
...@@ -1444,6 +1377,9 @@ void space_split ( struct space *s , struct cell *c ) { ...@@ -1444,6 +1377,9 @@ void space_split ( struct space *s , struct cell *c ) {
void space_recycle ( struct space *s , struct cell *c ) { void space_recycle ( struct space *s , struct cell *c ) {
/* Lock the space. */
lock_lock( &s->lock );
/* Clear the cell. */ /* Clear the cell. */
if ( lock_destroy( &c->lock ) != 0 ) if ( lock_destroy( &c->lock ) != 0 )
error( "Failed to destroy spinlock." ); error( "Failed to destroy spinlock." );
...@@ -1457,6 +1393,9 @@ void space_recycle ( struct space *s , struct cell *c ) { ...@@ -1457,6 +1393,9 @@ void space_recycle ( struct space *s , struct cell *c ) {
s->cells_new = c; s->cells_new = c;
s->tot_cells -= 1; s->tot_cells -= 1;
/* Unlock the space. */
lock_unlock_blind( &s->lock );
} }
...@@ -1471,6 +1410,9 @@ struct cell *space_getcell ( struct space *s ) { ...@@ -1471,6 +1410,9 @@ struct cell *space_getcell ( struct space *s ) {
struct cell *c; struct cell *c;
int k; int k;
/* Lock the space. */
lock_lock( &s->lock );
/* Is the buffer empty? */ /* Is the buffer empty? */
if ( s->cells_new == NULL ) { if ( s->cells_new == NULL ) {
if ( posix_memalign( (void *)&s->cells_new , 64 , space_cellallocchunk * sizeof(struct cell) ) != 0 ) if ( posix_memalign( (void *)&s->cells_new , 64 , space_cellallocchunk * sizeof(struct cell) ) != 0 )
...@@ -1491,6 +1433,9 @@ struct cell *space_getcell ( struct space *s ) { ...@@ -1491,6 +1433,9 @@ struct cell *space_getcell ( struct space *s ) {
if ( lock_init( &c->lock ) != 0 ) if ( lock_init( &c->lock ) != 0 )
error( "Failed to initialize cell spinlock." ); error( "Failed to initialize cell spinlock." );
/* Unlock the space. */
lock_unlock_blind( &s->lock );
return c; return c;
} }
...@@ -1519,8 +1464,10 @@ void space_init ( struct space *s , double dim[3] , struct part *parts , int N , ...@@ -1519,8 +1464,10 @@ void space_init ( struct space *s , double dim[3] , struct part *parts , int N ,
s->periodic = periodic; s->periodic = periodic;
s->nr_parts = N; s->nr_parts = N;
s->parts = parts; s->parts = parts;
if ( lock_init( &s->task_lock ) != 0 )
error( "Failed to create task spin-lock." ); /* Init the space lock. */
if ( lock_init( &s->lock ) != 0 )
error( "Failed to create space spin-lock." );
/* Build the cells and the tasks. */ /* Build the cells and the tasks. */
space_rebuild( s , 1 , h_max ); space_rebuild( s , 1 , h_max );
......
...@@ -87,9 +87,11 @@ struct space { ...@@ -87,9 +87,11 @@ struct space {
/* The list of tasks. */ /* The list of tasks. */
struct task *tasks; struct task *tasks;
int nr_tasks, next_task, tasks_size; int nr_tasks, tasks_size;
int *tasks_ind; int *tasks_ind;
lock_type task_lock;
/* General-purpose lock for this space. */
lock_type lock;
}; };
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
const char *taskID_names[task_type_count] = { "none" , "sort" , "self" , "pair" , "sub" , "ghost" }; const char *taskID_names[task_type_count] = { "none" , "sort" , "self" , "pair" , "sub" , "ghost" };
/* Error macro. */ /* Error macro. */
#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } #define error(s) { fprintf( stderr , "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); }
/** /**
...@@ -55,10 +55,13 @@ void task_rmunlock( struct task *ta , struct task *tb ) { ...@@ -55,10 +55,13 @@ void task_rmunlock( struct task *ta , struct task *tb ) {
int k; int k;
lock_lock( &ta->lock );
for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ ) for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ )
if ( ta->unlock_tasks[k] == tb ) { if ( ta->unlock_tasks[k] == tb ) {
ta->nr_unlock_tasks -= 1; ta->nr_unlock_tasks -= 1;
ta->unlock_tasks[k] = ta->unlock_tasks[ ta->nr_unlock_tasks ]; ta->unlock_tasks[k] = ta->unlock_tasks[ ta->nr_unlock_tasks ];
lock_unlock_blind( &ta->lock );
return; return;
} }
error( "Task not found." ); error( "Task not found." );
...@@ -80,12 +83,16 @@ void task_rmunlock_blind( struct task *ta , struct task *tb ) { ...@@ -80,12 +83,16 @@ void task_rmunlock_blind( struct task *ta , struct task *tb ) {
int k; int k;
lock_lock( &ta->lock );
for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ ) for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ )
if ( ta->unlock_tasks[k] == tb ) { if ( ta->unlock_tasks[k] == tb ) {
ta->nr_unlock_tasks -= 1; ta->nr_unlock_tasks -= 1;
ta->unlock_tasks[k] = ta->unlock_tasks[ ta->nr_unlock_tasks ]; ta->unlock_tasks[k] = ta->unlock_tasks[ ta->nr_unlock_tasks ];
return; break;
} }
lock_unlock_blind( &ta->lock );
} }
...@@ -105,10 +112,14 @@ void task_addunlock( struct task *ta , struct task *tb ) { ...@@ -105,10 +112,14 @@ void task_addunlock( struct task *ta , struct task *tb ) {
if ( ta == NULL || tb == NULL ) if ( ta == NULL || tb == NULL )
return; return;
lock_lock( &ta->lock );
/* Check if ta already unlocks tb. */ /* Check if ta already unlocks tb. */
for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ ) for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ )
if ( ta->unlock_tasks[k] == tb ) if ( ta->unlock_tasks[k] == tb ) {
lock_unlock_blind( &ta->lock );
return; return;
}
if ( ta->nr_unlock_tasks == task_maxunlock ) if ( ta->nr_unlock_tasks == task_maxunlock )
error( "Too many unlock_tasks in task." );