Monday, July 8, 2013

Google Summer of Code Week 3

Last week I got less work done than the previous ones. That said, I still got some things to show.

First of all, I finally managed to complete Extract Enum, though I ended up moving it back to MonoDevelop.

The main problem o DoGlobalOperationOn is the lack of NRefactory unit testing infrastruture. Whereas most actions and issues are easily tested and, therefore, require only minimal testing in the IDE, DoGlobalOperationOn must be manually tested all the way, which is massively inconvenient.

Also, not all extract enum features are fully available yet. It is not yet possible to choose which fields to include in the new enum due to a bug in Xwt.Gtk. Once that's fixed, I believe everything will work as expected.

I also investigated ILSpy to see how easy it would be to reuse code from it to implement a new code action: Convert LINQ fluent syntax to LINQ query. This would essentially be the opposite action of what I made last week.

The portion of ILSpy I studied was the decompiler. The decompiler takes .NET code (e.g. an executable or class library) and generates C# code back from it. It includes a variety of features including the ability to convert generated LINQ code back to LINQ queries.
The decompiler of LINQ queries seems to work in two phases: First, it generates simple queries, and then combines those queries:
list.Where(x => x > 0).Select(y = > y * 2);
//After phase 1
from y in from x in list
          where x > 0
          select x
select y * 2;
//After phase 2
from x in list
where x > 0
into y
select y * 2;

The code for the first phase (introduce queries) seems pretty solid, but the code for the second phase is far less so. It depends heavily on the particular output of the decompiler and, as such, is unsuitable for use in user code.

This week, I'll try to implement global operation unit testing and some other actions.

Monday, July 1, 2013

Code reuse and Global Operations - Google Summer of Code Week 2

This is the second week of my Google Summer of Code project. Last week, I implemented only two small and simple code actions. This week, I got a bit more ambitious.
Technically, some of this work was finished after the Midnight of Sunday, I'll leave it to the reader to decide if that's "cheating".

Null coallescing and pattern matching

The first action I'll be talking about is ConvertIfToNullCoalescing action.
//This action takes this:
object x = Foo();
if (x == null) x = Bar();

//And replaces it by this:
object x = Foo() ?? Bar();
This is a very simple action - similar to the stuff I wrote last week.
What changes this time is the heavy use of pattern matching.

With pattern matching, it's possible to define a certain pattern (e.g. if (someExpression) doSomething();) and check if a node matches that pattern. For instance, the pattern if (<Any>) { <Any> } recognizes if (true) { return 0; }, but not while (true) {} nor if (true) return 0;.

It's also possible to have named nodes. With named names, it's possible to know what matched certain parts of the pattern. For instance, using pattern if (<condition> = <Any>) {} to match if (true) {}, named nodes tell us that the condition is true.

Of course, this is a very simple example. Pattern matching gets more useful in more complex cases.
Also, note that patterns are created as normal C# objects - the string representation I used above was merely intended as an example.

Dispose and IDisposable

Another simple action I did was search for types with a void Dispose() method that didn't implement IDisposable. This issue detects and fixes the problem for classes, though it is disabled for interfaces.
public class Test
{
    public void Dispose() {}
}
//Is converted to
public class Test : System.IDisposable
{
    public void Dispose() {}
}
The issue is disabled for interfaces. I initially implemented that feature but ultimately decided to remove it due to a problematic edge case with explicit implementation:
interface IA {
    void Dispose();
}
class B : IA {
    void IA.Dispose() {}
}
//Let's say the action converted this to:
interface IA : System.IDisposable
{
}
class B : IA {
    void IA.Dispose() {} //ERROR here
}
The only way for this action to be effective for explicit interfaces would be to use a global operations, and I only figured out how to do this effectively on Sunday. More on global operations later.

LINQ Query and LINQ Fluent

One of the best features of .NET and C# is LINQ.
LINQ brings a declarative/functional taste to C# and greatly improves the readability of list operations - like SQL but better.

There are many ways to use LINQ in C#.
  1. One could use the methods of System.Linq.Enumerable, such as Enumerable.Where(myEnumerable, SomeFunction). This could be acceptable if there was no better way of doing things -- which there is;
  2. Use those same methods from Enumerable but in a different way. Those are extension methods and, therefore, can be used as myEnumerable.Where(SomeFunction). This is much better than the first option, especially because it's a Fluent Interface and therefore method calls can be chained -- myEnumerable.Where(Foo).Select(Bar)
  3. The last option is the query syntax. Instead of using the method calls, the programmer can use special syntax designed specifically for this purpose. More on this later
Query syntax is translated by the C# compiler to fluent syntax.
var seq = from item in myEnumerable
          where Foo(item)
          select Bar(item);

//Is converted to:
var seq = myEnumerable.Where(item => Foo(item)).Select(item => Bar(item));
Because query syntax is blindly converted to method calls, it can be used for a lot more than just Enumerables. Anything with the correct methods (e.g. Where and Select) can be used with query syntax, it doesn't need to be an extension method - a normal instance method works just fine.

And, in fact, the argument doesn't even need to be of type System.Func.

LINQ is not just used for enumerables. Some of the best uses of LINQ are related to databases.
int idToFind = 10;
var userWithId = from user in database.Users
                 where user.Id == idToFind
                 select new { user.Name, user.Score };
Console.WriteLine("User {0} has {1} point(s).", userWithId.Name, userWithId.Score);
//The query is translated to
int idToFind = 10;
var userWithId = database.Users.Where(user => user.Id == idToFind)
                     .Select(user => new { user.Name, user.Score });
Console.WriteLine("User {0} has {1} point(s).", userWithId.Name, userWithId.Score);
In this case, database.Users is not an enumerable. It's an instance of IQueryable.

Instances of IQueryable actually read the contents of the passed lambdas -- they use Expression Trees. And it works just fine.

So which is best? Query syntax or fluent syntax? It depends.
In some cases, fluent syntax is the simplest and most compact solution.
In other cases, query syntax is the most readable option - since joins and other complex queries do not involve complex method calls with anonymous types.

The action I implemented converts query syntax to fluent syntax. It should be capable of handing any query. So, whenever a query would be best written with fluent syntax, there's no need to manually convert it (and risking introducing new bugs!) - just let the code action do it.

This code action turned out to require less effort than I expected, since NRefactory already had a class to do what I needed - QueryExpressionExpander. Sadly, I didn't know about that class, so I ended up reading the relevant portion of the C# specification and writing my own code. Later, when I found out about QueryExpressionExpander, I rewrote a significant portion of my own work to use it - fixing a bug in NRefactory and adding a feature to QueryExpressionExpander along the way.

As it turns out, the C# specification is surprisingly readable. I was expecting an impenetrable wall of technical text but instead I was greeted by a text with lots of examples and simple to understand.

Convert to Enum - Global Operations

The last action I wrote this week is again a tale of finding the right class to use.

Anyway, the story of this action started with an old bug report (and by old I mean half a year old). An user with lots of Java code automatically converted to C# had lots of fields like public const int SOME_PREFIX_SOME_NAME and wanted to convert all those fields to enumerations. Code actions to the rescue!
//We have this
public const int SOME_PREFIX_A = 1;
public const int SOME_PREFIX_B = 2;
//We want this
public enum SOME_PREFIX : int
{
    A = 1,
    B = 2
}
I think it's not an overstatement to say this was the hardest action to implement so far. And I'm not even sure I'm done yet.

The main problem with this action that references to SOME_PREFIX_A become references to SOME_PREFIX.A. This means changing files besides the one with the field declaration.
It's not a simple find-and-replace either. We don't want to change strings nor comments. And we don't want to replace different fields with the same name.
As if things weren't bad enough, enumerations aren't implicitly cast to integers. The "perfect" version of this action would also replace method parameter types. This would soon become tremendously complex.
I opted to develop a more limited version of this conversion. Instead of replacing types everywhere, this just adds a type cast to the enum underlying type. So instead of SOME_PREFIX.A, we'd have ((int) SOME_PREFIX.A).

So, back to global operations. A global operation is an operation that changes files other than the current one. NRefactory has a method just for that: script.DoGlobalOperationOn.

