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