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