Saturday, November 08, 2008

MonoTorrent 0.62

MonoTorrent 0.62 has now been released which addresses a few major and minor issues with the 0.60 release. Here's the details:

* Fixed regression in message handling which resulted in 0.60 not transferring properly. Caused by not running the right NUnit tests before tagging.
* Performance optimisation for sending/receiving messages
* Fixed bug creating torrents with 2gb+ files with TorrentCreator
* Fixed issue with udp sockets in the Dht code which could cause dht to stop sending/receiving messages

I'd highly recommend upgrading from 0.60 to 0.62 as soon as possible.

Binary - http://monotorrent.com/Files/0.62/monotorrent-0.62-bin.zip
Source - http://monotorrent.com/Files/0.62/monotorrent-0.62.tar.gz

Thursday, November 06, 2008

Are you mapping those dlls?

One of the issues with writing cross platform applications is that if you do P/Invoke into a native library, the name of that library changes depending on the OS. Mono has built in support for selecting the right file at runtime. The problem is that it's hard to ensure that you've correctly mapped all the methods you P/Invoke.

So I wrote a little tool to do that for you. It was inspired by an attempt by Aaron Bockover which parsed the raw cs files. I figured it'd be much better to just parse the compiled assembly ;)


using System;
using System.Collections.Generic;
using System.Xml;
using Mono.Cecil;
using System.IO;

namespace DllImportVerifier
{
class MainClass
{
static void Main (string [] args)
{
args = new string [] { Environment.CurrentDirectory };
if (args.Length != 1) {
Console.WriteLine ("You must pass the path to the assemblies to be processed as the argument");
return;
}

List<string> assemblies = new List<string> ();

if (Directory.Exists (args[0])) {
assemblies.AddRange (Directory.GetFiles (args [0], "*.dll"));
assemblies.AddRange (Directory.GetFiles (args [0], "*.exe"));
}
else if (File.Exists (args [0])) {
assemblies.Add (args [0]);
}
else {
Console.WriteLine ("{0} is not a valid file or directory", args [0]);
return;
}

foreach (string assembly in assemblies)
ProcessConfig (assembly);
}

static void ProcessConfig (string assemblyName)
{
AssemblyDefinition assembly = null;
try {
assembly = AssemblyFactory.GetAssembly (assemblyName);
} catch {
Console.WriteLine ("{0} is not a valid .NET assembly", Path.GetFileName (assemblyName));
return;
}

List<string> dlls = new List<string> ();
try {
XmlTextReader doc = new XmlTextReader (assemblyName + ".config");
while (doc.Read ()) {
if (doc.Name != "dllmap")
continue;

string dll = doc.GetAttribute ("dll");
if (!dlls.Contains (dll))
dlls.Add (dll);
}
} catch {
// Ignore malformed or invalid config files. If the config is malformed,
// drop any successfully parsed dll maps
dlls.Clear();
}

List<string> unreferenced = VerifyReferences (assembly, dlls);
foreach (string dll in unreferenced)
Console.WriteLine("Assembly: '{0}' references '{1}' without mapping it", Path.GetFileName (assemblyName), dll);
}

static List<string> VerifyReferences (AssemblyDefinition assembly, List<string> dlls)
{
List<string> unreferenced = new List<string> ();
foreach (TypeDefinition type in assembly.MainModule.Types) {
foreach (MethodDefinition method in type.Methods) {
if (!method.IsPInvokeImpl)
continue;

string dll = method.PInvokeInfo.Module.Name;
if (!dlls.Contains (dll) && !unreferenced.Contains (dll))
unreferenced.Add (dll);
}
}

return unreferenced;
}
}
}

Wednesday, November 05, 2008

BarrierHandle

Did you ever have a case where you have a big dataset which can be processed in parallel, so long as all threads finish Step 1 before any thread starts Step 2? Thing is, there's no built in class to handle this case.

