Intro the `threads` module with support for POSIX threads, mutex and barriers. Also provides 2 ways to have thread-safe collections: refining existing collections and sub-classing them.
TODO:
* Complete the collection support.
* Modifiy `print` so that printing the user content and the new line is (generally) atomic
* Add more examples/use in real programs
* Reinforce error support.
* Fix compatibility with FFI's global refs.
* Add, when needed, GC_THREADS to `gc_chooser.[c|h]`
* Do not include `gc.h` when not using libgc
ping @egagnon
Pull-Request: #849
Reviewed-by: Jean-Philippe Caissy <jpcaissy@piji.ca>
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Etienne M. Gagnon <egagnon@j-meg.com>
--- /dev/null
+# POSIX Threads support
+
+The threads can be manipulated and synchronized using the classes `Thread`,
+`Mutex` and `Barrier`.
+
+This group also provides two optional modules with thread-safe collections:
+
+* `redef_collections` redefines existing collection to make them thread-safe.
+ This incures a small overhead in all usage of the redefined collections.
+* `concurrent_collections` intro new thread-safe collections.
+
+Theses services are implemented using the POSIX threads.
+
+## Known limitations:
+
+* Most services from the Nit library are not thread-safe. You must manage
+ your own mutex to avoid conflicts on shared data.
+* FFI's global references are not thread-safe.
+
+## For more information:
+
+* See: `man pthreads`
+* See: `examples/concurrent_array_and_barrier.nit`
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Introduces thread-safe concurrent collections
+#
+# This module offers new thread-safe collections. If you want to
+# render basic collections thread-safe and don't mind the overhead cost,
+# you can use `threads::redef_collections`.
+#
+# Concurrent collections:
+#
+# - [x] `ConcurrentArray`
+# - [x] `ConcurrentList`
+# - [ ] `ConcurrentHashMap`
+# - [ ] `ConcurrentHashSet`
+# - [ ] `ConcurrentContainer`
+# - [ ] `ConcurrentQueue`
+#
+# Introduced collections specialize their critical methods according to the
+# current implementation in the standard library. If additional services
+# are added to the underlying collections by refinement or evolution, they
+# might need to be covered in the concurrent version.
+module concurrent_collections
+
+import pthreads
+
+redef class Collection[E]
+ # Type of the concurrent variant of this collection
+ type CONCURRENT: ConcurrentCollection[E]
+
+ # Wraps `self` in a thread-safe collection
+ fun to_concurrent: CONCURRENT is abstract
+end
+
+redef class SequenceRead[E]
+ redef type CONCURRENT: ConcurrentSequenceRead[E]
+end
+
+redef class Sequence[E]
+ redef type CONCURRENT: ConcurrentSequence[E]
+end
+
+redef class Array[E]
+ redef type CONCURRENT: ConcurrentArray[E]
+
+ redef fun to_concurrent do return new ConcurrentArray[E].wrap(self)
+end
+
+redef class List[E]
+ redef type CONCURRENT: ConcurrentList[E]
+
+ redef fun to_concurrent do return new ConcurrentList[E].wrap(self)
+end
+
+# A concurrent variant to the standard `Collection`
+abstract class ConcurrentCollection[E]
+ super Collection[E]
+
+ # Type of the equivalent non thread-safe collection
+ type REAL: Collection[E]
+
+ # Collection wrapped by `self`
+ var real_collection: REAL is noinit
+
+ # `Mutex` used to synchronize access to `self`
+ #
+ # It is used by the implementation on each protected methods. It can also
+ # be used externally to ensure that no other `Thread` modify this object.
+ var mutex = new Mutex
+
+ redef fun count(e)
+ do
+ mutex.lock
+ var r = real_collection.count(e)
+ mutex.unlock
+ return r
+ end
+
+ redef fun first
+ do
+ mutex.lock
+ var r = real_collection.first
+ mutex.unlock
+ return r
+ end
+
+ redef fun has(e)
+ do
+ mutex.lock
+ var r = real_collection.has(e)
+ mutex.unlock
+ return r
+ end
+
+ redef fun has_all(e)
+ do
+ mutex.lock
+ var r = real_collection.has_all(e)
+ mutex.unlock
+ return r
+ end
+
+ redef fun has_only(e)
+ do
+ mutex.lock
+ var r = real_collection.has_only(e)
+ mutex.unlock
+ return r
+ end
+
+ redef fun is_empty
+ do
+ mutex.lock
+ var r = real_collection.is_empty
+ mutex.unlock
+ return r
+ end
+
+ redef fun iterator
+ do
+ mutex.lock
+ var r = real_collection.iterator
+ mutex.unlock
+ return r
+ end
+
+ redef fun length
+ do
+ mutex.lock
+ var r = real_collection.length
+ mutex.unlock
+ return r
+ end
+
+ redef fun to_a
+ do
+ mutex.lock
+ var r = real_collection.to_a
+ mutex.unlock
+ return r
+ end
+
+ redef fun rand
+ do
+ mutex.lock
+ var r = real_collection.rand
+ mutex.unlock
+ return r
+ end
+
+ redef fun join(sep)
+ do
+ mutex.lock
+ var r = real_collection.join(sep)
+ mutex.unlock
+ return r
+ end
+
+ redef fun to_s
+ do
+ mutex.lock
+ var r = real_collection.to_s
+ mutex.unlock
+ return r
+ end
+end
+
+# A concurrent variant to the standard `SequenceRead`
+abstract class ConcurrentSequenceRead[E]
+ super ConcurrentCollection[E]
+ super SequenceRead[E]
+
+ redef type REAL: SequenceRead[E]
+
+ redef fun ==(o)
+ do
+ mutex.lock
+ var r = real_collection == o
+ mutex.unlock
+ return r
+ end
+
+ redef fun [](index)
+ do
+ mutex.lock
+ var r = real_collection[index]
+ mutex.unlock
+ return r
+ end
+
+ redef fun hash
+ do
+ mutex.lock
+ var r = real_collection.hash
+ mutex.unlock
+ return r
+ end
+
+ redef fun index_of(index)
+ do
+ mutex.lock
+ var r = real_collection.index_of(index)
+ mutex.unlock
+ return r
+ end
+
+ redef fun index_of_from(index, from)
+ do
+ mutex.lock
+ var r = real_collection.index_of_from(index, from)
+ mutex.unlock
+ return r
+ end
+
+ redef fun iterator_from(index)
+ do
+ mutex.lock
+ var r = real_collection.iterator_from(index)
+ mutex.unlock
+ return r
+ end
+
+ redef fun last
+ do
+ mutex.lock
+ var r = real_collection.last
+ mutex.unlock
+ return r
+ end
+
+ redef fun last_index_of(e)
+ do
+ mutex.lock
+ var r = real_collection.last_index_of(e)
+ mutex.unlock
+ return r
+ end
+
+ redef fun last_index_of_from(e, from)
+ do
+ mutex.lock
+ var r = real_collection.last_index_of_from(e, from)
+ mutex.unlock
+ return r
+ end
+
+ redef fun reverse_iterator
+ do
+ mutex.lock
+ var r = real_collection.reverse_iterator
+ mutex.unlock
+ return r
+ end
+
+ redef fun reverse_iterator_from(from)
+ do
+ mutex.lock
+ var r = real_collection.reverse_iterator_from(from)
+ mutex.unlock
+ return r
+ end
+end
+
+# A concurrent variant to the standard `Sequence`
+abstract class ConcurrentSequence[E]
+ super ConcurrentSequenceRead[E]
+ super Sequence[E]
+
+ redef type REAL: Sequence[E]
+
+ redef fun []=(index, e)
+ do
+ mutex.lock
+ real_collection[index] = e
+ mutex.unlock
+ end
+
+ redef fun add(e)
+ do
+ mutex.lock
+ real_collection.add e
+ mutex.unlock
+ end
+
+ redef fun append(e)
+ do
+ mutex.lock
+ real_collection.append e
+ mutex.unlock
+ end
+
+ redef fun first=(e)
+ do
+ mutex.lock
+ real_collection.first = e
+ mutex.unlock
+ end
+
+ redef fun insert(e, i)
+ do
+ mutex.lock
+ real_collection.insert(e, i)
+ mutex.unlock
+ end
+
+ redef fun insert_all(from, pos)
+ do
+ mutex.lock
+ real_collection
+ mutex.unlock
+ end
+
+ redef fun last=(e)
+ do
+ mutex.lock
+ real_collection.last = e
+ mutex.unlock
+ end
+
+ redef fun pop
+ do
+ mutex.lock
+ var r = real_collection.pop
+ mutex.unlock
+ return r
+ end
+
+ redef fun prepend(e)
+ do
+ mutex.lock
+ real_collection.prepend e
+ mutex.unlock
+ end
+
+ redef fun push(e)
+ do
+ mutex.lock
+ real_collection.push e
+ mutex.unlock
+ end
+
+ redef fun remove_at(index)
+ do
+ mutex.lock
+ real_collection.remove_at(index)
+ mutex.unlock
+ end
+
+ redef fun shift
+ do
+ mutex.lock
+ var r = real_collection.shift
+ mutex.unlock
+ return r
+ end
+
+ redef fun unshift(e)
+ do
+ mutex.lock
+ real_collection.unshift(e)
+ mutex.unlock
+ end
+
+ redef fun subarray(start, len)
+ do
+ mutex.lock
+ var r = real_collection.subarray(start, len)
+ mutex.unlock
+ return r
+ end
+end
+
+# A concurrent variant to the standard `Array`
+class ConcurrentArray[E]
+ super ConcurrentSequence[E]
+ super Array[E]
+
+ redef type REAL: Array[E]
+
+ init wrap(real_collection: REAL) do self.real_collection = real_collection
+ init do self.real_collection = new Array[E]
+
+ redef fun clear
+ do
+ mutex.lock
+ real_collection.clear
+ mutex.unlock
+ end
+
+ redef fun enlarge(cap)
+ do
+ mutex.lock
+ real_collection.enlarge(cap)
+ mutex.unlock
+ end
+
+ redef fun remove_all(e)
+ do
+ mutex.lock
+ real_collection.remove_all(e)
+ mutex.unlock
+ end
+
+ redef fun swap_at(a, b)
+ do
+ mutex.lock
+ real_collection.swap_at(a, b)
+ mutex.unlock
+ end
+
+ #
+ ## The following method defs are conflict resolutions
+ #
+
+ redef fun add(e)
+ do
+ mutex.lock
+ real_collection.add e
+ mutex.unlock
+ end
+
+ redef fun length
+ do
+ mutex.lock
+ var r = real_collection.length
+ mutex.unlock
+ return r
+ end
+end
+
+# A concurrent variant to the standard `List`
+class ConcurrentList[E]
+ super ConcurrentSequence[E]
+ super List[E]
+
+ redef type REAL: List[E]
+
+ init wrap(real_collection: REAL) do self.real_collection = real_collection
+ init do self.real_collection = new List[E]
+
+ redef fun link(l)
+ do
+ mutex.lock
+ real_collection.link(l)
+ mutex.unlock
+ end
+
+ redef fun slice(from, to)
+ do
+ mutex.lock
+ var r = real_collection.slice(from, to)
+ mutex.unlock
+ return r
+ end
+
+ #
+ ## The following method defs are conflict resolutions
+ #
+
+ redef fun pop
+ do
+ mutex.lock
+ var r = real_collection.pop
+ mutex.unlock
+ return r
+ end
+
+ redef fun is_empty
+ do
+ mutex.lock
+ var r = real_collection.is_empty
+ mutex.unlock
+ return r
+ end
+
+ redef fun unshift(e)
+ do
+ mutex.lock
+ real_collection.unshift(e)
+ mutex.unlock
+ end
+end
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A basic usage example of the modules `pthreads` and `pthreads::cocurrent_collections`
+#
+# 20 threads share an array and a barrier. They each insert 1000 strings into
+# the array and wait at a barrier before finishing.
+module concurrent_array_and_barrier
+
+import pthreads::concurrent_collections
+
+private class MyThread
+ super Thread
+
+ # This `ConcurrentArray` has its own `Mutex`
+ var array: ConcurrentArray[String]
+
+ # Use an explicit `Barrier`
+ var barrier: Barrier
+
+ var id: Int
+
+ redef fun main
+ do
+ # Print and add to Array 1000 times
+ for i in 1000.times do
+ var str = "thread {id}: {i}"
+ array.add str
+ end
+
+ # Wait at the `barrier`
+ barrier.wait
+
+ return id
+ end
+end
+
+var n_threads = 20
+
+# This `ConcurrentArray` has its own `Mutex`
+var array = new ConcurrentArray[String]
+
+# Use an explicit `Barrier`
+var barrier = new Barrier(n_threads)
+
+# Create all our threads
+var threads = new Array[Thread]
+for t in n_threads.times do
+ var thread = new MyThread(array, barrier, t)
+ threads.add thread
+ thread.start
+end
+
+# Wait for the threads to complete
+for thread in threads do
+ print "Thread {thread.join or else "null"} is done"
+end
+
+print "{array.length} strings inserted"
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Main POSIX threads support and intro the classes `Thread`, `Mutex` and `Barrier`
+module pthreads is
+ c_compiler_option("-pthread")
+ c_linker_option("-pthread")
+end
+
+#
+## Native part
+#
+# Nity part at the bottom of the module.
+#
+
+in "C Header" `{
+ #include <pthread.h>
+`}
+
+in "C" `{
+ // TODO protect with: #ifdef WITH_LIBGC
+ // We might have to add the next line to gc_chooser.c too, especially
+ // if we get an error like "thread not registered with GC".
+ #define GC_THREADS
+ #include <gc.h>
+ //#endif
+`}
+
+redef class Sys
+
+ # `NativePthread` for running thread
+ private fun native_pthread_self: NativePthread `{
+ pthread_t *id = malloc(sizeof(pthread_t));
+ *id = pthread_self();
+ return id;
+ `}
+
+ private var self_thread_key = new NativePthreadKey
+
+ private var main_thread_cache: nullable MainThread = null
+ private var main_thread_mutex = new Mutex
+
+ # Handle to the program's main thread
+ fun main_thread: MainThread
+ do
+ var cache = main_thread_cache
+ if cache != null then return cache
+
+ main_thread_mutex.lock
+
+ # Recheck if cache has been updated since lock has been unlocked/locked
+ cache = main_thread_cache
+ if cache != null then
+ main_thread_mutex.unlock
+ return cache
+ end
+
+ # Create a `MainThread` exactly once
+ var thread = new MainThread
+ thread.native = sys.native_pthread_self
+ main_thread_cache = thread
+
+ main_thread_mutex.unlock
+ return thread
+ end
+end
+
+private extern class NativePthread in "C" `{ pthread_t * `}
+
+ new create(nit_thread: Thread) import Thread.main_intern `{
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+
+ pthread_t thread;
+ int r = pthread_create(&thread, &attr, (void * (*)(void *))&Thread_main_intern, nit_thread);
+
+ if (r == 0) {
+ pthread_t *pthread = malloc(sizeof(pthread_t));
+ memmove(pthread, &thread, sizeof(pthread_t));
+ return pthread;
+ }
+ return NULL;
+ `}
+
+ new create_ex(nit_thread: Thread, attr: NativePthreadAttr) import Thread.main_intern `{
+ pthread_t thread;
+ int r = pthread_create(&thread, attr, (void * (*)(void *))&Thread_main_intern, nit_thread);
+
+ if (r == 0) {
+ pthread_t *pthread = malloc(sizeof(pthread_t));
+ memmove(pthread, &thread, sizeof(pthread_t));
+ return pthread;
+ }
+ return NULL;
+ `}
+
+ fun join: nullable Object `{
+ void *thread_return;
+ pthread_join(*recv, &thread_return);
+ if(thread_return == NULL) thread_return = null_Object();
+ return (nullable_Object)thread_return;
+ `}
+
+ fun cancel: Bool `{
+ return pthread_cancel(*recv);
+ `}
+
+ fun attr: NativePthreadAttr `{
+ pthread_attr_t *pattr = malloc(sizeof(pthread_attr_t));
+ pthread_getattr_np(*recv, pattr);
+ return pattr;
+ `}
+
+ fun equal(other: NativePthread): Bool `{ pthread_equal(*recv, *other); `}
+
+ fun kill(signal: Int) `{ pthread_kill(*recv, signal); `}
+end
+
+private extern class NativePthreadAttr in "C" `{ pthread_attr_t * `}
+ new `{
+ pthread_attr_t attr;
+ int r = pthread_attr_init(&attr);
+ if (r == 0) {
+ pthread_attr_t *pattr = malloc(sizeof(pthread_attr_t));
+ memmove(pattr, &attr, sizeof(pthread_attr_t));
+ return pattr;
+ }
+ return NULL;
+ `}
+
+ fun destroy `{
+ pthread_attr_destroy(recv);
+ `}
+
+ # Most features of this class are still TODO
+ #
+ # * pthread_attr_setaffinity_np(3)
+ # * pthread_attr_setdetachstate
+ # * pthread_attr_setguardsize
+ # * pthread_attr_setinheritsched
+ # * pthread_attr_setschedparam
+ # * pthread_attr_setschedpolicy
+ # * pthread_attr_setscope
+ # * pthread_attr_setstack
+ # * pthread_attr_setstackaddr
+ # * pthread_attr_setstacksize
+end
+
+private extern class NativePthreadMutex in "C" `{ pthread_mutex_t * `}
+ new (attr: NativePthreadMutexAttr) `{
+ pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t));
+ int res = pthread_mutex_init(mutex, attr);
+ return mutex;
+ `}
+
+ fun destroy `{ pthread_mutex_destroy(recv); `}
+
+ fun lock `{ pthread_mutex_lock(recv); `}
+ fun try_lock: Bool `{ return pthread_mutex_trylock(recv); `}
+ fun unlock `{ pthread_mutex_unlock(recv); `}
+end
+
+private extern class NativePthreadMutexAttr in "C" `{ pthread_mutexattr_t * `}
+ new `{
+ pthread_mutexattr_t *attr = malloc(sizeof(pthread_mutexattr_t));
+ int res = pthread_mutexattr_init(attr);
+ return attr;
+ `}
+
+ fun destroy `{ pthread_mutexattr_destroy(recv); `}
+
+ fun set_type_normal `{ pthread_mutexattr_settype(recv, PTHREAD_MUTEX_NORMAL); `}
+ fun set_type_recursive `{ pthread_mutexattr_settype(recv, PTHREAD_MUTEX_RECURSIVE); `}
+ fun set_type_errorcheck `{ pthread_mutexattr_settype(recv, PTHREAD_MUTEX_ERRORCHECK); `}
+
+ # pthread_mutexattr_setpshared
+ # pthread_mutexattr_setprotocol
+ # pthread_mutexattr_setproceiling
+ # pthread_mutexattr_setrobust_np
+end
+
+private extern class NativePthreadBarrier in "C" `{ pthread_barrier_t * `}
+ new(count: Int) `{
+ pthread_barrier_t *barrier = malloc(sizeof(pthread_barrier_t));
+ int res = pthread_barrier_init(barrier, NULL, count);
+ return barrier;
+ `}
+
+ fun destroy `{ pthread_barrier_destroy(recv); `}
+
+ fun wait `{ pthread_barrier_wait(recv); `}
+end
+
+private extern class NativePthreadKey in "C" `{ pthread_key_t * `}
+ new `{
+ pthread_key_t *key = malloc(sizeof(pthread_key_t));
+ int res = pthread_key_create(key, NULL);
+ return key;
+ `}
+
+ fun get: nullable Object `{
+ void *val = pthread_getspecific(*recv);
+ if (val == NULL) val = null_Object();
+ return val;
+ `}
+
+ fun set(val: nullable Object) `{
+ pthread_setspecific(*recv, val);
+ `}
+end
+
+#
+## Nity part
+#
+# Cannot be extracted from this module because of the callback from C to `Thread::run`
+#
+
+# Handle to a thread
+#
+# Instances of this class are each used to launch and control a thread.
+abstract class Thread
+ super Finalizable
+
+ private var native: nullable NativePthread = null
+
+ # Main method of this thread
+ #
+ # The returned valued is passed to the caller of `join`.
+ fun main: nullable Object do return null
+
+ private fun main_intern: nullable Object
+ do
+ # Register thread local data
+ sys.self_thread_key.set self
+
+ return main
+ end
+
+ # Start executing this thread
+ #
+ # Will launch `main` on a different thread.
+ fun start
+ do
+ if native != null then return
+ native = new NativePthread.create(self)
+ end
+
+ # Join this thread to the calling thread
+ #
+ # Blocks until the method `main` returns or the target thread calls
+ # `Sys::thread_exit`. Returns the object returned from the other thread.
+ #
+ # Stats the thread if now already done by a call to `start`.
+ fun join: nullable Object
+ do
+ if native == null then start
+ var r = native.join
+ native = null
+ return r
+ end
+
+ # Cancel the execution of the thread
+ fun cancel
+ do
+ if native == null then return
+ native.cancel
+ native = null
+ end
+
+ redef fun finalize
+ do
+ if native == null then return
+ native.free
+ native = null
+ end
+end
+
+# The main thread of the program
+class MainThread
+ super Thread
+
+ private init do end
+end
+
+# Exit current thread and return `value` to caller of `Thread::join`
+fun exit_thread(value: nullable Object) `{ pthread_exit(value); `}
+
+# Does not return if the running thread is to be cancelled
+fun test_cancel `{ pthread_testcancel(); `}
+
+# Returns the handle to the running `Thread`
+fun thread: Thread
+do
+ var key = sys.self_thread_key
+ var val = key.get
+ if val == null then
+ # This is the original thread, get `Sys::main_thread` and store it
+ var thread = sys.main_thread
+ key.set thread
+ return thread
+ end
+
+ assert val isa Thread
+ return val
+end
+
+# Mutual exclusion synchronization tool
+#
+# Instances of this class can only be acquired by a single thread at any one
+# point in time. Uses the recursive protocol so they can be locked many time by
+# the same thread, must then be unlocked as many time.
+class Mutex
+ super Finalizable
+
+ private var native: nullable NativePthreadMutex is noinit
+
+ init
+ do
+ var attr = new NativePthreadMutexAttr
+ attr.set_type_recursive
+ native = new NativePthreadMutex(attr)
+ attr.destroy
+ attr.free
+ end
+
+ # Acquire this lock, wait until it is available
+ fun lock do native.lock
+
+ # Acquire this lock only if it is available
+ #
+ # Returns `true` if the lock has been acquired.
+ fun try_lock: Bool do return native.try_lock
+
+ # Release this lock, unblocking all callers of `lock`
+ fun unlock do native.unlock
+
+ redef fun finalize
+ do
+ var native = self.native
+ if native != null then
+ native.destroy
+ native.free
+ end
+ self.native = null
+ end
+end
+
+# Barrier synchronization tool
+#
+# Ensures that `count` threads call and block on `wait` before releasing them.
+class Barrier
+ super Finalizable
+
+ # Number of threads that must be waiting for `wait` to unblock
+ var count: Int
+
+ private var native: nullable NativePthreadBarrier is noinit
+
+ init do native = new NativePthreadBarrier(count)
+
+ # Wait at this barrier and block until there are a `count` threads waiting
+ fun wait do native.wait
+
+ redef fun finalize
+ do
+ var native = self.native
+ if native != null then
+ native.destroy
+ native.free
+ end
+ self.native = null
+ end
+end
+
+# Print `object` and '\n' with the same system call
+redef fun print(object)
+do
+ sys.stdout.write(object.to_s+"\n")
+end
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Redef _some_ basic collections to be thread-safe
+#
+# This modules is intended to be used with scripts or quick prototypes.
+# It makes thread safe _all_ instances of _some_ collections which
+# also slightly slow down single threaded use. For more robust software,
+# it is recommended to use `threads::concurrent_collections`.
+#
+# Thread-safe collections:
+#
+# - [x] `Array`
+# - [ ] `List`
+# - [ ] `HashMap`
+# - [ ] `HashSet`
+# - [ ] `Container`
+# - [ ] `Queue`
+module redef_collections
+
+import pthreads
+
+# Thread-safe refinements of most of the known methods (except `enlarge`)
+redef class Array
+ var mutex = new Mutex
+
+ redef fun add(e)
+ do
+ mutex.lock
+ super
+ mutex.unlock
+ end
+
+ redef fun []=(index, e)
+ do
+ mutex.lock
+ super
+ mutex.unlock
+ end
+
+ redef fun [](index)
+ do
+ mutex.lock
+ var r = super
+ mutex.unlock
+ return r
+ end
+
+ redef fun remove_at(index)
+ do
+ mutex.lock
+ super
+ mutex.unlock
+ end
+
+ redef fun shift
+ do
+ mutex.lock
+ var r = super
+ mutex.unlock
+ return r
+ end
+
+ redef fun unshift(e)
+ do
+ mutex.lock
+ super
+ mutex.unlock
+ end
+
+ redef fun insert_all(from, pos)
+ do
+ mutex.lock
+ super
+ mutex.unlock
+ end
+
+ redef fun swap_at(a, b)
+ do
+ mutex.lock
+ super
+ mutex.unlock
+ end
+
+ redef fun ==(o)
+ do
+ mutex.lock
+ var r = super
+ mutex.unlock
+ return r
+ end
+
+ redef fun enlarge(cap)
+ do
+ mutex.lock
+ super
+ mutex.unlock
+ end
+end
# Double linked lists.
class List[E]
super Sequence[E]
+
# Access
+
redef fun [](index) do return get_node(index).item
redef fun []=(index, item) do get_node(index).item = item
--- /dev/null
+Thread 0 is done
+Thread 1 is done
+Thread 2 is done
+Thread 3 is done
+Thread 4 is done
+Thread 5 is done
+Thread 6 is done
+Thread 7 is done
+Thread 8 is done
+Thread 9 is done
+Thread 10 is done
+Thread 11 is done
+Thread 12 is done
+Thread 13 is done
+Thread 14 is done
+Thread 15 is done
+Thread 16 is done
+Thread 17 is done
+Thread 18 is done
+Thread 19 is done
+20000 strings inserted