Add thread affinity support for FreeBSD
[ghc-hetmet.git] / rts / posix / OSThreads.c
1 /* ---------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 2001-2005
4  *
5  * Accessing OS threads functionality in a (mostly) OS-independent
6  * manner. 
7  *
8  * --------------------------------------------------------------------------*/
9
10 #if defined(__linux__)
11 /* We want GNU extensions in DEBUG mode for mutex error checking */
12 /* We also want the affinity API, which requires _GNU_SOURCE */
13 #define _GNU_SOURCE
14 #endif
15
16 #include "PosixSource.h"
17
18 #if defined(freebsd_HOST_OS)
19 /* Inclusion of system headers usually requires __BSD_VISIBLE on FreeBSD,
20  * because of some specific types, like u_char, u_int, etc. */
21 #define __BSD_VISIBLE   1
22 #endif
23
24 #include "Rts.h"
25
26 #if defined(THREADED_RTS)
27 #include "RtsUtils.h"
28 #include "Task.h"
29
30 #if HAVE_STRING_H
31 #include <string.h>
32 #endif
33
34 #if defined(darwin_HOST_OS) || defined(freebsd_HOST_OS)
35 #include <sys/types.h>
36 #include <sys/sysctl.h>
37 #endif
38
39 #if !defined(HAVE_PTHREAD_H)
40 #error pthreads.h is required for the threaded RTS on Posix platforms
41 #endif
42
43 #if defined(HAVE_SCHED_H)
44 #include <sched.h>
45 #endif
46
47 #if defined(HAVE_SYS_CPUSET_H)
48 #include <sys/param.h>
49 #include <sys/cpuset.h>
50 #endif
51
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55
56 #if defined(darwin_HOST_OS)
57 #include <mach/mach.h>
58 #endif
59
60 /*
61  * This (allegedly) OS threads independent layer was initially
62  * abstracted away from code that used Pthreads, so the functions
63  * provided here are mostly just wrappers to the Pthreads API.
64  *
65  */
66
67 void
68 initCondition( Condition* pCond )
69 {
70   pthread_cond_init(pCond, NULL);
71   return;
72 }
73
74 void
75 closeCondition( Condition* pCond )
76 {
77   pthread_cond_destroy(pCond);
78   return;
79 }
80
81 rtsBool
82 broadcastCondition ( Condition* pCond )
83 {
84   return (pthread_cond_broadcast(pCond) == 0);
85 }
86
87 rtsBool
88 signalCondition ( Condition* pCond )
89 {
90   return (pthread_cond_signal(pCond) == 0);
91 }
92
93 rtsBool
94 waitCondition ( Condition* pCond, Mutex* pMut )
95 {
96   return (pthread_cond_wait(pCond,pMut) == 0);
97 }
98
99 void
100 yieldThread()
101 {
102   sched_yield();
103   return;
104 }
105
106 void
107 shutdownThread()
108 {
109   pthread_exit(NULL);
110 }
111
112 int
113 createOSThread (OSThreadId* pId, OSThreadProc *startProc, void *param)
114 {
115   int result = pthread_create(pId, NULL, (void *(*)(void *))startProc, param);
116   if(!result)
117     pthread_detach(*pId);
118   return result;
119 }
120
121 OSThreadId
122 osThreadId()
123 {
124   return pthread_self();
125 }
126
127 rtsBool
128 osThreadIsAlive(OSThreadId id STG_UNUSED)
129 {
130     // no good way to implement this on POSIX, AFAICT.  Returning true
131     // is safe.
132     return rtsTrue;
133 }
134
135 void
136 initMutex(Mutex* pMut)
137 {
138 #if defined(DEBUG)
139     pthread_mutexattr_t attr;
140     pthread_mutexattr_init(&attr);
141     pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK);
142     pthread_mutex_init(pMut,&attr);
143 #else
144     pthread_mutex_init(pMut,NULL);
145 #endif
146     return;
147 }
148 void
149 closeMutex(Mutex* pMut)
150 {
151     pthread_mutex_destroy(pMut);
152 }
153
154 void
155 newThreadLocalKey (ThreadLocalKey *key)
156 {
157     int r;
158     if ((r = pthread_key_create(key, NULL)) != 0) {
159         barf("newThreadLocalKey: %s", strerror(r));
160     }
161 }
162
163 void *
164 getThreadLocalVar (ThreadLocalKey *key)
165 {
166     return pthread_getspecific(*key);
167     // Note: a return value of NULL can indicate that either the key
168     // is not valid, or the key is valid and the data value has not
169     // yet been set.  We need to use the latter case, so we cannot
170     // detect errors here.
171 }
172
173 void
174 setThreadLocalVar (ThreadLocalKey *key, void *value)
175 {
176     int r;
177     if ((r = pthread_setspecific(*key,value)) != 0) {
178         barf("setThreadLocalVar: %s", strerror(r));
179     }
180 }
181
182 void
183 freeThreadLocalKey (ThreadLocalKey *key)
184 {
185     int r;
186     if ((r = pthread_key_delete(*key)) != 0) {
187         barf("freeThreadLocalKey: %s", strerror(r));
188     }
189 }
190
191 static void *
192 forkOS_createThreadWrapper ( void * entry )
193 {
194     Capability *cap;
195     cap = rts_lock();
196     cap = rts_evalStableIO(cap, (HsStablePtr) entry, NULL);
197     taskTimeStamp(myTask());
198     rts_unlock(cap);
199     return NULL;
200 }
201
202 int
203 forkOS_createThread ( HsStablePtr entry )
204 {
205     pthread_t tid;
206     int result = pthread_create(&tid, NULL,
207                                 forkOS_createThreadWrapper, (void*)entry);
208     if(!result)
209         pthread_detach(tid);
210     return result;
211 }
212
213 nat
214 getNumberOfProcessors (void)
215 {
216     static nat nproc = 0;
217
218     if (nproc == 0) {
219 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
220         nproc = sysconf(_SC_NPROCESSORS_ONLN);
221 #elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF)
222         nproc = sysconf(_SC_NPROCESSORS_CONF);
223 #elif defined(darwin_HOST_OS) || defined(freebsd_HOST_OS)
224         size_t size = sizeof(nat);
225         if(0 != sysctlbyname("hw.ncpu",&nproc,&size,NULL,0))
226             nproc = 1;
227 #else
228         nproc = 1;
229 #endif
230     }
231
232     return nproc;
233 }
234
235 #if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_SETAFFINITY)
236 // Schedules the thread to run on CPU n of m.  m may be less than the
237 // number of physical CPUs, in which case, the thread will be allowed
238 // to run on CPU n, n+m, n+2m etc.
239 void
240 setThreadAffinity (nat n, nat m)
241 {
242     nat nproc;
243     cpu_set_t cs;
244     nat i;
245
246     nproc = getNumberOfProcessors();
247     CPU_ZERO(&cs);
248     for (i = n; i < nproc; i+=m) {
249         CPU_SET(i, &cs);
250     }
251     sched_setaffinity(0, sizeof(cpu_set_t), &cs);
252 }
253
254 #elif defined(darwin_HOST_OS) && defined(THREAD_AFFINITY_POLICY)
255 // Schedules the current thread in the affinity set identified by tag n.
256 void
257 setThreadAffinity (nat n, nat m GNUC3_ATTRIBUTE(__unused__))
258 {
259     thread_affinity_policy_data_t policy;
260
261     policy.affinity_tag = n;
262     thread_policy_set(mach_thread_self(), 
263                       THREAD_AFFINITY_POLICY,
264                       (thread_policy_t) &policy,
265                       THREAD_AFFINITY_POLICY_COUNT);
266 }
267
268 #elif defined(HAVE_SYS_CPUSET_H) /* FreeBSD 7.1+ */
269 void
270 setThreadAffinity(nat n, nat m)
271 {
272         nat nproc;
273         cpuset_t cs;
274         nat i;
275
276         nproc = getNumberOfProcessors();
277         CPU_ZERO(&cs);
278
279         for (i = n; i < nproc; i += m)
280                 CPU_SET(i, &cs);
281
282         cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), &cs);
283 }
284
285 #else
286 void
287 setThreadAffinity (nat n GNUC3_ATTRIBUTE(__unused__), 
288                    nat m GNUC3_ATTRIBUTE(__unused__))
289 {
290 }
291 #endif
292
293 #else /* !defined(THREADED_RTS) */
294
295 int
296 forkOS_createThread ( HsStablePtr entry STG_UNUSED )
297 {
298     return -1;
299 }
300
301 #endif /* !defined(THREADED_RTS) */