Now, it would be just fine if  I were to just use that and be done with it. Unfortunately, I only discovered that method (and what it really did) very late. Before finding it out, I had to go on a tour discovering MonoDevelop source code. While the information I learned has helped me understand MonoDevelop better, I went full circle and ended up where I started - in NRefactory.

This code action is still not fully tested, so it might not work perfectly in some edge cases yet.

Conclusion

This week's work was harder than last week's. Still, I managed to get most of my planned work done.
Additionally, I'm gradually learning about new APIs of NRefactory, so I expect things to get easier as I get more experienced.

The main problem this week was spending time figuring out how to do things, only to find out there was an API just for that. Fortunately, the number of APIs in NRefactory is finite, so I'm hoping this situation won't last forever.

See Also

Google Summer of Code - Week 1
Last week's post

mono-soc-2013/NRefactory
As usual, my work is available in the mono-soc-2013 NRefactory github repository. Check the branches with names starting with "luiscubal-". The exception is code that's been merged back into the master branch of the official repository. Those branches have been deleted.

Download C# Language Specification 5.0 from Official Microsoft Download Center
The spec also comes bundled with Visual Studio (except Express). So if you already have it installed, just go to the VC#\Specifications\1033 folder. The exact location of this folder will depend on where you decided to install Visual Studio, but Program Files is a good place to start the search.

Sunday, June 23, 2013

Google Summer of Code - Week 1

First of all, the good news: I have been accepted into Google Summer of Code 2013. I'll be working with MonoDevelop and NRefactory to add new source analysis features.
I'll start by explaining what these projects are, then I'll show my work this week and finish with a commentary of what I saw and learned this week.

An Introduction

MonoDevelop is a cross-platform IDE that supports several programming languages. Among those languages, the most important one is C#.

NRefactory is a C# library that allows applications to parse and manipulate C# source code (it also supports a few other languages, but my project won't cover those).
With NRefactory, it is possible to analyze source code for patterns and programmatically modify the code. This can be extremely helpful when refactoring.

NRefactory includes code actions and code issues.

Actions are simple ways to modify code to something slightly different. For instance, there is a code action to turn a variable declaration with type inference into a variable declaration with the explicit type.

//This:
var x = new List<int>();
//Becomes:
List<int> x = new List<int>();

Issues detect anti-patterns: Code that is likely hard to maintain and a potential source of bugs, or just poor taste - often, issues can be automatically fixed.
For instance, there is an issue that detects when a variable is assigned to itself.

class SomeType
{
    int x;

    public Constructor(int x) {
        x = x; // <-- This is useless, the programmer probably meant this.x = x
    }
}

Actions and issues are not the only features of NRefactory. It can be used to implement code completion, format code and pretty much anything that requires code manipulation.

MonoDevelop integrates NRefactory to provide a first-class programming experience. Right-clicking the code shows the applicable issues and actions. Issues are harder to ignore than actions, since the IDE will show those more prominently - bad code usually appears with a curly green underline.

Before proceeding, it might be a good idea to read Using NRefactory for analysing C# code on CodeProject. This post will be easier to understand if you do.

My Project - Week1

This summer, I will be adding new source analysis features to NRefactory and MonoDevelop. This is officially my first week of work. Because I've been busy with exams, I haven't been able to fully dedicate myself to this project. But still, I did manage to get some decent work done.

This week, I implemented one code issue, one code action and a bug fix.

lock(this) and MethodImplOptions.Synchronized


The code issue detects a synchronization anti-pattern.

Proper synchronization is a critical - yet hard - part of multi-threading. When synchronization is done right, threads work correctly. Poorly done synchronization can cause lots of headaches (see deadlocks and race conditions).

Because synchronization is so error-prone, it should be as surprise-free as possible. Synchronization code should be straightforward and have as few "gotchas" as possible - ideally, none.

If it's not obvious what code is locking what mutexes, then the code really isn't straightforward. If some random programmer using a library can lock a mutex used by the library and the original library author doesn't know about it - that can be a huge problem.

In C#, locking is done with the lock statement. Here's an example of what it looks like:

class SynchronizedExample
{
    string someString = string.Empty;
    public string SomeString {
        get { return someString; }
    }

    public void AddToString(string stringToAdd) {
        lock (this) { //this line is bad, we'll soon see why
            someString += stringToAdd;
        }
    }
}
The code above ensures there are no race conditions when appending data to a string. The this object is used as a mutex so two different objects can access their own strings simultaneously in different threads, but multiple threads can't append to the same string at the same time - if they try, one of them is forced to wait.

lock(this) is not a good idea.

The reason is that anyone with a SynchronizedExample instance can lock on it.
var example = new SynchronizedExample();
lock (example) { ... }

In practice, this means the original author of SynchronizedExample can never really know when the mutex is locked. This code is ticking bomb. It isn't blowing up, but it could at any time.

Ticking bombs are good candidates for code issues. The new LockThisIssue detects this problem and suggests a fix. The code above is underlined and right-clicking shows a message warning the programmer that lock(this) is bad. Clicking on auto-fix turns it into this:

class SynchronizedExample
{
    string someString = string.Empty;
    public string SomeString {
        get { return someString; }
    }

    object locker = new object();
    public void AddToString(string stringToAdd) {
        lock (locker) {
            someString += stringToAdd;
        }
    }
}

Well, the comment wouldn't be deleted, but other than that, everything is fine now. Synchronization remains correct, but now it's gotcha-free. No unpleasant surprises waiting to happen.

This is not all this issue corrects.

.NET has an attribute called MethodImplAttribute. This attribute exposes several features. One of those features is synchronization.
Exactly why .NET has this "feature" is unclear but, in short, it is roughly equivalent to adding a lock (this) statement that includes the entire method.
It sucked before, it still sucks now. So the issue detects the problem, removes or fixes the attribute and adds the proper lock statement.

Auto-Linq Sum


As I said before, actions allow transforming code. The code doesn't have to be a red flag. Actions can just show an alternative. Use it if you like the alternative, ignore it if you don't.

The action I implemented converts some foreach loops to LINQ expressions.

int result = 0;
foreach (int x in intList) {
    result += x;
}
//Is converted to:
int result = intList.Sum();


It also supports if/else statements in the loop, multiple statements and more complex expressions.

int result = 0;
foreach (int x in intList) {
    if (x > 0) {
        result += x * 2;
    }
}
//Is converted to:
int result = intList.Where(x => x > 0).Sum(x => x * 2);
Using a loop is not wrong, so it's not a code issue. But I believe loop-to-LINQ is still a refactoring tool worth having.

Final Notes

Both MonoDevelop and NRefactory are interesting projects and I expect my work to make it to future versions. In fact, the two major features I described in this post have already been merged into master.

Here are a few of the things I've found:

git submodules: Essentially, when Project A depends on Project B, the git repository for Project A can include the git repository for Project B as a submodule. The submodules have their own separate commits and branches - they are essentially independent since they are a separate repository. This seems like a good way to handle dependencies.

Unit Tests: NRefactory has the most extensive unit test suite I've ever seen and it's pretty cool. So far, I haven't had many opportunities to see unit tests in action. Most of the unit tests I've seen were really just examples, but NRefactory is a real-world case that really proves how much tests can help.
For instance, when testing a code action, the programmer can say which action to test, the original source code and the intended output. If they match, the test passes. Otherwise, the test fails.
One can just add a bunch of normal cases, a few harder ones (for edge cases) and tests to detect potential problems. NRefactory was the first time I ever used Test-Driven Development in a real-world scenario. I've found myself writing tests when the code action was merely a stub and I'm satisfied with the results.

Linking: The API does not always behave the way I'd expect it to behave. I've been bitten in a few ways since I started, though I'm getting used to the details quickly.
One example is linking: script.Link is a neat feature. It tells the IDE that some identifiers are actually one and the same.
If an action creates lots of statements in the code and they all refer to the same variable, Link lets the user choose a name and the whole code will be fixed to use that name.
Now, Link is not supposed to be applied to code that's been removed. This is perfectly acceptable (why change what no longer exists?), but nothing in the code detects when it happens accidentally. It is what I'd call "undefined behavior".
The tests just silently ignore it. Everything will seem to work except for some puzzling extra whitespace that shouldn't exist. But in MonoDevelop, that same code will behave differently - and generate code that's just wrong. Of course, the solution is not to use it that way, but again that does not make debugging easy when it happens by accident.
See the bug report (pictures included).

