0d75df82039fb032146caf58b4d3cdb02f935e4a
[ghc-hetmet.git] / ghc / rts / Task.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team 2001-2005
4  *
5  * The task manager subsystem.  Tasks execute STG code, with this
6  * module providing the API which the Scheduler uses to control their
7  * creation and destruction.
8  * 
9  * -------------------------------------------------------------------------*/
10
11 #include "Rts.h"
12 #if defined(RTS_SUPPORTS_THREADS) /* to the end */
13 #include "RtsUtils.h"
14 #include "OSThreads.h"
15 #include "Task.h"
16 #include "Stats.h"
17 #include "RtsFlags.h"
18 #include "Schedule.h"
19 #include "Hash.h"
20 #include "Capability.h"
21
22 #if HAVE_SIGNAL_H
23 #include <signal.h>
24 #endif
25
26 #define INIT_TASK_TABLE_SIZE 16
27
28 TaskInfo* taskTable;
29 static nat taskTableSize;
30
31 // maps OSThreadID to TaskInfo*
32 HashTable *taskHash;
33
34 nat taskCount;
35 static nat tasksRunning;
36 static nat workerCount;
37
38 #define DEFAULT_MAX_WORKERS 64
39 nat maxWorkers; // we won't create more workers than this
40
41 void
42 initTaskManager (void)
43 {
44     static int initialized = 0;
45
46     if (!initialized) {
47 #if defined(SMP)
48         taskTableSize = stg_max(INIT_TASK_TABLE_SIZE, 
49                                 RtsFlags.ParFlags.nNodes * 2);
50 #else
51         taskTableSize = INIT_TASK_TABLE_SIZE;
52 #endif
53         taskTable = stgMallocBytes( taskTableSize * sizeof(TaskInfo),
54                                     "initTaskManager");
55     
56         taskCount = 0;
57         workerCount = 0;
58         tasksRunning = 0;
59
60         taskHash = allocHashTable();
61   
62         maxWorkers = DEFAULT_MAX_WORKERS;
63
64         initialized = 1;
65     }
66 }
67
68 static void
69 expandTaskTable (void)
70 {
71     nat i;
72
73     taskTableSize *= 2;
74     taskTable = stgReallocBytes(taskTable, taskTableSize * sizeof(TaskInfo),
75                                 "expandTaskTable");
76
77     /* Have to update the hash table now... */
78     for (i = 0; i < taskCount; i++) {
79         removeHashTable( taskHash, taskTable[i].id, NULL );
80         insertHashTable( taskHash, taskTable[i].id, &taskTable[i] );
81     }
82 }
83
84 void
85 stopTaskManager (void)
86 {
87     IF_DEBUG(scheduler, sched_belch("stopping task manager, %d tasks still running", tasksRunning));
88 }
89
90
91 rtsBool
92 startTasks (nat num, void (*taskStart)(void))
93 {
94     nat i; 
95     for (i = 0; i < num; i++) {
96         if (!startTask(taskStart)) {
97             return rtsFalse;
98         }
99     }
100     return rtsTrue;
101 }
102
103 static TaskInfo*
104 newTask (OSThreadId id, rtsBool is_worker)
105 {
106     long currentElapsedTime, currentUserTime, elapsedGCTime;
107     TaskInfo *task_info;
108
109     if (taskCount >= taskTableSize) {
110         expandTaskTable();
111     }
112     
113     insertHashTable( taskHash, id, &(taskTable[taskCount]) );
114     
115     stat_getTimes(&currentElapsedTime, &currentUserTime, &elapsedGCTime);
116     
117     task_info = &taskTable[taskCount];
118     
119     task_info->id = id;
120     task_info->is_worker = is_worker;
121     task_info->stopped = rtsFalse;
122     task_info->mut_time = 0.0;
123     task_info->mut_etime = 0.0;
124     task_info->gc_time = 0.0;
125     task_info->gc_etime = 0.0;
126     task_info->muttimestart = currentUserTime;
127     task_info->elapsedtimestart = currentElapsedTime;
128     
129     taskCount++;
130     workerCount++;
131     tasksRunning++;
132
133     IF_DEBUG(scheduler,sched_belch("startTask: new task %ld (total_count: %d; waiting: %d)\n", id, taskCount, rts_n_waiting_tasks););
134     
135     return task_info;
136 }
137
138 rtsBool
139 startTask (void (*taskStart)(void))
140 {
141   int r;
142   OSThreadId tid;
143
144   r = createOSThread(&tid,taskStart);
145   if (r != 0) {
146     barf("startTask: Can't create new task");
147   }
148   newTask (tid, rtsTrue);
149   return rtsTrue;
150 }
151
152 TaskInfo *
153 threadIsTask (OSThreadId id)
154 {
155     TaskInfo *task_info;
156     
157     task_info = lookupHashTable(taskHash, id);
158     if (task_info != NULL) {
159         if (task_info->stopped) {
160             task_info->stopped = rtsFalse;
161         }
162         return task_info;
163     }
164
165     return newTask(id, rtsFalse);
166 }
167
168 TaskInfo *
169 taskOfId (OSThreadId id)
170 {
171     return lookupHashTable(taskHash, id);
172 }
173
174 void
175 taskStop (void)
176 {
177     OSThreadId id;
178     long currentElapsedTime, currentUserTime, elapsedGCTime;
179     TaskInfo *task_info;
180
181     id = osThreadId();
182     task_info = taskOfId(id);
183     if (task_info == NULL) {
184         debugBelch("taskStop: not a task");
185         return;
186     }
187     ASSERT(task_info->id == id);
188
189     stat_getTimes(&currentElapsedTime, &currentUserTime, &elapsedGCTime);
190     
191     task_info->mut_time = 
192         currentUserTime - task_info->muttimestart - task_info->gc_time;
193     task_info->mut_etime = 
194         currentElapsedTime - task_info->elapsedtimestart - elapsedGCTime;
195
196     if (task_info->mut_time < 0.0)  { task_info->mut_time = 0.0;  }
197     if (task_info->mut_etime < 0.0) { task_info->mut_etime = 0.0; }
198
199     task_info->stopped = rtsTrue;
200     tasksRunning--;
201 }
202
203 void
204 resetTaskManagerAfterFork (void)
205 {
206     rts_n_waiting_tasks = 0;
207     taskCount = 0;
208 }
209
210 rtsBool
211 maybeStartNewWorker (void (*taskStart)(void))
212 {
213     /* 
214      * If more than one worker thread is known to be blocked waiting
215      * on thread_ready_cond, don't create a new one.
216      */
217     if ( rts_n_waiting_tasks > 0) {
218         IF_DEBUG(scheduler,sched_belch(
219                      "startTask: %d tasks waiting, not creating new one", 
220                      rts_n_waiting_tasks););
221         // the task will run as soon as a capability is available,
222         // so there's no need to wake it.
223         return rtsFalse;
224     }
225     
226     /* If the task limit has been reached, just return. */
227     if (maxWorkers > 0 && workerCount >= maxWorkers) {
228         IF_DEBUG(scheduler,sched_belch("startTask: worker limit (%d) reached, not creating new one",maxWorkers));
229         return rtsFalse;
230     }
231     
232     return startTask(taskStart);
233 }
234
235 #endif /* RTS_SUPPORTS_THREADS */