Archived

This forum has been archived. Please start a new discussion on GitHub.

Mono 1.2.6 and Ice

Hi,

I have updated Mono on my machine (running Gentoo) to version 1.2.6 and executing my application (wich is using Ice) returns this exception:
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object
  at IceInternal.Network.fdToString (System.Net.Sockets.Socket socket) [0x00000]
  at IceInternal.TcpTransceiver..ctor (IceInternal.Instance instance, System.Net.Sockets.Socket fd) [0x00000]
  at IceInternal.TcpConnector.connect (Int32 timeout) [0x00000]
  at IceInternal.OutgoingConnectionFactory.create (IceInternal.EndpointI[] endpts, Boolean hasMore, Boolean threadPerConnection, System.Boolean& compress) [0x00000]
  at IceInternal.RoutableReference.createConnection (IceInternal.EndpointI[] allEndpoints, System.Boolean& comp) [0x00000]
  at IceInternal.DirectReference.getConnection (System.Boolean& comp) [0x00000]
  at Ice.ObjectDelM_.setup (IceInternal.Reference rf) [0x00000]
  at Ice.ObjectPrxHelperBase.getDelegate__ () [0x00000]
  at Ice.LocatorPrxHelper.findObjectById (Ice.Identity id, Ice.Context context__, Boolean explicitContext__) [0x00000]
  at Ice.LocatorPrxHelper.findObjectById (Ice.Identity id) [0x00000]
  at IceInternal.LocatorInfo.getEndpoints (IceInternal.IndirectReference ref, Int32 ttl, System.Boolean& cached) [0x00000]
  at Ice.ObjectAdapterI.isLocal (ObjectPrx proxy) [0x00000]
  at IceInternal.ObjectAdapterFactory.findObjectAdapter (ObjectPrx proxy) [0x00000]
  at Ice.ObjectPrxHelperBase.getDelegate__ () [0x00000]
  at Ice.ObjectPrxHelperBase.ice_isA (System.String id__, Ice.Context context__, Boolean explicitContext__) [0x00000]
  at Ice.ObjectPrxHelperBase.ice_isA (System.String id__) [0x00000]
  at IceGrid.QueryPrxHelper.checkedCast (ObjectPrx b) [0x00000]

Did you tried mono 1.2.6? Is it bug on their side or yours? Thanks

