I make tools to help people create wonderful things.

Journal

An Apologetic Sneak Peek

Unfortunately, I have blogged very little in recent months. I shall follow this post with a summary of the things I've skipped blogging, but for now, I offer up the following tantalising screenshot by way of apology:

Screenshot of ASP.NET event handling code completion in MonoDevelop.

Planning ASP.NET Code Completion for MonoDevelop

Now that MonoDevelop 1.0 is on the verge of shipping, I have begun to plan the parser that will underpin the ASP.NET code completion and visual designer in upcoming versions of MonoDevelop. During a discussion with our ASP.NET expert Marek, I found out about an obscure ASP.NET feature that currently causes problems for Mono's ASP.NET parser, and is entirely counterintuitive to anyone with any XML knowledge.

Apparently, ASP.NET server tags are able to generate any part of the page that they desire, except for other ASP.NET constructs, and the following vilely ugly code is completely valid on .NET:

<div>
    <asp:Literal id="this_is_stupid" runat="server" Text="<p style='color:red'>" />Hello</ p>
</div>

When a developer does this, the document's XHTML DOM cannot be parsed at design time unless the parser knows the exact HTML fragment that will be output by each ASP.NET control, and hence this can make HTML completion incredibly difficult. Although the Mono ASP.NET stack needs to support this "feature" for compatibility with .NET, I am firmly of the opinion that anyone doing this should be poked with a sharp stick until they stop.

It makes no sense to support this kind of unbalanced syntax in MonoDevelop, as it will hugely increase the complexity and development time. Indeed, when I investigated this on Windows, I found that VS2008 considers it to be a warnable offence.

The downside to my decision is that it's probably not going to be possible to continue sharing the tokeniser with Mono's ASP.NET stack. It currently tokenises the whole HTML DOM as well as the ASP.NET controls and expressions, but this is not necessary for Mono's ASP.NET parser and only makes it hard to handle these corner cases.

GHOP Help Needed

The Google Highly Open Participation (GHOP) contest has been running for a couple of weeks now, and the Mono Project has seen a fair bit of action. Sadly we've only had five completed tasks, with a couple more almost done. On a lot of the claimed tasks, many of the students and mentors have been slow to act, busy with real life work. I think that the timescale of the tasks makes things difficult. It's not easy to think up a task that an inexperienced student can complete within five days, especially considering that they often need to get up to speed with the technologies before starting, and need to fit it in around schoolwork. Hopefully now that the Christmas holidays are beginning, students will have a lot more time to spend on the GHOP!

I apologise to any student who's been held up from starting further tasks because of delays in reviewing and closing the tasks. Doing a proper job takes quite a bit of time, and the Mono team has a lot of other important things to do too, like getting the shiny new Mono 1.2.6 and MonoDevelop 1.0 Beta 3 releases ready.

I'd like to put out a general request to everyone and anyone in the Mono community, or working with Mono-based apps, to lend a hand for the GHOP. You can submit proposals for tasks on the Mono GHOP issue tracker, and critique students' work. It doesn't matter if you can't write code — help with docs or icons. Go out and send students our way. This has the potential to be a huge community event. You can find us hanging out on #mono-ghop as well as the usual IRC channels on GIMPNet IRC.

MonoDevelop in the GHOP

So far, all five completed tasks for the Mono GHOP have been MonoDevelop -related. I'm really pleased with the amount of interest we've had so far. I'd like to thank our hard-working students and mentors, and the wonderful people at Google's Open-Source Program Office who put this contest together.

  • Dan Abramov submitted a very cleanly-written addin to integrate the Smokey code analysis tool . I'll be using this to tidy up minor bugs in the MonoDevelop codebase, and hopefully we can ship it in the addin repository after the 1.0 release.
  • This was followed by some fantastic icons from Vinicius Depizzol ("videpizzol"), and these will certainly be in the 1.0 release. They're not yet in SVN due to my pending review of our icon usage and handling in MonoDevelop.
  • The completed Brazilian Portuguese translation by Renato Felipe Atilio ("renatoat") is now our most up-to-date first translation, though due to changes to our translation infrastructure it's not merged into SVN yet.
  • The latest addition to our still-meagre documentation comes from "entereczek", a Tutorial for C/C++ Projects. I've put it on the MonoDevelop wiki.
  • Although not strictly a MonoDevelop contribution, "dwashing" created a MonoDevelop solution for F-Spot that's been committed to SVN.

