lib & contrib & tests: update users of `cflags` and `ldflags`
[nit.git] / lib / pthreads / pthreads.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Main POSIX threads support and intro the classes `Thread`, `Mutex` and `Barrier`
18 module pthreads is
19 cflags "-pthread"
20 ldflags "-pthread"
21 pkgconfig "bdw-gc"
22 end
23
24 #
25 ## Native part
26 #
27 # Nity part at the bottom of the module.
28 #
29
30 in "C Header" `{
31 #include <pthread.h>
32 `}
33
34 in "C" `{
35 // TODO protect with: #ifdef WITH_LIBGC
36 // We might have to add the next line to gc_chooser.c too, especially
37 // if we get an error like "thread not registered with GC".
38 #ifndef ANDROID
39 #define GC_THREADS
40 #include <gc.h>
41 #endif
42 `}
43
44 redef class Sys
45
46 # `NativePthread` for running thread
47 private fun native_pthread_self: NativePthread `{
48 pthread_t *id = malloc(sizeof(pthread_t));
49 *id = pthread_self();
50 return id;
51 `}
52
53 private var self_thread_key = new NativePthreadKey
54
55 private var main_thread_cache: nullable MainThread = null
56 private var main_thread_mutex = new Mutex
57
58 # Handle to the program's main thread
59 fun main_thread: MainThread
60 do
61 var cache = main_thread_cache
62 if cache != null then return cache
63
64 main_thread_mutex.lock
65
66 # Recheck if cache has been updated since lock has been unlocked/locked
67 cache = main_thread_cache
68 if cache != null then
69 main_thread_mutex.unlock
70 return cache
71 end
72
73 # Create a `MainThread` exactly once
74 var thread = new MainThread
75 thread.native = sys.native_pthread_self
76 main_thread_cache = thread
77
78 main_thread_mutex.unlock
79 return thread
80 end
81 end
82
83 private extern class NativePthread in "C" `{ pthread_t * `}
84
85 new create(nit_thread: Thread) import Thread.main_intern `{
86 pthread_attr_t attr;
87 pthread_attr_init(&attr);
88
89 pthread_t thread;
90 int r = pthread_create(&thread, &attr, (void * (*)(void *))&Thread_main_intern, nit_thread);
91
92 if (r == 0) {
93 pthread_t *pthread = malloc(sizeof(pthread_t));
94 memmove(pthread, &thread, sizeof(pthread_t));
95 return pthread;
96 }
97 return NULL;
98 `}
99
100 new create_ex(nit_thread: Thread, attr: NativePthreadAttr) import Thread.main_intern `{
101 pthread_t thread;
102 int r = pthread_create(&thread, attr, (void * (*)(void *))&Thread_main_intern, nit_thread);
103
104 if (r == 0) {
105 pthread_t *pthread = malloc(sizeof(pthread_t));
106 memmove(pthread, &thread, sizeof(pthread_t));
107 return pthread;
108 }
109 return NULL;
110 `}
111
112 fun join: nullable Object `{
113 void *thread_return;
114 pthread_join(*recv, &thread_return);
115 if(thread_return == NULL) thread_return = null_Object();
116 return (nullable_Object)thread_return;
117 `}
118
119 fun equal(other: NativePthread): Bool `{ return pthread_equal(*recv, *other); `}
120
121 fun kill(signal: Int) `{ pthread_kill(*recv, signal); `}
122 end
123
124 private extern class NativePthreadAttr in "C" `{ pthread_attr_t * `}
125 new `{
126 pthread_attr_t attr;
127 int r = pthread_attr_init(&attr);
128 if (r == 0) {
129 pthread_attr_t *pattr = malloc(sizeof(pthread_attr_t));
130 memmove(pattr, &attr, sizeof(pthread_attr_t));
131 return pattr;
132 }
133 return NULL;
134 `}
135
136 fun destroy `{
137 pthread_attr_destroy(recv);
138 `}
139
140 # Most features of this class are still TODO
141 #
142 # * pthread_attr_setaffinity_np(3)
143 # * pthread_attr_setdetachstate
144 # * pthread_attr_setguardsize
145 # * pthread_attr_setinheritsched
146 # * pthread_attr_setschedparam
147 # * pthread_attr_setschedpolicy
148 # * pthread_attr_setscope
149 # * pthread_attr_setstack
150 # * pthread_attr_setstackaddr
151 # * pthread_attr_setstacksize
152 end
153
154 private extern class NativePthreadMutex in "C" `{ pthread_mutex_t * `}
155 new (attr: NativePthreadMutexAttr) `{
156 pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t));
157 int res = pthread_mutex_init(mutex, attr);
158 return mutex;
159 `}
160
161 fun destroy `{ pthread_mutex_destroy(recv); `}
162
163 fun lock `{ pthread_mutex_lock(recv); `}
164 fun try_lock: Bool `{ return pthread_mutex_trylock(recv); `}
165 fun unlock `{ pthread_mutex_unlock(recv); `}
166 end
167
168 private extern class NativePthreadMutexAttr in "C" `{ pthread_mutexattr_t * `}
169 new `{
170 pthread_mutexattr_t *attr = malloc(sizeof(pthread_mutexattr_t));
171 int res = pthread_mutexattr_init(attr);
172 return attr;
173 `}
174
175 fun destroy `{ pthread_mutexattr_destroy(recv); `}
176
177 fun set_type_normal `{ pthread_mutexattr_settype(recv, PTHREAD_MUTEX_NORMAL); `}
178 fun set_type_recursive `{ pthread_mutexattr_settype(recv, PTHREAD_MUTEX_RECURSIVE); `}
179 fun set_type_errorcheck `{ pthread_mutexattr_settype(recv, PTHREAD_MUTEX_ERRORCHECK); `}
180
181 # pthread_mutexattr_setpshared
182 # pthread_mutexattr_setprotocol
183 # pthread_mutexattr_setproceiling
184 # pthread_mutexattr_setrobust_np
185 end
186
187 private extern class NativePthreadKey in "C" `{ pthread_key_t * `}
188 new `{
189 pthread_key_t *key = malloc(sizeof(pthread_key_t));
190 int res = pthread_key_create(key, NULL);
191 return key;
192 `}
193
194 fun get: nullable Object `{
195 void *val = pthread_getspecific(*recv);
196 if (val == NULL) val = null_Object();
197 return val;
198 `}
199
200 fun set(val: nullable Object) `{
201 pthread_setspecific(*recv, val);
202 `}
203 end
204
205 private extern class NativePthreadCond in "C" `{ pthread_cond_t * `}
206 new `{
207 pthread_cond_t cond;
208 int r = pthread_cond_init(&cond, NULL);
209 if (r == 0) {
210 pthread_cond_t *pcond = malloc(sizeof(pthread_cond_t));
211 memmove(pcond, &cond, sizeof(pthread_cond_t));
212 return pcond;
213 }
214 return NULL;
215 `}
216
217 fun destroy `{ pthread_cond_destroy(recv); `}
218
219 fun signal `{ pthread_cond_signal(recv); `}
220
221 fun broadcast `{ pthread_cond_broadcast(recv); `}
222
223 fun wait(mutex: NativePthreadMutex) `{ pthread_cond_wait(recv, mutex); `}
224 end
225
226 #
227 ## Nity part
228 #
229 # Cannot be extracted from this module because of the callback from C to `Thread::run`
230 #
231
232 # Handle to a thread
233 #
234 # Instances of this class are each used to launch and control a thread.
235 abstract class Thread
236 super Finalizable
237
238 private var native: nullable NativePthread = null
239
240 # Main method of this thread
241 #
242 # The returned valued is passed to the caller of `join`.
243 fun main: nullable Object do return null
244
245 private fun main_intern: nullable Object
246 do
247 # Register thread local data
248 sys.self_thread_key.set self
249
250 return main
251 end
252
253 # Start executing this thread
254 #
255 # Will launch `main` on a different thread.
256 fun start
257 do
258 if native != null then return
259 native = new NativePthread.create(self)
260 end
261
262 # Join this thread to the calling thread
263 #
264 # Blocks until the method `main` returns or the target thread calls
265 # `Sys::thread_exit`. Returns the object returned from the other thread.
266 #
267 # Stats the thread if now already done by a call to `start`.
268 fun join: nullable Object
269 do
270 if native == null then start
271 var r = native.join
272 native = null
273 return r
274 end
275
276 redef fun finalize
277 do
278 if native == null then return
279 native.free
280 native = null
281 end
282 end
283
284 # The main thread of the program
285 class MainThread
286 super Thread
287
288 private init do end
289 end
290
291 # Exit current thread and return `value` to caller of `Thread::join`
292 fun exit_thread(value: nullable Object) `{ pthread_exit(value); `}
293
294 # Returns the handle to the running `Thread`
295 fun thread: Thread
296 do
297 var key = sys.self_thread_key
298 var val = key.get
299 if val == null then
300 # This is the original thread, get `Sys::main_thread` and store it
301 var thread = sys.main_thread
302 key.set thread
303 return thread
304 end
305
306 assert val isa Thread
307 return val
308 end
309
310 # Mutual exclusion synchronization tool
311 #
312 # Instances of this class can only be acquired by a single thread at any one
313 # point in time. Uses the recursive protocol so they can be locked many time by
314 # the same thread, must then be unlocked as many time.
315 class Mutex
316 super Finalizable
317
318 private var native: nullable NativePthreadMutex is noinit
319
320 init
321 do
322 var attr = new NativePthreadMutexAttr
323 attr.set_type_recursive
324 native = new NativePthreadMutex(attr)
325 attr.destroy
326 attr.free
327 end
328
329 # Acquire this lock, wait until it is available
330 fun lock do native.lock
331
332 # Acquire this lock only if it is available
333 #
334 # Returns `true` if the lock has been acquired.
335 fun try_lock: Bool do return native.try_lock
336
337 # Release this lock, unblocking all callers of `lock`
338 fun unlock do native.unlock
339
340 redef fun finalize
341 do
342 var native = self.native
343 if native != null then
344 native.destroy
345 native.free
346 end
347 self.native = null
348 end
349 end
350
351 # Barrier synchronization tool
352 #
353 # Ensures that `count` threads call and block on `wait` before releasing them.
354 class Barrier
355 super Finalizable
356
357 private var mutex = new Mutex
358 private var cond: nullable NativePthreadCond = new NativePthreadCond
359
360 # Number of threads that must be waiting for `wait` to unblock
361 var count: Int
362
363 private var threads_waiting = 0
364
365 # Wait at this barrier and block until there are a `count` threads waiting
366 fun wait
367 do
368 mutex.lock
369 threads_waiting += 1
370 if threads_waiting == count then
371 threads_waiting = 0
372 cond.broadcast
373 else
374 cond.wait(mutex.native.as(not null))
375 end
376 mutex.unlock
377 end
378
379 redef fun finalize
380 do
381 var cond = self.cond
382 if cond != null then
383 cond.destroy
384 cond.free
385 end
386 self.cond = null
387 end
388 end
389
390 # Print `object` and '\n' with the same system call
391 redef fun print(object)
392 do
393 sys.stdout.write(object.to_s+"\n")
394 end