瀏覽代碼

Added language thread support.

Mark Sibly 7 年之前
父節點
當前提交
0ba9e9e039

+ 4 - 0
modules/monkey/native/bbarray.h

@@ -2,7 +2,11 @@
 #ifndef BB_ARRAY_H
 #define BB_ARRAY_H
 
+#ifdef BB_THREADS
+#include "bbgc_mx.h"
+#else
 #include "bbgc.h"
+#endif
 
 template<class T,int D> struct bbArray{
 

+ 91 - 55
modules/monkey/native/bbdebug.cpp

@@ -16,11 +16,26 @@
 
 typedef void(*dbEmit_t)(void*);
 
+#if BB_THREADS
+namespace bbGC{
+	void suspendThreads();
+	void resumeThreads();
+}
+#endif
+
 namespace bbDB{
 
-	int nextSeq;
+	bool fatality;
 	
+#if BB_THREADS	
+	std::atomic_int nextSeq;
+	thread_local bbDBContext *currentContext;
+#else
+	int nextSeq;
 	bbDBContext *currentContext;
+#endif
+
+	bbDBContext mainContext;
 	
 #if !_WIN32
 	void breakHandler( int sig ){
@@ -29,31 +44,32 @@ namespace bbDB{
 	}
 #endif
 
+	//Note: doesn't work on non-mainthread on at least windows.
+	//
 	void sighandler( int sig  ){
 	
-		const char *err="Unknown signal";
+		const char *err="SIGNAL: Unknown signal";
+		
 		switch( sig ){
-		case SIGSEGV:err="Memory access violation";break;
-		case SIGILL:err="Illegal instruction";break;
-		case SIGFPE:err="Floating point exception";break;
+		case SIGSEGV:err="SIGNAL: Memory access violation";break;
+		case SIGILL:err= "SIGNAL: Illegal instruction";break;
+		case SIGFPE:err= "SIGNAL: Floating point exception";break;
 #if !_WIN32
-		case SIGBUS:err="Bus error";
+		case SIGBUS:err= "SIGNAL: Bus error";break;
 #endif	
 		}
-				
-#ifndef NDEBUG
+		
+		fatality=true;
 		error( err );
 		exit( 0 );
-#endif
-		bb_printf( "Caught signal:%s\n",err );
-		exit( -1 );
 	}
 	
 	void init(){
 	
-		currentContext=new bbDBContext;
-		currentContext->init();
+		mainContext.init();
 		
+		currentContext=&mainContext;
+	
 		signal( SIGSEGV,sighandler );
 		signal( SIGILL,sighandler );
 		signal( SIGFPE,sighandler );
@@ -77,9 +93,22 @@ namespace bbDB{
 #else		
 		signal( SIGTSTP,breakHandler );
 #endif
-
 #endif
-}
+	}
+	
+	void emit( const char *e ){
+	
+		if( const char *p=strchr( e,':' ) ){
+			dbEmit_t dbEmit=(dbEmit_t)( strtol( p+1,0,16 ) );
+			dbEmit( (void*)strtol( e,0,16 ) );
+		}else{
+			bbGCNode *node=(bbGCNode*)strtoll( e,0,16 );
+			node->dbEmit();
+		}
+		
+		puts( "" );
+		fflush( stdout );
+	}
 	
 	void emitVar( bbDBVar *v ){
 		bbString id=v->name;
@@ -104,66 +133,64 @@ namespace bbDB{
 		}
 	}
 	
-	void stop(){
-	
-		//currentContext->stopped=1;	//stop on *next* stmt.
-		
-		stopped();						//stop on DebugStop() stmt.
-	}
-	
-	void emit( const char *e ){
-	
-		if( const char *p=strchr( e,':' ) ){
-			dbEmit_t dbEmit=(dbEmit_t)( strtol( p+1,0,16 ) );
-			dbEmit( (void*)strtol( e,0,16 ) );
-		}else{
-			bbGCNode *node=(bbGCNode*)strtoll( e,0,16 );
-			node->dbEmit();
-		}
-		
-		puts( "" );
-		fflush( stdout );
+	void error( bbString msg ){
+#if _WIN32
+		MessageBoxW( 0,bbWString( msg ),L"Monkey 2 Runtime Error",MB_OK );
+#else
+		bb_print( msg );
+#endif
+		stopped();
 	}
 	
 	void stopped(){
 	
-		bb_printf( "{{!DEBUG!}}\n" );
-		
-		emitStack();
+#ifdef NDEBUG
+		exit( -1 );
+#endif
+
+#ifdef BB_THREADS
+		bbGC::suspendThreads();
+#endif
 
-		bb_printf( "\n" );
-		fflush( stdout );
-		
 #ifdef __EMSCRIPTEN__
 		emscripten_pause_main_loop();
 #endif
+
+		bb_printf( "{{!DEBUG!}}\n" );
+		emitStack();
+		bb_printf( "\n" );
+		
+		fflush( stdout );
 		
 		for(;;){
 		
 			char buf[256];
 			char *e=fgets( buf,256,stdin );
-			if( !e ) exit( -1 );
 			
+			if( !e || fatality ) exit( -1 );
+			
+#ifdef BB_THREADS
+			bbGC::resumeThreads();
+#endif
+
+#ifdef __EMSCRIPTEN__
+			emscripten_resume_main_loop();
+#endif
 			switch( e[0] ){
-			case 'r':currentContext->stopped=0;currentContext->stepMode=0;return;
-			case 'e':currentContext->stopped=1;currentContext->stepMode=0;return;
-			case 's':currentContext->stopped=1;currentContext->stepMode='s';return;
-			case 'l':currentContext->stopped=0;currentContext->stepMode='l';return;
+			case 'r':currentContext->stopped=0;currentContext->stepMode=0;break;
+			case 'e':currentContext->stopped=1;currentContext->stepMode=0;break;
+			case 's':currentContext->stopped=1;currentContext->stepMode='s';break;
+			case 'l':currentContext->stopped=0;currentContext->stepMode='l';break;
 			case '@':emit( e+1 );continue;
-			case 'q':exit( 0 );return;
+			case 'q':exit( 0 );break;
+			default:
+				bb_printf( "Unrecognized debug cmd: %s\n",buf );fflush( stdout );
+				exit( -1 );
 			}
-			bb_printf( "Unrecognized debug cmd: %s\n",buf );fflush( stdout );
-			exit( -1 );
+			return;
 		}
 	}
 	
-	void error( bbString msg ){
-		
-		bb_printf( "\n%s\n",msg.c_str() );
-		
-		stopped();
-	}
-	
 	bbArray<bbString> stack(){
 	
 		int n=0;
@@ -179,12 +206,21 @@ namespace bbDB{
 		
 		return st;
 	}
+	
+	void stop(){
+	
+		//currentContext->stopped=1;	//stop on *next* stmt.
+		
+		stopped();						//stop on DebugStop() stmt.
+	}
+	
 }
 
 void bbDBContext::init(){
 	if( !localsBuf ) localsBuf=new bbDBVar[16384];
 	locals=localsBuf;
 	frames=nullptr;
+	stepMode=0;
 	stopped=0;
 }
 

+ 115 - 20
modules/monkey/native/bbfunction.h

@@ -28,8 +28,12 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 	}
 	
 	struct Rep{
-
-		bb_atomic_int refs=0;
+		
+#ifdef BB_THREADS
+		std::atomic_int refs;
+#else
+		int refs;
+#endif
 
 		virtual ~Rep(){
 		}
@@ -134,6 +138,27 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 			return rhs( a... );
 		}
 		
+#ifdef BB_THREADS
+		virtual Rep *remove( Rep *rep ){
+		
+			if( rep==this ) return &_nullRep;
+			
+			Rep *lhs2=lhs._rep.load()->remove( rep );
+			Rep *rhs2=rhs._rep.load()->remove( rep );
+			
+			if( lhs2==lhs._rep && rhs2==rhs._rep ) return this;
+			
+			if( lhs2==&_nullRep ) return rhs2;
+			if( rhs2==&_nullRep ) return lhs2;
+			
+			return new SequenceRep( lhs2,rhs2 );
+		}
+		
+		virtual void gcMark(){
+			lhs._rep.load()->gcMark();
+			rhs._rep.load()->gcMark();
+		}
+#else
 		virtual Rep *remove( Rep *rep ){
 		
 			if( rep==this ) return &_nullRep;
@@ -153,11 +178,24 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 			lhs._rep->gcMark();
 			rhs._rep->gcMark();
 		}
+#endif
 	};
+
+	static Rep _nullRep;
 	
-	Rep *_rep;
+#ifdef BB_THREADS
+	std::atomic<Rep*> _rep;
 	
-	static Rep _nullRep;
+	void retain()const{
+		++_rep.load()->refs;
+	}
+	
+	void release(){
+		Rep *rep=_rep.load();
+		if( !--rep->refs && rep!=&_nullRep ) delete rep;
+	}
+#else
+	Rep *_rep;
 	
 	void retain()const{
 		++_rep->refs;
@@ -166,6 +204,7 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 	void release(){
 		if( !--_rep->refs && _rep!=&_nullRep ) delete _rep;
 	}
+#endif
 	
 	bbFunction( Rep *rep ):_rep( rep ){
 		retain();
@@ -175,10 +214,16 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 	
 	bbFunction():_rep( &_nullRep ){
 	}
-	
+
+#ifdef BB_THREADS
+	bbFunction( const bbFunction &p ):_rep( p._rep.load() ){
+		retain();
+	}
+#else
 	bbFunction( const bbFunction &p ):_rep( p._rep ){
 		retain();
 	}
+#endif
 	
 	template<class C> bbFunction( C *c,typename MethodRep<C>::T p ):_rep( new MethodRep<C>(c,p) ){
 		retain();
@@ -192,12 +237,57 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 		release();
 	}
 	
+	/*
 	void discard(){
 		if( _rep==&_nullRep ) return;
 		delete _rep;
 		_rep=&_nullRep;
 	}
+	*/
+	
+#ifdef BB_THREADS
+	bbFunction &operator=( const bbFunction &p ){
+		Rep *oldrep=_rep,*newrep=p._rep;
+		if( _rep.compare_exchange_strong( oldrep,newrep ) ){
+			++newrep->refs;
+			if( !--oldrep->refs && oldrep!=&_nullRep ) delete oldrep;
+		}
+		return *this;
+	}
+
+	bbFunction operator+( const bbFunction &rhs )const{
+		Rep *tlhs=_rep,*trhs=rhs._rep;
+		if( tlhs==&_nullRep ) return trhs;
+		if( trhs==&_nullRep ) return tlhs;
+		return new SequenceRep( tlhs,trhs );
+	}
+	
+	bbFunction operator-( const bbFunction &rhs )const{
+		return _rep.load()->remove( rhs._rep );
+	}
 	
+	bbBool operator==( const bbFunction &rhs )const{
+		return _rep.load()->equals( rhs._rep );
+	}
+	
+	bbBool operator!=( const bbFunction &rhs )const{
+		return !_rep.load()->equals( rhs._rep );
+	}
+
+	operator bbBool()const{
+		return _rep.load()==&_nullRep;
+	}
+	
+	R operator()( A...a )const{
+		return _rep.load()->invoke( a... );
+	}
+
+	operator F()const{	//cast to simple static function ptr
+		FunctionRep *t=dynamic_cast<FunctionRep*>( _rep.load() );
+		if( t ) return t->p;
+		return castErr;
+	}
+#else
 	bbFunction &operator=( const bbFunction &p ){
 		p.retain();
 		release();
@@ -210,20 +300,10 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 		if( rhs._rep==&_nullRep ) return *this;
 		return new SequenceRep( *this,rhs );
 	}
-	
+
 	bbFunction operator-( const bbFunction &rhs )const{
 		return _rep->remove( rhs._rep );
 	}
-	
-	bbFunction &operator+=( const bbFunction &rhs ){
-		*this=*this+rhs;
-		return *this;
-	}
-	
-	bbFunction &operator-=( const bbFunction &rhs ){
-		*this=*this-rhs;
-		return *this;
-	}
 
 	bbBool operator==( const bbFunction &rhs )const{
 		return _rep->equals( rhs._rep );
@@ -232,7 +312,7 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 	bbBool operator!=( const bbFunction &rhs )const{
 		return !_rep->equals( rhs._rep );
 	}
-	
+
 	operator bbBool()const{
 		return _rep==&_nullRep;
 	}
@@ -241,13 +321,22 @@ template<class R,class...A> struct bbFunction<R(A...)>{
 		return _rep->invoke( a... );
 	}
 	
-	//cast to simple static function ptr
-	//
-	operator F()const{
+	operator F()const{	//cast to simple static function ptr
 		FunctionRep *t=dynamic_cast<FunctionRep*>( _rep );
 		if( t ) return t->p;
 		return castErr;
 	}
+#endif
+
+	bbFunction &operator+=( const bbFunction &rhs ){
+		*this=*this+rhs;
+		return *this;
+	}
+	
+	bbFunction &operator-=( const bbFunction &rhs ){
+		*this=*this-rhs;
+		return *this;
+	}
 };
 
 template<class R,class...A> typename bbFunction<R(A...)>::Rep bbFunction<R(A...)>::_nullRep;
@@ -264,9 +353,15 @@ template<class R,class...A> bbFunction<R(A...)> bbMakefunc( R(*p)(A...) ){
 	return bbFunction<R(A...)>( p );
 }
 
+#if BB_THREADS
+template<class R,class...A> void bbGCMark( const bbFunction<R(A...)> &t ){
+	t._rep.load()->gcMark();
+}
+#else
 template<class R,class...A> void bbGCMark( const bbFunction<R(A...)> &t ){
 	t._rep->gcMark();
 }
+#endif
 
 template<class R,class...A> int bbCompare( const bbFunction<R(A...)> &x,const bbFunction<R(A...)> &y ){
 	return x._rep->compare( y._rep );

+ 764 - 0
modules/monkey/native/bbgc_mx.cpp

@@ -0,0 +1,764 @@
+
+#ifdef BB_THREADS
+
+#include "bbmonkey.h"
+//#include "bbgc_mx.h"
+#include "bbweakref.h"
+
+#include <mutex>
+#include <condition_variable>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#include <unistd.h>
+#include <signal.h>
+#endif
+
+namespace bbDB{
+
+	void stop();
+	
+	void stopped();
+	
+	void error( bbString err );
+}
+
+namespace{
+
+	typedef std::chrono::duration<double> Duration;
+	typedef std::chrono::high_resolution_clock Clock;
+	
+	double now(){
+	
+		static Clock::time_point start=Clock::now();
+		
+		auto elapsed=(Clock::now()-start).count();
+	
+		return elapsed * ((double)Clock::period::num/(double)Clock::period::den);
+	}
+}
+
+namespace bbGC{
+
+	size_t trigger=4*1024*1024;
+	int suspended=1;
+	
+	size_t memused;
+	size_t malloced;
+
+	bbGCThread *threads;
+	thread_local bbGCThread *currentThread;
+	thread_local bbGCFiber *currentFiber;
+
+	std::atomic_char markedBit;
+	std::atomic_char unmarkedBit;
+	std::atomic<bbGCNode*> markQueue;
+	
+	std::mutex collectorMutex;
+	
+	bbGCRoot *roots;
+	bbGCTmp *retained;
+	bbGCNode *markedList;
+	bbGCNode *unmarkedList;
+	bbGCNode markLists[2];
+	bbGCNode freeList;
+	
+	size_t markedBytes;
+	size_t unmarkedBytes;
+	size_t allocedBytes;
+	
+	void *pools[32];
+	unsigned char *poolBuf;
+	size_t poolBufSize;
+	std::mutex poolsMutex;
+	
+	void suspendSigHandler( int sig );
+	
+	bool inited;
+	
+	void init(){
+
+		if( inited ) return;
+		inited=true;
+		
+		printf( "sizeof( std::atomic_char )=%i\n",sizeof( std::atomic_char ) );
+		printf( "std::atomic_char().is_lock_free()=%i\n",std::atomic_char().is_lock_free() );
+		fflush( stdout );
+		
+		markedBit=1;
+		markedList=&markLists[0];
+		markedList->succ=markedList->pred=markedList;
+		
+		unmarkedBit=2;
+		unmarkedList=&markLists[1];
+		unmarkedList->succ=unmarkedList->pred=unmarkedList;
+		
+		freeList.succ=freeList.pred=&freeList;
+		
+		threads=new bbGCThread;
+		
+		currentThread=threads;
+		
+		currentFiber=currentThread->fibers;
+		
+#ifndef _WIN32
+		struct sigaction action;
+		memset( &action,0,sizeof(action) );
+		action.sa_handler=suspendSigHandler;
+		action.sa_flags=SA_RESTART;
+			
+		if( sigaction( SIGUSR2,&action,0 )<0 ) exit(-1);
+#endif
+
+		suspended=0;
+	}
+	
+	void setTrigger( size_t size ){
+	
+		trigger=size;
+	}
+	
+	void suspend(){
+	
+		++suspended;
+	}
+	
+	void resume(){
+	
+		--suspended;
+	}
+	
+	__forceinline void insert( bbGCNode *p,bbGCNode *succ ){
+		p->succ=succ;
+		p->pred=succ->pred;
+		p->pred->succ=p;
+		succ->pred=p;
+	}
+
+	__forceinline void remove( bbGCNode *p ){	
+		p->pred->succ=p->succ;
+		p->succ->pred=p->pred;
+	}
+	
+	void lockCollector(){
+	
+//		printf( "lockCollector\n" );fflush( stdout );
+	
+		if( inited ) collectorMutex.lock();
+	}
+	
+	void unlockCollector(){
+	
+//		printf( "unlockCollector\n" );fflush( stdout );
+	
+		if( inited ) collectorMutex.unlock();
+	}
+	
+
+#ifdef _WIN32
+
+	//collectorMutex locked.
+	//
+	void suspendThreads(){
+	
+		bbGCThread *thread=threads;
+		
+		for( ;; ){
+		
+			if( thread!=currentThread ){
+
+				int n=(int)SuspendThread( thread->handle );
+						
+				if( n<0 ){ printf( "SuspendThread failed! n=%i\n",n );fflush( stdout );exit( -1 ); }
+			
+				CONTEXT context={0};//CONTEXT_CONTROL};
+						
+				if( !GetThreadContext( thread->handle,&context ) ){ printf( "GetThreadContext failed\n" );fflush( stdout );exit( -1 ); }
+			}
+			
+			thread=thread->succ;
+			
+			if( thread==threads ) break;
+		}
+	}
+	
+	//collectorMutex locked.
+	//
+	void resumeThreads(){
+	
+		bbGCThread *thread=threads;
+		
+		for( ;; ){
+		
+			if( thread!=currentThread ){
+				
+				ResumeThread( thread->handle );
+			}
+
+			thread=thread->succ;
+			
+			if( thread==threads ) break;
+		}
+	}
+
+#else
+
+	std::atomic_int resumeCount{0};
+	std::atomic_bool threadSuspended{false};
+	
+	std::mutex suspendMutex;
+	std::condition_variable_any suspendCondvar;
+	
+	std::mutex resumeMutex;
+	std::condition_variable_any resumeCondvar;
+	
+	void suspendSigHandler( int sig ){
+	
+		int resume=resumeCount+1;
+	
+		//signal suspended
+		suspendMutex.lock();
+		threadSuspended=true;
+		suspendMutex.unlock();
+		suspendCondvar.notify_one();
+		
+		//wait for resume
+		resumeMutex.lock();
+		while( resumeCount!=resume ) resumeCondvar.wait( resumeMutex );
+		resumeMutex.unlock();
+	}
+	
+	//collectorMutex locked.
+	//
+	void suspendThreads(){
+	
+		bbGCThread *thread=threads;
+		
+		suspendMutex.lock();
+		for( ;; ){
+		
+			if( thread!=currentThread ){
+				
+				threadSuspended=false;
+				suspendMutex.unlock();
+				
+				pthread_kill( (pthread_t)thread->handle,SIGUSR2 );
+
+				suspendMutex.lock();
+				while( !threadSuspended ) suspendCondvar.wait( suspendMutex );
+			}
+			
+			thread=thread->succ;
+			
+			if( thread==threads ) break;
+		}
+		suspendMutex.unlock();
+	}
+	
+	//collectorMutex locked.
+	//
+	void resumeThreads(){
+	
+		//signal resume
+		resumeMutex.lock();
+		resumeCount+=1;
+		resumeMutex.unlock();
+		resumeCondvar.notify_all();
+	}
+
+#endif
+	
+	//collectorMutex locked.
+	//
+	void reclaim( size_t size ){
+	
+		size_t freed=0;
+	
+		while( freeList.succ!=&freeList && freed<size ){
+		
+			bbGCNode *p=freeList.succ;
+			
+			freed+=mallocSize( p );
+			
+			remove( p );
+			
+			if( p->flags & 2 ){
+
+				//printf( "deleting weak refs for: %s %p\n",p->typeName(),p );fflush( stdout );
+				
+				bbGCWeakRef **pred=&bbGC::weakRefs,*curr;
+				
+				while( curr=*pred ){
+					if( curr->target==p ){
+						curr->target=0;
+						*pred=curr->succ;
+					}else{
+						pred=&curr->succ;
+					}
+				}
+			}
+			
+			if( p->flags & 1 ){
+				
+				//printf( "finalizing: %s %p\n",p->typeName(),p );fflush( stdout );
+				
+				++suspended;
+				
+				p->state=char(unmarkedBit);
+				
+				p->gcFinalize();
+				
+				if( p->state==markedBit ) bbRuntimeError( "Object resurrected in finalizer" );
+					
+				--suspended;
+			}
+			
+			p->~bbGCNode();
+			
+			bbGC::free( p );
+		}
+	}
+	
+	//collectorMutex locked.
+	//
+	void markRoots(){
+	
+		for( bbGCRoot *root=roots;root;root=root->succ ){
+		
+			root->gcMark();
+		}
+	}
+	
+	//collectorMutex locked + threads suspended.
+	//
+	void markRetained(){
+	
+		for( bbGCTmp *tmp=retained;tmp;tmp=tmp->succ ){
+		
+			enqueue( tmp->node );
+		}
+	}
+	
+	//collectorMutex locked + threads suspended.
+	//
+	void markFibers(){
+	
+		bbGCThread *thread=threads;
+		
+		for(;;){
+		
+			bbGCMark( thread->entry );
+		
+			bbGCFiber *fiber=thread->fibers;
+		
+			for(;;){
+			
+				bbGCMark( fiber->entry );
+	
+				for( bbGCFrame *frame=fiber->frames;frame;frame=frame->succ ){
+				
+					frame->gcMark();
+				}
+				
+				for( bbGCNode *node=fiber->ctoring;node;node=node->qsucc ){
+				
+					node->gcMark();
+				}
+				
+				for( bbGCTmp *tmp=fiber->tmps;tmp;tmp=tmp->succ ){
+				
+					enqueue( tmp->node );
+				}
+				
+				fiber=fiber->succ;
+				
+				if( fiber==thread->fibers ) break; 
+			}
+			
+			thread=thread->succ;
+			
+			if( thread==threads ) break;
+		}
+	}
+	
+	//collectorMutex locked.
+	//
+	void markQueued( size_t tomark ){
+	
+		while( markQueue && markedBytes<tomark ){
+			
+			bbGCNode *p=markQueue;
+			while( !markQueue.compare_exchange_weak( p,p->qsucc ) ){}
+				
+			remove( p );
+			
+			p->gcMark();
+			
+			insert( p,markedList );
+			
+			p->state=char(markedBit);
+
+			markedBytes+=mallocSize( p );
+		}
+	}
+
+	//collectorMutex locked.
+	//
+	void sweep(){
+	
+		double start=now();
+		
+//		printf( "GC info: sweeping...\n" );fflush( stdout );
+		
+		suspendThreads();
+		
+		markRetained();
+		
+		markFibers();
+	
+		markQueued( SIZE_MAX );
+		
+		if( unmarkedList->succ!=unmarkedList ){
+			
+			//append unmarked to end of free queue
+			unmarkedList->succ->pred=freeList.pred;
+			unmarkedList->pred->succ=&freeList;
+			freeList.pred->succ=unmarkedList->succ;
+			freeList.pred=unmarkedList->pred;
+			
+			//clear unmarked
+			unmarkedList->succ=unmarkedList->pred=unmarkedList;
+		}
+
+		//swap mark/unmarked lists
+		
+		auto tmp1=markedList;markedList=unmarkedList;unmarkedList=tmp1;
+		auto tmp2=char(markedBit);markedBit=char(unmarkedBit);unmarkedBit=tmp2;
+		unmarkedBytes=markedBytes;
+		markedBytes=0;
+
+		//start new sweep phase
+		allocedBytes=0;
+
+		resumeThreads();
+		
+		markRoots();
+		
+		double elapsed=now()-start;
+		
+		bb_printf( "sweep=%g (%ims)",elapsed,int(elapsed*1000+0.5) );fflush( stdout );
+		
+//		printf( "end sweep\n" );fflush( stdout );
+	}
+	
+	void retain( bbGCNode *node ){
+		
+		if( !node ) return;
+		
+		bbGCTmp *tmp=currentFiber->freeTmps;
+		if( tmp ) currentFiber->freeTmps=tmp->succ; else tmp=new bbGCTmp;
+		tmp->node=node;
+		
+		lockCollector();
+		
+		tmp->succ=retained;
+		retained=tmp;
+		
+		unlockCollector();
+	}
+	
+	void release( bbGCNode *node ){
+		if( !node ) return;
+		
+		lockCollector();
+		
+		bbGCTmp **p=&retained;
+		while( bbGCTmp *tmp=*p ){
+			if( tmp->node!=node ){
+				p=&tmp->succ;
+				continue;
+			}
+			*p=tmp->succ;
+			tmp->succ=currentFiber->freeTmps;
+			currentFiber->freeTmps=tmp;
+			break;
+		}
+		
+		unlockCollector();
+	}
+	
+	void *malloc( size_t size ){
+	
+//		printf( "malloc %u\n",size );fflush( stdout );
+	
+		size=(size+8+7) & ~7;
+		
+		memused+=size;
+		
+		if( !suspended ){
+			
+			lockCollector();
+
+			if( allocedBytes+size>=trigger ){
+				
+				sweep();
+				
+			}else{
+			
+				markQueued( double( allocedBytes+size ) / double( trigger ) * double( unmarkedBytes + trigger ) );
+			}
+			
+			reclaim( size );
+			
+			unlockCollector();
+		}
+		
+		void *p;
+		
+		if( size<256 ){
+			
+			if( inited ) poolsMutex.lock();
+			
+			if( pools[size>>3] ){
+				
+				p=pools[size>>3];
+				pools[size>>3]=*(void**)p;
+				
+			}else{
+			
+				if( size>poolBufSize ){
+					if( poolBufSize ){
+						*(void**)poolBuf=pools[poolBufSize>>3];
+						pools[poolBufSize>>3]=poolBuf;
+					}
+					poolBufSize=65536;
+					poolBuf=(unsigned char*)::malloc( poolBufSize );
+					malloced+=poolBufSize;
+				}
+				p=poolBuf;
+				poolBuf+=size;
+				poolBufSize-=size;
+			}
+			
+			if( inited ) poolsMutex.unlock();
+			
+		}else{
+			p=::malloc( size );
+			malloced+=size;
+		}
+		
+		allocedBytes+=size;
+		size_t *q=(size_t*)p;
+		if( sizeof(size_t)==4 ) ++q;
+		*q++=size;
+
+//		printf( "end malloc %u\n",size );fflush( stdout );
+	
+		return q;
+	}
+	
+	size_t mallocSize( void *p ){
+	
+		if( p ) return *((size_t*)p-1);
+		
+		return 0;
+	}
+	
+	void free( void *p ){
+	
+		if( !p ) return;
+		
+		size_t *q=(size_t*)p;
+		size_t size=*--q;
+		if( sizeof(size_t)==4 ) --q;
+		
+#ifndef NDEBUG
+		memset( q,0xa5,size );
+#endif
+		
+		memused-=size;
+		
+		if( size<256 ){
+
+			if( inited ) poolsMutex.lock();
+			
+			*(void**)q=pools[size>>3];
+			pools[size>>3]=q;
+
+			if( inited ) poolsMutex.unlock();
+			
+		}else{
+			malloced-=size;
+			::free( q );
+		}
+	}
+	
+	void collect(){
+	
+		if( !inited ) return;
+		
+		static size_t maxused;
+		
+		lockCollector();
+	
+		sweep();
+		
+		reclaim( SIZE_MAX );
+
+		unlockCollector();
+		
+		if( memused>maxused ) maxused=memused;
+		
+//		printf( "Collect complete: memused=%i max memused=%i\n",memused,maxused );fflush( stdout );
+	}
+
+#ifndef NDEBUG	
+	void qinsert( bbGCNode *p ){
+
+		static int max_n;
+		
+		int n=0;
+		
+		p->qsucc=markQueue;
+		while( !markQueue.compare_exchange_weak( p->qsucc,p ) ){ ++n; }
+			
+		if( n>max_n ){ max_n=n;printf( "GC info: max spins=%i\n",max_n );fflush( stdout ); }			
+	}
+	
+	void enqueue( bbGCNode *p ){
+		
+		if( !p || p->state.load()!=unmarkedBit ) return;
+		
+		char oldstate=p->state.exchange( 4 );
+		
+		if( oldstate==4 ) return;
+		
+		if( oldstate!=unmarkedBit ){ printf( "GC info: redundant enqueue\n" );fflush( stdout ); }
+		
+		qinsert( p );
+	}
+	
+	void beginCtor( bbGCNode *p ){
+		p->state=4;
+		p->flags=0;
+		p->qsucc=currentFiber->ctoring;
+		currentFiber->ctoring=p;
+	}
+	
+	void endCtor( bbGCNode *p ){
+		currentFiber->ctoring=p->qsucc;
+		p->succ=p->pred=p;
+		qinsert( p );
+	}
+#endif
+}
+
+// ***** bbGCNode *****
+
+void bbGCNode::gcNeedsFinalize(){
+	flags|=1;
+}
+
+void bbGCNode::gcFinalize(){
+}
+
+void bbGCNode::gcMark(){
+}
+
+void bbGCNode::dbEmit(){
+}
+
+const char *bbGCNode::typeName()const{
+
+	return "bbGCNode";
+}
+
+// ***** bbGCThread *****
+
+bbGCThread::bbGCThread():succ( this ),pred( this ),fibers( new bbGCFiber ){
+
+#if _WIN32
+	handle=OpenThread( THREAD_ALL_ACCESS,FALSE,GetCurrentThreadId() );
+#else
+	handle=(void*)pthread_self();
+#endif
+
+}
+
+bbGCThread::~bbGCThread(){
+
+#ifdef _WIN32
+	CloseHandle( handle );
+#endif
+}
+
+void bbGCThread::link(){
+
+	bbGC::lockCollector();
+	
+	succ=bbGC::threads;
+	pred=bbGC::threads->pred;
+	bbGC::threads->pred=this;
+	pred->succ=this;
+	
+	bbGC::currentThread=this;
+	bbGC::currentFiber=fibers;
+	
+	bbGC::unlockCollector();
+}
+
+void bbGCThread::unlink(){
+
+	bbGC::lockCollector();
+	
+	pred->succ=succ;
+	succ->pred=pred;
+	
+	bbGC::currentThread=0;
+	bbGC::currentFiber=0;
+	
+	bbGC::unlockCollector();
+}
+
+// ***** bbGCFiber *****
+
+bbGCFiber::bbGCFiber():succ( this ),pred( this ),frames( nullptr ),tmps( nullptr ),freeTmps( nullptr ),ctoring( nullptr ){
+}
+	
+void bbGCFiber::link(){
+
+	succ=bbGC::currentThread->fibers;
+	pred=bbGC::currentThread->fibers->pred;
+	bbGC::currentThread->fibers->pred=this;
+	pred->succ=this;
+}
+
+void bbGCFiber::unlink(){
+
+	pred->succ=succ;
+	succ->pred=pred;
+}
+
+// ***** bbGCFrame *****
+
+void bbGCFrame::gcMark(){
+}
+
+// ***** bbGCRoot *****
+
+bbGCRoot::bbGCRoot(){
+
+	bbGC::lockCollector();
+	
+	succ=bbGC::roots;
+	bbGC::roots=this;
+	
+	bbGC::unlockCollector();
+}
+
+void bbGCRoot::gcMark(){
+}
+
+#endif

+ 179 - 0
modules/monkey/native/bbgc_mx.h

@@ -0,0 +1,179 @@
+
+#ifndef BB_GC_H
+#define BB_GC_H
+
+#ifndef BB_THREADS
+#error "Wrong gc header"
+#endif
+
+#include "bbtypes.h"
+#include "bbfunction.h"
+
+struct bbGCNode;
+struct bbGCThread;
+struct bbGCFiber;
+struct bbGCFrame;
+struct bbGCRoot;
+struct bbGCTmp;
+	
+namespace bbGC{
+
+	extern bbGCThread *threads;
+	extern thread_local bbGCThread *currentThread;
+	extern thread_local bbGCFiber *currentFiber;
+	
+	extern std::atomic_char markedBit;
+	extern std::atomic_char unmarkedBit;
+	extern std::atomic<bbGCNode*> markQueue;
+}
+
+struct bbGCNode{
+	
+	bbGCNode *succ;
+	bbGCNode *pred;
+	bbGCNode *qsucc;
+	std::atomic_char state;
+	char flags;		//1=finalize
+	char pad[2];
+
+	bbGCNode(){}
+	virtual ~bbGCNode(){}
+	
+	void gcNeedsFinalize();
+	
+	virtual void gcFinalize();
+	virtual void gcMark();
+	virtual void dbEmit();
+	virtual const char *typeName()const;
+};
+
+struct bbGCThread{
+	
+	bbGCThread *succ,*pred;
+	bbGCFiber *fibers;
+	void *handle;
+
+	bbFunction<void()> entry;
+	
+	bbGCThread();
+	~bbGCThread();
+	
+	void link();
+	void unlink();
+};
+
+struct bbGCFiber{
+
+	bbGCFiber *succ;
+	bbGCFiber *pred;
+	bbGCFrame *frames;
+	bbGCTmp *tmps;
+	bbGCTmp *freeTmps;
+	bbGCNode *ctoring;
+	
+	bbFunction<void()> entry;
+	
+	bbGCFiber();
+	
+	void link();
+	void unlink();
+};
+
+struct bbGCFrame{
+	
+	bbGCFrame *succ;
+	
+	bbGCFrame():succ( bbGC::currentFiber->frames ){
+		bbGC::currentFiber->frames=this;
+	}
+	
+	~bbGCFrame(){
+		bbGC::currentFiber->frames=succ;
+	}
+	
+	virtual void gcMark();
+};
+
+struct bbGCRoot{
+	
+	bbGCRoot *succ;
+	
+	bbGCRoot();
+	
+	virtual void gcMark();
+};
+
+struct bbGCTmp{
+	bbGCTmp *succ;
+	bbGCNode *node;
+};
+
+namespace bbGC{
+
+	void init();
+	void setTrigger( size_t trigger );
+	void suspend();
+	void resume();
+	void retain( bbGCNode *p );
+	void release( bbGCNode *p );
+	void setDebug( bool debug );
+	void *malloc( size_t size );
+	size_t mallocSize( void *p );
+	void free( void *p );
+	void collect();
+	
+	inline void pushTmp( bbGCNode *p ){
+		bbGCTmp *tmp=currentFiber->freeTmps;
+		if( tmp ) currentFiber->freeTmps=tmp->succ; else tmp=new bbGCTmp;
+		tmp->succ=currentFiber->tmps;
+		tmp->node=p;
+		currentFiber->tmps=tmp;
+	}
+	
+	inline void popTmps( int n ){
+		while( n-- ){
+			bbGCTmp *tmp=currentFiber->tmps;
+			currentFiber->tmps=tmp->succ;
+			tmp->succ=currentFiber->freeTmps;
+			currentFiber->freeTmps=tmp;
+		}
+	}
+	
+	template<class T> T *tmp( T *p ){
+		pushTmp( p );
+		return p;
+	}
+	
+#ifdef NDEBUG
+	__forceinline void enqueue( bbGCNode *p ){
+		if( !p || p->state.load()!=unmarkedBit ) return;
+		if( p->state.exchange( 4 )==4 ) return;
+		p->qsucc=markQueue;
+		while( !markQueue.compare_exchange_weak( p->qsucc,p ) ){}
+	}
+
+	inline void beginCtor( bbGCNode *p ){
+		p->state=4;
+		p->flags=0;
+		p->qsucc=currentFiber->ctoring;
+		currentFiber->ctoring=p;
+	}
+	
+	inline void endCtor( bbGCNode *p ){
+		currentFiber->ctoring=p->qsucc;
+		p->succ=p->pred=p;
+		p->qsucc=markQueue;
+		while( !markQueue.compare_exchange_weak( p->qsucc,p ) ){}
+	}
+#else
+	void enqueue( bbGCNode *p );
+	void beginCtor( bbGCNode *p );
+	void endCtor( bbGCNode *p );
+#endif
+
+}
+
+template<class T> void bbGCMark( T const& ){
+}
+
+#endif

+ 6 - 1
modules/monkey/native/bbmonkey.h

@@ -7,7 +7,6 @@
 #include "bbassert.h"
 #include "bbstring.h"
 #include "bbdebug.h"
-#include "bbgc.h"
 #include "bbarray.h"
 #include "bbfunction.h"
 #include "bbobject.h"
@@ -16,6 +15,12 @@
 #include "bbtypeinfo_t.h"
 #include "bbdeclinfo.h"
 
+#ifdef BB_THREADS
+#include "bbgc_mx.h"
+#else
+#include "bbgc.h"
+#endif
+
 extern int bb_argc;
 
 extern char **bb_argv;

+ 6 - 1
modules/monkey/native/bbobject.h

@@ -2,8 +2,13 @@
 #ifndef BB_OBJECT_H
 #define BB_OBJECT_H
 
+#ifdef BB_THREADS
+#include "bbgc_mx.h"
+#else
 #include "bbgc.h"
-#include "bbstring.h"
+#endif
+
+//#include "bbstring.h"
 #include "bbdebug.h"
 
 struct bbObject : public bbGCNode{

+ 8 - 0
modules/monkey/native/bbstd.h

@@ -15,4 +15,12 @@
 #include <cctype>
 #include <cmath>
 
+#ifdef BB_THREADS
+#include <atomic>
+#endif
+
+#ifndef _MSC_VER
+#define __forceinline inline __attribute__((always_inline))
+#endif
+
 #endif

+ 31 - 9
modules/monkey/native/bbstring.cpp

@@ -7,6 +7,10 @@
 #include <cwctype>
 #include <clocale>
 
+#ifdef BB_THREADS
+#include <mutex>
+#endif
+
 bbString::Rep bbString::_nullRep;
 
 #if BB_ANDROID
@@ -56,9 +60,14 @@ namespace{
 	
 	void initLocale(){
 	
+#if BB_THREADS
+		static std::atomic_bool inited;
+		if( inited.exchange( true ) ) return;
+#else
 		static bool inited;
 		if( inited ) return;
 		inited=true;
+#endif
 		
 #if BB_ANDROID
 		JNIEnv *env=(JNIEnv*)SDL_AndroidGetJNIEnv();
@@ -208,8 +217,9 @@ bbString::bbString( const void *p ){
 		_rep=Rep::create( cp,sz );
 		return;
 	}
-	_rep=Rep::alloc( n );
-	utf8ToChars( cp,_rep->data,n );
+	Rep *rep=Rep::alloc( n );
+	utf8ToChars( cp,rep->data,n );
+	_rep=rep;
 }
 
 bbString::bbString( const void *p,int sz ){
@@ -227,8 +237,9 @@ bbString::bbString( const void *p,int sz ){
 		_rep=Rep::create( cp,sz );
 		return;
 	}
-	_rep=Rep::alloc( n );
-	utf8ToChars( cp,_rep->data,n );
+	Rep *rep=Rep::alloc( n );
+	utf8ToChars( cp,rep->data,n );
+	_rep=rep;
 }
 
 bbString::bbString( const bbChar *data ):_rep( Rep::create( data ) ){
@@ -326,8 +337,8 @@ bbString::bbString( double n ){
 }
 
 void bbString::toCString( void *buf,int size )const{
-
-	charsToUtf8( _rep->data,_rep->length,(char*)buf,size );
+	Rep *rep=_rep;
+	charsToUtf8( rep->data,rep->length,(char*)buf,size );
 }
 
 void bbString::toWString( void *buf,int size )const{
@@ -347,12 +358,23 @@ const char *bbString::c_str()const{
 
 	static int _sz;
 	static char *_tmp;
-	
+
 	int sz=utf8Length()+1;
+	
+#ifdef BB_THREADS
+	std::mutex _mutex;
+	_mutex.lock();
+#endif
+
 	if( sz>_sz ){
 		::free( _tmp );
 		_tmp=(char*)::malloc( _sz=sz );
 	}
+	
+#ifdef BB_THREADS
+	_mutex.unlock();
+#endif
+	
 	toCString( _tmp,sz );
 	return _tmp;
 }
@@ -463,7 +485,7 @@ bbString bbString::operator*( int n )const{
 	Rep *rep=Rep::alloc( length()*n );
 	bbChar *p=rep->data;
 	for( int j=0;j<n;++j ){
-		for( int i=0;i<_rep->length;++i ) *p++=data()[i];
+		for( int i=0;i<length();++i ) *p++=data()[i];
 	}
 	return rep;
 }
@@ -580,7 +602,7 @@ bbString bbString::dup( int n )const{
 	Rep *rep=Rep::alloc( length()*n );
 	bbChar *p=rep->data;
 	for( int j=0;j<n;++j ){
-		for( int i=0;i<_rep->length;++i ) *p++=data()[i];
+		for( int i=0;i<length();++i ) *p++=data()[i];
 	}
 	return rep;
 }

+ 53 - 6
modules/monkey/native/bbstring.h

@@ -15,7 +15,11 @@ class bbCString;
 class bbString{
 
 	struct Rep{
-		bb_atomic_int refs;
+#ifdef BB_THREADS
+		std::atomic_int refs;
+#else
+		int refs;
+#endif
 		int length;
 		bbChar data[0];
 		
@@ -32,13 +36,27 @@ class bbString{
 			while( *e ) ++e;
 			return create( p,e-p );
 		}
-
 	};
-	
-	Rep *_rep;
 
 	static Rep _nullRep;
 	
+#ifdef BB_THREADS
+
+	std::atomic<Rep*> _rep;
+
+	void retain()const{
+		++_rep.load()->refs;
+	}
+	
+	void release(){
+		Rep *rep=_rep.load();
+		if( !--rep->refs && rep!=&_nullRep ) bbGC::free( rep );
+	}
+	
+#else
+
+	Rep *_rep;
+	
 	void retain()const{
 		++_rep->refs;
 	}
@@ -47,7 +65,9 @@ class bbString{
 		if( !--_rep->refs && _rep!=&_nullRep ) bbGC::free( _rep );
 	}
 	
-	bbString( Rep *rep ):_rep( rep ){
+#endif
+
+	bbString( Rep *rep ):_rep(rep){
 	}
 	
 	public:
@@ -57,9 +77,15 @@ class bbString{
 	bbString():_rep( &_nullRep ){
 	}
 	
+#if BB_THREADS
+	bbString( const bbString &s ):_rep( s._rep.load() ){
+		retain();
+	}
+#else
 	bbString( const bbString &s ):_rep( s._rep ){
 		retain();
 	}
+#endif
 	
 	bbString( const void *data );
 	
@@ -99,6 +125,15 @@ class bbString{
 		release();
 	}
 	
+#if BB_THREADS
+	const bbChar *data()const{
+		return _rep.load()->data;
+	}
+	
+	int length()const{
+		return _rep.load()->length;
+	}
+#else
 	const bbChar *data()const{
 		return _rep->data;
 	}
@@ -106,7 +141,8 @@ class bbString{
 	int length()const{
 		return _rep->length;
 	}
-	
+#endif
+
 	bbChar operator[]( int index )const{
 		bbDebugAssert( index>=0 && index<length(),"String character index out of range" );
 		return data()[index];
@@ -126,12 +162,23 @@ class bbString{
 	
 	bbString operator*( int n )const;
 	
+#ifdef BB_THREADS	
+	bbString &operator=( const bbString &str ){
+		Rep *oldrep=_rep,*newrep=str._rep;
+		if( _rep.compare_exchange_strong( oldrep,newrep ) ){
+			++newrep->refs;
+			if( !--oldrep->refs && oldrep!=&_nullRep ) bbGC::free( oldrep );
+		}
+		return *this;
+	}
+#else
 	bbString &operator=( const bbString &str ){
 		str.retain();
 		release();
 		_rep=str._rep;
 		return *this;
 	}
+#endif
 	
 	template<class C> bbString &operator=( const C *data ){
 		release();

+ 0 - 10
modules/monkey/native/bbtypes.h

@@ -4,10 +4,6 @@
 
 #include "bbstd.h"
 
-#if BB_THREADS
-#include <atomic>
-#endif
-
 typedef bool bbBool;
 typedef signed char bbByte;
 typedef unsigned char bbUByte;
@@ -88,10 +84,4 @@ bbString bbDBValue(X*);
 
 #endif
 
-#if BB_THREADS
-typedef std::atomic_int bb_atomic_int;
-#else
-typedef int bb_atomic_int;
-#endif
-
 #endif

+ 9 - 5
src/mx2cc/mx2cc.monkey2

@@ -22,12 +22,14 @@ Global opts_time:Bool
 
 Global StartDir:String
 
-Const TestArgs:="mx2cc makemods"
+Global profileName:String
+
+'Const TestArgs:="mx2cc makemods monkey"
 
 'Const TestArgs:="mx2cc makemods"' -clean mojo"
 'Const TestArgs:="mx2cc makedocs std"
  
-'Const TestArgs:="mx2cc makeapp src/mx2cc/test.monkey2"
+Const TestArgs:="mx2cc makeapp src/mx2cc/test.monkey2"
 
 Function Main()
 	
@@ -179,7 +181,7 @@ Function GenInfo:Bool( args:String[] )
 	ChangeDir( cd )
 	
 	Print ""
-	Print "***** Generating info for "+opts.mainSource+"' ("+opts.target+" "+opts.config+" "+opts.arch+" "+opts.toolchain+") *****"
+	Print "***** Generating info for "+opts.mainSource+"' "+profileName+" *****"
 	Print ""
 
 	New BuilderInstance( opts )
@@ -228,7 +230,7 @@ Function MakeApp:Bool( args:String[] )
 	opts.mainSource=srcPath
 	
 	Print ""
-	Print "***** Making app '"+opts.mainSource+"' ("+opts.target+" "+opts.config+" "+opts.arch+" "+opts.toolchain+") *****"
+	Print "***** Making app '"+opts.mainSource+"' "+profileName+" *****"
 	Print ""
 
 	New BuilderInstance( opts )
@@ -287,7 +289,7 @@ Function MakeMods:Bool( args:String[] )
 		If Not path Fail( "Module '"+modid+"' not found" )
 	
 		Print ""
-		Print "***** Making module '"+modid+"' ("+opts.target+" "+opts.config+" "+opts.arch+" "+opts.toolchain+") *****"
+		Print "***** Making module '"+modid+"' "+profileName+" *****"
 		Print ""
 		
 		opts.mainSource=RealPath( path )
@@ -572,6 +574,8 @@ Function ParseOpts:String[]( opts:BuildOpts,args:String[] )
 	Default
 		Fail( "Unrecognized apptype '"+opts.appType+"'" )
 	End
+	
+	profileName="("+opts.target+" "+opts.config+" "+opts.arch+" "+opts.toolchain+(opts.threads ? " mx" Else "")+")"
 		
 	Return args
 End