소스 검색

Moved mojo process and timer files.

Mark Sibly 9 년 전
부모
커밋
9db4bd2ab4

+ 512 - 0
modules/mojo/process/native/process.cpp

@@ -0,0 +1,512 @@
+
+#include "process.h"
+
+#ifndef EMSCRIPTEN
+
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+
+#include <SDL.h>
+
+bbInt g_mojo_app_AppInstance_AddAsyncCallback(bbFunction<void()> l_func);
+
+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();
+	}
+};
+
+#if _WIN32
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+#else
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#endif
+
+namespace{
+
+	const int INVOKE=0x40000000;
+	const int REMOVE=0x80000000;
+
+	void postEvent( int code ){
+		SDL_UserEvent event;
+		event.type=SDL_USEREVENT;
+		event.code=code;
+		event.data1=0;
+		event.data2=0;
+		if( SDL_PeepEvents( (SDL_Event*)&event,1,SDL_ADDEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT )!=1 ){
+			printf(" SDL_PeepEvents error!\n" );fflush( stdout );
+		}
+	}
+	
+#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;
+	}
+	
+#endif	
+
+#ifndef _WIN32
+
+	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
+
+}
+
+struct bbProcess::Rep{
+
+	std::atomic_int refs;
+	
+	semaphore stdoutSema;
+	char stdoutBuf[4096];
+	char *stdoutGet;
+	int stdoutAvail=0;
+	bool terminated=false;
+	int exit;
+
+#if _WIN32
+
+	HANDLE proc;
+	HANDLE in;
+	HANDLE out;
+	HANDLE err;
+	
+	Rep( HANDLE proc,HANDLE in,HANDLE out,HANDLE err ):proc( proc ),in( in ),out( out ),err( err ),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 );
+
+	STARTUPINFOA si={sizeof(si)};
+	si.dwFlags=STARTF_USESTDHANDLES;//|STARTF_USESHOWWINDOW;
+	si.hStdInput=in[0];
+	si.hStdOutput=out[1];
+	si.hStdError=err[1];
+//	si.wShowWindow=SW_HIDE;
+
+	PROCESS_INFORMATION pi={0};
+    
+	DWORD flags=CREATE_NEW_PROCESS_GROUP;
+    
+	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] );
+    
+#else
+  
+	int in[2],out[2],err[2];
+
+	pipe( in );
+	pipe( out );
+	pipe( err );
+
+	char **argv=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();
+
+    int callback=g_mojo_app_AppInstance_AddAsyncCallback( finished );
+    
+    std::thread( [=](){
+    
+		#if _WIN32
+		
+	    	WaitForSingleObject( rep->proc,INFINITE );
+	    	
+	    	GetExitCodeProcess( rep->proc,(DWORD*)&rep->exit );
+	    		
+	    	CloseHandle( rep->proc );
+	    	
+		#else
+		
+			int status;
+			waitpid( rep->proc,&status,0 );
+			
+			if( WIFEXITED( status ) ){
+				rep->exit=WEXITSTATUS( status );
+			}else{
+				rep->exit=-1;
+			}
+			
+		#endif
+    		
+	    	postEvent( callback|INVOKE|REMOVE );
+    		
+    		rep->release();
+
+	} ).detach();
+	
+	
+	//Create stdoutReady thread
+	rep->retain();
+	
+	int callback2=g_mojo_app_AppInstance_AddAsyncCallback( 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;
+			
+			postEvent( callback2|INVOKE );
+			
+			rep->stdoutSema.wait();
+			
+			if( rep->stdoutAvail ) break;
+		}
+		
+		rep->stdoutAvail=0;
+		
+		postEvent( callback2|INVOKE|REMOVE );
+		
+		rep->release();
+
+	} ).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
+	GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT,GetProcessId( _rep->proc ) );
+#else
+	killpg( _rep->proc,SIGTSTP );
+#endif
+}
+
+void bbProcess::terminate(){
+
+	if( !_rep ) return;
+
+#if _WIN32
+	TerminateProcessGroup( _rep->proc,-1 );
+#else
+	killpg( _rep->proc,SIGTERM );
+#endif
+}
+
+#else
+
+//***** Dummy emscripten version *****
+
+struct bbProcess::Rep{
+};
+
+void bbProcess::discard(){
+}
+
+bbBool bbProcess::start( bbString cmd ){
+	return false;
+}
+	
+bbInt bbProcess::exitCode(){
+	return -1;
+}
+	
+bbInt bbProcess::stdoutAvail(){
+	return 0;
+}
+	
+bbString bbProcess::readStdout(){
+	return "";
+}
+
+bbInt bbProcess::readStdout( void *buf,bbInt count ){
+	return 0;
+}
+
+void bbProcess::writeStdin( bbString str ){
+}
+
+void bbProcess::sendBreak(){
+}
+
+void bbProcess::terminate(){
+}
+
+#endif

+ 40 - 0
modules/mojo/process/native/process.h

@@ -0,0 +1,40 @@
+
+#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;
+};
+
+#endif

+ 38 - 0
modules/mojo/process/process.monkey2

@@ -0,0 +1,38 @@
+
+Namespace mojo.process
+
+#Import "native/process.h"
+#Import "native/process.cpp"
+
+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

