Software Quality 14 - OoI and OoDFiled Under: Weekly Tuesday Dose of goodness
- Software Quality - Part 1
- Software Quality - Part 2
- Software Quality - Part 3
- Software Quality - Part 4
- Software Quality - Part 5
- Software Quality - Part 6
- Software Quality - Part 7
- Software Quality - Part 8
- Software Quality - Part 9
- Software Quality - Part 10
- Software Quality - Part 11
- Software Quality - Part 12
- Software Quality - Part 13 (Backtracking time?)
- Software Quality 14 - OoI and OoD
This week, we’re going to talk about OoI and OoD.
Don’t get me wrong, they’re not meant to be read literally.
OoI stands for Order of Initialization
OoD stands for Order of Destruction
The former applies to all programming language with object (or memory buffer) constructs while the latter only apply to C/C++ only. Or does it?
So what’s so problematic about them that leads to software quality issues?
Read on…
Introduction
Programmers usually have the tendency to take certain things for granted. One of the many things is the order of initialization(construction) and destruction.
Naturally some might argue that it’s not truth. Fine. However, it’s hard to say that programmers don’t take other people’s order of constructs seriously.
When this happens, the effects are obvious. Not only objects may not be initialized properly, it may even cause memory leaks in languages that have garbage collectors. This is especially so if one’s codes have a A->B and B->A relationship. This is possible in C++ using forward declarations and is also possible in other languages just as Java or C#.
Problems
How does it happen? A has B - B is the full implementation, child class. A is the class holding B. Thus it can be said that A is composed of B and other objects.
Now, let’s look at B. If B requires a parameter, ie, the parent of A. In this case, in Java or C# it can be the base object class. So if the initialization is like: (In Java)
public class A extends ParentObject {
private B bObj;
//constructor
public A() {
this.bObj = new B(this);
}
};
public class B {
private ParentObject pObj;
//constructor
public B(ParentObject obj) {
this.pObj = obj;
}
};
At this point, you’re technically cross-referencing both objects. I’m not 100% sure on how Java garbage collector works but if it’s just a simple reference counting pointer, then there’ll a memory leak still.
Why? Because the instance of A will be lost in its composed object, bObj. Even if A is set to null, it won’t be able to garbage collect since B is holding another reference to A. Remember, if A extends ParentObject, then A IS ParentObject! There’s no difference in terms of base memory address.
In this scenario, unless the garbage collector has additional information to decide that bObj is useless as well, otherwise it cannot, by the designs of reference counting pointer mechanisms, release the instance of A. Thus causing a memory leak.
Problems In C++
Order of destruction problems are compounded in C++ by the fact that you can arbitrarily delete objects at any time. Thus even with reference counting pointers, programmers cannot escape order of destruction problems.
The main reason is the order itself. A lot of programmers like to delegate the order of destruction to the garbage collector, in C++, it’ll be the reference counting pointer container to handle.
The over-reliance on this mechanism easily leads to crashes that doesn’t make a lot of sense. That’s because we’re taught to understand reference counting pointer that there’s only valid or null pointers. Nothing else.
Messing up the order of destruction will overturn this teaching.
Reason? Well, the problems generated by the order of destruction isn’t about null pointer dereferencing (assuming you’ve done your null checks properly). The biggest problem is generated by dangling pointers where certain modules are released already without waiting for the reference counting pointer.
How is this possible?
The reference counting pointer can only cover as far as it could based on allocator and deallocator. It doesn’t cover things like when a DLL is detached, for example, how can you ensure that every reference counting container is set to NULL?
You can’t.
Thus the only way to prevent this without using the full version of Strides Interactive’s cSmartPtr<T>, is to ensure that the order of destruction is done properly. This require proper design adherence and design declaration in the early stages of the project. I’ll not go into details for these 2 terms just yet.
So, how is it so that Strides’ smart pointer can handle what normal reference counting pointer can’t?
Well, it allows you to terminate an object and ensures that everybody else returns a NULL when checked against.
Order of Initialization Problems
So we’ve covered order of destruction problems. How about order of initialization? Surely there isn’t any wrong with allocating objects right? Does memory limitations play a part?
Well - there’s nothing wrong allocating objects and memory limitations do play a part but in terms of the order, it doesn’t play a major role unless the case is very specific.
We all know that all applications require a specific sequence of initialization. This sequence actually has an underlying order which is categorized accordingly to certain application stages.
Here’s an example of an order of initialization of a typical game:
1) Initialize Windows API to create a form
2) Initialize all devices (renderer, audio, input, etc)
3) Initialize game engine
4) Initialize script engine
5) Initialize game
Usually the first 4 steps are done for you if you use a 3rd party engine. Thus leaving the order of initializing the game to the programmer.
So what happens in a game order of initialization? Let’s look at this example:
1) Load the textures
2) Load the meshes
3) Create the particle samples
4) Create the particle emitters
5) Load data
6) Create entities
7) Create user interface to expose entity properties
If you notice, there’s a certain dependency in the order of initialization. If this dependency is broken, it’ll usually lead to broken implementations and functionality. Thus, it’s important to understand this order and respect the dependencies.
The keyword is respect. That means, to take it really seriously, consult with the technical design and ask the right questions.
Self-awareness
It’s also important to understand your own module and be aware of what’s required for your codes to function properly. With that, you’ll better understand where and when your codes can be initialized.
Sometimes, it’s not a matter with the codes, but rather, a problem with the order of scripting.
“How can a particle be created without a texture?” . These type of questions should be asked. If there’s no answer, then there can only be the following possibilities:
1) Your module is not applicable to the project at all
2) Your module is completed way too early for any integration to be possible
3) The technical design of the module isn’t clear enough
4) Project management isn’t good enough to optimize your time and skills
Conclusion
Thus, as I sign off, I’d like to stress the importance of the Order of Initialization and the Order of Destruction. You’ll need to seriously respect these 2 nature of things in order for your project to be better off.
Why do I say it’s nature of things rather than nature of programming? That’s because, we’re sub-consciously doing it already. We cannot expect things to happen just like that. There’s a natural progression and a sequence of events that will lead to it.
Normally, we’ll even plan that sequence and hope that things will go our way. And most of the time, it doesn’t go our way, leading to exceptions.
So if we want to go toilet, we’ll first need to walk to it right? Sounds simple? Yes.
Taken for granted? Yes, unfortunately. It may not be just this case, but many other cases. For example, when you order food, do you expect it to come in a timely fashion? Yes you do. But over time, we take it for granted.
When it comes to programming, there’ll be no one to cover us taking for granted and such problems may not be easily traceable as it seems to be. However, once you’ve traced it - there’ll be at least 2 outcomes.
1) You get confused by the design of the flow and head back to the docs for an answer
2) You get demoralized because this is a technically very simple (and possibly stupid) mistake.
Thus, please - have respect for nature and you should be just fine.
Signing off for now. Have a great week ahead!
Regards,
Jeremy
- Permalink
- Admin
- 12 Apr 2011 2:36 PM
- Comments (0)