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