ソースを参照

Cleaned up/rearranged std. Added std.timer and std.process.

Mark Sibly 9 年 前
コミット
96685bc06a

+ 20 - 0
modules/std/async/async.monkey2

@@ -0,0 +1,20 @@
+
+Namespace std.async
+
+#import "native/async.cpp"
+#import "native/async.h"
+
+Extern
+
+#rem monkeydoc @hidden
+
+Internal struct used to deliver events from remote threads.
+
+#end
+Struct AsyncEvent="bbAsync::Event"
+	
+	Method Dispatch()="dispatch"
+
+End
+
+Public

+ 103 - 0
modules/std/async/native/async.cpp

@@ -0,0 +1,103 @@
+
+#include "async.h"
+
+namespace bbAsync{
+
+	typedef std::chrono::duration<double> secs_t;
+	typedef std::chrono::high_resolution_clock clock_t;
+	typedef std::chrono::time_point<clock_t,secs_t> time_t;
+
+	struct DelayedEvent{
+		DelayedEvent *succ;
+		Event *event;
+		time_t time;
+	};
+
+	DelayedEvent *que;
+	DelayedEvent *free_que;
+	std::mutex que_mutex;
+	std::condition_variable que_condvar;
+	
+	void initQue(){
+	
+		static bool inited;
+		if( inited ) return;
+		inited=true;
+	
+		std::thread( [](){
+		
+			std::unique_lock<std::mutex> lock( que_mutex );
+			
+			for(;;){
+			
+				if( que ){
+					que_condvar.wait_for( lock,que->time-clock_t::now() );
+				}else{
+					que_condvar.wait( lock );
+				}
+
+				//prevent spamming...?				
+				time_t now=clock_t::now();
+				
+				while( que && que->time<=now ){
+					
+					DelayedEvent *devent=que;
+	
+					devent->event->post();
+					
+					que=devent->succ;
+					
+					devent->succ=free_que;
+					
+					free_que=devent;
+				}
+			}
+	
+		} ).detach();
+	}
+
+	void (*postEventFilter)( Event* );
+	
+	void setPostEventFilter( void(*filter)(Event*) ){
+
+		postEventFilter=filter;
+	}
+
+	void Event::post(){
+
+		postEventFilter( this );
+	}
+	
+	void Event::post( double delay ){
+	
+		time_t now=clock_t::now();
+		
+		initQue();
+		
+		{
+			std::unique_lock<std::mutex> lock( que_mutex );
+			
+			DelayedEvent *devent=free_que;
+			if( devent ){
+				free_que=devent->succ;
+			}else{
+				devent=new DelayedEvent;
+			}
+			
+			devent->event=this;
+			devent->time=now+secs_t( delay );
+	
+			DelayedEvent *succ,**pred=&que;
+			
+			while( succ=*pred ){
+				if( devent->time<succ->time ) break;
+				pred=&succ->succ;
+			}
+			
+			devent->succ=succ;
+			*pred=devent;
+		}
+		
+		que_condvar.notify_one();
+	}
+}

+ 43 - 0
modules/std/async/native/async.h

@@ -0,0 +1,43 @@
+
+#ifndef BB_STD_ASYNC_H
+#define BB_STD_ASYNC_H
+
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+
+namespace bbAsync{
+
+	struct Semaphore{
+	
+		int count=0;
+		std::mutex mutex;
+		std::condition_variable cond_var;
+		
+		void wait(){
+			std::unique_lock<std::mutex> lock( mutex );
+			while( !count ) cond_var.wait( lock );
+			--count;
+		}
+		
+		void signal(){
+			std::unique_lock<std::mutex> lock( mutex );
+			++count;
+			cond_var.notify_one();
+		}
+	};
+	
+	struct Event{
+	
+		void post();
+		
+		void post( double delay );
+	
+		virtual void dispatch()=0;
+	};
+	
+	void setPostEventFilter( void(*filter)(Event*) );
+}
+
+#endif

+ 9 - 0
modules/std/fiber/fiber.monkey2

@@ -21,6 +21,8 @@ Function TerminateFiber:Void( fiber:Int )="bbFiber::terminateFiber"
 
 Function SuspendCurrentFiber:Void()="bbFiber::suspendCurrentFiber"
 
+Function CurrentFiberSleep:Void( seconds:Double )="bbFiber::currentFiberSleep"
+
 Function GetCurrentFiber:Int()="bbFiber::getCurrentFiber"
 
 Public
@@ -93,9 +95,16 @@ Struct Fiber
 		Return New Fiber( GetCurrentFiber() )
 	End
 	
