[Mono-list] Writing daemons in C#

Jonathan Pryor jonpryor@vt.edu
20 Nov 2002 10:37:16 -0500


I would suggest an alternative approach.  Use P/Invoke for everything,
but instead of having the C# values differ from the platform-specific
values, let them be the same.  Instead, use P/Invoke to also get the
platform-specific values.

It could look like this:

	public sealed class OpenMode {
		[DllImport ("posix-values")]
		private static extern int posix_get_O_CREAT ();

		[DllImport ("posix-values")]
		private static extern int posix_get_O_EXCL ();

		[DllImport ("posix-values")]
		private static extern int posix_get_O_APPEND ();

		public static int O_CREAT {
			get {return posix_get_O_CREAT();}
		}

		public static int O_EXCL {
			get {return posix_get_O_EXCL ();}
		}

		public static int O_APPEND {
			get {return posix_get_O_APPEND ();}
		}
	}

	public sealed class PosixFunctions {
		[DllImport("libc")]
		public static extern int open (string pathname, int flags, int mode);
	}

The advantage to this is that you don't have to write as much custom
code.  All of the `posix_get_CONSTANT()' functions can be generated with
a single perl script:

	#!/usr/bin/perl

	print <<EOF;
	/*
	 * Defines functions to get the value of a POSIX constant
	 */
	#include <fcntl.h>
	#include <sys/stat.h>

	EOF

	while (<>) {
	  $constant = $_;
	  chop $constant;
	  print <<EOF;
	int posix_get_$constant ()
	{
		return $constant;
	}
	
	EOF
	}

It reads from standard input one constant per line, so the input could
be:

	O_CREAT
	O_EXCL
	O_APPEND

and the script would generate all the appropriate `posix_get_'
functions.

Actually, you could probably automate the generate of most of the Posix
constants in this fashion, minimizing the amount of custom code you
would have to write and maintain.

It might also be interesting to look at Gtk#, which appears to use XML
input files to describe the GTK+ API and automatically generates some
bindings from the XML description.  I would imagine that similar things
could be done for POSIX.

 - Jon

On Wed, 2002-11-20 at 05:44, Miguel de Icaza wrote:
> Hello,
> 
> > I'm presuming DllImport and P/Invoke aren't the preferred methods here? 
> > I looked at System.Net.Socket, since that would appear to be a task
> > similar to implementing POSIX APIs.  It uses InternalCall, which I'm
> > presuming (C# In A Nutshell is not so clear on this) is implemented
> > within the interpreter.  I poked through mono and found the file
> > mono/io-layer/sockets.c which appears to be the internal implementation
> > of the Sockets API.  Would the POSIX implementation look similar to
> > this?  Is there someplace all this is documented or am I on my own?
> 
> Ok, here is where it gets tricky.  The official response is `Only use
> P/Invoke', but sometimes it might be convenient (for speed reasons) to
> use internal calls.  The tricky bit is: internal calls are only
> available on the runtime.
> 
> Now, I figured that there is a *hack* we can use, but it is a hack, in
> which you P/Invoke into a bootstrap routine (called from a static
> constructor), and the bootstrap routine in turn registers the
> InternalCalls with the runtime, and then the rest of the code uses
> internalcalls.
> 
> My suggestion is: for now stick to using P/Invoke, and we will later
> implement the performance hack.
> 
> Now, another of the tricky bits is this: enumerations and constants are
> different across the various Unix implementations, so it is not possible
> to P/Invoke a library with constants that are defined in C# directly,
> because they will likely change (in fact, the values change across Linux
> ports: the same system constant value is different across ports).
> 
> So the process to assemble this is multi-process:
> 
> 	* A C# file containing all the enumeration values.
> 
> 	* A C# program that can read these enumeration values and
> 	  generate "support" header file for C consumption.
> 
> 	* A C program that includes the header file, and maps constants
> 	  to their native valuess.
> 
> 	* A C# set of definitions to P/Invoke into it.
> 
> Let me give you an illustration:
> 
> PosixEnums.cs:
> [IncludeFile ("fcntl.h")]
> [IncludeFile ("sys/stat.h")]
> 	enum OpenMode {
> 		O_CREAT,
> 		O_EXCL,
> 		O_APPEND
> 	}
> 
> The a C# program uses reflection to generate the following header file:
> 
> #include <fcntl.h>
> #include <sys/stat.h>
> #define OpenMode_O_CREAT	O_CREAT
> #define OpenMode_O_EXCL         O_EXCL
> #define OpenMode_O_APPEND       O_APPEND
> 
> And the following C helper file:
> 
> #include "PosixEnums.h"
> map_OpenMode (int flags)
> {
> 	switch (flags){
> 		case OpenMode_O_CREAT: return O_CREAT;
> 		case OpenMode_O_EXCEL: return O_EXCL;
> 		case OpenMode_O_APPEND: return O_APPEND;
> 	}
> }
> 
> Notice that the source file contains the attribute "IncludeFile" (that
> you will have to create, and "pull" it out yourself to generate the
> corresponding header files).
> 
> Then you have to write the C code, *manually*:
> 
> int posix_open (string filename, int flags)
> {
> 	return open (filename, 	map_OpenMode (flags));
> }
> 
> Then from C# you write (again manually) the P/Invoke invocation:
> 
> [DllImport ("Mono.Posix.so", EntryPoint="posix_open")]
> int open (string filename, OpenMode mode);
> 
> Once you have the code, we can integrate it into the build.
> 
> Hope this helps,
> Miguel.
> 
> _______________________________________________
> Mono-list maillist  -  Mono-list@ximian.com
> http://lists.ximian.com/mailman/listinfo/mono-list