Monday, August 24, 2009

Object Ownership and Lifetime: Argument against garbage collection and reference counting

One of the biggest issue I came back face to face while trying out C++ on iPhone is object lifetime. To rephrase that, it is the prevention of memory leaks.

It is an issue that has long been place at the back of my mind and forgotten since I did my development on Java and C#. You simply create new object instances, and rely on the framework to properly garbage collect them.

That would probably work on iPhone too, if you stick to Objective C and use the auto-release memory pool. Simply mark all new object instances as auto-release and let the application 'garbage collect' them when the auto release memory pool is popped from the stack.

An approach to simulate garbage collection in C++ has long been reference counting. Every time a part of the application requires a strong reference to an object, it increments the object reference count. Every time an object goes out of scope, it reduces the reference count of all strongly held object references it held. When it reaches zero, the object is deleted/released/freed.

But personally, I disliked this approach. It is how COM works. It seemed like an unnecessary overhead. Why?

Because for a given program, you could easily identify a natural owner of an object. Object usage is shared. Object ownerships are not shared.

For example, assuming that I have a texture, that is used to render two different game objects. To me, I would identify the need of a texture manager that owns the texture. The two different game objects does not own the texture, but only use the texture.

The primary driving reason for reference counting is to prevent a situation when your texture manage release the texture, the game objects still have access to a valid texture.

But this argument is flawed in my opinion. When such a thing happen, it usually indicate that your program has entered an unknown state. Why did you actually unload the texture, when you know that two game objects need to use it? Or rather, on the other hand, are you unaware of what is using or needing what at any particular moment of the application execution?

Of course, there are times when you DO need to unload a texture to free up enough memory for another texture, and you do know at that point of time the two game objects are 'dormant'. They are not drawn and can request the texture to be loaded back in at a later stage (this is more like resource handles concept and I will describe them in a later post). But if you had reference counting, in this case, you would not be able to free the memory up! It's reduced flexibility!

But back to the original argument. Since we can easily identify owners of resources and objects, the argument of reference counting is void.

For this game development, I am sticking to a few 'managed containers', and constraining myself to 'managed pointers' to help own my objects.

To sidetrack slightly, a managed pointer is a template class that manage the lifetime of an object. When such a pointer goes out of scope, the managed object is deleted/released/freed. The most popular implementation of this is probably std::auto_ptr, found in the Standard C++ Library. There are no naked pointers in my program. I use managed pointers as class members, or local variables in a function call.

To extend the idea, I created managed containers. They are simply an extension of the common containers like array, vector, list, map. During their destructor call, they will iterate over all the items and delete/release/free them. Again, they are used as class members or local variables in a function call.

Back to game development, what I end up with is as follows:
1. Each stage of the game is actually a single object
2. The stage object has a few managed containers, which manage top level objects of each stage like game objects, texture resources, script resources.
3. Extra care is taken that every time a new top level object is created, they will be added to the managed containers.

But we still have a big problem. For any given game execution, there should be two type of object instances.
1. Resources and objects that last throughout the execution of a stage. Texture, scripts would belong to this category.
2. Game object instances that are dynamically created and destroyed during the execution of a stage. Enemy spawns, particle effects, etc would belong to this category.

A game object could hold references to objects of type 1, and not have any problems at all. If however a game object hold references to object of type 2, we have a problem.

Objects of type 2 seemed like a big problem on initial look. Upon closer examination, however, they ended up not being a problem at all. Because it goes back to above argument. An object really should not reference another destroyed object. If we reach this stage, we have a bug. We should not go defensive on this and build in reference counting to prevent this. Instead, we should be on the offensive, and encourage such bugs to surface as soon as possible.

Because after all, there is no reasonable way to recover gracefully from such a bug :)

My conclusion? Garbage collection is really a big help as it takes away the burden of releasing memory for you. But when you think hard about it, you could actually properly identify owners and lifetime of each object instance. When you reach that point, garbage collection becomes so much more undeterministic (beyond the promise that at some time in the execution your garbage will be collected).

