Răsfoiți Sursa

Added WIP thread module.

Mark Sibly 7 ani în urmă
părinte
comite
490552b9f6

+ 8 - 0
modules/thread/module.json

@@ -0,0 +1,8 @@
+{
+	"module":"thread",
+	"about":"Monkey2 threads",
+	"author":"Mark Sibly",
+	"version":"1.0.0",
+	"support":"http://monkeycoder.co.nz",
+	"depends":["libc"]
+}

+ 46 - 0
modules/thread/native/bbthread.cpp

@@ -0,0 +1,46 @@
+
+#include "bbthread.h"
+
+std::atomic_int bbThread::next_id{1};
+
+thread_local int bbThread::current_id{1};
+
+int bbThread::start( bbFunction<void()> entry ){
+
+	int id=++next_id;
+	
+	running=true;
+	
+	thread=std::thread( [=](){
+	
+		bbGCThread gcThread;
+		gcThread.link();
+		
+		bbDBContext dbContext;
+		dbContext.init();
+		
+		bbDB::currentContext=&dbContext;
+		
+		current_id=id;
+		
+		entry();
+		
+		running=false;
+		
+		bbDB::currentContext=nullptr;
+		
+		gcThread.unlink();
+	} );
+	
+	return id;
+}
+
+void bbThread::detach(){
+
+	thread.detach();
+}
+
+void bbThread::join(){
+
+	thread.join();
+}

+ 35 - 0
modules/thread/native/bbthread.h

@@ -0,0 +1,35 @@
+
+#ifndef BB_THREAD_H
+#define BB_THREAD_H
+
+#include "bbmonkey.h"
+
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+
+struct bbThread{
+
+	static std::atomic_int next_id;
+	
+	static thread_local int current_id;
+	
+	std::thread thread;
+	
+	bool running=false;
+	
+	int  start( bbFunction<void()> entry );
+	
+	void detach();
+	
+	void join();
+};
+
+struct bbMutex : public std::mutex{
+};
+
+struct bbCondvar : public std::condition_variable_any{
+};
+
+#endif

+ 77 - 0
modules/thread/tests/test1.monkey2

@@ -0,0 +1,77 @@
+
+#rem Threading note
+
+* Const/Global vars use TLS
+
+* Const/Global vars in namespace/type scopes are only initialized on main thread.
+
+* Const/Global vars in block scopes are initialized for every thread.
+
+#end
+
+#Import "<thread>"
+
+Class C
+End
+
+Function Test()
+	
+	Const N:=10
+	
+	Local threads:=New Thread[N]
+	
+	For Local i:=0 Until N
+		
+		Print "Starting thread "+i
+		
+		Local sema:=New Semaphore
+	
+		threads[i]=New Thread( Lambda()
+		
+			Print "Starting thread "+Thread.Current().Id
+			
+			sema.Signal()
+		
+			For Local i:=0 Until 10
+				
+				Print "thread="+Thread.Current().Id+" i="+i
+				
+				For Local j:=0 Until 1000
+					Local tmp:=New C
+					Local tmp2:=tmp
+				Next
+			Next
+			
+		End )
+		
+		sema.Wait()
+		
+		Print "Thread started "+threads[i].Id
+	
+	Next
+	
+	For Local i:=0 Until N
+		
+		Print "Joining "+i
+		
+		threads[i].Join()
+
+		Print "Joined "+i
+	Next
+End
+
+Function Main()
+	
+	Local N:=10
+	
+	GCSetTrigger( 65536 )
+	
+	For Local i:=0 Until N
+	
+		Print "Test "+i
+		
+		Test()
+	Next
+	
+	Print "Goodbye!"
+End

+ 197 - 0
modules/thread/thread.monkey2