AutoResetEvent won't do it because it because it only signals *one* of the waiting threads, not *all* of them. What you need is a manual reset event and some complex handling.

I give you BarrierHandle:


using System;
using System.Threading;

class SpectralNorm
{
public class BarrierHandle : System.Threading.WaitHandle
{
int current;
int threads;
ManualResetEvent handle = new ManualResetEvent (false);

public BarrierHandle (int threads)
{
this.current = threads;
this.threads = threads;
}

public override bool WaitOne()
{
ManualResetEvent h = handle;
if (Interlocked.Decrement (ref current) > 0) {
h.WaitOne ();
}
else {
handle = new ManualResetEvent (false);
Interlocked.Exchange (ref current, threads);
h.Set ();
h.Close ();
}

return true;
}
}

public static void Main(String[] args)
{
int threadCount = 5;//Environment.ProcessorCount;

// Lets assume that there's 20 units of data for each thread
int[] dataset = new int [threadCount * 20];

// This is the handle we use to ensure all threads complete the current step
// before continuing to the next step
BarrierHandle barrier = new BarrierHandle(threadCount);

// Fire up the threads
for (int i = 0; i < threadCount; i++) {
ThreadPool.QueueUserWorkItem (delegate {
int jjj = i;
try {
Step1 (dataset, jjj * 20, 20);
barrier.WaitOne ();
Step2 (dataset, jjj * 20, 20);
barrier.WaitOne ();
Step3 (dataset, jjj * 20, 20);
} catch (Exception ex) {
Console.WriteLine (ex);
}
});
}

System.Threading.Thread.Sleep (3000);
}

private static void Step1 (int[] array, int offset, int count)
{
Console.WriteLine ("Step1: {0} - {1}", offset, offset + count);
Thread.Sleep (500);
}
private static void Step2 (int[] array, int offset, int count)
{
Console.WriteLine ("Step2: {0} - {1}", offset, offset + count);
Thread.Sleep (500);
}
private static void Step3 (int[] array, int offset, int count)
{
Console.WriteLine ("Step3: {0} - {1}", offset, offset + count);
Thread.Sleep (500);
}
}

Monday, November 03, 2008

MonoTorrent 0.60

So here are the bugfixes and features for 0.60 as compared to 0.50:

Bug fixes:
* Fixed critical regression in 0.50 whereby transfers would be incredibly slow.
* Fixed issue where announce/scrape requests to offline trackers may never time out
* Added a few memory optimisations when reading piece data from disk.
* Add the ability to report an alternate IP/Port combo to the tracker
* Optimised the encrypted handshake - its now a bit faster
* Fixed bug where wrong message ID was sent for extension messages
* Fixed bug where pieces which do not exist would be requested from webseeds.
* Fixed several bugs in TorrentCreator class where relative paths were used instead of absolute paths.
* Disabled UdpTracker support as the current implementation locked up until a response was received.
* Fixed a possible issue with multi-tier torrents.
* Added the ability to tell whether peers come from PeerExchange, DHT or the tracker.
* Some big performance boosts for webseeds by using a better method of picking pieces.
* Fixed a corner case whereby certain torrents may not get their last piece requested.
* When hosting a tracker,"/announce" is appended when you use the construtor overload which takes an IPEndPoint.

New Features:
* Implemented DHT support.
* Implemented sparse file support on the NTFS file system.

All in all, an awesome release. I have to give a shout out to Matthew Raymer who has been tirelessly testing MonoTorrent for the last few weeks. Without his constant bug reports, some of those bugs would never have been found, such as the one with UdpTrackers. Great stuff.

Packages:
Binary: http://www.monotorrent.com/Files/0.60/monotorrent-0.60-bin.zip
Source: http://www.monotorrent.com/Files/0.60/monotorrent-0.60.tar.gz

There are also packages for openSUSE in the openSUSE Build Service for those of you interested in those.

Hit Counter