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