[kaffe] RFC: Invocation daemon / client support

jserv at linux2.cc.ntu.edu.tw jserv at linux2.cc.ntu.edu.tw
Sat Apr 30 04:44:20 PDT 2005


Hi list,

  I have done an interesting patch for reducing the VM loading / unload
time cost via the "invocation daemon / client" idea. You can see the
Java implementation of libraries/javalib/kaffe/net/InvocationDaemon.java 
which acts as the daemon listening to the requests from client. Once the
connection between the daemon and clients is established, new instance
will be create by default ClassLoader. Of course, you can always make
the daemon listen to the in-comming requests, and no more VM load and
initialization will take away.

  To test the small patch, you must compile InvocationDaemon.java by
hand:
  # cd libraries/javalib
  # jikes -cp /usr/jre/lib/rt.jar kaffe/net/InvocationDaemon.java

And launch the daemon:
  # kaffe -cp .:$YOUR_ADDITIONAL_CLASSPATH kaffe.net.InvocationDaemon

Later, you can drive the client:
  # kaffe -Xclient <CLASS_NAME>

I hope the patch is useful.

cheers,
Jim Huang
-------------- next part --------------
diff -u -p -r1.82 main.c
--- kaffe/kaffe/main.c	26 Mar 2005 00:10:19 -0000	1.82
+++ kaffe/kaffe/main.c	30 Apr 2005 11:36:41 -0000
@@ -62,12 +62,18 @@ KaffeVM_Arguments vmargs;
 JNIEnv* global_env;
 JavaVM* global_vm;
 static int isJar = 0;
+static int asClient = 0;
+#define DEFAULT_CLIENT_PORT 9615
+#define DEFAULT_CLIENT_HOST "localhost"
+char *client_host = DEFAULT_CLIENT_HOST;
+int client_port;
 static char *jvm_onload;
 
 static int options(char**, int);
 static void usage(void);
 static size_t parseSize(char*);
 static int checkException(void);
+static void clientIoLoop(int fd);
 static int main2(JNIEnv* env, char *argv[], int farg, int argc);
 
 #define	KAFFEHOME	"KAFFEHOME"
@@ -176,6 +182,58 @@ main(int argc, char* argv[])
 		exit(1);
 	}
 
+	/* Handle -Xclient */
+	if (asClient) {
+		int fd;
+		struct sockaddr_in sa;
+		struct hostent *hp;
+		struct servent *sp;
+		char cwd[256];
+		int i, len;
+		if (client_port == 0 ) {
+			if ( (sp = getservbyname("InvocationDaemon", "tcp")))
+				client_port = sp->s_port;
+			else
+				client_port = DEFAULT_CLIENT_PORT;
+		}
+		if (! (hp = gethostbyname(client_host))) {
+			fprintf(stderr, _("Error: Unknown host %s\n"), client_host);
+			exit(1);
+		}
+		memset((char*)&sa, 0, sizeof( sa));
+		memcpy((char*)&sa.sin_addr, (char*)hp->h_addr, hp->h_length);
+		sa.sin_family = AF_INET;
+		sa.sin_port = htons(client_port);
+
+		if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ){
+			perror("socket");
+			exit(1);
+		}
+
+		if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0 ) {
+			perror("connect");
+			exit(1);
+		}
+		/* Write argument */
+		if (! getcwd(cwd, sizeof(cwd) - 2)) {
+			fprintf( stderr, "Error: path buffer overflow\n");
+			exit(1);
+		}
+		else {
+			len = strlen(cwd);
+			write(fd, cwd, len);
+		}
+		for ( i=0; i < argc; i++) {
+			write(fd, " ", 1);
+			write(fd, argv[farg + i], strlen(argv[farg + i]));
+		}
+		write(fd, "\n", 1);
+		clientIoLoop(fd);
+		close(fd);
+		/* Abort */
+		exit(0);
+	}
+				
 	/* Initialise */
 	JNI_CreateJavaVM(&global_vm, &global_env, &vmargs);
 
@@ -445,6 +503,51 @@ setKaffeAWT(const char * propStr)
 	return prop;
 }
 
+static int readWrite(int rfd, int wfd)
+{
+	int len;
+	char buf[1024];
+
+	if ((len = read(rfd, buf, sizeof(buf)))) {
+		write(wfd, buf, len);
+	}
+
+	return len;
+}
+
+/*
+ * wait for input arriving on stdin and output arriving on fd
+ */
+static void clientIoLoop(int fd)
+{
+	fd_set rfds, xfds;
+	int ret;
+
+	do {
+		FD_ZERO(&xfds);
+		FD_SET(fd, &xfds);
+
+		FD_ZERO(&rfds);
+		FD_SET(0, &rfds);
+		FD_SET(fd, &rfds);
+
+		if ((ret = select( fd+1, &rfds, NULL, &xfds, NULL))) {
+			if (FD_ISSET(fd, &rfds)) {
+				if (! readWrite(fd, 1))
+					break;	/* end-of-file encountered */
+			}
+
+			if (FD_ISSET(fd, &xfds)) {
+				break;
+			}
+
+			if (FD_ISSET(0, &rfds)) {
+				readWrite(0, fd);
+			}
+		}
+	} while (1);
+}
+
 /*
  * Process program's flags.
  */