+ 132 - 0
modules/mojo/timer/native/timer.cpp

@@ -0,0 +1,132 @@
+
+#include "timer.h"
+
+#include <SDL.h>
+
+bbInt	g_mojo_app_AppInstance_AddAsyncCallback(bbFunction<void()> l_func);
+void	g_mojo_app_AppInstance_RemoveAsyncCallback( bbInt id );
+void	g_mojo_app_AppInstance_EnableAsyncCallback( bbInt id );
+void	g_mojo_app_AppInstance_DisableAsyncCallback( bbInt id );
+
+struct bbTimer::Rep{
+
+	int _freq;
+	int	_interval;
+	int _remainder;
+	int _accumulator;
+
+	bool _suspended;
+	bool _cancelled;
+	bool _reset;
+	
+	int _callback;
+	int _timer;
+};
+
+namespace{
+
+	const int INVOKE=0x40000000;
+	const int REMOVE=0x80000000;
+
+	void postEvent( int code ){
+		SDL_UserEvent event;
+		event.type=SDL_USEREVENT;
+		event.code=code;
+		event.data1=0;
+		event.data2=0;
+		
+		if( SDL_PeepEvents( (SDL_Event*)&event,1,SDL_ADDEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT )!=1 ){
+			printf(" SDL_PeepEvents error!\n" );fflush( stdout );
+		}
+	}
+}
+
+bbTimer::bbTimer( int freq,bbFunction<void()> fired ){
+
+	int p=1000/freq;
+
+	_rep=new Rep;
+
+	_rep->_freq=freq;
+	_rep->_interval=p;
+	_rep->_remainder=1000-p*freq;
+	_rep->_accumulator=0;
+	_rep->_suspended=true;
+	_rep->_cancelled=false;
+	_rep->_reset=false;
+	
+	_rep->_callback=g_mojo_app_AppInstance_AddAsyncCallback( fired );
+	
+	setSuspended( false );
+}
+
+bool bbTimer::suspended(){
+	if( !_rep ) return false;
+	
+	return _rep->_suspended;
+}
+
+void bbTimer::setSuspended( bool suspended ){
+	if( !_rep ) return;
+	
+	if( suspended==_rep->_suspended ) return;
+	
+	if( _rep->_suspended=suspended ){
+	
+		SDL_RemoveTimer( _rep->_timer );
+	
+		g_mojo_app_AppInstance_DisableAsyncCallback( _rep->_callback );
+
+	}else{
+
+		g_mojo_app_AppInstance_EnableAsyncCallback( _rep->_callback );
+	
+		_rep->_timer=SDL_AddTimer( _rep->_interval,sdl_timer_callback,(void*)_rep );
+
+	}
+}
+
+void bbTimer::reset(){
+	if( !_rep ) return;
+	
+	_rep->_reset=true;
+}
+
+void bbTimer::cancel(){
+	if( !_rep ) return;
+
+	g_mojo_app_AppInstance_RemoveAsyncCallback( _rep->_callback );
+
+	_rep->_suspended=true;
+	_rep->_cancelled=true;
+	
+	_rep=nullptr;
+}
+
+unsigned bbTimer::sdl_timer_callback( unsigned interval,void *param ){
+	
+	bbTimer::Rep *rep=(bbTimer::Rep*)param;
+	
+	if( rep->_suspended ){
+		if( rep->_cancelled ) delete rep;
+		return 0;
+	}
+	
+	/*
+	if( rep->_reset ){
+		rep->_reset=false;
+		rep->_accumulator=0;
+		return SDL_GetTicks()+interval;
+	}
+	*/
+	
+	postEvent( rep->_callback|INVOKE );
+	
+	rep->_accumulator+=rep->_remainder;
+	if( rep->_accumulator>=rep->_freq ){
+		rep->_accumulator-=rep->_freq;
+		return rep->_interval+1;
+	}
+	
+	return rep->_interval;
+}

+ 28 - 0
modules/mojo/timer/native/timer.h

@@ -0,0 +1,28 @@
+
+#ifndef BB_TIMER_H
+#define BB_TIMER_H
+
+#include <bbmonkey.h>
+
+class bbTimer : public bbObject{
+public:
+
+	bbTimer( int freq,bbFunction<void()> fired );
+	
+	bool suspended();
+	
+	void setSuspended( bool suspended );
+	
+	void reset();
+	
+	void cancel();
+	
+private:
+	
+	struct Rep;
+	Rep *_rep;
+	
+	static unsigned sdl_timer_callback( unsigned interval,void *param );
+};
+
+#endif

+ 27 - 0
modules/mojo/timer/timer.monkey2

@@ -0,0 +1,27 @@
+
+Namespace mojo.timer
+
+#Import "native/timer.cpp"
+#Import "native/timer.h"
+
+Extern
+
+Class Timer="bbTimer"
+
+	#rem monkeydoc Creates a new timer.
+	#end
+	Method New( fps:Int,fired:Void() )
+
+	#rem monkeydoc Suspended state.
+	#end
+	Property Suspended:Bool()="suspended"
+	Setter( suspended:Bool )="setSuspended"
+
+	#rem monkeydoc Cancels the timer.
+
+	Once cancelled, a timer can no longer be used.
+	
+	#end
+	Method Cancel()="cancel"
+
+End