@@ -0,0 +1,197 @@
+
+'Namespace std.thread
+
+#If __THREADS__
+
+#Import "<std>"
+
+#Import "native/bbthread.cpp"
+#Import "native/bbthread.h"
+
+Using std..
+
+Extern Private
+
+Struct bbThread="bbThread"
+
+	Global current_id:Int
+
+	Field running:Bool
+	
+	Method start:Int( entry:Void() )
+	Method detach()
+	Method join()
+End
+
+Struct bbMutex="bbMutex"
+
+	Method try_lock:Bool()
+	Method lock()
+	Method unlock()
+End
+
+Struct bbCondvar="bbCondvar"	'condition_variable_any"
+	
+	Method wait( mutex:bbMutex )
+	Method notify_one()
+	Method notify_all()
+End
+
+Public
+
+Class Thread
+	
+	Method New( entry:Void() )
+		FlushZombies()
+		_entry=entry
+		_id=_thread.start( Lambda()
+			_mutex.lock()
+			_threads[bbThread.current_id]=Self
+			_mutex.unlock()
+			_entry()
+		End )
+	End
+	
+	Property Id:Int()
+		Return _id
+	End
+	
+	Property Running:Bool()
+		Return _thread.running
+	End
+	
+	Method Detach()
+		If Not _id Return
+		_mutex.lock()
+		_zombies.Add( Self )
+		_threads.Remove( _id )
+		_mutex.unlock()
+		_thread.detach()
+		_id=0
+	End
+	
+	Method Join()
+		If Not _id Return
+		_thread.join()
+		_mutex.lock()
+		_threads.Remove( _id )
+		_mutex.unlock()
+		_id=0
+	End
+	
+	Function Current:Thread()
+		_mutex.lock()
+		Local thread:=_threads[bbThread.current_id]
+		_mutex.unlock()
+		Return thread
+	End
+
+	Function CurrentId:Int()
+		Return bbThread.current_id
+	End
+	
+	Function Main:Thread()
+		_mutex.lock()
+		If Not _threads.Contains( 1 ) _threads[1]=New Thread( 1 )
+		Local thread:=_threads[1]
+		_mutex.unlock()
+		Return thread
+	End
+	
+	Private
+	
+	Global _mutex:bbMutex
+	Global _zombies:=New Stack<Thread>
+	Global _threads:=New IntMap<Thread>
+	
+	Field _thread:bbThread
+	Field _entry:Void()
+	Field _id:Int
+	
+	Method New( id:Int )
+		_id=id
+	End
+	
+	Function FlushZombies()
+		If _zombies.Empty Return
+		_mutex.lock()
+		Local put:=0
+		For Local thread:=Eachin _zombies
+			If Not thread.Running Continue
+			_zombies[put]=thread
+			put+=1
+		Next
+		_zombies.Resize( put )
+		_mutex.unlock()
+	End
+	
+End
+
+Class Mutex
+	
+	Method TryLock:Bool()
+		Return _mutex.try_lock()
+	End
+	
+	Method Lock()
+		_mutex.lock()
+	End
+	
+	Method Unlock()
+		_mutex.unlock()
+	End
+	
+	Private
+	
+	Field _mutex:bbMutex
+End
+
+Class Condvar
+	
+	Method Wait( mutex:Mutex )
+		_condvar.wait( mutex._mutex )
+	End
+	
+	Method Notify()
+		_condvar.notify_one()
+	End
+	
+	Method NotifyAll()
+		_condvar.notify_all()
+	End
+	
+	Private
+	
+	Field _condvar:bbCondvar
+End
+
+Class Semaphore
+	
+	Method New( count:Int=0 )
+		_count=count
+	End
+	
+	Method Wait()
+		_mutex.lock()
+		While( _count<=0 )
+			_condvar.wait( _mutex )
+		Wend
+		_count-=1
+		_mutex.unlock()
+	End
+	
+	Method Signal()
+		_mutex.lock()
+		_count+=1
+		_condvar.notify_one()
+		_mutex.unlock()
+	End
+	
+	Private
+	
+	Field _count:Int
+	Field _mutex:bbMutex
+	Field _condvar:bbCondvar
+End
+
+#endif