@@ -735,6 +838,39 @@ options(char** argv, int argc)
                 else if (strcmp(argv[i], "-jar") == 0) {
                         isJar = 1;
                 }
+		else if (strcmp(argv[i], "-Xclient") == 0) {
+			asClient = 1;
+		}
+		else if (strncmp(argv[i], "-Xclient-port", (j=13)) == 0) {
+			if (argv[i][j] == 0) {
+                                i++;
+                                if (argv[i] == 0) {
+                                        fprintf(stderr,  
+						_("Error: No client port found for "
+						  "-Xclinet-port option.\n"));
+                                        exit(1);
+                                }
+				client_port = atoi(argv[i]);
+                        } 
+			else {
+				client_port = parseSize(&argv[i][j]);
+                        }
+		}
+		else if (strncmp(argv[i], "-Xclient-host", (j=13)) == 0) {
+			if (argv[i][j] == 0) {
+				i++;
+				if (argv[i] == 0) {
+					fprintf(stderr,
+						_("Error: No client port found for "
+						  "-Xclinet-port option.\n"));
+					exit(1);
+				}
+				client_host = argv[i];
+			}
+			else {
+				client_host = &argv[i][j];
+			}
+		}
 		else if (strncmp(argv[i], "-Xrun", 5) == 0) {
 			jvm_onload = argv[i];
 		}
@@ -939,7 +1075,8 @@ usage(void)
 			  "	-noasyncgc *		 Do not garbage collect asynchronously\n"
 			  "	-cs, -checksource *	 Check source against class files\n"
 			  "	-oss <size> *		 Maximum java stack size\n"
-			  "	-jar                     Executable is a JAR\n"));
+			  "	-jar                     Executable is a JAR\n"
+			  "	-Xclient                 Execution as client\n"));
 #ifdef KAFFE_VMDEBUG
         fprintf(stderr, _("	-vmdebug <flag{,flag}>	 Internal VM debugging.  Set flag=list for a list\n"));
 #endif