Comments

  • We haven't tried with Mono 1.2.6 yet. I'll have a look at what's going on.

    Cheers,

    Michi.
  • The problem is that, as of Mono 1.2.6, IPEndPoint.LocalEndPoint and IPEndPoint.RemoteEndPoint return null for a socket that was connected non-blocking. (In versions prior to 1.2.6, these properties returned the correct endpoint.)

    One could argue that this is not a bug because .NET does the same thing :mad:

    If you look in Network.cs, around line 1320, you will find a number of DllImport statements. You need to replace these with the name of your C library, for example
    #if __MonoCS__
            [DllImport("libc-2.3.6.so")]
    #else
            [DllImport("wsock32.dll")]
    #endif
            private static extern int getsockname(IntPtr s, ref sockaddr name, ref int namelen);
    

    Do the same for the getpeername, inet_ntoa, and ntohs imports.

    Then change the definitions of getLocalAddress and getRemoteAddress as follows:
            public static IPEndPoint
            getLocalAddress(Socket socket)
            {
                //
                // .Net BUG: The LocalEndPoint and RemoteEndPoint properties
                // are null for a socket that was connected in non-blocking
                // mode. As of Mono 1.2.6, Mono behaves the same way.
                // The only way to make this work is to step down to
                // the native API and use platform invoke :-(
                //
                IPEndPoint localEndpoint;
                sockaddr addr = new sockaddr();
                int addrLen = 16;
    
                if(getsockname(socket.Handle, ref addr, ref addrLen) != 0)
                {
                    throw new Ice.SyscallException();
                }
                string ip = Marshal.PtrToStringAnsi(inet_ntoa(addr.sin_addr));
                int port = ntohs(addr.sin_port);
                localEndpoint = new IPEndPoint(IPAddress.Parse(ip), port);
                return localEndpoint;
            }
    
            public static IPEndPoint
            getRemoteAddress(Socket socket)
            {
                //
                // .Net BUG: The LocalEndPoint and RemoteEndPoint properties
                // are null for a socket that was connected in non-blocking
                // mode. As of Mono 1.2.6, Mono behaves the same way.
                // The only way to make this work is to step down to
                // the native API and use platform invoke :-(
                //
                IPEndPoint remoteEndpoint = null;
                sockaddr addr = new sockaddr();
                int addrLen = 16;
    
                if(getpeername(socket.Handle, ref addr, ref addrLen) == 0)
                {
                    string ip = Marshal.PtrToStringAnsi(inet_ntoa(addr.sin_addr));
                    int port = ntohs(addr.sin_port);
                    remoteEndpoint = new IPEndPoint(IPAddress.Parse(ip), port);
                }
                return remoteEndpoint;
            }
    

    This fixes the problem. I'm not entirely happy with this P/Invoke fix though because the library name is platform dependent :mad:

    For the moment, this fix should get you off the hook. I'll talk to the Mono people to see whether there is a better way to do this.

    Cheers,

    Michi.
  • The same icecs.dll for Windows and Linux

    thank you.

    But I don't like the fix. I would like to use the same icecs.dll on both Linux and Windows in my application.

    so I don't like this:
    #if __MonoCS__

    I found that you use it only in src\Ice\Application.cs. Can you remove it? I use Mono also on Windows so it is not the right swich and such switches should be done at runtime not at compilation time.

    You can for example define the methods twice in different classes with the same interface and at runtime check the platform...

    thanks
  • kovacm wrote: »

    But I don't like the fix. I would like to use the same icecs.dll on both Linux and Windows in my application.

    so I don't like this:
    #if __MonoCS__

    I don't like it either. But I'm afraid that there is no way to avoid this if we are forced to use P/Invoke because of a deficiency in .NET.
    I found that you use it only in src\Ice\Application.cs. Can you remove it?

    No, we use P/Invoke in a number of places, not just Application.cs.
    I use Mono also on Windows so it is not the right swich and such switches should be done at runtime not at compilation time.

    I agree that this would be preferable. But there is no mechanism in the language to delay this decision until run time. The DllImport mechanism is hopelessly broken because it requires the library name to be hardwired into the code at compile time. What really should be there is a mechanism that can accept more than one library name at run time, and search a number of libraries for a particular symbol, similar to LD_LIBRARY_PATH.
    You can for example define the methods twice in different classes with the same interface and at runtime check the platform...

    As far as I know, that's not possible for P/Invoke. But, if you have a solution to this, I'd be keen to hear about it!

    Cheers,

    Michi.
  • I agree that this would be preferable. But there is no mechanism in the language to delay this decision until run time. The DllImport mechanism is hopelessly broken because it requires the library name to be hardwired into the code at compile time. What really should be there is a mechanism that can accept more than one library name at run time, and search a number of libraries for a particular symbol, similar to LD_LIBRARY_PATH.
    Will this help?
    CodeProject: Late binding on native DLLs with C#. Free source code and programming articles
  • Thanks for the tip. But this requires assembly language, so we'd simply swap one platform dependence for another.

    Michi.
  • DllImport loads the library at runtime

    It is true that the DllImport "requires the library name to be hardwired into the code at compile time", but the DllImport loads the library first time the method is called! So if you have in the code methods with the DllImport attribute for both linux and windows and you call only these which are in actual platform everything works.
  • Thanks for the top. The following works:
    [DllImport("wsock32.dll", EntryPoint="getsockname")]
    private static extern int getsocknameWin(IntPtr s, ref sockaddr name, ref int namelen);
    
    [DllImport("libc.so.6", EntryPoint="getsockname")]
    private static extern int getsocknameMono(IntPtr s, ref sockaddr name, ref int namelen);
    

    Then the code can select which function to call at run time.

    This is a little better than #if __MonoCS__, but still has the disadvantage of hard-wiring the name of the C library.

    And, due to the limitations of the C# preprocessor, we cannot define the name of the library in the makefile and then substitute it into the code. As far as I can see, the only way to use a DllImport attribute is to have the name of the library as a string literal in the program text :mad:

    Cheers,

    Michi.
  • library

    Read Interop with Native Libraries - Mono. You can specify only a name without sufixes and prefixes, so if I uderstand correctly this is the right way for the libc.so.6:
    [DllImport("c", EntryPoint="getsockname")]
    private static extern int getsocknameMono(IntPtr s, ref sockaddr name, ref int namelen);
    
    You can later override the name in mono by <dllmap/> elements in the .config file... Still not best, but better than nothing.
  • Ps

    And getsocknameGnu or getsocknameUnix (the same name as in System.PlatformID enumeration) is better name for that method, because if you are running Mono on windows this method should not be called.
  • kovacm wrote: »

    Now that looks promising. I'll have a close look at that, thanks a lot!

    Cheers,

    Michi.
  • When do you plan to release new version with the fix? (support for mono 1.2.6 + one .dll for both windows and unix)

    thanks,
    Michal
  • matthew
    matthew NL, Canada
    If it is possible to fix then we'll release this with Ice 3.3 due Q1 2008.
  • kwaclaw
    kwaclaw Oshawa, Canada
    michi wrote: »
    Now that looks promising. I'll have a close look at that, thanks a lot!

    Cheers,

    Michi.

    I saw this discussion late. Anyway, it would be on my wishlist if IceCS could be fully managed code, because than it could run in security restricted situations, where P/Invoke is not allowed.

    Regarding the issue at hand - Mono duplicating a .NET bug/deficiency, I ran your test code which I found on Google, where you demonstrated that a non-blocking socket will have null endpoint properties even if the Poll succeeded:

    Sockets and non-blocking connect - .NET C#

    I then added a simple Bind call with unspecified address and port -- s.Bind(new IPEndPoint(IPAddress.Any, 0)); -- before calling connect, and then both local and remote endpoints were not null.

    Could that work for you?

    Karl
  • Thanks for the tip. Someone on the Mono mailing list mentioned the bind() option to me too. I'll try this out. If it works, that'll get rid of the P/Invoke in this case.

    We are using P/Invoke in a number of other places to work around limitations of .NET, and we also use P/Invoke for protocol compression. Getting rid of all of these might be difficult.

    Cheers,

    Michi.
  • kwaclaw
    kwaclaw Oshawa, Canada
    michi wrote: »
    Thanks for the tip. Someone on the Mono mailing list mentioned the bind() option to me too. I'll try this out. If it works, that'll get rid of the P/Invoke in this case.

    We are using P/Invoke in a number of other places to work around limitations of .NET, and we also use P/Invoke for protocol compression. Getting rid of all of these might be difficult.

    Cheers,

    Michi.

    It probably requires some study as to which managed API is a good replacement for some of your interop calls. I just had a quick look, and maybe there are a few avenues worth looking at:

    1) For SetConsoleCtrlHandler, the Console.CancelKeyPressEvent would be an alternative, as long as all you care about is capturing Ctrl+C and Ctrl+Break.

    2) It looks as if Guid.NewGuid() does the same as UUIDCreate in rpcrt4.dll.

    3) It seems there is also a managed alternative for libbz2.dll:
    .NET Zip Library #ziplib (SharpZipLib)

    Karl
  • matthew
    matthew NL, Canada
    How much of a performance cost are you willing to bear? Some of the marshaling code is also unsafe -- this could be replaced with safe code but its quite a bit slower.

    What environments you are considering that require fully manged code?
  • kwaclaw
    kwaclaw Oshawa, Canada
    matthew wrote: »
    How much of a performance cost are you willing to bear? Some of the marshaling code is also unsafe -- this could be replaced with safe code but its quite a bit slower.

    What environments you are considering that require fully manged code?

    We have an internal ASP.NET site that runs assemblies in semi-trusted mode. That excludes unsafe code and native interop. We are also considering running one of our apps from a web server (to simplify deployment). Though this app is currently not using ICE, I would not want to exclude ICE in the future simply because it can only run in a fully trusted context. I would also think that in Silverlight 2.0 (which apparently will have Socket support) using full IceCS instead of the HTTP bridge would be advantageous.

    I have looked at your ByteBuffer class, the only place where I found unsafe code. I think the overall performance impact might not be as bad. It appears the unsafe code is mostly used for marshaling value types. For example, once could replace the unsafe getShort() and putShort() methods with this (untested!) code:
    private const UInt16 ByteLoMask = 0x00FF;
    
    public short getShort()
    {
      unchecked {
        checkUnderflow(2);
        int ret;
        if(NO._o == _order)
        {
          ret = (_bytes[_position] << 8;
          ret |= _bytes[_position + 1];
        }
        else
        {
          ret = _bytes[_position + 1] << 8;
          ret |= _bytes[_position];
        }
        position += 2;
        return (short)ret;
      }
    }
    
    public ByteBuffer putShort(short val)
    {
      unchecked {
        checkOverflow(2);
        if(NO._o == _order)
        {
          _bytes[_position] = (byte)(value >> 8);
          _bytes[_position + 1] = (byte)(value & ByteLoMask);
        }
        else
        {
          _bytes[_position + 1] = (byte)(value >> 8);
          _bytes[_position] = (byte)(value & ByteLoMask);
        }
        _position += 2;
      }	
      return this;
    }
    

    with no significant performance loss.

    Karl
  • I agree that getting rid of the P/Invoke calls and the unsafe code would be nice. I wasn't aware that there is a .NET compression library available now--it's worth a try.

    It will still be difficult to get rid off all the P/Invoke code though. We are using it for monotonic time, and CancelKeyPressEvent doesn't deal with window closure, logoff, and shutdown events (which SetConsoleCtrlHandler does).

    Cheers,

    Michi.
  • matthew
    matthew NL, Canada
    kwaclaw wrote: »
    ...

    with no significant performance loss.

    Karl

    Its also possible to use BitConverter. At any rate, for sure its slower depending on what your application is doing (for example, marshaling an array of shorts or similar). The question is whether this matters to you :)
  • kwaclaw
    kwaclaw Oshawa, Canada
    matthew wrote: »
    Its also possible to use BitConverter. At any rate, for sure its slower depending on what your application is doing (for example, marshaling an array of shorts or similar). The question is whether this matters to you :)

    From my experience, .NET optimizes quite well, and since there is also a little overhead pinning memory (as in the 'fixed' statement), I did some benchmarking myself, using your ByteBuffer, but adding safe variants of putInt(), getInt(), putIntSeq() and getIntSeq(). I thought a performance difference would show up more clearly for ints than for shorts. The safe variants are coded like what I outlined previously. I built the app under VS 2005 in release mode, as debug mode is actually slower for this kind of application.

    In my measurements, the safe code was most of the time *faster* than the unsafe code (it varied from one test run to the other). I also uncovered one specific *huge* performance issue with your code: if it has to reverse the byte order, the unsafe code is more than 10 times as slow as the safe code. This is normally the case on little-endian CPUs, i.e. almost always. The reason I think is the use of all those Buffer.SetByte/GetByte calls that are obviously not inlined by the compiler.

    My CPU is an Athlon x64 dual core 3600.
    If you like, I can send you the VS 2005 project (just tell where to) so that you can check for yourself and try it on other CPUs.

    Karl
  • kwaclaw
    kwaclaw Oshawa, Canada
    michi wrote: »
    I agree that getting rid of the P/Invoke calls and the unsafe code would be nice. I wasn't aware that there is a .NET compression library available now--it's worth a try.

    It will still be difficult to get rid off all the P/Invoke code though. We are using it for monotonic time, and CancelKeyPressEvent doesn't deal with window closure, logoff, and shutdown events (which SetConsoleCtrlHandler does).

    Cheers,

    Michi.

    I could not find any P/Invoke calls that are time related. Where should I look?

    Regarding SetConsoleCtrlHandler : It is only used in your helper class, so it is not an essential part of IceCS. Maybe one could spin off non-essential classes into a separate assembly?

    Karl
  • kwaclaw wrote: »
    In my measurements, the safe code was most of the time *faster* than the unsafe code (it varied from one test run to the other).

    I benchmarked this fairly extensively when I initially wrote the C# implementation. Back then, the unsafe version was faster than than the safe one. But that was several iterations of the compiler and run time ago, so this may have changed in the mean time.
    I also uncovered one specific *huge* performance issue with your code: if it has to reverse the byte order, the unsafe code is more than 10 times as slow as the safe code. This is normally the case on little-endian CPUs, i.e. almost always. The reason I think is the use of all those Buffer.SetByte/GetByte calls that are obviously not inlined by the compiler.

    I'll have a look at this too. It may well be that doing this differently will be faster now with the later version of the compiler and run time. Note that the byte order on the wire is little-endian--for little-endian CPUs, no byte swapping should take place.
    If you like, I can send you the VS 2005 project (just tell where to) so that you can check for yourself and try it on other CPUs.

    Yes, that would be useful. Can you just email me the code please?

    Thanks,

    Michi.
  • kwaclaw
    kwaclaw Oshawa, Canada
    michi wrote: »
    Yes, that would be useful. Can you just email me the code please?

    Thanks,

    Michi.

    I e-mailed the code to the e-mail address on your home page, hope that is the correct one.

    Karl