Please continue to suggest, claim and review tasks. We still have plenty left on the issue tracker over at Google Code.Pop over to #mono-ghop on GIMPNet IRC to discuss things or to hang out with us.

Be careful using anonymous delegates across threads

[UPDATE]: It has been pointed out to me that the premise of this article is wrong. The compiler specifically avoids this problem by hoisting the captured variables into an inner class of which the generated method is also an instance member, and creating a new instance of this class for each call. I'm not sure what bug I ran into that causes the behaviour I denounced, or whether it was simply sloppy handling of reference types on my part -- but I feel particularly stupid having vaguely remembered "inner classes" from reading Raymond Chen's fine explanation of this over a year ago, yet not having checked it before posting this article. Nonetheless, I shall let the article remain as a reminder of the code that this wonderful feature saves you from having to write. Also, the queue/lock-based code may still be useful when one needs data passed to the GTK thread in a guaranteed order.

Anonymous delegates are incredibly useful, especially in that they can "capture" variables from a parent scope. Used within a single thread, they are very easy to understand. However, if you're using them to pass data across threads, you need to understand how the variable capture works.

Consider the following code snippet, in which the "data" variable is captured from the delegate's parent scope.

void ProcessIncomingData (string data)
{
	Gtk.Application.Invoke (delegate (object sender, EventArgs e) {
			textBuffer.AddText (data);
		});
}

What's wrong with this?

Let's expand the anonymous method into something resembling the compiler-generated code.

void ProcessIncomingData (string data)
{
	compilerHoistedVariable = name;
	Gtk.Application.Invoke (new EventHandler (compilerGeneratedMethod));
}
 
string compilerHoistedVariable = null;
 
void compilerGeneratedMethod (object sender, EventArgs e)
{
	textBuffer.AddText (compilerHoistedVariable);
}

Notice that the variable has been "hoisted" into the class scope, so that the generated method used for the anonymous delegate can access it. (The actual compiler-generated code is a bit different, encapsulating the hoisted variables in an inner class, but the principle is the same).

Now consider what happens if ProcessIncomingData gets called by the data-processing thread several times before the GTK+ loop gets run and can handle the invocations. Only the most recent value remains in the compiler-hoisted variable, and the earlier data strings are lost.

This isn't a problem if you're passing transient state, for example the visibility state of a button. But if you're passing data, think carefully about the internal mechanics. You may have to implement locking on the shared data structure.

void ProcessIncomingData (string data)
{
	lock (textBufferQueue) {
		textBufferQueue.Enqueue (data);
		if (!queueHandlerRunning) {
			queueHandlerRunning = true;
			Gtk.Application.Invoke (
				delegate (object sender, EventArgs e) {
					lock (textBufferQueue) {
						while (textBufferQueue.Count > 0) {
							textBuffer.AddText (textBufferQueue.Dequeue ());
						}
						queueHandlerRunning = false;
					}
				});
		}
	}
}
bool queueHandlerRunning = false;
Queue<string> textBufferQueue = new Queue<string> ();

This could hardly be described as "pretty", and the anonymous delegate has grown so big that we may as well move its definition out into a conventional method.

Things are easier if the delegate consumer offers an alternate call allowing you to pass arguments along with the delegate, such as Gtk.Application.Invoke and its Invoke (object sender, System.EventArgs args, System.EventHandler d) overload.

void ProcessIncomingData (string data)
{
	Gtk.Application.Invoke (
		this,
		new dataEventArgs (data),
		delegate (object sender, dataEventArgs e) {
			textBuffer.AddText (dataEventArgs.Data);
		});
}
 
class dataEventArgs : EventArgs
{
	public string Data;
	public dataEventArgs (string data)
	{
		this.Data = data;
	}
}

This is much more verbose than the method with which we started, but at least you won't lose any data and don't have to worry about locking.