--- /dev/null	2005-04-30 03:42:14.314172960 +0800
+++ libraries/javalib/kaffe/net/InvocationDaemon.java	2005-04-30 19:29:09.955545472 +0800
@@ -0,0 +1,599 @@
+/**
+ * InvocationDaemon - standalone daemon for class invocation.
+ *
+ * Copyright (c) 2005
+ *	The Kaffe.org developers, see ChangeLog for details. 
+ *	All rights reserved.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file.
+ */
+
+package kaffe.net;
+
+import java.lang.String;
+import java.io.BufferedReader;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+public class InvocationDaemon extends Thread {
+	ServerSocket ListenSocket;
+	boolean Log;
+	int Port;
+	boolean GoOn = true;
+	int NConnect;
+	static PrintStream SysOut = System.out;
+	static InputStream SysIn = System.in;
+	final static int DEF_PORT = 9615;
+
+	class Connection implements Runnable {
+		BufferedReader CliOut;
+		PrintStream CliIn;
+		Socket Client;
+		String UserDir;
+
+		Connection( Socket cs, DaemonThreadGroup tg ) {
+			Client = cs;
+			try {
+				InputStream client_is =
+					Client.getInputStream();
+				OutputStream client_os =
+					Client.getOutputStream();
+				CliOut = new BufferedReader(
+					new InputStreamReader( client_is ) );
+				CliIn = new PrintStream( client_os );
+				tg.setOut( CliIn );
+				tg.setIn( client_is );
+			}
+			catch ( IOException x ) {
+				try {
+					Client.close();
+				}
+				catch ( IOException xx ) {}
+				System.err.println(
+					"cannot open streams on socket" );
+			}
+		}
+
+		void callMain( String[] args ) {
+			if ( args.length < 2 )
+				return;
+
+			try {
+				Class klass = Class.forName( args[ 1 ] );
+				Class[] parTypes = { args.getClass() };
+				Method mm  = klass.getMethod(
+					"main", 
+					parTypes );
+				int n = args.length - 2;
+				String[] sArr = new String[ n ];
+				Object[] mArgs = { sArr };
+
+				if ( args.length > 1 )
+					System.arraycopy( args, 2, sArr, 0, n );
+
+				UserDir = args[ 0 ];
+				if ( Log ) {
+					SysOut.print(
+						"invoke: " + 
+						klass.getName() + 
+						".main( " );
+					for ( int i = 0; i < sArr.length; i++ ) {
+						SysOut.print( sArr[ i ] );
+						SysOut.print( ' ' );
+					}
+					SysOut.println( ")" );
+				}
+				mm.invoke( null, mArgs );
+			}
+			catch ( SecurityException sx ) {
+				// System.exit was called.
+				if ( "system_exit".equals( sx.getMessage() ) ) {
+					if ( Log )
+						SysOut.println(
+							"caught System.exit()" );
+					return;
+				}
+				else {
+					SysOut.println(
+						"received Security exception in TG: " +
+						Thread.currentThread().getThreadGroup() );
+					sx.printStackTrace();
+				}
+			}
+			catch ( Exception x ) {
+				x.printStackTrace();
+			}
+		}
+
+		String[] getArgs( String input ) {
+			Vector v = new Vector( 5 );
+			StringTokenizer st = new StringTokenizer( input );
+			String[] args;
+			String s;
+			int i, n;
+
+			while ( st.hasMoreTokens() )
+				v.addElement( st.nextToken() );
+
+			n = v.size();
+			args = new String[ n ];
+
+			for ( i = 0; i < n; i++ )
+				args[ i ] = (String) v.elementAt( i );
+
+			return args;
+		}
+
+		public void run() {
+			String input;
+			String[] args;
+			String[] targets;
+
+			try {
+				input = CliOut.readLine();
+				if ( input != null ) {
+					if ( input.equals( "STOP" ) ) {
+						GoOn = false;
+						return;
+					}
+					if ( Log ) {
+						SysOut.print(
+							"InvocationDaemon request: " );
+						SysOut.println( input );
+					}
+					args = getArgs( input );
+					callMain( args );
+				}
+			}
+			catch ( IOException x ) {
+				x.printStackTrace();
+			}
+
+			finally {
+				try {
+					Client.close();
+				}
+				catch ( IOException x ) {}
+			}
+		}
+	}
+	
+	class DaemonThreadGroup extends ThreadGroup {
+		InputStream In;
+		PrintStream Out;
+
+		DaemonThreadGroup( String name ) {
+			super( name);
+		}
+
+		void setIn( InputStream in ) {
+			In = in;
+		}
+
+		void setOut( PrintStream out ) {
+			Out = out;
+		}
+	}
+	class DaemonSecurityManager extends SecurityManager {
+		DaemonSecurityManager() {
+		}
+
+		public void checkAccept( String host, int port ) {
+		}
+
+		public void checkAccess( Thread t ) {
+		}
+
+		public void checkAccess( ThreadGroup tg ) {
+		}
+
+		public void checkAwtEventQueueAccess() {
+		}
+
+		public void checkConnect( String host, int port ) {
+		}
+
+		public void checkConnect( 
+				String host, 
+				int port, 
+				Object context ) {
+		}
+
+		public void checkCreateClassLoader() {
+		}
+
+		public void checkDelete( String file ) {
+		}
+
+		public void checkExec( String cmd ) {
+		}
+
+		public void checkExit( int ret ) {
+			throw new SecurityException( "system_exit" );
+		}
+
+		public void checkLink( String lib ) {
+		}
+
+		public void checkListen( int port ) {
+		}
+
+		public void checkMemberAccess( Class klass, int which ) {
+		}
+
+		public void checkMulticast( InetAddress maddr ) {
+		}
+
+		public void checkMulticast( InetAddress maddr, byte ttl ) {
+		}
+
+		public void checkPackageAccess( String pkg ) {
+		}
+
+		public void checkPackageDefinition( String pkg ) {
+		}
+
+		public void checkPrintJobAccess() {
+		}
+
+		public void checkPropertiesAccess() {
+		}
+
+		public void checkPropertyAccess( String key ) {
+		}
+
+		public void checkRead( FileDescriptor fd ) {
+		}
+
+		public void checkRead( String file ) {
+		}
+
+		public void checkRead( String file, Object context ) {
+		}
+
+		public void checkSecurityAccess( String action ) {
+		}
+
+		public void checkSetFactory() {
+		}
+
+		public void checkSystemClipboardAccess() {
+		}
+
+		public boolean checkTopLevelWindow ( Object window ) {
+			return true;
+		}
+
+		public void checkWrite( FileDescriptor fd ) {
+		}
+
+		public void checkWrite( String file ) {
+		}
+	}
+	
+	class DaemonPrintStream extends PrintStream {
+		DaemonPrintStream( PrintStream defStream ) {
+			super( defStream, true );
+		}
+
+		public void print( Object o ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup()) 
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( o );
+			else
+				super.print( o );
+		}
+
+		public void print( String s ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( s );
+			else
+				super.print( s );
+		}
+
+		public void print( boolean b ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( b );
+			else
+				super.print( b );
+		}
+
+		public void print( char[] ca ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( ca );
+			else
+				super.print( ca );
+		}
+
+		public void print( double d ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( d );
+			else
+				super.print( d );
+		}
+
+		public void print( float f ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( f );
+			else
+				super.print( f );
+		}
+
+		public void print( int i ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( i );
+			else
+				super.print( i );
+		}
+
+		public void print( long l ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.print( l );
+			else
+				super.print( l );
+		}
+
+		public void println() {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup ) {
+				((DaemonThreadGroup) tg).Out.println();
+			}
+			else {
+				super.println();
+			}
+		}
+
+		public void println( Object o ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( o );
+			else
+				super.println( o );
+		}
+
+		public void println( String s ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( s );
+			else
+				super.println( s );
+		}
+
+		public void println( boolean b ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( b );
+			else
+				super.println( b );
+		}
+
+		public void println( char[] ca ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( ca );
+			else
+				super.println( ca );
+		}
+
+		public void println( double d ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( d );
+			else
+				super.println( d );
+		}
+
+		public void println( float f ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( f );
+			else
+				super.println( f );
+		}
+
+		public void println( int i ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( i );
+			else
+				super.println( i);
+		}
+
+		public void println( long l ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup()) 
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.println( l );
+			else
+				super.println( l);
+		}
+
+		public void write( byte buf[], int off, int len ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup()) 
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.write(
+					buf, off, len );
+			else
+				super.write( buf, off, len );
+		}
+
+		public void write( int n ) {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup()) 
+					instanceof DaemonThreadGroup )
+				((DaemonThreadGroup) tg).Out.write( n );
+			else
+				super.write( n );
+		}
+	}
+	
+	class DaemonInputStream extends InputStream {
+		InputStream StdIn;
+
+		DaemonInputStream( InputStream stdin ) {
+			StdIn = stdin;
+		}
+
+		public int read() throws IOException {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup()) 
+					instanceof DaemonThreadGroup )
+				return ((DaemonThreadGroup) tg).In.read();
+			else
+				return StdIn.read();
+		}
+
+		public int read( byte b[], int off, int len )
+				throws IOException {
+			ThreadGroup tg;
+
+			if ( (tg = Thread.currentThread().getThreadGroup())
+					instanceof DaemonThreadGroup )
+				return ((DaemonThreadGroup) tg).In.read(
+						b, off, len );
+			else
+				return StdIn.read( b, off, len );
+		}
+	}
+
+	public InvocationDaemon( String args[] ) {
+		setArgs( args );
+
+		SysOut = System.out;
+		System.setOut( new DaemonPrintStream( SysOut ) );
+
+		SysIn = System.in;
+		System.setIn( new DaemonInputStream( SysIn ) );
+
+		System.setSecurityManager(
+			new InvocationDaemon.DaemonSecurityManager() );
+
+		run();
+	}
+
+	protected void finalize() throws Throwable {
+		if ( ListenSocket != null ) {
+			ListenSocket.close();
+		}
+		super.finalize();
+	}
+
+	public static void main( String[] args ) {
+		InvocationDaemon daemon = new InvocationDaemon( args );
+	}
+
+	public void run() {
+		try {
+			ListenSocket = new ServerSocket( Port, 10 );
+		}
+		catch ( IOException x ) {
+			System.err.println(
+				"could not open ServerSocket on port: " + 
+				Port );
+			System.exit( 1 );
+		}
+
+		try {
+			if ( Log )	
+				SysOut.println(
+					"InvocationDaemon started on port: " + 
+					Port );
+
+			while ( GoOn ) {
+				if ( Log )
+					SysOut.println(
+						"InvocationDaemon ready.." );
+
+				Socket client = ListenSocket.accept();
+				NConnect++;
+
+				DaemonThreadGroup tg = new DaemonThreadGroup(
+					Integer.toString( NConnect ) );
+				Connection connection = new Connection(
+					client, tg );	
+				Thread t = new Thread( tg, connection );
+				t.start();
+			}
+		}
+		catch ( IOException x ) {
+			System.err.println( "connection failed" );
+		}
+	}
+
+	void setArgs( String[] args ) {
+		int i;
+
+		Port = 0;
+
+		try {	
+			for ( i = 0; i < args.length; i++ ) {
+				if ( args[i].equals( "-port") ) {
+					Port = Integer.parseInt( args[ ++i ] );
+				}
+				else if ( args[ i ].equals( "-log" ) ) {
+					Log = true;
+				}
+			}
+		}
+		catch ( Exception x ) {}
+
+		if ( Port == 0 )
+			Port = DEF_PORT;
+	}
+}


More information about the kaffe mailing list