Adventures in Mono

Published on 2010-12-10

In 24 hours I installed Ubuntu, downloaded the latest version of mono, fixed a bug in it and submitted a pull request – the whole process was really simple and I want to let everybody else know how simple it is so they might be inspired to do the same.

In the beginning

I took the decision this week to join the growing number of .NET developers who choose to run Linux, not for any ideological reasons in particular – but just to experience this side of the fence for the first time in a few years, catch up on what has changed and to get my code working over here in Mono.

To that end, I installed Ubuntu 10.04, installed VirtualBox with Windows 7 and Visual Studio, and then proceeded to start building my existing projects in mono to see how they fared. The great thing about Ubuntu is that it comes with Mono, and I haven’t got to do anything to kick-start this process. (Ooh, flame bait here)

My process for building my .NET projects is simply to execute the solution file with xbuild like so

xbuild myAmazingProject.Sln

I started with my .NET 2.0 projects, and all but my largest work project built and executed on the version of Mono that ships with Ubuntu 10.04 (the current LTS) – that’s Mono 2.4 – this crashed the C# compiler in a number of places and therefore did not work.

In an effort to get this working I upgraded to Ubuntu 10.10 which gave me Mono 2.6.7, which fared better but still fell over in a number of other slightly different places.

I then decided to upgrade to 2.8, and found somebody who had set up a script to install a parallel version of Mono (without overwriting the system version – rather important for me as I was interested in not breaking my system through ignorance). This had the effect of getting my big failing work .NET 3.5 work project to build (On top of NH, SolrNet, Moq, MySQL, ASP.NET MVC etc – the usual suspects).

Great, now for RavenDB

RavenDB is always going to be an interesting one, it is written in C#4 on top of .NET 4.0 and uses a lot of the latest features in these technologies, the C#4 compiler hasn’t a hope of dealing with this, not even in 2.8 – and falls over with some fairly hairy messages.

There is no technical reason why RavenDB should not work on Linux, and I’m not starting my Mono experience trying to fix C# compiler issues – so I pop into my Windows Virtual Box and do a RavenDB build in Visual Studio via the standard build scripts.

The server is a massive piece of kit, and in the 2.8 environment I set up typing something like this

mono Raven.Server.exe /ram

Resulted in the square root of diddly squat occuring, asking mono to give me a verbose trace of what occurred with

mono Raven.Server.exe -v --trace /ram

This left me with a large trace and none the wiser about what really went wrong. Back to basics then, eating an elephant and all that.

public static void Main (string[] args)
{
         using(var store = new EmbeddableDocumentStore(){
                 RunInMemory = true
         })
         {
                 store.Initialize();
 
         }
}
 
Running the bare minimum for RavenDB, specifying in-memory so we’re definitely using the managed storage engine (not esent), and not even having manage the file aspect of this problem. I build this in MonoDevelop and run it under 2.8.

This falls over with the useful error:

Unhandled Exception: System.MissingMemberException: The type being lazily initialized does not have a public, parameterless constructor.
  at System.Threading.LazyInitializer.GetDefaultCtorValue[IStorageActionsAccessor] () [0x00000] in <filename unknown>:0 
  at System.Threading.ThreadLocal`1+<DataSlotCreator>c__AnonStorey42[Raven.Database.Storage.IStorageActionsAccessor].<>m__45 () [0x00000] in <filename unknown>:0 

This is clearly a bug in ThreadLocal<T>, and it is at this point I stop and take stock of the situation I find myself in

Let me tell you, option #1 is looking really appealing right now, I am not a Linux user and have survived this far into the process by being a dumb person clicking on buttons in Ubuntu and things somehow “just working” (Parallel Mono 2.8 environment included in that).

After consultation on Twitter, I am assured that the Mono team are friendly and that I’ll get plenty of help on IRC if I need it. I decide to take the plunge and go for Option #2.

Running bleeding edge mono

sudo apt-get install irrssi
irssi
/connect irc.gimp.net
/join #mono
How I mine for fish?
 
Okay, I didn’t ask that, but what I did ask was

I felt like an idiot for asking such an RTFM-answerable question, but thankfully as promised I got a friendly response pointing me to

http://www.mono-project.com/Parallel_Mono_Environments

http://mono-project.com/Compiling_Mono_From_Git

Not only two very good links (I’d not have found the first, I’m only now using the term “Parallel Mono Environments”) because I now know that’s what you call them, I also received good solid warnings about doing the right things in the right order to ensure that I built Mono to a secondary location and didn’t overwrite my Ubuntu version.

By following these two sets of instructions, it_just_worked, the process went something like this

vim ~/mono-git-environment

MONO_PREFIX=/opt/mono-git
GNOME_PREFIX=/usr
export DYLD_LIBRARY_FALLBACK_PATH=$MONO_PREFIX/lib:$DYLD_LIBRARY_FALLBACK_PATH
export LD_LIBRARY_PATH=$MONO_PREFIX/lib:$LD_LIBRARY_PATH
export C_INCLUDE_PATH=$MONO_PREFIX/include:$GNOME_PREFIX/include
export ACLOCAL_PATH=$MONO_PREFIX/share/aclocal
export PKG_CONFIG_PATH=$MONO_PREFIX/lib/pkgconfig:$GNOME_PREFIX/lib/pkgconfig
export PATH=$MONO_PREFIX/bin:$PATH
PS1="[mono] \w @ 

:wq!

source ~/mono-git-environment
 
git clone git://github.com/mono/mono.git
 
./autogen.sh –prefix=/opt/mono-git
 
make PROFILE=net_4_0 && make install PROFILE=net_4_0
 

There, I now have the latest version of mono and by typing

source ~/mono-git-environment 

At any time, whatever I run using mono will use this version of mono! I must admit, I messed it up at first and forgot the PROFILE=net_4_0 bit that I need if I want .NET 4 (which I do), and then I pasted the above script for setting up the mono git environment wrong and missed out the first two characters (oops), but on figuring out that it just worked!

The best thing about this, in order to get whatever everybody has been working on lately, I just do

source ~/mono-git-environment 
 
git pull
 
make PROFILE=net_4_0 && make install PROFILE=net_4_0
 
This is so exciting guys, I am literally dancing in my chair at the prospect of writing and modifying the mono class libraries this effortlessly.

Isolating the bug

mono –v --trace bin/Debug/TestProject.exe > log.txt

This traces every method called during the process of executing my test project and gives me a giant file containing that information, I had to look up a vim cheatsheet for navigating and searching/paging through this mess.

tracelog

This didn’t really give me a lot of information beyond the error I got when running it the first time, but it did give me a history of calls made and a complete stack trace at the point of crashing.

This led me to the following method in RavenDB

public void Batch(Action<IStorageActionsAccessor> action)
       {
           if (disposed)
           {
               Trace.WriteLine("TransactionalStorage.Batch was called after it was disposed, call was ignored.");
               return; // this may happen if someone is calling us from the finalizer thread, so we can't even throw on that
           }
           if(current.Value != null)
           {
               action(current.Value);
               return;
           }
           disposerLock.EnterReadLock();
           try
           {
               Interlocked.Exchange(ref lastUsageTime, DateTime.Now.ToBinary());
               using (tableStroage.BeginTransaction())
               {
                   var storageActionsAccessor = new StorageActionsAccessor(tableStroage, uuidGenerator, DocumentCodecs);
                   current.Value = storageActionsAccessor;
                   action(current.Value);
                   tableStroage.Commit();
                   storageActionsAccessor.InvokeOnCommit();
                   onCommit();
               }
           }
           finally
           {
               disposerLock.ExitReadLock();
               current.Value = null;
           }
       }

Mmm, tasty  - I know that the exception occurs when  trying to access the .Value property of the ThreadLocal<ISomeInterface>, so I start with the first instance of it and write a test program

class MainClass
{
        public static void Main (string[] args)
        {
           ThreadLocal<IPointlessActions> newLocal = new ThreadLocal<IPointlessActions>();
           bool willThrowAnException = newLocal.Value == null;
        }
}
 
public interface IPointlessActions
{
        void DoSomething();
}
 
There we go, in Windows on the MS implementation of .NET, Value returns null if not initialized, and on Mono it throws an exception because it can’t find a default constructor for IPointlessActions (Well of course not, it’s an interface!)

Verifying that it is a bug

Back to #mono on irc.gimp.net, I paste the above code example into pastebin, and link to the documentation for the class on MSDN.

We agree that it is probably a bug, and I get helpful feedback on where to start if I want to stand a chance of getting anything pulled into Mono (It’s a really trivial issue, and probably a really trivial fix, not worth the effort of pulling/merging anyway), I’m also advised to add a bugzilla for it on the mono site because that makes it easier to do changelogs.

This is all information that they’ve no doubt had to divulge numerous times to complete novices like myself, and I’m not getting a single snide remark or passive aggressive behaviour – very refreshing!

Fixing the bug

I add a bugzilla, and open two files in vim side by side, they are

mcs/class/corlib/System.Threading/ThreadLocal.cs

and

mcs/class/corlib/Test/System.Threading/ThreadLocalTests.cs

The first thing to do is write a test demonstrating the failing behaviour

[Test]
  public void DefaultValueIsUsedIfNoInitializerSupplied()
  {
          ThreadLocal<IEnumerable> local  = new ThreadLocal<IEnumerable>();
          IEnumerable value = local.Value;
          Assert.AreEqual(null, value);
  }

I can then run the test by typing

make run-test FIXTURE=System.Threading.ThreadLocalTests PROFILE=net_4_0

On verifying the fail, I make the fix (passing in () => default(X) in the default constructor or something to that end), run the test again and push it up to github with

git commit –m “some fix message”
 
git push robashton
 
and make a pull  request which I link to from bugzilla, my work here is done!

Running RavenDB

I do a build with

make PROFILE=net_4_0 && make install PROFILE=net_4_0

And re-run the in memory application, success – it works! I then check basic persistence

using(var store = new EmbeddableDocumentStore(){
         RunInMemory = true
})
{
         store.Initialize();
 
 
         using(var session = store.OpenSession()){
                 session.Store(new {
                         Id = "Test/1",
                         FirstWord = "Hello",
                         SecondWorld = "World"
                 });
                 session.SaveChanges();
         }
 
}
 
This runs with no problems, now I can continue writing tests that expose more and more of RavenDB until I am happy it is functioning as expected.

A direction for this

I have no timeline for the above, I'm deliberately saving the hardest till last - and I strongly suspect I'll be relying on others taking up the strain where I struggle there, but I've heard good things about turnaround times on compiler bugs so I'm not worried about that

With the support I've seen from the mono community I've contacted so far, I don't have any doubt that this can be made to work as it should, great stuff

2018 © Rob Ashton. ALL Rights Reserved.