Sadly, the anonymous types in C# 3.0 can't be used to simplify this. For a start, they don't allow inheritance. One might think that the "object sender" argument could be abused to pass an anonymous type, however in order to leave the scope of a method, the anonymous type must be cast to "object". The only way to access its properties would be via reflection, which would not only be verbose but would also carry a performance penalty.

How not to break Mono installations

It's a bad idea to mess with the packaged version of Mono on your Linux distro by installing another version of Mono on top of it or into another of your distro's standard prefixes such as /usr/local. Your distro's developers, testers and packagers have tested the packaged version of Mono to make sure that it works with the various applications that depend on it, such as MonoDevelop, Tomboy, F-Spot, Beagle and Banshee. In addition, you're likely to end up with unusual errors due to mismatched bits and pieces interacting in unpredictable ways.

If you need a more recent version in order to test new features and bugfixes, please keep a properly separated parallel Mono environment. By following these instructions you can ensure that you don't affect your stable Mono platform while experimenting with newer versions.

This applies to platform bindings such as GTK#, GtkSourceView# and GNOME# too!

GtkSourceView 2 in MonoDevelop

I recently added support for GtkSourceView 2 to MonoDevelop, and it can be enabled with the "--enable-gtksourceview2" configure switch. Unfortunately the Boo Binding and the Database Addin depend on GtkSourceView# directly, and aren't compatible with the API changes in 2.0. I may get round to fixing them later. However, I've already switched on GtkSourceView2 support in my MonoDevelop build repository. The main reason I did this, of course, was the wonderful dark grey colour scheme "oblivion" :-)

The GtkSourceView2# binding isn't officially released yet, and will probably get absorbed into the Gnome# platform bindings, but right now it seems to work fine.
Packages for openSUSE 10.3 are available from my MonoDevelop build repository.

Notable new features are:

  • Improved syntax highlighting
  • Support for colour schemes

Notable missing/removed features are:

  • Printing
  • Bookmarks (also used for debugger breakpoints, when the debugger worked)

There isn't a UI for changing colour schemes yet, but it can be done manually in the ~/.config/MonoDevelop/MonoDevelopProperties.xml file by setting/adding the property <Property key="GtkSourceViewStyleScheme" value="oblivion" /> within the property <Property key="MonoDevelop.TextEditor.Document.Document.DefaultDocumentAggregatorProperties">.

MonoDevelop trunk builds

Hot on the heels of Lluis's MonoDevelop 1.0 Beta 2 announcement, I'd like to announce the availability of frequent builds of MonoDevelop from SVN trunk, packaged for openSUSE 10.3. If you'd like to get the latest and greatest bug fixes and features but don't want to build MD yourself, head over to my openSUSE Build Service Sandbox. The packages will also come up in the openSUSE search for MonoDevelop packages.

Warning: these packages are not guaranteed to be stable, though I'll try to push only usable revisions. I may also enable and disable features at a whim, e.g. disabling the Boo binding and MonoDevelop.Database addin in order to enable GtkSourceView 2 support.

Update: the sandbox link currently requires that you're logged into the openSUSE Build Service, so here's a direct link to the repository.

Joined Novell, and moving to Boston

I apologise for not having updated my blog more often. Things have been quite hectic recently...

A month ago (as announced by Miguel de Icaza) I joined the fantastic Mono team at Novell, working on the MonoDevelop IDE. Since then I've been busy getting the ASP.NET Project features ready for MonoDevelop 1.0 Beta 1, which we finally released on Monday. It's essentially feature complete for 1.0, except for the "Web Deploy" feature that didn't quite make it into the beta but is currently in SVN trunk.

My "free" time has been taken up with preparing for my upcoming move to Boston, MA, USA this weekend. I'll soon be working with the cool people in the Novell offices in Cambridge! This move is a little daunting, as I've never been outside of Europe before, but I'm really excited about it.

International Talk Like Pirate Day

Fetch me spyglass! I just realised 'tis International Talk Like A Pirate Day and I ha'nae installed th' Drupal Pirate Filter on this incarnation o' me site.

Pages

Subscribe to Journal