In fact, garbage colleciton (and reference counting) actually hides bug. You have objects that live longer than they should, because some live objects are holding on references to them. You lost sight of who are the true owners. You have weird and unexpected behavior because an object that you thought was released actually got triggered.

Do some C++. It is good for your mind.

Monday, August 17, 2009

Game Architecture: Contexts

I have had my dabble in game development. But ever since I left the game industry back in 2006, I have been spending more of my time on my full time career, which is J2EE related development. And even till then, my main experience were in J2ME and some symbian.

And now that I am doing some hobbyist game development, I had actually looked around to find out more about game architecture, which had been a sparsely undocumented and unchartered waters. The best books I had seen were the following:

1. Game Architecture and Design: Learn the Best Practices for Game Design and Programming http://www.amazon.com/Game-Architecture-Design-Practices-Programming/dp/1576104257
2. 3D Game Engine Design : A Practical Approach to Real-Time Computer Graphics (The Morgan Kaufmann Series in Interactive 3D Technology) http://www.amazon.com/Game-Engine-Design-Interactive-Technology/dp/1558605932

And recently, I stumbled upon GameArchitect(http://gamearchitect.net/), which provide many interesting ideas. There was one which in particularly interest me, which was An Anatomy of Despair: Managers and Contexts (http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/), which was singleton vs grouping them into a single context class.

Coming from an enterprise development background, I long knew the evil of singleton. And the idea of an environment context object sounds very attractive. I could simply pass a single object around, and all my objects would have access to all system services, from file management to logging manager to profiler to system timer and so on!

Surely there is no drawback.

Or is there?

After using it for a while, it became an extremely bad idea.

It complicates unit testing for one. There are some part of the game that I want to test, eg. script parsing. In order to test them, under the normal java/c# approach, I would mock the context. But being in C++, mocking is definitely more tedious (no I did not try out google mock).

In fact, for some of them, the services are not meant to be interfaces. They are concrete implementations. For example, a profiler service. It's not exactly a costly instantiation, but still, unnecessary. In the world of unit testing, I should not be creating services that a particular test does not use. They instead become unnecessary baggage to be carried around.

Of course, that brings up the second problem. Unnecessary baggage. It is very tempting to give the context to every object/component in the system. Even if all they need are the logging manager, and maybe profiler. Not all of them need the object repository. Not all of them need the script interpreter. But they still carry along the unnecessary baggage. This just irks me. A class has unnecessary knowledge of another interface.

For now, I'm dropping the idea of contexts. That does not mean that I am reverting to singletons. Right now I'm simply going to pass whatever is needed. Nothing more, nothing less. The dependencies are clearer in the class declaration.

As for global services like logging and profiler? These are more of debugging tools. For a game, well... I don't see them to be fully enabled. It will probably be wrapped by MACRO. And since a class might not use them, it makes more sense for them to be C style functions or singletons. Such dependency should not be visible as a constructor argument or setter method.

Of course, if this is an enterprise application, I probably will have them passed as an argument... they would still be in used during production.

Tuesday, August 11, 2009

Abstracting iPhone code into C++

iPhone development is quite the darling of the world right now. Everyday there are various applications launched on the app store, and everyday there are many more people learning to develop on the iPhone.

And of course, such hype sure got me interested too.

The primary programming language of iPhone application development is to use Objective C. I took a dive in it. I liked it. Almost. The seperation of implementation and interface, private 'modules', named arguments and properties were all very helpful.

But my biggest gripe is the 'reference counting' memory mechanism I have to deal with. It is a good idea, along with the memory pool and auto-release idea. But faced with the huge API library list, I simply do not have the confidence to manage it properly.

And the next big gripe is that you had to be on a Mac to develop for it (or as far as I know).

And then someone told me about using C++ with Objective C. So I had to hunt around information for it. But there really is not that much information on the internet about it. All they provided were basic information.

So, was C++ with iPhone information lacking? Or was it really that simple and not much need to be mentioned?

The example provided in Apple's "The Objective C 2.0 Programming Language" showed how to mix C++ in Objective C, but honestly, it gave me more doubts instead.

The best way out is via experiments. This is what I did.

1. Declare a C++ class, with .h and .cpp files, with virtual methods.
2. Derive a C++ class from above, overriding the virtual methods.
3. Write out some string in the constructor and destructor of the C++ class.
4. Declare the derived C++ class in the Objective C class as a local variable.
5. Declare a pointer to the C++ class in Objective C class, and use new/delete on it to instantiate the derived class.
6. Invoke the virtual methods on both the local variable and the pointers.

The result of this experiment prove that:

1. An iPhone application is able to compile cpp files alongside with m and mm files.
2. The mm extension is required to use C++ in the file.
3. An iPhone application obey C++ virtual method rules.
4. An iPhone application correctly invokes C++ destructors.

Turned out it really is that simple.

So we have answered the question from Objective C to C++. But how about the other way? There will definitely be times when an application logic in C++ might have to call on some Objective C API, either because the Objective C API are better, or that they are the only way.

After much googling, turned out that the only way for a cpp file (note the extension) to invoke Objective C API, is through C wrapper. Wrap the Objective C API in C style functions, and then call them in C++.

So we had covered both directions. Objective C -> C/C++, and C/C++ -> Objective C.

The next logical step would be on abstracting enough of the application into a core C++ Application class. Why such an approach?

1. I am very much more comfortable with C++ compared to Objective C (primary reason).
2. Having the core application logic in C++ allows me to port the same logic to be run on Windows and Linux, if need be. At least if there is to be a fellow developer who does not have a Mac, he could do so in Windows.

But of course, I think an important guideline is to be obeyed. This has no actual experiment results to back it up, but is simply an intuation from past cross language developments.

Obey language boundaries. Treat each boundary API call as seperate object space. Meaning, if an object is allocated memory in Objective C, avoid releasing the memory in C++ code. Do so only by calling an Objective C function. Of course, the primary driving theoratical reason is the memory pool in Objective C. And that also mean that, try not to hang on to objects from Objective C for longer than necessary in C++, unless you are absolutely certain of the lifespan of the Objective C object.

Sorry for the side-track, and now back to the abstraction. To help in the abstraction, an outline of an application lifecycle and its possible events could be help. At the most basic level, these are the possible events and lifecycle.

1. Create Application
2. Receive request to render
3. Receive request for input
4. Destroy Application

I am going to assume basic iPhone knowledge, and paste the snippet of the iPhone UIView here. This is actually the bridge from Objective C into our core C++ application.

@implementation View
- (id)initWithFrame:(CGRect)frame {
     if (self = [super initWithFrame:frame]) {
          CGRect rect = self.bounds;
          application = new Application(rect.size.width, rect.size.height);
          [self startTimer];
     }
     return self;
}
- (void)layoutSubviews {
     application->resize();
     application->render();
}
- (void)onTimer {
     application->tick();
     application->render();
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch *touch = [touches anyObject];
     CGPoint tapPoint = [touch locationInView:self];
    
     application->onTouchBegan(tapPoint.x, tapPoint.y);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch *touch = [touches anyObject];
     CGPoint tapPoint = [touch locationInView:self];
    
     application->onTouchMoved(tapPoint.x, tapPoint.y);
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch *touch = [touches anyObject];
     CGPoint tapPoint = [touch locationInView:self];
     int tapCount = [touch tapCount];
    
     application->onTouchEnded(tapPoint.x, tapPoint.y, tapCount);
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
     application->onTouchCancelled();
}
- (void)dealloc {
     [self stopTimer];
     delete application;
     [super dealloc];
}
@end

And a bare bone snippet of our C++ Application class:

class Application {
     public :
     explicit Application(const float newWidth, const float newHeight);
     ~Application();
    
     void resize(void *const windowContext);
     void render();
     void tick();
    
     void onTouchBegan(const int touchID, const float x, const float y);
     void onTouchMoved(const float x, const float y);
     void onTouchEnded(const float x, const float y, const int tapCount)
     void onTouchCancelled();
};

I have purposefully left out OpenGL ES related code, as that is not the focus of this post.

There, now we can concentrate most of our time in C++ development!

Link to bare bone implementation project

Monday, August 3, 2009

Organizing your life: 6 levels of focus with GTD

I have been a GTD fan for as long as I could remember reading about it, and I had been trying hard to apply it to my life. It requires a lot of discipline and motivation to maintain the current actions list, but it does help me greatly.

But my biggest benefit from learning about GTD is probably the 6 levels of focus they introduced. It is such a thing of common sense, yet we do not use it. And if there is many aspects of GTD that one could not bring themselves to do, I would still strongly recommend this.

Basically, the 6 levels of focus are as follows:

1. Current actions
2. Current projects
3. Areas of responsibility
4. Yearly goals
5. 5 year vision
6. Life goals

As one can see, the 6 level of focus is like a planning tool (at least I treat it as a planning tool). At the highest level, we have the life goals. And then the 3-5 year goals, the yearly goals, what you are in charge of now, what you are involved now, and what you need to do now.

For a long time, I have a vaguely sense of my life goals. However, I have no idea if what I do now, or have as a goal is in support of my life goals. Or rather, in most cases, they are not. I simply decide things on almost a whim. As a result, I dropped many goals and projects from time to time.

If your current projects and goals are aligned to your life goals, you are definitely more motivated to do them.

An example might be useful, and I will use my planning as an example.

So I started out with level 6, life goals. I lay out a few goals, and it goes something like this...

1. As a christian, my life goal is definitely to serve the Lord, in a ministry of his choice.
2. I would like to have a family, with two kids.
3. For career, I definitely would like to move in the way of being a software consultant.
4. And my interest, software development, I still would like to have some product that is industry changing.

Pretty simple life goals. But with them clearly mapped out, I can plan my 5 years vision with ease.

1. I definitely would like to complete a bible study of the New Testament. Having good knowledge and application of these would help in whatever ministry God puts me.
2. So I guess... I should have my first kid by then! Which indirectly leads to saving up sufficiently for my first kid.
3. Working in a big company like IBM or Oracle will definitely help in my consultant career.
4. I ought to have a few products released as an indie developer.

Notice that most of them are still a 1-to-1 mapping. Personally, I feel that 5 years vision should be kept simple and few. 5 years are a long time. It is a vision to work towards to, and anything could happen that changes the 5 years vision too.

Moving down, we are at the 1-2 years goals. The 1-to-1 mapping restriction (for me) no longer applies. I could have multiple goals supporting a single vision.

1. I definitely would want to finish reading the bible once.
2. Well, marriage is needed before kids.
3. Having an Oracle SOA certification helps in consultation.
4. As an indie developer, I am very interested in Mac application, especially for GTD.
5. An iPhone game would be good too.

There used to be a 6th goal, SCEA, but I already achieved that. And I see no need to add a new goal too.

Now we are at a more complicated area. Areas of Responsibility. What are these? They are basically roles you have currenly. I'll describe it in terms of myself.

1. I am a christian.
2. I lead a small group
3. I have a girlfriend
4. I use various IT devices
5. My interest is in IT development
6. I work as a software developer
7. I work as a software consultant.

Sometimes, an area of responsibility might not help or contribute to the above layer, 1-2 years goal. At this point, it's good to question... what are you doing with your life doing something that does not help in your life goal? :)

Ok, so moving down, current projects. These are usually a result of your area of responsibility. For example, a work project, or a project to upgrade your computer, or a project to organize a small group meeting.

And finally, current actions. What is the next action in a project? You maintain a list here.

The current projects and actions are the hardest part for me, but the layers above that really help organize my life. I hope it will help you too.