X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FTask.c;h=46cfa264b20b8bbaf7a2a5bfb7fbfc6fb6975a5b;hb=89d6434a7ddb499c5b09eb3c70437782b0dcd501;hp=6579abc3aeb558d6dfa0cf1f96175749cd4577cc;hpb=4ff7d0b15769c360b3f54e857a6880edbcd46d26;p=ghc-hetmet.git diff --git a/ghc/rts/Task.c b/ghc/rts/Task.c index 6579abc..46cfa26 100644 --- a/ghc/rts/Task.c +++ b/ghc/rts/Task.c @@ -1,25 +1,13 @@ /* ----------------------------------------------------------------------------- * - * (c) The GHC Team 2001- + * (c) The GHC Team 2001-2005 * * The task manager subsystem. Tasks execute STG code, with this * module providing the API which the Scheduler uses to control their * creation and destruction. - * - * Two kinds of RTS builds uses 'tasks' - the SMP and the - * 'native thread-friendly' builds. * - * The SMP build lets multiple tasks concurrently execute STG code, - * all sharing vital internal RTS data structures in a controlled manner - * (see details elsewhere...ToDo: fill in ref!) - * - * The 'threads' build has at any one time only one task executing STG - * code, other tasks are either busy executing code outside the RTS - * (e.g., a C call) or waiting for their turn to (again) evaluate some - * STG code. A task relinquishes its RTS token when it is asked to - * evaluate an external (C) call. - * * -------------------------------------------------------------------------*/ + #include "Rts.h" #if defined(RTS_SUPPORTS_THREADS) /* to the end */ #include "RtsUtils.h" @@ -28,184 +16,249 @@ #include "Stats.h" #include "RtsFlags.h" #include "Schedule.h" +#include "Hash.h" +#include "Capability.h" -/* There's not all that much code that is shared between the - * SMP and threads version of the 'task manager.' A sign - * that the code ought to be structured differently..(Maybe ToDo). - */ - -/* - * The following Task Manager-local variables are assumed to be - * accessed with the RTS lock in hand. - */ -#if defined(SMP) -static TaskInfo* taskTable; +#if HAVE_SIGNAL_H +#include #endif -/* upper bound / the number of tasks created. */ -static nat maxTasks; -/* number of tasks currently created */ -static nat taskCount; -#if defined(SMP) +#define INIT_TASK_TABLE_SIZE 16 + +TaskInfo* taskTable; +static nat taskTableSize; + +// maps OSThreadID to TaskInfo* +HashTable *taskHash; + +nat taskCount; +static nat tasksRunning; +static nat workerCount; + +#define DEFAULT_MAX_WORKERS 64 +nat maxWorkers; // we won't create more workers than this + void -startTaskManager( nat maxCount, void (*taskStart)(void) ) +initTaskManager (void) { - nat i; - static int initialized = 0; - - if (!initialized) { - - taskCount = 0; - maxTasks = maxCount; - /* allocate table holding task metadata */ + static int initialized = 0; + + if (!initialized) { +#if defined(SMP) + taskTableSize = stg_max(INIT_TASK_TABLE_SIZE, + RtsFlags.ParFlags.nNodes * 2); +#else + taskTableSize = INIT_TASK_TABLE_SIZE; +#endif + taskTable = stgMallocBytes( taskTableSize * sizeof(TaskInfo), + "initTaskManager"); + + taskCount = 0; + workerCount = 0; + tasksRunning = 0; + + taskHash = allocHashTable(); - if (maxCount > 0) { - taskTable = stgMallocBytes(maxCount * sizeof(TaskInfo), - "startTaskManager:tasks"); - - /* and eagerly create them all. */ - for (i = 0; i < maxCount; i++) { - startTask(taskStart); - taskCount++; - } + maxWorkers = DEFAULT_MAX_WORKERS; + + initialized = 1; } - initialized = 1; - } } -void -startTask ( void (*taskStart)(void) ) +static void +expandTaskTable (void) { - int r; - OSThreadId tid; + nat i; - r = createOSThread(&tid,taskStart); - if (r != 0) { - barf("startTask: Can't create new task"); - } - - taskTable[taskCount].id = tid; - taskTable[taskCount].mut_time = 0.0; - taskTable[taskCount].mut_etime = 0.0; - taskTable[taskCount].gc_time = 0.0; - taskTable[taskCount].gc_etime = 0.0; - taskTable[taskCount].elapsedtimestart = stat_getElapsedTime(); + taskTableSize *= 2; + taskTable = stgReallocBytes(taskTable, taskTableSize * sizeof(TaskInfo), + "expandTaskTable"); - IF_DEBUG(scheduler,fprintf(stderr,"scheduler: Started task: %ld\n",tid);); - return; + /* Have to update the hash table now... */ + for (i = 0; i < taskCount; i++) { + removeHashTable( taskHash, taskTable[i].id, NULL ); + insertHashTable( taskHash, taskTable[i].id, &taskTable[i] ); + } } void -stopTaskManager () +stopTaskManager (void) { - nat i; - OSThreadId tid = osThreadId(); - - /* Don't want to use pthread_cancel, since we'd have to install - * these silly exception handlers (pthread_cleanup_{push,pop}) around - * all our locks. - */ -#if 0 - /* Cancel all our tasks */ - for (i = 0; i < RtsFlags.ParFlags.nNodes; i++) { - pthread_cancel(taskTable[i].id); - } - - /* Wait for all the tasks to terminate */ - for (i = 0; i < maxCount; i++) { - IF_DEBUG(scheduler,fprintf(stderr,"scheduler: waiting for task %ld\n", - taskTable[i].id)); - pthread_join(taskTable[i].id, NULL); - } -#endif + nat i; - /* Send 'em all a SIGHUP. That should shut 'em up. */ - await_death = maxCount - 1; - for (i = 0; i < maxCount; i++) { - /* don't cancel the thread running this piece of code. */ - if ( taskTable[i].id != tid ) { - pthread_kill(taskTable[i].id,SIGTERM); + IF_DEBUG(scheduler, sched_belch("stopping task manager, %d tasks still running", tasksRunning)); + for (i = 1000; i > 0; i--) { + if (tasksRunning == 0) { + IF_DEBUG(scheduler, sched_belch("all tasks stopped")); + return; + } + IF_DEBUG(scheduler, sched_belch("yielding")); + prodWorker(); + yieldThread(); } - } - while (await_death > 0) { - sched_yield(); - } - - return; + IF_DEBUG(scheduler, sched_belch("%d tasks still running, exiting anyway", tasksRunning)); + + /* + OLD CODE follows: + */ +#if old_code + /* Send 'em all a SIGHUP. That should shut 'em up. */ + awaitDeath = taskCount==0 ? 0 : taskCount-1; + for (i = 0; i < taskCount; i++) { + /* don't cancel the thread running this piece of code. */ + if ( taskTable[i].id != tid ) { + pthread_kill(taskTable[i].id,SIGTERM); + } + } + while (awaitDeath > 0) { + sched_yield(); + } +#endif // old_code } -void -resetTaskManagerAfterFork () + +rtsBool +startTasks (nat num, void (*taskStart)(void)) { - barf("resetTaskManagerAfterFork not implemented for SMP"); + nat i; + for (i = 0; i < num; i++) { + if (!startTask(taskStart)) { + return rtsFalse; + } + } + return rtsTrue; } -#else -/************ THREADS version *****************/ - -void -startTaskManager( nat maxCount, - void (*taskStart)(void) STG_UNUSED ) +static TaskInfo* +newTask (OSThreadId id, rtsBool is_worker) { - /* In the threaded case, maxCount is used to limit the - the creation of worker tasks. Tasks are created lazily, i.e., - when the current task gives up the token on executing - STG code. - */ - maxTasks = maxCount; - taskCount = 0; + long currentElapsedTime, currentUserTime, elapsedGCTime; + TaskInfo *task_info; + + if (taskCount >= taskTableSize) { + expandTaskTable(); + } + + insertHashTable( taskHash, id, &(taskTable[taskCount]) ); + + stat_getTimes(¤tElapsedTime, ¤tUserTime, &elapsedGCTime); + + task_info = &taskTable[taskCount]; + + task_info->id = id; + task_info->is_worker = is_worker; + task_info->stopped = rtsFalse; + task_info->mut_time = 0.0; + task_info->mut_etime = 0.0; + task_info->gc_time = 0.0; + task_info->gc_etime = 0.0; + task_info->muttimestart = currentUserTime; + task_info->elapsedtimestart = currentElapsedTime; + + taskCount++; + workerCount++; + tasksRunning++; + + IF_DEBUG(scheduler,sched_belch("startTask: new task %ld (total_count: %d; waiting: %d)\n", id, taskCount, rts_n_waiting_tasks);); + + return task_info; } -void -startTask ( void (*taskStart)(void) ) +rtsBool +startTask (void (*taskStart)(void)) { int r; OSThreadId tid; - - /* If more than one worker thread is known to be blocked waiting - on thread_ready_cond, don't create a new one. - */ - if ( rts_n_waiting_tasks > 0) { - IF_DEBUG(scheduler,fprintf(stderr, - "scheduler: startTask: %d tasks waiting, not creating new one.\n", - rts_n_waiting_tasks);); - // the task will run as soon as a capability is available, - // so there's no need to wake it. - return; - } - - /* If the task limit has been reached, just return. */ - if (maxTasks > 0 && taskCount == maxTasks) { - IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: task limit (%d) reached, not creating new one.\n",maxTasks)); - return; - } - r = createOSThread(&tid,taskStart); if (r != 0) { barf("startTask: Can't create new task"); } - taskCount++; - - IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: new task %ld (total_count: %d; waiting: %d)\n", tid, taskCount, rts_n_waiting_tasks);); - return; + newTask (tid, rtsTrue); + return rtsTrue; } +TaskInfo * +threadIsTask (OSThreadId id) +{ + TaskInfo *task_info; + + task_info = lookupHashTable(taskHash, id); + if (task_info != NULL) { + if (task_info->stopped) { + task_info->stopped = rtsFalse; + } + return task_info; + } + return newTask(id, rtsFalse); +} + +TaskInfo * +taskOfId (OSThreadId id) +{ + return lookupHashTable(taskHash, id); +} void -stopTaskManager () +taskStop (void) { + OSThreadId id; + long currentElapsedTime, currentUserTime, elapsedGCTime; + TaskInfo *task_info; + + id = osThreadId(); + task_info = taskOfId(id); + if (task_info == NULL) { + debugBelch("taskStop: not a task"); + return; + } + ASSERT(task_info->id == id); + stat_getTimes(¤tElapsedTime, ¤tUserTime, &elapsedGCTime); + + task_info->mut_time = + currentUserTime - task_info->muttimestart - task_info->gc_time; + task_info->mut_etime = + currentElapsedTime - task_info->elapsedtimestart - elapsedGCTime; + + if (task_info->mut_time < 0.0) { task_info->mut_time = 0.0; } + if (task_info->mut_etime < 0.0) { task_info->mut_etime = 0.0; } + + task_info->stopped = rtsTrue; + tasksRunning--; } void -resetTaskManagerAfterFork ( void ) +resetTaskManagerAfterFork (void) { - rts_n_waiting_tasks = 0; - taskCount = 0; + rts_n_waiting_tasks = 0; + taskCount = 0; } -#endif +rtsBool +maybeStartNewWorker (void (*taskStart)(void)) +{ + /* + * If more than one worker thread is known to be blocked waiting + * on thread_ready_cond, don't create a new one. + */ + if ( rts_n_waiting_tasks > 0) { + IF_DEBUG(scheduler,sched_belch( + "startTask: %d tasks waiting, not creating new one", + rts_n_waiting_tasks);); + // the task will run as soon as a capability is available, + // so there's no need to wake it. + return rtsFalse; + } + + /* If the task limit has been reached, just return. */ + if (maxWorkers > 0 && workerCount >= maxWorkers) { + IF_DEBUG(scheduler,sched_belch("startTask: worker limit (%d) reached, not creating new one",maxWorkers)); + return rtsFalse; + } + + return startTask(taskStart); +} #endif /* RTS_SUPPORTS_THREADS */