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