Edge cases: C# is a big language with lots of features. This is great for the programmer, but it does mean that tools such as NRefactory must be tested in lots of edge cases. In fact, pretty much all bugs I found in NRefactory (the ones that weren't my fault, that is) were related to features the original author just didn't think of when writing the code: type inference, anonymous types, extension methods, generics and the many things that can be on the right side of a lambda expression (is it a statement? An expression? Is is a method with void return type?).

This concludes my work for the week.

Further Reading

Official NRefactory github repository (icsharpcode/NRefactory)
This is where the code for NRefactory is.

Mono Summer of Code 2013 NRefactory github repository (mono-soc-2013/NRefactory)
The code for this year's mono summer of code projects. My branches are prefixed with "luiscubal-" so they should be easy to find. Once my pull requests are accepted (or no longer applicable), my corresponding branch is deleted, so the work I described here is found on the official NRefactory repository.

Mike's MonoDevelop Blog: How To: Write a C# Issue Provider
This post explains how to create a new code issue for NRefactory

Source Analysis Improvements in MonoDevelop - Project details
My summer of code project page. Very little to see here, move along. (I should probably improve it... one of these days)

.net - What does MethodImplOptions.Synchronized do? - Stack Overflow
A very brief and oversimplified explanation of what MethodImplOptions.Synchronized does.

Sunday, April 21, 2013

Asynchronous OpenGL texture loading with C# and OpenTK

Games often have a large quantity of resources to load. When streaming is not an option (or not a good option), loading screens are one common solution.
Loading screens should be as short as possible (to reduce player frustration) and should include some sort of animation to let the player know that the game isn't frozen.
This post focus on two questions:
  • How can we take advantage of multi-core processors to reduce loading times?
  • How can we create/load OpenGL textures (and other OpenGL resources) and render something to the screen at the same time?
We'll be exploring the following methods to implement loading screens:
  • Synchronous texture loading.
  • Single-threaded alternating texture loading.
  • Helper loading thread.
  • Multi-threaded loading with Parallel.ForEach.
  • Task-based loading with TaskCompletionSource or a custom TaskScheduler.

Required Knowledge

  • The code is written in C#, so basic knowledge of C# is necessary.
  • Basic OpenGL knowledge would help.  We'll be using OpenTK in this post - a .NET library that allows C# programs to use OpenGL (and a few other APIs as well).

Synchronous Texture Loading

First, we'll explore how a synchronous texture loading system could work, its advantages and its shortcomings.
To create a texture in OpenGL, we need to follow these steps:
  1. Read the contents of the texture file
  2. Decode them
  3. Create a new OpenGL texture name (see glGenTextures).
  4. Send the texture data to OpenGL with glTexImage2D.
  5. Set the image parameters.
  6. Release the data of the texture file we loaded.