+	#rem monkeydoc Puts current fiber to sleep.
+	#end
+	Function Sleep( seconds:Double )
+		CurrentFiberSleep( seconds )
+	End
+	
 	#rem monkeydoc @hidden - not really needed?
 	#end
 	Function CreateSuspended:Fiber( entry:Void() )
+	
 		Return New Fiber( CreateFiber( entry ) )
 	End
 	

+ 31 - 1
modules/std/fiber/native/fiber.cpp

@@ -2,6 +2,9 @@
 #include "fiber.h"
 #include "fcontext.h"
 
+#include "../../time/native/time.h"
+#include "../../async/native/async.h"
+
 namespace bbFiber{
 
 	const int MAX_FIBERS=1024;
@@ -151,6 +154,9 @@ namespace bbFiber{
 	
 		Fiber *fiber=getFiber( id );
 		if( !fiber ){
+			//
+			// could signal a semaphore...
+			//
 			bbDB::error( "Invalid fiber id" );
 			return;
 		}
@@ -165,6 +171,9 @@ namespace bbFiber{
 	void suspendCurrentFiber(){
 	
 		if( currFiber==mainFiber ){
+			//
+			// could wait on a semaphore...
+			//
 			bbDB::error( "Can't suspend main fiber" );
 			return;
 		}
@@ -176,6 +185,27 @@ namespace bbFiber{
 		setCurrFiber( fiber );
 	}
 	
+	void currentFiberSleep( double seconds ){
+	
+		struct ResumeEvent : public bbAsync::Event{
+		
+			int fiber;
+			
+			ResumeEvent():fiber( getCurrentFiber() ){}
+			
+			void dispatch(){
+			
+				resumeFiber( fiber );
+			}
+		};
+		
+		ResumeEvent event;
+		
+		event.post( seconds );
+		
+		suspendCurrentFiber();
+	}
+	
 	int startFiber( bbFunction<void()> entry ){
 	
 		int id=createFiber( entry );
@@ -186,7 +216,7 @@ namespace bbFiber{
 	}
 	
 	void terminateFiber( int id ){
-	
+
 	}
 	
 	int getCurrentFiber(){

+ 2 - 0
modules/std/fiber/native/fiber.h

@@ -18,6 +18,8 @@ namespace bbFiber{
 	
 	void suspendCurrentFiber();
 	
+	void currentFiberSleep( double seconds );
+	
 	int getCurrentFiber();
 }
 

+ 11 - 9
modules/std/misc/filesystem.monkey2 → modules/std/filesystem/filesystem.monkey2

@@ -79,11 +79,11 @@ Const FILETYPE_UNKNOWN:=FileType.Unknown
 #end
 Function AssetsDir:String()
 
-#If __TARGET__="desktop" And __HOSTOS__="macos"
+#If __TARGET__="macos"
 
 	Return AppDir()+"../Resources/"
 	
-#Else __TARGET__="desktop" Or __TARGET__="emscripten"
+#Else If __DESKTOP_TARGET__ Or __TARGET__="emscripten"
 
 	Return AppDir()+"assets/"
 	
@@ -97,13 +97,15 @@ End
 
 Function DesktopDir:String()
 
-#If __TARGET__="desktop"
-	#If __HOSTOS__="windows"
-		Return (String.FromCString( getenv( "HOMEDRIVE" ) )+String.FromCString( getenv( "HOMEPATH" ) )).Replace( "\","/" )+"/Desktop/"
-	#Else
-		Return String.FromCString( getenv( "HOME" ) )+"/Desktop/"
-	#endif
-#endif
+#If __TARGET__="windows"
+	
+	Return (String.FromCString( getenv( "HOMEDRIVE" ) )+String.FromCString( getenv( "HOMEPATH" ) )).Replace( "\","/" )+"/Desktop/"
+	
+#Else If __DESKTOP_TARGET__
+
+	Return String.FromCString( getenv( "HOME" ) )+"/Desktop/"
+
+#Endif
 
 	Return ""
 

+ 0 - 0
modules/std/misc/native/filesystem.cpp → modules/std/filesystem/native/filesystem.cpp


+ 0 - 0
modules/std/misc/native/filesystem.h → modules/std/filesystem/native/filesystem.h


+ 18 - 0
modules/std/geom/affinemat3.monkey2

@@ -40,6 +40,18 @@ Struct AffineMat3<T>
 		Self.i.x=ix;Self.i.y=iy;Self.j.x=jx;Self.j.y=jy;Self.t.x=tx;Self.t.y=ty
 	End
 	
+	#rem monkeydoc Converts the matrix to a matrix of a different type.
+	#end
+	Operator To<C>:AffineMat3<C>()
+		Return New AffineMat3<C>( i.x,i.y,j.x,j.y,t.x,t.y )
+	End 
+	
+	#rem monkeydoc Converts the matrix to a printable string.
+	#end
+	Operator To:String()
+		Return "AffineMat3("+i.x+","+i.y+","+j.x+","+j.y+","+t.x+","+t.y+")"
+	End
+	
 	#rem monkeydoc Returns the inverse of the matrix.
 	#end
 	Operator-:AffineMat3()
@@ -64,6 +76,12 @@ Struct AffineMat3<T>
 			i.x*m.j.x + j.x*m.j.y       , i.y*m.j.x + j.y*m.j.y ,
 			i.x*m.t.x + j.x*m.t.y + t.x , i.y*m.t.x + j.y*m.t.y + t.y )
 	End
+	
+	#rem monkeydoc Multiplies a vector by the matrix and returns the result.
+	#end
+	Method Transform:Vec2<T>( v:Vec2<T> )
+		Return New Vec2<T>( i.x*v.x + j.x*v.y + t.x , i.y*v.x + j.y*v.y + t.y )
+	End
 			
 	#rem monkeydoc Multiplies a vector by the matrix and returns the result.
 	#end

+ 16 - 1
modules/std/geom/rect.monkey2

@@ -47,6 +47,18 @@ Struct Rect<T>
 		Self.max=New Vec2<T>( x1,y1 )
 	End
 	
+	#rem monkeydoc Converts the rect to a rect of a different type
+	#end
+	Operator To<C>:Rect<C>()
+		Return New Rect<C>( min.x,min.y,max.x,max.y )
+	End
+	
+	#rem  monkeydoc Converts the rect to a printable string.
+	#end
+	Operator To:String()
+		Return "Rect("+min.x+","+min.y+","+max.x+","+max.y+")"
+	End
+	
 	#rem monkeydoc The minimum X coordinate.
 	#end
 	Property X:T()
@@ -321,9 +333,12 @@ Struct Rect<T>
 	End
 	
 	#rem monkeydoc Gets a string describing the rect.
+	
+	Deprecated: Use Operator To:String instead.
+	
 	#end
 	Method ToString:String()
-		Return "Rect("+min.ToString()+","+max.ToString()+")"
+		Return Self
 	End
 	
 End

+ 14 - 6
modules/std/geom/vec2.monkey2

@@ -26,15 +26,23 @@ Struct Vec2<T>
 	Method New()
 	End
 	
-'	Method New( v:Vec2 )
-'		x=v.x;y=v.y
-'	End
-	
 	Method New( x:T,y:T )
 		Self.x=x
 		Self.y=y
 	End
 	
+	#rem monkeydoc Converts the vector to a vector of a different type.
+	#end
+	Operator To<C>:Vec2<C>()
+		Return New Vec2<C>( x,y )
+	End
+
+	#rem monkeydoc Converts the vector to a printable string.
+	#end
+	Operator To:String()
+		Return "Vec2("+x+","+y+")"
+	End
+
 	#rem monkeydoc The X coordinate of the vector.
 	#end
 	Property X:T()
@@ -104,7 +112,7 @@ Struct Vec2<T>
 	Operator-:Vec2( s:T )
 		Return New Vec2( x-s,y-s )
 	End
-
+	
 	#rem monkeydoc The length of the vector.
 	#end
 	Property Length:Double()
@@ -132,7 +140,7 @@ Struct Vec2<T>
 	#rem monkeydoc Gets a string representation for the vector.
 	#end
 	Method ToString:String()
-		Return "Vec2("+x+","+y+")"
+		Return Self
 	End
 
 End

+ 8 - 2
modules/std/graphics/color.monkey2

@@ -88,6 +88,12 @@ Struct Color
 		Self.a=a
 	End
 	
+	#rem monkeydoc Converts the color to printable string.
+	#end
+	Operator To:String()
+		Return "Color("+r+","+g+","+b+","+a+")"
+	End
+	
 	#rem monkeydoc The Red color component.
 	#end
 	Property R:Float()
@@ -172,11 +178,11 @@ Struct Color
 	Method ToARGB:UInt()
 		Return UInt(a*255) Shl 24 | UInt(r*255) Shl 16 | UInt(g*255) Shl 8 | UInt(b*255)
 	End
-	
+
 	#rem monkeydoc Converts the color to printable string.
 	#end
 	Method ToString:String()
-		Return "Color("+r+","+g+","+b+","+a+")"
+		Return Self
 	End
 	
 	#rem monkeydoc Creates a color from hue, saturation and value.

+ 351 - 0
modules/std/process/native/process.cpp

@@ -0,0 +1,351 @@
+
+#include "process.h"
+#include "procutil.h"
+
+#include "../../../std/async/native/async.h"
+
+struct bbProcess::Rep{
+
+	struct FinishedEvent : public bbAsync::Event{
+		Rep *rep;
+		bbFunction<void()> func;
+		void dispatch(){
+			func();
+			rep->release();
+		}
+	};
+
+	struct StdoutEvent : public bbAsync::Event{
+		Rep *rep;
+		bbFunction<void()> func;
+		void dispatch(){
+			bool release=rep->stdoutAvail==0;
+			func();
+			if( release ) rep->release();
+		}
+	};
+
+	std::atomic_int refs;
+	
+	bbAsync::Semaphore stdoutSema;
+	char stdoutBuf[4096];
+	char *stdoutGet;
+	int stdoutAvail=0;
+	bool terminated=false;
+	int exit;
+	
+	FinishedEvent finishedEvent;
+	StdoutEvent stdoutEvent;
+
+#if _WIN32
+
+	HANDLE proc;
+	HANDLE in;
+	HANDLE out;
+	HANDLE err;
+	HANDLE breakEvent;
+	
+	Rep( HANDLE proc,HANDLE in,HANDLE out,HANDLE err,HANDLE breakEvent ):proc( proc ),in( in ),out( out ),err( err ),breakEvent( breakEvent ),exit( -1 ),refs( 1 ){
+	}
+	
+	void close(){
+		CloseHandle( in );
+		CloseHandle( out );
+		CloseHandle( err );
+	}
+
+#else
+
+	int proc;
+	int in;
+	int out;
+	int err;
+
+	Rep( int proc,int in,int out,int err ):proc( proc ),in( in ),out( out ),err( err ),exit( -1 ),refs( 1 ){
+	}
+	
+	void close(){
+		::close( in );
+		::close( out );
+		::close( err );
+	}
+
+#endif
+	
+	void retain(){
+		++refs;
+	}
+	
+	void release(){
+		if( --refs ) return;
+		
+		close();
+		
+		delete this;
+	}
+};
+
+bbProcess::bbProcess():_rep( nullptr ){
+}
+
+bbProcess::~bbProcess(){
+
+	if( _rep ) _rep->release();
+}
+
+bbBool bbProcess::start( bbString cmd ){
+
+	if( _rep ) return false;
+	
+#if _WIN32
+
+	HANDLE in[2],out[2],err[2];
+	SECURITY_ATTRIBUTES sa={sizeof(sa),0,1};
+	CreatePipe( &in[0],&in[1],&sa,0 );
+	CreatePipe( &out[0],&out[1],&sa,0 );
+	CreatePipe( &err[0],&err[1],&sa,0 );
+	HANDLE breakEvent=CreateEvent( &sa,0,0,"MX2_BREAK_EVENT" );
+
+	STARTUPINFOA si={sizeof(si)};
+	si.dwFlags=STARTF_USESTDHANDLES;
+	si.hStdInput=in[0];
+	si.hStdOutput=out[1];
+	si.hStdError=err[1];
+
+	PROCESS_INFORMATION pi={0};
+    
+	DWORD flags=CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW;
+	
+	int res=CreateProcessA( 0,(LPSTR)cmd.c_str(),0,0,TRUE,flags,0,0,&si,&pi );
+
+	CloseHandle( in[0] );
+	CloseHandle( out[1] );
+	CloseHandle( err[1] );
+
+	if( !res ){
+		CloseHandle( in[1] );
+		CloseHandle( out[0] );
+		CloseHandle( err[0] );
+		return false;
+	}
+
+	CloseHandle( pi.hThread );
+	
+	Rep *rep=new Rep( pi.hProcess,in[1],out[0],err[0],breakEvent );
+    
+#else
+  
+	int in[2],out[2],err[2];
+
+	pipe( in );
+	pipe( out );
+	pipe( err );
+
+	char **argv=bbProcUtil::makeargv( bbCString( cmd ) );
+	
+	bool failed=false;
+
+	int proc=vfork();
+
+	if( !proc ){
+
+#if __linux
+		setsid();
+#else
+		setpgid(0,0);
+#endif
+
+		dup2( in[0],0 );
+		dup2( out[1],1 );
+		dup2( err[1],2 );
+
+		execvp( argv[0],argv );
+		
+		failed=true;
+
+		_exit( 127 );
+	}
+	
+	if( failed ) proc=-1;
+
+	close( in[0] );
+	close( out[1] );
+	close( err[1] );
+
+	if( proc==-1 ){
+		close( in[1] );
+		close( out[0] );
+		close( err[0] );
+		return false;
+	}
+  
+	Rep *rep=new Rep( proc,in[1],out[0],err[0] );
+	
+#endif
+
+	//Create finished thread    
+    rep->retain();
+    rep->finishedEvent.rep=rep;
+    rep->finishedEvent.func=finished;
+    
+    std::thread( [=](){
+    
+		#if _WIN32
+		
+	    	WaitForSingleObject( rep->proc,INFINITE );
+	    	
+	    	GetExitCodeProcess( rep->proc,(DWORD*)&rep->exit );
+	    	
+	    	CloseHandle( rep->breakEvent );
+	    		
+	    	CloseHandle( rep->proc );
+	    	
+		#else
+		
+			int status;
+			waitpid( rep->proc,&status,0 );
+			
+			if( WIFEXITED( status ) ){
+				rep->exit=WEXITSTATUS( status );
+			}else{
+				rep->exit=-1;
+			}
+			
+		#endif
+		
+			rep->finishedEvent.post();
+
+	} ).detach();
+	
+
+	// Create stdoutReady thread
+	//
+	rep->retain();
+	rep->stdoutEvent.rep=rep;
+	rep->stdoutEvent.func=stdoutReady;
+	
+	std::thread( [=](){
+	
+		for(;;){
+		
+#if _WIN32		
+			DWORD n=0;
+			if( !ReadFile( rep->out,rep->stdoutBuf,4096,&n,0 ) ) break;
+			if( n<=0 ) break;
+#else
+			int n=read( rep->out,rep->stdoutBuf,4096 );
+			if( n<=0 ) break;
+#endif
+			rep->stdoutGet=rep->stdoutBuf;
+			
+			rep->stdoutAvail=n;
+			
+			rep->stdoutEvent.post();
+
+			rep->stdoutSema.wait();
+			
+			if( rep->stdoutAvail ) break;
+		}
+		
+		rep->stdoutAvail=0;
+		
+		rep->stdoutEvent.post();
+
+	} ).detach();
+	
+	_rep=rep;
+    
+    return true;
+}
+
+int bbProcess::exitCode(){
+
+	if( !_rep ) return -1;
+
+	return _rep->exit;
+}
+
+bbInt bbProcess::stdoutAvail(){
+
+	if( !_rep ) return 0;
+
+	return _rep->stdoutAvail;
+}
+
+bbString bbProcess::readStdout(){
+
+	if( !_rep || !_rep->stdoutAvail ) return "";
+
+	bbString str=bbString::fromCString( _rep->stdoutGet,_rep->stdoutAvail );
+	
+	_rep->stdoutAvail=0;
+	
+	_rep->stdoutSema.signal();
+	
+	return str;
+}
+
+bbInt bbProcess::readStdout( void *buf,int count ){
+
+	if( !_rep || count<=0 || !_rep->stdoutAvail ) return 0;
+
+	if( count>_rep->stdoutAvail ) count=_rep->stdoutAvail;
+	
+	memcpy( buf,_rep->stdoutGet,count );
+	
+	_rep->stdoutGet+=count;
+
+	_rep->stdoutAvail-=count;
+	
+	if( !_rep->stdoutAvail ) _rep->stdoutSema.signal();
+	
+	return count;
+}
+
+void bbProcess::writeStdin( bbString str ){
+
+	if( !_rep ) return;
+
+#if _WIN32	
+	WriteFile( _rep->in,str.c_str(),str.length(),0,0 );
+#else
+	write( _rep->in,str.c_str(),str.length() );
+#endif
+}
+
+void bbProcess::sendBreak(){
+
+	if( !_rep ) return;
+	
+#if _WIN32
+	SetEvent( _rep->breakEvent );
+#else
+	killpg( _rep->proc,SIGTSTP );
+#endif
+}
+
+void bbProcess::terminate(){
+
+	if( !_rep ) return;
+
+#if _WIN32
+
+	bbProcUtil::TerminateProcessGroup( _rep->proc,-1 );
+	
+	CancelIoEx( _rep->out,0 );
+
+#else
+	killpg( _rep->proc,SIGTERM );
+#endif
+}
+
+void bbProcess::gcMark(){
+
+	bbGCMark( finished );
+	bbGCMark( stdoutReady );
+
+	if( !_rep ) return;
+	
+	bbGCMark( _rep->finishedEvent.func );
+	bbGCMark( _rep->stdoutEvent.func );
+}

+ 43 - 0
modules/std/process/native/process.h

@@ -0,0 +1,43 @@
+
+#ifndef BB_STD_PROCESS_H
+#define BB_STD_PROCESS_H
+
+#include <bbmonkey.h>
+
+class bbProcess : public bbObject{
+public:
+
+	bbProcess();
+	~bbProcess();
+	
+	void discard();
+	
+	bbFunction<void()> finished;
+
+	bbFunction<void()> stdoutReady;
+	
+	bbBool start( bbString cmd );
+	
+	bbInt exitCode();
+	
+	bbInt stdoutAvail();
+	
+	bbString readStdout();
+	
+	bbInt readStdout( void *buf,bbInt count );
+	
+	void writeStdin( bbString str );
+	
+	void sendBreak();
+	
+	void terminate();
+	
+private:
+	struct Rep;
+	
+	Rep *_rep;
+	
+	void gcMark();
+};
+
+#endif

+ 102 - 0
modules/std/process/native/procutil.cpp

@@ -0,0 +1,102 @@
+
+#import "procutil.h"
+
+namespace bbProcUtil{
+
+#if _WIN32
+
+	void terminateChildren( DWORD procid,HANDLE snapshot,int exitCode ){
+	
+		PROCESSENTRY32 procinfo;
+			
+		procinfo.dwSize=sizeof( procinfo );
+		
+		int gotinfo=Process32First( snapshot,&procinfo );
+			
+		while( gotinfo ){
+		
+			if( procinfo.th32ParentProcessID==procid ){
+			
+//				printf("process=%i parent=%i module=%x path=%s\n",procinfo.th32ProcessID,procinfo.th32ParentProcessID,procinfo.th32ModuleID,procinfo.szExeFile);
+
+				terminateChildren( procinfo.th32ProcessID,snapshot,exitCode );
+				 
+				HANDLE child=OpenProcess( PROCESS_ALL_ACCESS,0,procinfo.th32ProcessID );
+				
+				if( child ){
+					int res=TerminateProcess( child,exitCode );
+					CloseHandle( child );
+				}
+			}
+			
+			gotinfo=Process32Next( snapshot,&procinfo );
+		}	
+	}
+	
+	int TerminateProcessGroup( HANDLE prochandle,int exitCode ){
+
+		HANDLE snapshot;
+		
+		int procid=GetProcessId( prochandle );
+		
+		snapshot=CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS,0 );
+		
+		if( snapshot!=INVALID_HANDLE_VALUE ){
+		
+			terminateChildren( GetProcessId( prochandle ),snapshot,exitCode );
+
+			CloseHandle( snapshot );
+		}
+			
+		int res=TerminateProcess( prochandle,exitCode );
+		return res;
+	}
+
+#else
+
+	char **makeargv( const char *cmd ){
+	    int n,c;
+	    char *p;
+	    static char *args,**argv;
+	
+	    if( args ) free( args );
+	    if( argv ) free( argv );
+	    args=(char*)malloc( strlen(cmd)+1 );
+	    strcpy( args,cmd );
+	
+	    n=0;
+	    p=args;
+	    while( (c=*p++) ){
+	        if( c==' ' ){
+	            continue;
+	        }else if( c=='\"' ){
+	            while( *p && *p!='\"' ) ++p;
+	        }else{
+	            while( *p && *p!=' ' ) ++p;
+	        }
+	        if( *p ) ++p;
+	        ++n;
+	    }
+	    argv=(char**)malloc( (n+1)*sizeof(char*) );
+	    n=0;
+	    p=args;
+	    while( (c=*p++) ){
+	        if( c==' ' ){
+	            continue;
+	        }else if( c=='\"' ){
+	            argv[n]=p;
+	            while( *p && *p!='\"' ) ++p;
+	        }else{
+	            argv[n]=p-1;
+	            while( *p && *p!=' ' ) ++p;
+	        }
+	        if( *p ) *p++=0;
+	        ++n;
+	    }
+	    argv[n]=0;
+	    return argv;
+	}
+	
+#endif
+
+}

+ 35 - 0
modules/std/process/native/procutil.h

@@ -0,0 +1,35 @@
+
+#ifndef BB_STD_PROCUTIL_H
+#define BB_STD_PROCUTIL_H
+
+#if _WIN32
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+extern "C" WINBASEAPI WINBOOL WINAPI CancelIoEx( HANDLE hFile,LPOVERLAPPED lpOverlapped );
+
+namespace bbProcUtil{
+
+	int TerminateProcessGroup( HANDLE prochandle,int exitCode );
+}
+
+#else
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+namespace bbProcUtil{
+
+	char **makeargv( const char *cmd );
+
+}
+
+#endif
+
+#endif
+

+ 44 - 0
modules/std/process/process.monkey2

@@ -0,0 +1,44 @@
+
+#If __TARGET__<>"emscripten"
+
+#Import "native/process.cpp"
+#Import "native/procutil.cpp"
+#Import "native/process.h"
+
+Namespace std.process
+
+Extern
+
+Class Process="bbProcess"
+
+	Field Finished:Void()="finished"
+	
+	Field StdoutReady:Void()="stdoutReady"
+	
+	Field StderrReady:Void()="stderrReady"
+
+	Property ExitCode:Int()="exitCode"
+	
+	Property StdoutAvail:Int()="stdoutAvail"
+
+	Property StderrAvail:Int()="stderrAvail"
+	
+	Method Start:Bool( cmd:String )="start"
+	
+	Method ReadStdout:String()="readStdout"
+	
+	Method ReadStdout:Int( buf:Void Ptr,count:Int )="readStdout"
+
+	Method ReadStderr:String()="readStderr"
+	
+	Method ReadStderr:Int( buf:Void Ptr,count:Int )="readStderr"
+
+	Method WriteStdin( str:String )="writeStdin"
+	
+	Method SendBreak()="sendBreak"
+	
+	Method Terminate:Void()="terminate"
+
+End
+
+#endif

+ 9 - 6
modules/std/std.monkey2

@@ -41,18 +41,21 @@ Namespace std
 #import "audio/load_wav"
 #import "audio/load_vorbis"
 
+#Import "async/async"
+#Import "time/time"
+#Import "time/timer"
+#Import "fiber/fiber"
+#Import "fiber/future"
+#Import "process/process"
+#Import "filesystem/filesystem"
+
 #Import "misc/random"
 #Import "misc/chartype"
-#Import "misc/filesystem"
 #Import "misc/stringio"
 #Import "misc/json"
 #Import "misc/jsonify"
-#Import "misc/time"
 #Import "misc/zipfile"
 
-#Import "fiber/fiber"
-#Import "fiber/future"
-
 Private
 
 Function Main()
@@ -73,7 +76,7 @@ Function Main()
 	
 	'Note: "asset::" support for android/ios is in mojo, as it uses SDL_RWop and we don't want to std to be dependant on SDL2...
 	'	
-#if __TARGET__="desktop" Or __TARGET__="emscripten"
+#If Not __MOBILE_TARGET__
 	
 	Stream.OpenFuncs["asset"]=Lambda:Stream( proto:String,path:String,mode:String )
 

+ 72 - 0
modules/std/time/native/time.cpp

@@ -0,0 +1,72 @@
+
+#include "time.h"
+
+#if _WIN32
+
+#include <windows.h>
+
+#else
+
+#include <sys/time.h>
+
+#endif
+
+namespace bbTime{
+
+#if _WIN32
+
+	long start;
+	double freq;
+	
+	double now(){
+	
+		LARGE_INTEGER icounter;
+		QueryPerformanceCounter( &icounter );
+		long counter=icounter.QuadPart;
+		
+		if( !start ){
+			start=counter;
+			LARGE_INTEGER ifreq;
+			QueryPerformanceFrequency( &ifreq );
+			freq=double( ifreq.QuadPart );
+			return 0;
+		}
+		
+		counter-=start;
+		return double( counter )/freq;
+	}
+	
+	void sleep( double seconds ){
+	
+		Sleep( seconds * 1000 );
+	}
+	
+#else
+
+	long start;
+	
+	double now(){
+	
+		timeval tv;
+		gettimeofday( &tv,0 );
+		long counter=tv.tv_sec*1000000+tv.tv_usec;
+		
+		if( !start ){
+			start=counter;
+			return 0;
+		}
+		
+		counter-=start;
+		return double( counter )/1000000.0;
+	}
+	
+	void sleep( double seconds ){
+	
+		sleep( seconds * 1000 );
+//		usleep( seconds * 1000000 );
+	}
+	
+	
+#endif
+
+}

+ 12 - 0
modules/std/time/native/time.h

@@ -0,0 +1,12 @@
+
+#ifndef BB_STD_TIME_H
+#define BB_STD_TIME_H
+
+namespace bbTime{
+
+	double now();
+	
+	void sleep( double seconds );
+}
+
+#endif

+ 37 - 4
modules/std/misc/time.monkey2 → modules/std/time/time.monkey2

@@ -1,12 +1,41 @@
 
+#Import "native/time.h"
+#Import "native/time.cpp"
+
 Namespace std.time
 
 Private
 
 Global _us0:Long
 
+Extern
+
+#rem monkeydoc Gets the number of seconds elapsed since the app started.
+
+#end
+Function Now:Double()="bbTime::now"
+
+#rem monkeydoc Puts app to sleep.
+
+Note: this will also cause all fibers to sleep.
+
+#end
+Function Sleep( seconds:Double )="bbTime::sleep"
+
 Public
 
+#rem monkeydoc Gets the number of microseconds since the app started.
+#end
+Function Microsecs:Long()
+	Return Now() * 1000000
+End
+
+#rem monkeydoc Gets the number of milliseconds since the app started.
+#end
+Function Millisecs:Int()
+	Return Now() * 1000
+End
+
 #rem monkeydoc @hidden Time class.
 #end
 Class Time
@@ -109,6 +138,14 @@ Class Time
 	
 End
 
+#rem
+
+#rem monkeydoc Gets the number of seconds since the app started.
+#end
+Function Now:Double()
+	Return Double( Microsecs() )/1000000
+End
+
 #rem monkeydoc Gets the number of microseconds since the app started.
 #end
 Function Microsecs:Long()
@@ -126,8 +163,4 @@ Function Millisecs:Int()
 	Return Microsecs()/1000
 End
 
-#rem monkeydoc @hidden Gets the number of seconds since the app started.
 #end
-Function Seconds:Double()
-	Return Microsecs()/1000000.0
-End

+ 69 - 0
modules/std/time/timer.monkey2

@@ -0,0 +1,69 @@
+
+#If __TARGET__="emscripten"
+
+#Import "timer_emscripten"
+
+#Else
+
+Namespace std.timer
+
+Private
+
+Using std.time
+Using std.collections
+Using std.fiber
+
+Public
+
+Class Timer
+
+	Method New( hertz:Double,fired:Void() )
+	
+		New Fiber( Lambda()
+		
+			Local period:=1/hertz
+		
+			Local timeout:=Now()+period
+	
+			While Not _cancelled
+			
+				Local now:=Now()
+			
+				Local sleep:=timeout-now
+				If sleep>0
+					Fiber.Sleep( sleep )
+					Continue
+				Endif
+				
+				If Not _suspended fired()
+
+				timeout+=period
+
+			Wend
+		
+		End )
+
+	End
+	
+	Property Suspended:Bool()
+	
+		Return _suspended
+	
+	Setter( suspended:Bool )
+	
+		_suspended=suspended
+	End
+	
+	Method Cancel()
+	
+		_cancelled=True
+	End
+	
+	Private
+	
+	Field _suspended:Bool
+	
+	Field _cancelled:Bool
+End
+
+#Endif

+ 92 - 0
modules/std/time/timer_emscripten.monkey2

@@ -0,0 +1,92 @@
+
+#If __TARGET__="emscripten"
+
+#Import "<emscripten>"
+
+Namespace std.timer
+
+Private
+
+Using std.time
+Using std.collections
+Using emscripten
+
+'GC protection!
+Global _timers:=New Map<Timer,Bool>
+
+Public
+
+'Have to poll with emscripten...
+'
+Class Timer
+
+	Method New( hertz:Double,fired:Void() )
+	
+		_period=1/hertz
+		_timeout=Now()+_period
+		_fired=fired
+		
+		_context.timer=self
+		
+		emscripten_async_call( Callback,Cast<Byte Ptr>( Varptr _context ),_period * 1000 )
+
+		_timers[Self]=True
+	End
+	
+	Property Suspended:Bool()
+	
+		Return _suspended
+	
+	Setter( suspended:Bool )
+	
+		_suspended=suspended
+	End
+	
+	Method Cancel()
+	
+		_cancelled=True
+	End
+	
+	Private
+	
+	Struct Context
+		Field timer:Timer
+	End
+
+	Field _period:Double
+	Field _timeout:Double
+	Field _fired:Void()
+	Field _suspended:Bool
+	Field _cancelled:Bool
+	Field _context:Context
+	
+	Function Callback( arg:Void Ptr )
+	
+		Local context:=Cast<Context Ptr>( arg )
+		
+		context->timer.Update()
+	End
+	
+	Method Update()
+	
+		While Not _cancelled
+			
+			Local now:=Now()
+			
+			Local sleep:=Int((_timeout-now)*1000)
+			If sleep>0
+				emscripten_async_call( Callback,Cast<Byte Ptr>( Varptr _context ),sleep )
+				Return
+			Endif
+				
+			If Not _suspended _fired()
+
+			_timeout+=_period
+		Wend
+		
+		_timers.Remove( Self )
+	End
+	
+End
+
+#Endif