1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Main POSIX threads support and intro the classes `Thread`, `Mutex` and `Barrier`
19 cflags
"-pthread -Wno-unknown-attributes"
22 new_annotation threaded
28 # Nity part at the bottom of the module.
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".
42 #include "TargetConditionals.h"
43 #if TARGET_OS_IPHONE == 1
48 #if !defined(__ANDROID__) && !defined(IOS)
56 # `NativePthread` for running thread
57 private fun native_pthread_self
: NativePthread `{
58 pthread_t *id = malloc(sizeof(pthread_t));
63 private var self_thread_key
= new NativePthreadKey
65 private var main_thread_cache
: nullable MainThread = null
66 private var main_thread_mutex
= new Mutex
68 # Handle to the program's main thread
69 fun main_thread
: MainThread
71 var cache
= main_thread_cache
72 if cache
!= null then return cache
74 main_thread_mutex
.lock
76 # Recheck if cache has been updated since lock has been unlocked/locked
77 cache
= main_thread_cache
79 main_thread_mutex
.unlock
83 # Create a `MainThread` exactly once
84 var thread
= new MainThread
85 thread
.native
= sys
.native_pthread_self
86 main_thread_cache
= thread
88 main_thread_mutex
.unlock
93 private extern class NativePthread in "C" `{ pthread_t * `}
95 new create(nit_thread: Thread) import Thread.main_intern `{
97 pthread_attr_init
(&attr
);
100 int r
= pthread_create
(&thread
, &attr
, (void
* (*)(void
*))&Thread_main_intern, nit_thread
);
103 pthread_t
*pthread
= malloc
(sizeof
(pthread_t
));
104 memmove
(pthread
, &thread
, sizeof
(pthread_t
));
110 new create_ex(nit_thread: Thread, attr: NativePthreadAttr) import Thread.main_intern `{
112 int r
= pthread_create
(&thread
, attr
, (void
* (*)(void
*))&Thread_main_intern, nit_thread
);
115 pthread_t
*pthread
= malloc
(sizeof
(pthread_t
));
116 memmove
(pthread
, &thread
, sizeof
(pthread_t
));
122 fun join: nullable Object `{
124 pthread_join
(*self, &thread_return
);
125 if(thread_return
== NULL) thread_return
= null_Object
();
126 return (nullable_Object
)thread_return
;
129 fun equal(other: NativePthread): Bool `{ return pthread_equal(*self, *other); `}
131 fun kill
(signal
: Int) `{ pthread_kill(*self, signal); `}
134 private extern class NativePthreadAttr in "C" `{ pthread_attr_t * `}
137 int r = pthread_attr_init(&attr);
139 pthread_attr_t *pattr = malloc(sizeof(pthread_attr_t));
140 memmove(pattr, &attr, sizeof(pthread_attr_t));
147 pthread_attr_destroy(self);
150 # Most features of this class are still TODO
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
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
);
175 fun destroy `{ pthread_mutex_destroy(self); `}
177 fun lock
`{ pthread_mutex_lock(self); `}
178 fun try_lock: Bool `{ return pthread_mutex_trylock(self); `}
179 fun unlock
`{ pthread_mutex_unlock(self); `}
182 private extern class NativePthreadMutexAttr in "C" `{ pthread_mutexattr_t * `}
184 pthread_mutexattr_t *attr = malloc(sizeof(pthread_mutexattr_t));
185 int r = pthread_mutexattr_init(attr);
193 fun destroy
`{ pthread_mutexattr_destroy(self); `}
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); `}
199 # pthread_mutexattr_setpshared
200 # pthread_mutexattr_setprotocol
201 # pthread_mutexattr_setproceiling
202 # pthread_mutexattr_setrobust_np
205 private extern class NativePthreadKey in "C" `{ pthread_key_t * `}
207 pthread_key_t
*key
= malloc
(sizeof
(pthread_key_t
));
208 int r
= pthread_key_create
(key
, NULL);
216 fun get: nullable Object `{
217 void
*val
= pthread_getspecific
(*self);
218 if (val
== NULL) val
= null_Object
();
222 fun set(val: nullable Object) `{
223 pthread_setspecific
(*self, val
);
227 private extern class NativePthreadCond in "C" `{ pthread_cond_t * `}
230 int r = pthread_cond_init(&cond, NULL);
232 pthread_cond_t *pcond = malloc(sizeof(pthread_cond_t));
233 memmove(pcond, &cond, sizeof(pthread_cond_t));
239 fun destroy
`{ pthread_cond_destroy(self); `}
241 fun signal `{ pthread_cond_signal(self); `}
243 fun broadcast
`{ pthread_cond_broadcast(self); `}
245 fun wait(mutex: NativePthreadMutex) `{ pthread_cond_wait(self, mutex); `}
251 # Cannot be extracted from this module because of the callback from C to `Thread::run`
256 # Instances of this class are each used to launch and control a thread.
257 abstract class Thread
260 # Type returned by `main`
261 type E
: nullable Object
263 private var native
: nullable NativePthread = null
265 # Is this thread finished ? True when main returned
268 # Main method of this thread
270 # The returned valued is passed to the caller of `join`.
271 fun main
: E
do return null
273 private fun main_intern
: E
275 # Register thread local data
276 sys
.self_thread_key
.set
self
282 # Start executing this thread
284 # Will launch `main` on a different thread.
287 if native
!= null then return
288 native
= new NativePthread.create
(self)
291 # Join this thread to the calling thread
293 # Blocks until the method `main` returns or the target thread calls
294 # `Sys::thread_exit`. Returns the object returned from the other thread.
296 # Stats the thread if now already done by a call to `start`.
299 if native
== null then start
307 if native
== null then return
313 # The main thread of the program
320 # Exit current thread and return `value` to caller of `Thread::join`
321 fun exit_thread
(value
: nullable Object) `{ pthread_exit(value); `}
323 # Returns the handle to the running `Thread`
326 var key = sys.self_thread_key
329 # This is the original thread, get `Sys::main_thread
` and store it
330 var thread = sys.main_thread
335 assert val isa Thread
339 # Mutual exclusion synchronization tool
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.
347 private var native: nullable NativePthreadMutex is noinit
351 var attr = new NativePthreadMutexAttr
352 attr.set_type_recursive
353 native = new NativePthreadMutex(attr)
358 # Acquire this lock, wait until it is available
359 fun lock do native.lock
361 # Acquire this lock only if it is available
363 # Returns `true` if the lock has been acquired.
364 fun try_lock: Bool do return native.try_lock
366 # Release this lock, unblocking all callers of `lock
`
367 fun unlock do native.unlock
371 var native = self.native
372 if native != null then
380 # Barrier synchronization tool
382 # Ensures that `count
` threads call and block on `wait
` before releasing them.
386 private var mutex = new Mutex
387 private var cond: nullable NativePthreadCond = new NativePthreadCond
389 # Number of threads that must be waiting for `wait
` to unblock
392 private var threads_waiting = 0
394 # Wait at this barrier and block until there are a `count
` threads waiting
399 if threads_waiting == count then
403 cond.wait(mutex.native.as(not null))
419 # Print `object
` and '\n' with the same system call
420 redef fun print(object)
422 sys.stdout.write(object.to_s+"\n")