To load and decode textures, we'll use the System.Drawing.Bitmap class.
using (var bitmap = new Bitmap(filename)) {
    width = bitmap.Width;
    height = bitmap.Height;
    textureId = GL.GenTexture();
    GL.BindTexture(TextureTarget.Texture2D, textureId);
    var bitmapData = bitmap.LockBits(
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
        System.Drawing.Imaging.ImageLockMode.ReadOnly,
        System.Drawing.Imaging.PixelFormat.Format32bppArgb);
    try
    {
        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
            bitmap.Width, bitmap.Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte,
            bitmapData.Scan0);
    }
    finally
    {
        bitmap.UnlockBits(bitmapData);
    }
    GL.TexParameter(TextureTarget.Texture2D,
        TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
    GL.TexParameter(TextureTarget.Texture2D,
        TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
}
This approach is simple. And simplicity is a feature.
The main problem of this approach is that it is blocking. While the textures are being loaded, nothing else can be executed. That includes rendering.
Until all textures are loaded, the game freezes. This is undesirable.
I've tested this method to load 1500 textures and it took around 2200ms. That's two seconds waiting for something to happen with no visual feedback.

Loading Screen

So now the big question is how to render and load at the same time.
One possible answer is to load a few textures per frame.
For instance, if we have 1500 textures to load, we could load 50 each frame and render normally.
After 30 frames, the loading would be over.

This approach works because we never load too many textures in each frame (and each texture individually takes only a short time to load) so the loading screen is somewhat smooth.
Since it's just a loading screen, it probably won't have anything too heavy anyway and frame rate drops are acceptable.

I haven't measured this method, but I'd expect it to take a bit longer than the blocking one.

But we still have one remaining question: How can we use multi-core processors to speed this up?

Concurrency

There are many ways to implement concurrency.
The best known method is probably multi-threading.
Essentially, if you want to execute three tasks at once you create three threads. Each thread then executes a task.
All running programs have at least one thread.
The thing about threads is that aside from sharing memory, they are essentially independent of each other. This is, of course, a feature. But thinking about multiple pieces of code executing at the same time tends to be harder than thinking about the equivalent single-threaded code, since threads introduce several brand new types of errors.
As a result, it is currently harder to write correct multi-threaded programs than to write correct single-threaded programs.
So why bother?
While the usefulness of threads is somewhat limited (but still existent) in single-core processors, it's on multi-core (or at least hyper-threaded) processors that threads really shine.
Typically, the operating system will assign threads to processor cores. Often, we'll have different threads running on different cores. It's when this happens that we get true parallelism (as opposed to preemptive multitasking). In theory, parallelism can speed up programs up to N times (where N is the number of cores). EDIT: In some rare cases, it can be higher than N - See super-linear speedup.
In practice, however, not all tasks can be parallelized and multithreading adds some overhead - so the speed up is typically smaller than that. How much smaller? It depends. In some cases, the multi-threaded program might actually be slower than the single-threaded one.

So how do we use threads to improve our loading screen?
The first option is to create a helper thread that does the loading, while our main thread is rendering.
This approach essentially offers very little advantage over our load-few-textures-per-thread.
  • It can only use up to two cores - so if we have more than that tough luck.
  • Only one core does the loading. We can expect one of the cores to be significantly busier than the other one. So our program won't be anywhere near twice as fast as the first one. I haven't really tested this approach, but I expect it to take around 2200ms as well (the time the blocking method takes). Perhaps a bit longer, due to the overhead of multi-threading.
The first issue we'd encounter if we tried this method is that OpenGL would refuse to load our textures from the loader thread.
To understand why, we need to understand what OpenGL contexts are.

OpenGL contexts

A context represents the state of OpenGL and all sorts of info that OpenGL needs to know in order to do anything.
That means no OpenGL command can be executed without a context.
Many libraries isolate the creation of a context from the programmer so that OpenGL can be used in blissful ignorance.
All that bliss comes crashing down when threads are introduced.

Each thread can only have one context (called the current context) and each context can only be current to one thread. A context doesn't necessarily have to be the current context of any thread, but a context that's never made current is worthless.
Changing the current context of a thread is an expensive operation and, as such, should be avoided as much as possible.

Contexts can be independent of each other or they can share resources such as textures. By default, context sharing is enabled in OpenTK.

So for our loader thread proposal to work, we'd have to create a new OpenGL context and make it current for the loader thread. Then it could work.
But at this point, we still don't take full advantage of multi-threading.

Parallel.ForEach

Threads incur overhead. That much is a fact. This overhead can be mitigated but never eliminated.
This overhead is acceptable when the gains are enough to exceed it. But the gains of multi-threading are limited.
Let's simplify the problem and ignore the whole mess of I/O bound vs CPU bound and hyperthreading. Then we can say that if there are more threads than processor cores, the threads can't be all run in parallel. We get some parallelism, but the operating system has to use other methods to ensure that all threads get to run.
Then we still pay the cost of threads, but performance starts to degrade.
So let's say using 10 threads gives us the best result, but we have 1000 textures to load. How do we split work across the 10 threads?
The obvious answer is "let each thread load 100 textures each".

But not all textures necessarily take the same time to load. One small texture will typically be faster to load than one large texture. What is a thread gets 100 fast textures and one thread gets 100 slow textures? Clearly, we'd not be taking advantage of threads properly.
And how do we even know 10 threads are the best?

These are all hard problems.
In .NET, this problem was solved with the introduction of the Task Parallel Library. There's no need to reinvent the wheel in this case.

Here's how the code would look like:
//Sequential code
foreach (var textureToLoad in texturesToLoad) {
    LoadTexture(textureToLoad);
}

//Parallel code
Parallel.ForEach(texturesToLoad, textureToLoad => {
    LoadTexture(textureToLoad);
});
This example assumes, of course, that LoadTexture is thread-safe. In my own code, I decided to store all loaded textures in a Dictionary. However, Dictionary is not thread-safe, so I had to replace it with a ConcurrentDictionary. Always be careful about that sort of things when writing multithreaded code.
Although we could rewrite this example using PLINQ Select, I've decided to stick to Parallel.ForEach because we're not done yet. We're going to use a different Parallel.ForEach overload - and I'm unaware of any PLINQ equivalent.

The first problem is, once again, OpenGL contexts. Since Parallel.ForEach may use multiple threads, we need to set a new context for each thread. And, of course, destroy the context when we're done.

The second problem is that Parallel.ForEach is blocking. Even though it runs in parallel, it still makes our main thread wait for all the worker threads to finish their work. We do not want this.
Here's the fixed code:
//We'll be covering what the Task class is and what it does soon.
Task.Factory.StartNew(() =>
{
    Parallel.ForEach(texturesToLoad,
    () =>
    {
        //This is the code to initialize each new thread
        var window = new GameWindow(width, height, graphicsMode, "", gameWindowFlags);
        window.MakeCurrent();

        return window;
    },
    (textureToLoad, state, window) =>
    {
        //This is the code to load each texture
        LoadTexture(textureToLoad);

        return window;
    },
    (window) =>
    {
        //This is the code to finalize each thread
        window.Dispose();
    });
});
This solves our problem and takes advantage of parallelism to achieve faster results.
In my own tests, loading 1500 textures (the same textures we've loaded before) takes almost 2000ms. This is faster than our previous code but still somewhat disappointing.
We can do better than this.

Tasks and continuations

We've used Task.Factory before, but now it's time to really cover what it is.

Essentially, a Task is a unit of work. The best part about using tasks is that it opens up the way to use C# 5 syntax sugar that makes asynchronous programming less painful to use.
Though the prospect of using syntax sugar in the future is appealing, we will not actually be doing that today.
Today, we're going low-level.

So, how do we create a task anyway?
We've created a task in our example above using Task.Factory.StartNew. This is the simplest way to do it.
Task.Factory.StartNew(() => {
    Console.WriteLine("Hello from task");
});
The example above "just works". Once that code is executed, it'll create a task that's (potentially) executed in parallel.
Notice that the task is started as soon as it's created. This behavior is not strictly required. It's possible to construct a Task that's not immediately started with "new Task(action)", but by convention all functions that return tasks start them unless there's a really good reason not to.

At this point, Tasks don't do much we couldn't already do with Parallel.ForEach, with the added inconvenience that we can't set the OpenGL context of each thread.

For tasks to become more useful, we first need to introduce the concept of task continuations.
The purpose of task continuations is to allow performing some action only after a task has been completed.
Task.Factory.StartNew(() => 3)
    .ContinueWith(task => Console.WriteLine(task.Result));
In the example above, first we launch a task that returns 3 and we indicate that when that task is completed, we want to print the result in the screen.
With this, we can create tasks that depend on other tasks.
Task.Factory.StartNew(() => 3)
    .ContinueWith(task => {
        Task.Factory.StartNew(() => {
            Console.WriteLine(task.Result);
        });
    });
This example launches a second task after the first one has been completed. We'll soon see why this matters.

Loading textures in two steps

We'll now be trying a different approach to load our textures.
Up until now, we've been trying to parallelize the entire loading process. Now, we'll instead parallelize only a portion of the work.
Because we're parallelizing less work, we could expect performance to degrade but the opposite seems to happen.

Let's go back to the steps required to load a texture:
  1. Read the contents of the texture file
  2. Decode them
  3. Create a new OpenGL texture name.
  4. Send the texture data to OpenGL.
  5. Set the image parameters.
  6. Release the data of the texture file we loaded.
The most expensive steps are the first two. What if we parallelized only these and kept the remaining ones single threaded?
foreach (var textureToLoad in texturesToLoad)
{
    var baseTask = new Task<Bitmap>(() =>
    {
        var bitmap = new Bitmap(textureToLoad.AsFilename());
        return bitmap;
    });

    baseTask.ContinueWith(task =>
    {
        ScheduleToBeLoadedInMainThread(() => {
            var bitmap = task.Result;

            LoadTextureFromBitmap(bitmap);

            bitmap.Dispose();
        });
    });

    baseTask.Start();
}
For each texture to load, we launch a new task. Once that task is completed, we run a second operation, which schedules the bitmap to be loaded later by the main thread.

Now, we combine this approach with our alternating loading approach.
We go to UpdateFrame method and execute up to N actions (load up to N textures). The exact value of N depends on how much work you're willing to let each individual frame do. In my tests, I've used N=50.
We'll soon see how we can schedule work to be run in main thread.

Right now, we'll focus on a different problem. How to tell when we've finished loading.
If we're in a loading screen, we want to know we've finished loading so we can move on to the next screen.
For this, I've used a counter that indicates how many operations are left. When this counter reaches 0, we know we're done. Because the counter is read and modified by multiple threads, we must be careful and use atomic operations as necessary.
int counter = 0;
var baseTasks = new List<Task>();
foreach (var textureToLoad in texturesToLoad)
{
    var baseTask = new Task<Bitmap>(() =>
    {
        var bitmap = new Bitmap(textureToLoad.AsFilename());
        return bitmap;
    });

    baseTask.ContinueWith(task =>
    {
        ScheduleToBeLoadedInMainThread(() => {
            var bitmap = task.Result;

            LoadTextureFromBitmap(bitmap);

            bitmap.Dispose();

            if (Interlocked.Decrement(ref counter) == 0)
            {
                //We have no further textures to load
                ready = true;
            }
        });
    });

    baseTasks.Add(baseTask);
}

counter = baseTasks.Count;

foreach (var baseTask in baseTasks)
{
    baseTask.Start();
}
This is good and all, but the ready variable does not really allow the user of this code to fully take advantage of tasks. For instance, what if we want to add a continuation to run after we've finished loading our threads?

TaskCompletionSource

TaskCompletionSource (TCS, for short) is enables a whole new way to create tasks.

Up until now, we would start a new task by giving .NET a new action to run. The code would execute and then the task continuations would be executed.

With TCS, we tell C# "Hey, we're doing some stuff. We'll let you know when we're done and what the result was."
So while the tasks we've used up until now were somewhat active - they decided when they were done - the TCS paradigm is more passive - we decide when they are done.

TCS allows us to run code using a completely custom mechanism, and notify C# when we're done to allow continuations to be executed as they normally would.
public static Task WaitOneSecond() {
    var tcs = new TaskCompletionSource<object>();

    new Thread(() => {
        Thread.Sleep(1000);
        tcs.SetResult(null);
    }).Start();

    return tcs.Task;
}
Let's study the example above.
This function creates a new pending task. Then, we create a new thread that sleeps for one second and lets C# know when the waiting time is over.
We use TaskCompletionSource<object> because C# doesn't offer a non-generic TaskCompletionSource. TaskCompletionSource<object>.Task gives us an instance of Task<object> which can, fortunately, be implicitly cast to Task.
Note that we don't really "start" the task because it's up to us to decide how and when the task is executed.

We could use this function like this:
WaitOneSecond().ContinueWith(task => { Console.WriteLine("We're done!"); });
This code snippet waits one second and then prints a message on the screen.

In order to use TCS in our texture loader code, we only need to make very small adjustments.
(EDIT
int counter = 0;

var tcs = new TaskCompletionSource<object>();

var baseTasks = new List<Task>();
foreach (var textureToLoad in texturesToLoad)
{
    var baseTask = new Task<Bitmap>(() =>
    {
        var bitmap = new Bitmap(textureToLoad.AsFilename());
        return bitmap;
    });

    baseTask.ContinueWith(task =>
    {
        ScheduleToBeLoadedInMainThread(() => {
            var bitmap = task.Result;

            LoadTextureFromBitmap(bitmap);

            bitmap.Dispose();

            if (Interlocked.Decrement(ref counter) == 0)
            {
                //We have no further textures to load
                tcs.SetResult(null); //Tell TCS we're done!
            }
        });
    });

    baseTasks.Add(baseTask);
}

counter = baseTasks.Count;

foreach (var baseTask in baseTasks)
{
    baseTask.Start();
}

return tcs.Task;
And we're done.

There's one last missing piece. The ScheduleToBeLoadedInMainThread function.
This function can be implemented in many ways. We'll see one way that's easily reusable by other related classes (that also need to run code in the OpenGL thread) and integrates nicely with other tasks.

TaskFactory and TaskScheduler

First of all, how would we implement ScheduleToBeLoadedInMainThread with TaskCompletionSource? We'd keep a list of actions to execute, then provide a method to add a new action to this list and return a TaskCompletionSource.
Our UpdateFrame method would take elements from this list, execute the action and then notify the TCS.
Now we need to keep track of actions and their respective completion sources.
This works and it's simple. However, we'll cover a different approach here.

So far, we've created new tasks with the Task constructor or Task.Factory.StartNew(action).
Let's see what factories are with further detail.

A TaskFactory is exactly what the name tells us it is: A factory that creates tasks.
This is useful when we want to create many tasks with a bunch of shared special properties
var taskFactory = new TaskFactory();
taskFactory.StartNew(() => { Console.WriteLine("Do something"); });
This example is not particularly impressive. We're not doing anything we couldn't do before.
The thing is, now we can change the properties of the factory and let all created tasks share those same properties.
The one we're going to change is TaskScheduler.

A scheduler is a class that queues tasks and controls when and how they are executed. We'll be subclassing TaskScheduler.

We want our TaskScheduler to store the pending tasks and then allow its owner to decide when to execute a task.
class UpdateTaskScheduler : TaskScheduler
{
    ConcurrentQueue<Task> tasks = new ConcurrentQueue<Task>();

    protected override void QueueTask(Task task)
    {
        tasks.Enqueue(task);
    }

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        return false;
    }

    protected override bool TryDequeue(Task task)
    {
        return false;
    }

    protected override IEnumerable GetScheduledTasks()
    {
        return new List(tasks).AsReadOnly();
    }

    public override int MaximumConcurrencyLevel
    {
        get
        {
            return 1;
        }
    }

    bool ExecuteNextTask()
    {
        Task task;
        if (tasks.TryDequeue(out task))
        {
            bool result = base.TryExecuteTask(task);
            return true;
        }
        return false;
    }

    public static TaskScheduler CreateNew(out Func executionFunction)
    {
        var scheduler = new UpdateTaskScheduler();
        executionFunction = scheduler.ExecuteNextTask;

        return scheduler;
    }
}
Let's see what this does.
First, we offer a mechanism to queue tasks. We disable task dequeuing so once a task is queued, it must run.
We also disable task inlining. Check the Further Reading section for more details.
The GetSchedulerTasks() method is used for Visual Studio debugging. We just return our list of pending tasks.
MaximumConcurrencyLevel indicates that only one single task from this scheduler is ever running at any given point in time.
Our ExecuteNextTask function executes a task. It returns true if it did manage to execute a task, false if there were no more tasks to execute.
We offer a CreateNew static method to create new schedulers. The out delegate allows the caller to use the ExecuteNextTask method of its scheduler.

To call the tasks in our main thread, we first create the scheduler and corresponding factory:
UITaskScheduler = UpdateTaskScheduler.CreateNew(out executeTask);
UITaskFactory = new TaskFactory(new CancellationToken(),
    TaskCreationOptions.HideScheduler,
    TaskContinuationOptions.HideScheduler, UITaskScheduler);
We add the following code in our UpdateFrame method to execute the tasks.
for (int i = 0; i < 50 && executeTask(); ++i)
{
}
Now, we're ready to complete our texture loader.
var uiFactory = Application.CurrentApplication.UITaskFactory;

int counter = 0;

var tcs = new TaskCompletionSource<object>();

var baseTasks = new List<Task>();
foreach (var textureToLoad in texturesToLoad)
{
    var baseTask = new Task<Bitmap>(() =>
    {
        var bitmap = new Bitmap(textureToLoad.AsFilename());
        return bitmap;
    });

    baseTask.ContinueWith(task =>
    {
        uiFactory.StartNew(() => {
            var bitmap = task.Result;

            LoadTextureFromBitmap(bitmap);

            bitmap.Dispose();

            if (Interlocked.Decrement(ref counter) == 0)
            {
                //We have no further textures to load
                tcs.SetResult(null); //Tell TCS we're done!
            }
        });
    });

    baseTasks.Add(baseTask);
}

counter = baseTasks.Count;

foreach (var baseTask in baseTasks)
{
    baseTask.Start();
}

return tcs.Task;
And that's it.

Final Adjustments

We have one final problem.
Running the code above may cause an OutOfMemoryException.
The problem is that we're loading lots of Bitmaps to memory, but we're not exporting them to OpenGL and disposing them fast enough.
We could try to set MaximumDegreeOfParallelism, but that wouldn't solve the problem. MaximumDegreeOfParallelism merely tells us how many tasks can be run at the same time, not how many tasks we can have pending in our UI scheduler.
One possible solution uses semaphores. We create a new semaphore (count=100) when the start the texture loading method, use semaphore.WaitOne() before allocating a new Bitmap and semaphore.Release() after bitmap.Dispose().
That way, there are never more than 100 bitmaps in memory waiting to be loaded to OpenGL.(EDIT: I'm not sure if this is a good idea. Semaphores are blocking, and tasks should generally avoid using blocking methods).
And that's it.

Conclusion

In my own tests, this final proposal takes 1100ms. That's twice as fast as the first approach.
While one second is not that long, this approach could be further tweaked to support slower use cases.
Also, we now have a framework to allow easily mixing worker threads and UI calls with C#'s TPL. Since we've used C# Tasks, our code is now compatible with the new C# 5 syntax sugar, so we could await our texture loading.
That's all.

Further Reading

No complete source code repository this time, sorry.
Still, the relevant code is all in the post, so you should still be able to use it. The code in this post was written by me (Copyright (C) Luís Reis, 2013) and it's MIT licensed.

Amdahl's law - Wikipedia, the free encyclopedia
The reason parallelism does not linearly improve the speed of most applications.

The Open Toolkit Library | OpenTK
The library the code in this post uses for OpenGL calls.

c# - What is the difference between task and thread? - Stack Overflow
If you're having trouble figuring out the difference.

Parallel.ForEach Method (System.Threading.Tasks)
The many ways to run a foreach loop in parallel in .NET.

Task Parallelism (Task Parallel Library)
Explains multiple task-related concepts, including some concepts not covered here such as task cancellation, child tasks, nested tasks, WhenAll and WhenAny.

Asynchronous Programming with Async and Await (C# and Visual Basic)
Shows how the new syntax sugar helps with task-based programming

Task.Wait and "Inlining" - .NET Parallel Programming - Site Home - MSDN Blogs
An overview of what task inlining is and how it works.

Sunday, November 11, 2012

A simple Mark&Sweep Garbage collector

Last Friday, I woke up wondering "how hard would it really be to write my own garbage collector"?
Now, I have an answer.

Required Knowledge

  • In order to understand this post, you will need moderate programming knowledge.
  • You should understand how to code in Java (or a similar language) and C or very low-level C++.
  • Basic assembly concepts will be needed as well (e.g. you should understand what a processor register is).
  • Understanding the x86 architecture will also help, but it's not strictly required.
  • Understanding how the JVM or .NET runtimes work will also help, but it's not required either
  • Basic compiler theory knowledge will help, although this prototype is not technically a compiler.

What is a Garbage Collector?

Pretty much all applications need to store data somewhere. Most programming languages have the concept of variable - or, at very least, the concept of function argument - but those have to be stored somewhere. The two major options are CPU registers (which can only store small amounts of memory at any given time) or computer memory.
In most cases, there are three major memory regions where data can be stored:
  • For global data, it might be stored in dedicated memory sections (when talking about this data, you may see the names "data section" and "bss section", but I won't cover these details here).
  • For local data, needed only for the duration of a function (e.g. function arguments and local function variables), the preferred choice is to store data in the stack. However, variables stored in the stack shouldn't be too many nor too big or else bad things will happen.
  • For everything else: data that isn't in a global variable, but can't just be tossed away when the function returns, we store it elsewhere in the heap.
Unfortunately, heap memory requires special care, unlike global and stack memory which don't usually need much attention from the programmer.
Heap memory isn't "just there to be used" like the other two mentioned memory storage options. Instead, we must tell the operating system when we need more memory(allocate memory) and when we no longer need it (deallocate or free memory).

In the old days, heap memory was manually allocated and deallocated by the programmer as needed.

#include <stdlib.h>

struct foo {
    int x, y;
};

int main() {
    //First we allocate memory in the heap
    struct foo* bar = malloc(sizeof(struct foo));

    //Then we use it
    bar->x = 2;
    bar->y = 3;

    //Then we deallocate it
    free(bar);

    return 0;
}

So far, so good. But it turns out that things aren't always this simple.
Often, heap memory will be around for a long time, be used in many portions of the program, and then nobody knows who is supposed to deallocate it.
Sometimes, the program might try to deallocate memory multiple times or it might try to use memory after being deallocated, resulting in serious problems ranging from weird unpredictable behavior to program crashes.
On the other hand, some variable might no longer be needed and, therefore, be forgotten by the program. But it's still there - the program merely forgot to deallocate it. If this happens with big variables or too often, then these no longer needed chunks of memory gradually accumulate over time, and then we have small programs consuming massive amounts of memory but only needing a small fraction of it - we call that a memory leak - an annoying and problematic type of bug.
Nowadays, there are many ways to ensure proper heap memory usage. Programming languages such as C++ offer multiple mechanisms to safely deal with heap memory(such as std::unique_ptr).
Other programming languages, such as Java, C# and JavaScript, used an entirely different approach - to take away the responsibility of dealing with heap deallocation away from the programmer entirely.
Instead of relying on the programmer to deallocate memory, these languages allow (or, in fact, force) the programmer to let the computer automatically take care of that. In that case, the computer automatically detects unreachable memory - memory that the computer knows won't ever be used again - and frees it without asking for the programmer's permission. We call the subsystems that deal with that garbage collectors.
These subsystems often add overhead to the program - making it slower, consuming memory of their own, or both - so a lot of research has been done to minimize their negative effects.

In truth, some types of garbage collectors can be combined with some sort of manual memory management, but that's outside of the scope of this post.

Previous Work - Reference Counters

I had already some limited previous experience in garbage collectors, since I had already written at least one reference counter before.
A reference counter is a primitive type of garbage collector. With these systems, allocated memory comes with an integer variable that's the counter.
Every time a new part of the program needs the variable, the counter is incremented.

#include <stdlib.h>

struct foo {
    int x, y;
    unsigned counter;
};

int main() {
    //First we allocate memory in the heap
    struct foo* bar = malloc(sizeof(struct foo));
    
    //We are currently referencing it with variable bar, so
    //our counter must be 1
    bar->counter = 1;

    //Then we use it
    bar->x = 2;
    bar->y = 3;

    //Then we deallocate it
    --bar->counter;
    if (bar->counter == 0) {
        free(bar);
    }

    return 0;
}

C++ makes reference counters easy to implement and use safely:

struct foo {
    int x, y;
    int counter;
    foo() : counter(0) {}
};

class foo_ref {
    foo* ref;
public:
    foo_ref() : ref(new foo()) { ref->counter = 1; }
    foo_ref(const foo_ref& other) : ref(other.ref) {
        if (ref) {
            ++ref->counter;
        }
    } 
    foo_ref& operator =(const foo_ref& other) {
        if (other.ref) {
            ++other.ref->counter;
        }
        if (ref) {
            --ref->counter;
            if (ref->counter == 0) {
                delete ref;
            }
        }
        ref = other.ref;
    }
    ~foo_ref() {
        if (ref) {
            --ref->counter;
            if (ref->counter == 0) {
                delete ref;
            }
        }
    }

    foo_ref* operator *() { return ref; }
    foo_ref* operator ->() { return ref; }
};

int main() {
    //First we allocate the memory
    foo_ref bar;

    //Then we use it
    bar->x = 2;
    bar->y = 3;

    //Then C++ releases it for us
    return 0;
}

However, as you can see, the basic concept is the same - it's just easier to use.

Every time some part of the program no longer needs the variable, the counter is decremented. When this happens, if the counter has reached zero, that means nobody needs the variable anymore and its memory is deallocated.
Reference counters are very simple, well understood and can be made thread-safe. Because of this, many programmers - even those that dislike garbage collectors in general - often use them. After all, being simple and well understood usually means having few bugs.
They are so popular that even C++'s standard libraries include them nowadays.

However, reference counters have a few problems.
First of all, all allocated heap memory needs a counter. Counters need memory, so reference counters add some memory overhead.
In second place, incrementing and decrementing that counter takes CPU time. If this happens too often, the program becomes slower.
Lastly and, perhaps most importantly, reference counters are occasionally unable to prevent memory leaks. This third point needs some extra explanation.

Often variables add reference other variables. For instance, consider the following Java program:

public class Foo {
    public Foo next;
    public int value;
}

In the program above, a variable of type Foo might reference another Foo, which may in turn reference another Foo. References form a directed graph.
This is all fine until we introduce cycles to the graph. Consider this case:

Foo a = new Foo();
Foo b = new Foo();
Foo c = new Foo();
a.next = b;
b.next = c;
c.next = a;

This is where our problems begin. Because a needs b, we increment the counter for b. Because b needs c, we increment the counter for c. And because c needs a, we increment the counter for a.
Because they each need each other, even when they go out of scope, they still reference each other. Because they are referencing each other, their counters never reach zero. And because their counters never reach zero, they are never deallocated. So, once again, we have a memory leak.
Other types garbage collectors fix this problem.

There are tons of garbage collector algorithms to choose from. Each has different advantages and different problems. An example of a simple garbage collector that does not have this problem is the mark and sweep.

Mark and Sweep

Mark and sweep is a very simple - and supposedly not very efficient - garbage collector. Unlike the reference counter, mark and sweep handles cyclic references without any problems.
The algorithm is something like this:
  • Mark which regions of memory are still needed
  • Sweep away those that are not.
So, how exactly do we find out which regions of memory are still needed?
First of all, we'll define the concept of roots. A root is an object that is currently and directly being referenced. For instance, a variable in a currently-being-executed function is a root. Another type of root is a static class field.
Roots can never be garbage collected, since they might still be needed by the program.

An object might still be needed if it is reachable - that is, if it is a root or if it is referenced by another reachable object.
So, if S is the set of all objects, R is the set of all roots and T is the set of all objects that are currently still needed, the mark phase is equivalent to this (although implemented differently):
  1. Let T = R
  2. Add to T all objects in S that are currently being referenced by an object in T
  3. Repeat 2 until no object in T references an object not in T.
In practice, this can be implemented by something like this:

void mark(Object o) {
    if (!o.marked) {
        o.marked = true;
        for (Object o2 : o.getReferencedObjects()) {
            mark(o2);
        }
    }
}

void mark() {
    for (Object o : objects) {
        o.marked = false; //Initially, no object is marked
    }
    for (Object o : roots) {
        mark(o); //Mark all roots.
    }
}

Then, once that is done, we proceed to the sweep algorithm:

void sweep() {
    for (Object o : objects) {
        if (!o.marked) {
            free(o);
        }
    }
}

This might look simple, but there are a few problems:
  • We need to be able to identify which objects are roots.
  • We need to be able to identify which objects an object references.
The solution to these problems is an implementation detail.
Good news for those who just need to understand the big picture, but it's not much of a consolation for those who need to implement one because when we are implementing something, pretty much by definition, we do care about implementation details. Heck, we make the implementation details.

My Implementation

My code, available in https://github.com/luiscubal/gctest, is written in low-level C++ (with a little bit of x86-64 assembly). That means it is painfully aware of details such as POD types. Class inheritance is manually implemented using lots of somewhat ugly pointer operations.

This project attempts to simulate a programming language environment similar to - but probably simpler than - Java. It has objects, primitives and arrays. Classes can extend other classes, but multiple inheritance is not supported.
This hypothetical language does support static fields, but that feature wasn't implemented in this prototype.

This prototype also does not attempt to specify how virtual functions are called. It doesn't offer any special support for that but it does allow finding out what the class type of an object is, so virtual methods are certainly possible, albeit somewhat inefficient.

It also does not enforce access modifiers itself. Instead, it relies on an hypothetical code generator subsystem to do that.

The prototype does not run garbage collection on classes themselves. For instance, if the language offered some sort of ClassLoader-like API to load new classes, those classes wouldn't be collected even if they went out of scope. However, I believe that adding support for this wouldn't be too difficult.

The prototype does not offer support for any sort of C#-style structs, but I believe those should be relatively easy to add. As for generics, those could probably be implemented on the code generator, without needing any special treatment from the GC. As far as the GC subsystem would be concerned, Foo<A> and Foo<B> would be entirely different unrelated types.

Finally, the runtime does not offer any support for constructors, destructors nor exceptions. The garbage collector/allocator was developed under the assumption that constructors and exceptions are the sole responsibility of the code generator - e.g. a LLVM-based compiler.
Destructors could potentially be implemented in a later phase, but those would require some extra work - probably an additional step between mark and sweep (mark, destroy and sweep?). Destructors are problematic because they might themselves cause the object to become reachable:

public class Foo {
    public static Foo x;

    ~Foo() { Foo.x = this; }
}


My implementation is written in a class named gc_context. I've tried to avoid global variables as much as possible because I wanted it to be possible to run many isolated garbage collected environments in a single process. However, my testing program does indeed create a global gc_context object.

Because no complex pointer arithmetic is applied to gc_contexts, all the C++ bells and whistles can be used. gc_context is not a POD type.

The code was written to allow the following program execution design:
  1. The environment - e.g. class type information - is constructed, possibly generated from an external source (e.g. the equivalent of a JAR file). In this prototype, this step was hard-coded in the main function.
  2. The "Context" then generates required runtime information - e.g. the size of each class and the memory offsets of each class field. This step was fully implemented in this prototype.
  3. The code itself - possibly generated with some JIT compilation technology - is then executed. In this prototype, the code was written in a C++ subset designed to mimic the generated JIT code, and compiled using an ordinary C++ compiler (Windows 64-bit GCC/MinGW).
The first problem I attempted to solve was "how to identify the roots". In this prototype, the roots are all in the stack (since there are no static fields yet), so all I needed to do was figure out how to read the stack.
In the x86 architecture, the current stack location is stored in the %esp register (%rsp in 64-bits) and the stack grew downwards.
Since the GC subsystem runs in the same thread as the rest of the program, they share threads.
Therefore, the GC can find out what the current stack position is and, with it, what portion of the stack is "active".

Of course, it still doesn't know which parts of the stack are pointers and which are integers that just happen to look like pointers. However, the code doesn't care and sees everything in the stack as a potential root. This does mean there's a risk of false positives occurring during the marking phase, but it simplifies the garbage collector and it's very fast. Also, the probability that some random integer exactly matches a valid pointer is very low.
The Mono project SGen pages contain some interesting information, and it's where I took the idea of accepting false positives in the stack, although Mono's SGen uses an algorithm that's different from what we do.

In the future, I might have to add the general purpose registers to the list of roots, but for now this seems to work.

Without further delay, here is a portion of the test code:

gc_context* ctx;

int main() {
    //Build environment
    ctx = new gc_context(get_stack_pointer());

    class_type core_Link;
    core_Link.full_name = "core.Link";
    core_Link.base_type = 0;

    field core_Link_prev;
    core_Link_prev.type = "Lcore.Link;";
    core_Link_prev.flags.is_static = 0;
    core_Link.fields.push_back(core_Link_prev);

    field core_Link_next;
    core_Link_next.type = "Lcore.Link;";
    core_Link_next.flags.is_static = 0;
    core_Link.fields.push_back(core_Link_next);

    field core_Link_val;
    core_Link_val.type = "I";
    core_Link_val.flags.is_static = 0;
    core_Link.fields.push_back(core_Link_val);

    ctx->push_class_type(&core_Link);

    //Compute sizes and offsets
    ctx->compute_sizes();
    ctx->log_headers();

    //Execute code
    test_array();
    cout << "Now list" << endl;
    test_linked_list();
    cout << "Array again" << endl;
    test_array();

    //Some debugging ending code
    cout << ctx->count_heaps() << endl;

    cout.flush();
    cerr.flush();
}

In the example above, test_array and test_linked_list are two functions designed to stress the garbage collector in different ways.

More details

Each program object instance follows the same convention: A common header named core_representation that - among other things - specifies its type and some content.
The header is specified as a POD C-style struct, and lots of horrible pointer arithmetic operations are done with it.
The size of each instance equals the size of the header plus the size of its fields (plus some extra padding for alignment purposes).

struct core_representation {
    const char* type;
    mark_id_t last_mark;
};

void do_something(core_representation* instance) {
    *((uint32_t*) ((char*) instance + some_uint32_field_offset)) = 10;
}

The garbage collector is triggered any time a memory allocation fails.
The GC context owns a number of gc_heap objects which are regions of heap memory. Every time those gc_heap objects get full, it first attempts to clean them and, if that's not enough, it allocates more memory.

gc_heap objects have a default size of 4 KiB. However, if an object is too big to fit in that default size, a bigger gc_heap object is created.

Each gc_heap object is divided into small units. For each of those units, there are two bits - one specifying if it is being used and another one specifying if it is the beginning of an object/array.
The used bit is stored in a variable named heap_bitset. The start-of-an-object bit is stored in a variable named heap_starts. These variables can be represented with the STL type vector<bool>, since we do not need gc_heap objects to be POD types.

Every time memory allocation is requested, the gc_context goes through every gc_heap object it owns and asks for free contiguous space until it finds enough.

Once the memory is allocated, the bits are set to indicate that the memory is being used and the heap_starts flag is activated for the bit where the object starts. Note that the heap_starts is only for the unit where the object starts.
So if an object with a length 3 units is stored at position 2 and another with a length of a single unit is stored at position 6, here is what the two bitsets look like:

Index 0 1 2 3 4 5 6 7
heap_bitset 0 0 1 1 1 0 1 0
heap_starts 0 0 1 0 0 0 1 0

Because flagging every object as "not-marked" every time the GC algorithm runs is annoying, I decided to give each mark execution an "ID".
Every time the mark phase runs, it sets the "last mark id" of all active objects as the ID of the current execution.
Once sweep kicks in, it checks for objects with old IDs. When an old ID is found, then it knows that the object is unreachable and ready to be deallocated.
When an object is deallocated, the corresponding bitsets are once again set to 0.

Since the "last mark id" is an actual part of the object layout, marking an invalid object pointer means changing an incorrect part of the memory.
All objects in the stack and candidates for marking, but many are invalid, random, integers so, before marking an object, the GC checks that the object does belong to a heap and then that it is indeed a valid pointer. It does so using the following algorithm:

const gc_heap* gc_context::is_heap_object(void* obj, bool is_gc_object) const {
    if (obj < first_heap || obj >= last_heap) {
        return false;
    }

    for (const gc_heap& heap : heaps) {
        if (heap.contains(obj, is_gc_object)) {
            return true;
        }
    }

    return false;
}

Where heap.contains checks that the object is within the allocated space for that heap, is aligned with the unit size (which all valid allocations must be) and has its heap_starts bit set.
first_heap and last_heap are a fast check to exclude all pointers that known to be outside all heaps.

Also during the mark phase, there's the chance that an object might be referenced multiple times. The algorithm must detect that an object was already marked during that execution and skip it to prevent infinite loops.

In hindsight, the mark phase was way harder to implement than the sweep phase.

Arrays

Arrays are special types. They have a "content type" which may or may not be another array, and a length.
Arrays have the same memory layout prologue as classes, but they have a few extra fields. Notably, they include a pointer to their content.
In an array of length 10 with content-type "32-bits integer", the content points to a chunk of memory with 40 bytes, that contains the 10 integers.
In an array of length 10 with content-type "object of some type" or "array of some type", the content points to a chunk of memory that contains the references to the objects or arrays.

struct array_representation {
    core_representation core;
    size_t array_length;
    void* content;
};

In other words, arrays have two parts: The "header" and the "content", which are in two separate memory locations.
I initially considered merging the two but so far I have decided not to. I might revert that decision later.

Problems

Since this is low-level C++ programming, problems are sure to arise. I will list some of the most noteworthy here.

At some point, for long reference chains, such as large linked lists, the program would crash with segmentation fault. I managed to figure out that this was actually caused by a segmentation fault in the mark phase.
I had already suspected that the deeply recursive marking algorithm, if implemented in a language like C++, would cause trouble, so when I got this error I already had a pretty good idea of what could be causing it.
Consider a linked list of A->B->C. In that case the mark phase would mark A, then call a function to mark B and then call a function to mark C. If the linked list had thousands of elements, the call stack would grow very large.
This was solved by replacing the recursion with a queue of objects that must be marked. Without deep recursion, the stack overflow issue is avoided.

Another weird issue happened when I attempted to combine C++ and x86-64 Assembly. Doing so would cause the program to immediately crash:

extern "C" {
    extern __cdecl void* get_stack_pointer();
}
.text

.global get_stack_pointer

get_stack_pointer:
 movq %rsp, %rax
 ret

This was solved by adding some SEH-related lines.

.text

.global get_stack_pointer
.seh_proc get_stack_pointer

get_stack_pointer:
 .seh_endprologue
 movq %rsp, %rax
 ret
 .seh_endproc

I'm not entirely sure what the .seh lines do, but they make it work, so I'll leave them alone. I probably wouldn't have figured it out by myself without the help of gcc -S.

Another problem I had was an apparent memory leak. When testing my program with many large arrays, I noticed that the memory would grow a lot - sometimes to nearly 4 GiB.
This happened because the GC algorithm would only collect when the gc_heap objects were getting full. However, because the arrays themselves - the "headers" - were very small, the algorithm decided this was not worth the effort. At that point, the content of the headers was allocated in separate using a normal malloc call and completely ignoring the gc_heap objects.
Once the code was changed to have the array contents be allocated on the gc_heap objects, the problem was gone. It is worth noting that while array contents do set the heap_bitset, they have no effect on the heap_starts, which remains unset for the entire array contents.

Profiling and Optimization

Even when the garbage collector was working, it was somewhat slow. In order to make it faster, some optimization was needed.
Since I had very little clue on what was making it slow, I decided this was a good opportunity to improve my profiling skills.
The first thing I had to do was figuring out which profiler to use. I decided to try gprof, which comes with MinGW.
It turns out that using gprof with eclipse is not that hard. The following steps are required:
  1. Enable gprof support in the project. To do so, go to Project Settings > C/C++ Build > Settings and then, in GCC C/C++ Compiler > Debugging, activate -p and -pg.
  2. Run the program. A file "gmon.out" will be generated on exit.
  3. Run gprof executable_name gmon.out > gmon.txt on the command line (I used the PowerShell).
A "gmon.txt" file will be produced. Open it and you'll find something like this:


 time   seconds   seconds    calls   s/call   s/call  name    
 47.71     79.01    79.01                             _mcount_private
 24.86    120.17    41.16                             __fentry__
  5.40    129.12     8.95 263627666     0.00     0.00  std::_Bit_iterator_base::_Bit_iterator_base(unsigned long*, unsigned int)
  2.45    133.17     4.05 3020571660     0.00     0.00  std::_Bit_const_iterator::_Bit_const_iterator(std::_Bit_iterator const&)
  1.64    135.88     2.71        2     1.36     1.37  test_array()
  1.46    138.29     2.41 1537955729     0.00     0.00  std::vector >::operator[](unsigned long long)
  1.36    140.55     2.26 1510285830     0.00     0.00  std::operator-(std::_Bit_iterator_base const&, std::_Bit_iterator_base const&)
...

This table indicates how long the program spent in each function, how many times that function was called and how long it spent per each function.
Stack Overflow has a long list of reasons explaining why gprof sucks, but it's still better than having no profiler at all.

The first two lines (_mcount_private and _fentry__) are apparently overhead from gprof itself and therefore not very interesting for us to study.

However, on earlier versions of this prototype, std::vector<bool> iterator-related functions were taking up a significant amount of time.
Because the size of the vector is not known at compile time, I could not switch to a std::bitset<N>. So I developed my own fast_bitset class.
Besides the usual get, set and unset operations, fast_bitset offers some functions to quickly find the next set bit, the next unset bit and to set or unset multiple bits at once. Instead of checking bit-by-bit, it often checks 32-bits at once, which (in a best-case situation and in theory) can make it 32-times faster.
And, indeed, once I replaced even just a few of the std::vector<bool> usages with it, times improved.

Multi-threading

At this point, the prototype does not use multi-threading at all. However, it should be possible to implement Web Workers-style multi-threading.
Web Workers do not actually share memory. They communicate with message passing. Unlike full threads, this type of threads should actually work with the current system.
This GC was designed to allow multiple instances of gc_context to coexist in the same program. As long as the gc_context is thread-local and that all memory allocations for a thread happen in that thread, it should work.
For full Java-like threads, the GC would not work as-is. The collector would most likely need to stop-the-world and some way to find out the contents of the stacks (and registers) of the remaining threads.

However, even for a single-threaded program, the collector itself could be modified to split work across multiple threads. Both the mark and the sweep phases should be easy to parallelize. The mark phase is based on a queue - which could probably be made thread-safe. The sweep phase could be divided so that each gc_heap sweep is run from a different thread. For N gc_heap instances, up to N threads could be used. This is not possible in the current prototype, but it should be trivial to add (whether it would perform well is an entirely different problem).

Potential future problems

  • I currently do not treat for registers as GC roots.
  • I always assume that all pointers in the stack are perfectly aligned.
  • Once a gc_heap object is allocated, it is never deallocated until the context itself is deallocated.

Future Work

There are multiple portions of this prototype that could use some improvements.
Here's my wish-list:
  • Static fields
  • First-class support for virtual methods
  • Support for C#-style structs and delegates.
  • Support for destructors and weak references.
  • Proper multithreading
  • Support for other processor architectures (currently, this is x86-64 only)
And, lastly:
  • Turning this into a full language runtime with the help of LLVM.

Conclusion

Mark and sweep is a simple algorithm that does not require too much voodoo to work properly.
Knowing the exact object layout helps the algorithm by reducing false positives from random integers that look like pointers, but it might not be worth the effort.
Writing a simple garbage collector is not that hard - seriously, it took me about as much time as writing this post. Of course, it won't necessarily be very efficient nor very feature complete.
That's all.

Further Reading

Source code (hosted at github)
See all the dirty details here.

MinGW-builds
Includes 64-bit MinGW.

Garbage collection (computer science) - Wikipedia, the free encyclopedia
As usual, Wikipedia remains a great source to learn about a programming topic.

Boehm-Demers-Weiser - A garbage collector for C and C++
A conservative garbage collector that works in C and C++ programs. It usually doesn't require changes to the source code. It has the problem that it also scans class fields conservatively (and not just the stack), but its high compatibility with pretty much anything makes it a good option for C++ programmers who want garbage collection. It has also been used to detect memory leaks in programs.

std::shared_ptr - cppreference.com
A reference counter from C++'s own standard library.

Generational GC - Mono
The Mono Project has a few projects detailing how their brand new garbage collector works. Previously they used the "Boehm GC".

SuspendThread function (Windows)
A Windows API function that pauses a thread. Can be combined with ResumeThread to stop the world. Unfortunately, a quick google search suggests that this is The Source Of All Evil™ and special care is required to use it. I'm also unable to find out an equivalent for POSIX platforms.

GetThreadContext function (Windows)
Once the world has been stopped, this function can be used to obtain the registers of a thread.

The Java™ Virtual Machine Specification
If you're really really bored.