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 c_compiler_option
("-pthread")
20 c_linker_option
("-pthread")
26 # Nity part at the bottom of the module.
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".
44 # `NativePthread` for running thread
45 private fun native_pthread_self
: NativePthread `{
46 pthread_t *id = malloc(sizeof(pthread_t));
51 private var self_thread_key
= new NativePthreadKey
53 private var main_thread_cache
: nullable MainThread = null
54 private var main_thread_mutex
= new Mutex
56 # Handle to the program's main thread
57 fun main_thread
: MainThread
59 var cache
= main_thread_cache
60 if cache
!= null then return cache
62 main_thread_mutex
.lock
64 # Recheck if cache has been updated since lock has been unlocked/locked
65 cache
= main_thread_cache
67 main_thread_mutex
.unlock
71 # Create a `MainThread` exactly once
72 var thread
= new MainThread
73 thread
.native
= sys
.native_pthread_self
74 main_thread_cache
= thread
76 main_thread_mutex
.unlock
81 private extern class NativePthread in "C" `{ pthread_t * `}
83 new create(nit_thread: Thread) import Thread.main_intern `{
85 pthread_attr_init
(&attr
);
88 int r
= pthread_create
(&thread
, &attr
, (void
* (*)(void
*))&Thread_main_intern, nit_thread
);
91 pthread_t
*pthread
= malloc
(sizeof
(pthread_t
));
92 memmove
(pthread
, &thread
, sizeof
(pthread_t
));
98 new create_ex(nit_thread: Thread, attr: NativePthreadAttr) import Thread.main_intern `{
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 fun join: nullable Object `{
112 pthread_join
(*recv
, &thread_return
);
113 if(thread_return
== NULL) thread_return
= null_Object
();
114 return (nullable_Object
)thread_return
;
118 return pthread_cancel
(*recv
);
121 fun attr: NativePthreadAttr `{
122 pthread_attr_t
*pattr
= malloc
(sizeof
(pthread_attr_t
));
123 pthread_getattr_np
(*recv
, pattr
);
127 fun equal(other: NativePthread): Bool `{ pthread_equal(*recv, *other); `}
129 fun kill
(signal
: Int) `{ pthread_kill(*recv, signal); `}
132 private extern class NativePthreadAttr in "C" `{ pthread_attr_t * `}
135 int r = pthread_attr_init(&attr);
137 pthread_attr_t *pattr = malloc(sizeof(pthread_attr_t));
138 memmove(pattr, &attr, sizeof(pthread_attr_t));
145 pthread_attr_destroy(recv);
148 # Most features of this class are still TODO
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
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
);
169 fun destroy `{ pthread_mutex_destroy(recv); `}
171 fun lock
`{ pthread_mutex_lock(recv); `}
172 fun try_lock: Bool `{ return pthread_mutex_trylock(recv); `}
173 fun unlock
`{ pthread_mutex_unlock(recv); `}
176 private extern class NativePthreadMutexAttr in "C" `{ pthread_mutexattr_t * `}
178 pthread_mutexattr_t *attr = malloc(sizeof(pthread_mutexattr_t));
179 int res = pthread_mutexattr_init(attr);
183 fun destroy
`{ pthread_mutexattr_destroy(recv); `}
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); `}
189 # pthread_mutexattr_setpshared
190 # pthread_mutexattr_setprotocol
191 # pthread_mutexattr_setproceiling
192 # pthread_mutexattr_setrobust_np
195 private extern class NativePthreadBarrier in "C" `{ pthread_barrier_t * `}
197 pthread_barrier_t
*barrier
= malloc
(sizeof
(pthread_barrier_t
));
198 int res
= pthread_barrier_init
(barrier
, NULL, count
);
202 fun destroy `{ pthread_barrier_destroy(recv); `}
204 fun wait
`{ pthread_barrier_wait(recv); `}
207 private extern class NativePthreadKey in "C" `{ pthread_key_t * `}
209 pthread_key_t *key = malloc(sizeof(pthread_key_t));
210 int res = pthread_key_create(key, NULL);
214 fun get
: nullable Object `{
215 void *val = pthread_getspecific(*recv);
216 if (val == NULL) val = null_Object();
220 fun set
(val
: nullable Object) `{
221 pthread_setspecific(*recv, val);
228 # Cannot be extracted from this module because of the callback from C to `Thread::run`
233 # Instances of this class are each used to launch and control a thread.
234 abstract class Thread
237 private var native
: nullable NativePthread = null
239 # Main method of this thread
241 # The returned valued is passed to the caller of `join`.
242 fun main
: nullable Object do return null
244 private fun main_intern
: nullable Object
246 # Register thread local data
247 sys
.self_thread_key
.set
self
252 # Start executing this thread
254 # Will launch `main` on a different thread.
257 if native
!= null then return
258 native
= new NativePthread.create
(self)
261 # Join this thread to the calling thread
263 # Blocks until the method `main` returns or the target thread calls
264 # `Sys::thread_exit`. Returns the object returned from the other thread.
266 # Stats the thread if now already done by a call to `start`.
267 fun join
: nullable Object
269 if native
== null then start
275 # Cancel the execution of the thread
278 if native
== null then return
285 if native
== null then return
291 # The main thread of the program
298 # Exit current thread and return `value` to caller of `Thread::join`
299 fun exit_thread
(value
: nullable Object) `{ pthread_exit(value); `}
301 # Does not return if the running thread is to be cancelled
302 fun test_cancel `{ pthread_testcancel(); `}
304 # Returns the handle to the running `Thread`
307 var key
= sys
.self_thread_key
310 # This is the original thread, get `Sys::main_thread` and store it
311 var thread
= sys
.main_thread
316 assert val
isa Thread
320 # Mutual exclusion synchronization tool
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.
328 private var native
: nullable NativePthreadMutex is noinit
332 var attr
= new NativePthreadMutexAttr
333 attr
.set_type_recursive
334 native
= new NativePthreadMutex(attr
)
339 # Acquire this lock, wait until it is available
340 fun lock
do native
.lock
342 # Acquire this lock only if it is available
344 # Returns `true` if the lock has been acquired.
345 fun try_lock
: Bool do return native
.try_lock
347 # Release this lock, unblocking all callers of `lock`
348 fun unlock
do native
.unlock
352 var native
= self.native
353 if native
!= null then
361 # Barrier synchronization tool
363 # Ensures that `count` threads call and block on `wait` before releasing them.
367 # Number of threads that must be waiting for `wait` to unblock
370 private var native
: nullable NativePthreadBarrier is noinit
372 init do native
= new NativePthreadBarrier(count
)
374 # Wait at this barrier and block until there are a `count` threads waiting
375 fun wait
do native
.wait
379 var native
= self.native
380 if native
!= null then
388 # Print `object` and '\n' with the same system call
389 redef fun print
(object
)
391 sys
.stdout
.write
(object
.to_s
+"\n")