Jump to content

  • Log In with Google      Sign In   
  • Create Account

Portable Use And Disabling Of Exceptions


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
33 replies to this topic

#1 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 03:08 PM

I really want to use exceptions in my code, because they are about everything that I've ever wanted in terms of handling exceptional occurrences. However, I was concerned about the overhead of exceptions, in either speed or space. However, I was reading and reading about exceptions, and I'm starting to accept that it is worthwhile to use them, and there aren't as many platforms that I support that don't have compilers that allow standard exceptions as there once was.

So, I'm looking for advice on it. For these platforms, can you tell me if standard exceptions are supported by any compilers and allow them to be disabled, and bear in mind, I don't support all of them:

Xbox
Xbox 360
PS2

PS3

PSP

Pandora
Dreamcast

Nintendo DS

Symbian OS, any versions after s60v3 (I hear the compilers finally support standard exceptions)
GCC on Windows and Unix derivatives
Visual Studio on Windows
Android (I hear the NDK does now)

iOS (mixing Objective-C with C++)

 

Additionally, for most platforms that support disabling exceptions through a compiler switch, what happens when a throw is encountered? Does it raise some sort of signal?

EDIT:
My most frequent use-case for exceptions is to indicate something that should not happen in a normal work load has happened (out of memory, data that goes into an internal structure from external code is invalid, attempting to use something that is in a valid but explicitly unusable state). In these cases, I would use a debugger to figure it out, but swallowing exceptions is against my style, so they would only occur in areas that must be fixed; a perfect run should never throw them, but still have the option of handling them just in case, like the extremely rare circumstance of expecting memory allocation to fail if you allocate based on input, and displaying an error asking for a different value, though nothrow new could just as easily be used.

 

For the sake of conversation, I use assertions for things that should never happen, and I use status codes for states that are expected to occur. It is very likely that trying to open a file will result in failing to find it, so returning a code indicating this is ideal. Ensuring that the pointer to my main structure isn't null should be left up to an assert; if it fails, then it is a logic error, that shouldn't have anything to do with what is passed from external code.

Further clarification, for how I use each:

1. Exceptions are for public interfaces that interact with external code. An exception signifies that something unexpected has happened, and it is the external code's fault.

2. Returning status codes is for both internal and external code. Status codes indicate that the expected behavior is for one of many events to occur, not all of them positive, though they are _recoverable_.
3. Using assertions is for internal code. The external code should not be able to cause an assertion.

If anyone has tips on how to improve this methodology, don't hesitate to contribute.


Edited by Ectara, 20 February 2013 - 03:24 PM.


Sponsor:

#2 Cornstalks   Crossbones+   -  Reputation: 6991

Like
2Likes
Like

Posted 20 February 2013 - 03:21 PM

So, I'm looking for advice on it. For these platforms, can you tell me if standard exceptions are supported by any compilers and allow them to be disabled, and bear in mind, I don't support all of them:
Xbox
Xbox 360
PS2
PS3
PSP
Pandora
Dreamcast
Nintendo DS
Symbian OS, any versions after s60v3 (I hear the compilers finally support standard exceptions)

Don't know about these...
 

GCC on Windows and Unix derivatives

Yes.
 

Visual Studio on Windows

Yes.
 

Android (I hear the NDK does now)

Yes.
 

iOS (mixing Objective-C with C++)

Yes.
 

Additionally, for most platforms that support disabling exceptions through a compiler switch, what happens when a throw is encountered? Does it raise some sort of signal?

Depends on the compiler. For G++, it calls abort().


[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#3 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 03:27 PM

Calling abort() is ideal behavior in the absence of exceptions, in my opinion, and I'm relieved to hear that. My three main reasons for avoiding exceptions so far are (1) lack of compiler support on some fringe cases of my supported platforms, (2) speed penalty, especially if the compiler won't let you disable them, and (3) what happens if the compiler does let you disable them?

Also, see my edit, if your post came while I was revising.



#4 swiftcoder   Senior Moderators   -  Reputation: 10445

Like
1Likes
Like

Posted 20 February 2013 - 03:27 PM

GCC on Windows and Unix derivatives
Visual Studio on Windows
Android (I hear the NDK does now)
iOS (mixing Objective-C with C++)

All of the above fully support exceptions.

 

PS3

As I understand it, the PS3's hardware isn't well suited to exceptions (i.e. the SPU programming model). Then again, it's also not very well suited to software that wasn't written with the SPU in mind - the chances that your general-purpose library will be used here are quite slim.

 

Xbox 360

Supported, but disabled by default.

 

Xbox
PS2
Dreamcast
Symbian OS

Are any of these actually realistic target platforms for a modern game development library? Might as well throw BeOS and Amiga into the mix...


Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#5 Cornstalks   Crossbones+   -  Reputation: 6991

Like
0Likes
Like

Posted 20 February 2013 - 03:30 PM

My most frequent use-case for exceptions is to indicate something that should not happen in a normal work load has happened (out of memory, data that goes into an internal structure from external code is invalid, attempting to use something that is in a valid but explicitly unusable state). In these cases, I would use a debugger to figure it out, but swallowing exceptions is against my style, so they would only occur in areas that must be fixed; a perfect run should never throw them, but still have the option of handling them just in case, like the extremely rare circumstance of expecting memory allocation to fail if you allocate based on input, and displaying an error asking for a different value, though nothrow new could just as easily be used.

May I ask why you can't just use assert()?


[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#6 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 03:40 PM

the chances that your general-purpose library will be used here are quite slim.


I figured, after reading a lot about the architecture. Mostly out of curiosity; if I ever do write homebrew for the console, I'd be doing quite a bit of rewriting with the architecture in mind, or using a 3rd party library, so knowing if exceptions are usable would still be good knowledge; ideally, I'd be hitting the exceptions while testing on a machine that is set up to perform like it. The way I outline using exceptions would make it so that hardware differences would result in triggering an assertion; anything that would fail the same way on a test machine as it would on the target platform would be covered by exceptions.
 

 

May I ask why you can't just use assert()?

 

assert() can't be handled, so I would reserve it for the most extreme cases when an invariant is violated, and the execution _cannot_ proceed. For an example, a UTf-8 string class.

 

I'd return an error code if a character isn't found in the string. This is one of many expected results.
I'd throw if someone inputs invalid data. This is unexpected, and someone else's fault. They can catch it an fix it if they feel compelled to do so. I'd just validate/sanitize first.
I'd assert if I encounter invalid data in the private string processing functions. This should never happen, and there's no way to recover. If the string is now invalid when it shouldn't be, I have no idea what the string should be, or what to do with it. The behavior cannot match what it would be if the string were perfect, and a program that centers entirely around string handling will be devastated by this event.

 



#7 swiftcoder   Senior Moderators   -  Reputation: 10445

Like
2Likes
Like

Posted 20 February 2013 - 03:43 PM

Ok, so in general, there are 3 sources of error:

  1. Bad input (wrong input, wrong charset, etc).
  2. Negative outcomes of normal conditions (divide by zero, null pointer, etc).
  3. Bad Shit™ (out of memory, solar flares, etc.)

You can (in theory) always sanitize/discard (1), and always anticipate (2). Unfortunately, you just have to deal with (3) as it occurs.

 

***

 

C++ is notoriously bad about (2), so you are actually forced to anticipate those (and mostly, either prevent or ignore them).

 

You should always be validating (1) at the surfaces of your system, so those can be handled with a simple error code return.

 

But I've never found a decent way to deal with (3) in the absence of exceptions...


Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#8 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 04:31 PM


Ok, so in general, there are 3 sources of error:

  1. Bad input (wrong input, wrong charset, etc).
  2. Negative outcomes of normal conditions (divide by zero, null pointer, etc).
  3. Bad Shit™ (out of memory, solar flares, etc.)

You
can (in theory) always sanitize/discard (1), and always anticipate (2).
Unfortunately, you just have to deal with (3) as it occurs.

 

***

 

C++ is notoriously bad about (2), so you are actually forced to anticipate those (and mostly, either prevent or ignore them).

 

You should always be validating (1) at the surfaces of your system, so those can be handled with a simple error code return.

 

But I've never found a decent way to deal with (3) in the absence of exceptions...


I agree, for the most part, however (2) is a bit of a gray area.

There are many instances where I'd return an error code, throw an exception, or trigger an assertion for these things.
Some examples:

Return an status code:
Specifying an aspect ratio with a zero denominator. It'd be business as usual to report failure in some cases, or ask the user for another input.
A function that manipulates a structure, or if the structure is null, allocates a new one instead. The return value would change from being the one passed to the newly allocated buffer.
strtok() is a murky example; passing null is valid, and will result in different behavior than passing a non-null value. However, if you pass null without first making a call with a valid pointer, there is an error. Thus, passing null may or may not be an error condition. And if it isn't an error condition, the return value can still change. You might choose to have an implementation throw on receiving null on the first call, or simply return null, as if the string were empty.

Throw an exception (not exactly null pointer or divide by zero):
Failure to allocate memory. This is capable of terminating an application if not handled, but it can be valid if you allocate a large buffer to do things faster, but if it fails, choose to slowly handle one element at a time.
Reading in an XML file. The file exists, the header is fine, but it encounters a control character in the text. It's possible to provide a lot more information about what happened if you throw an exception (what it was and where), rather than return a simple status code saying "SYNTAX_ERROR" or "INVALID_CHARACTER".
Access a vector element out of bounds. There is no way that you can return a unique error value, and an assertion should be avoided (in my opinion) because it was caused by code outside of the vector's implementation. It may be possible to recover from this, so the caller should be given the opportunity to do so.
A divide by zero is caused by code no-where near where the division occurred, so a return code can't help it. If it doesn't violate an invariant of the class, it may be a good idea to allow the caller to handle it, like a scientific calculator that is allowed to have either operand be zero, so dividing two variables wouldn't be anything noticeable until the values are evaluated. Then assert or throw.

Trigger an assertion:
An image's bytes per pixel is zero, when there's an invariant in place that it must be valid. This shouldn't happen.
A memory allocator's pool is null. That can't be good, and chances are, this can't be recovered due to being unable to allocate.
A HUD drawing function tries to render the text in a null string. This should have been handled somewhere way higher up.

Some of these, are merely differences of opinion. For instance, I'd make out of memory an exception, and solar flares an assertion.



#9 swiftcoder   Senior Moderators   -  Reputation: 10445

Like
0Likes
Like

Posted 20 February 2013 - 04:46 PM

I agree, for the most part, however (2) is a bit of a gray area.

Everything in (2) is things that you can't catch in C++. 

 

There is no portable way to recover from dereferencing a null pointer, or a divide by zero exception (and on a PowerPC chip, 1/0 = 0, so you won't even know it has occurred).


Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#10 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 05:11 PM

dereferencing a null pointer


Ah, _dereferencing_ a null pointer. Yeah, that gives a better idea of what you mean. Yeah, for those events, I can only think of avoiding them.

However, as far as (3) goes, I draw a division of what to do based on whose fault it was. If it was their fault, the problem can possibly be remedied, and thus a catchable exception should be thrown. If it was my fault, due to invariants being violated, or things that shouldn't be possible occurring, I use an assertion.

Though, we may be agreeing perfectly on this, because solar flares aren't something the language is capable of detecting. :)

#11 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 05:26 PM

Also, what is your opinion on defining new exception classes? Do you always extend a standard exception class, or do you define your own base class? Additionally, do you define all of your own exceptions, or do you only define new exceptions that don't cover what the standard ones don't?



#12 Adam_42   Crossbones+   -  Reputation: 2619

Like
1Likes
Like

Posted 20 February 2013 - 05:56 PM

Here's an alternative to throwing exceptions for game development purposes:

 

1. Any error that's caused by bad code, or is unrecoverable, should call a custom assert() macro so the program halts immediately (when IsDebuggerPresent() returns true) so the author of the bad code can see what's wrong and fix it. Any build that's not a final one should report the issue to the tester somehow so they can submit a bug report. Ideally you'd save out a crash dump to go in the bug report.

 

2. Any error that is caused by bad data should (in non-final builds) inform the user at runtime e.g. with MessageBox() for at least the first instance of that error so that someone who adds bad data and then tries to run the game can identify what's bad with their data and fix it. The game should then work round the error, e.g. by returning some default data that visually represents an error. There's not much data in a game that can't be replaced reliably like that. You could also have a button on the message box to allow a tester to treat it as if it was a fatal error.

 

3. For error conditions where it's unclear, go for #2 if possible.

 

 

Generally this will all be done via game engine code, so a portable library could expose error / warning callbacks, and provide a default implementation that does something simple and portable (e.g. call printf() and assert(0)).

 

 

The D3D11 implementation has a similar approach, but it's overkill for a small library. It lets you disable specific warnings, and ask it to break in the debugger on the error types you're interested in.



#13 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 06:29 PM

I used to be a huge fan of assertions, though exceptions speak to me in a higher-level sense.

Another problem that I encounter, is that if I am going with the rationale that I'd disable exceptions in a release build, then no behavior that relies on exceptions being caught can be used, because attempting to swallow an exception will instead never get called, and the application would terminate, or whatever the compiler does decide to do. Thus, exceptions can only be used for fatal circumstances, resulting in behavior that is like glorified assertions.

Can anyone provide a reason to use exceptions over assertions, given that exceptions may be disabled at any point? I feel that I should use exceptions, to have code that will provide added functionality if the application is compiled with exceptions enabled in all builds, like an application that isn't relying on the fastest execution time.



#14 ApochPiQ   Moderators   -  Reputation: 16415

Like
1Likes
Like

Posted 20 February 2013 - 07:28 PM

Exceptions and assertions are for different things.


Use exceptions if you need your program's flow to reactively change to unexpected circumstances. Use assertions if you realize that some invariant or expectation has been violated and terminating the program is preferable to continuing blindly into whatever chaos may result. These are orthogonal concerns.


Exception support generally sucks on the current console platforms, btw, and is almost universally disabled for performance-intensive projects. My general rule is: assertions should kill the program and dump core. Anything that would normally trip an exception should kill the program and dump core. Recoverable errors should be handled in that the design of the system should elegantly allow for the program to continue and, well, recover. If all of your target platforms have exceptions and you are very good at exception safe design in C++ and you like them, go ahead and use them. If any of those things fails to be true, don't mess with them (in C++ specifically) because you're liable to blow something up if and when a real error does occur.

I'm on the side of forbidding the use of exceptions entirely in C++, FWIW.

#15 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 08:16 PM

I'm still wavering heavily on the performance concerns, but there's still one way that I can think of exceptions as negating the speed penalty: propagating return codes. I used to spend so much time checking if a return code indicates success, and if not, clean up and return the error to the caller. Then the caller checks if the return code indicates success, and if not, clean up and return the error to the caller... ad infinitum.

Propagating error codes made code much less readable, and it causes you to perform a ton of conditional checks repeatedly to ensure that no error occurs.

It is not hard to imagine that checking error codes results in more time being spent checking for errors during successful execution than when an error occurs; handling an error is very fast to propagate the error, and then have someone take responsibility and do something about the error at the top of the call chain. However, there is an extremely larger percentage of time where an error does not occur. As a result, all of these conditionals to check each and every return code, not even to handle the error but to propagate it up the call stack to the code that will finally handle the error code.

This tells me that even while there is a penalty for using exceptions, it might not be any worse than checking error codes for every little thing.

The only way I can think of eliminating all error checking overhead is to make almost every error a fatal one, where it doesn't get propagated at all, at the expense of being unable to recover from a possibly recoverable error.



#16 swiftcoder   Senior Moderators   -  Reputation: 10445

Like
0Likes
Like

Posted 20 February 2013 - 08:44 PM

I'm still wavering heavily on the performance concern

The performance issues are very different, depending on whether we are discussing consoles, or general purpose PCs.

 

Consoles tend to have abysmal performance when it comes to exceptions. In part this is because the compilers and runtimes have not been extensively optimised for the use of exceptions, and in part because consoles operate within strict hardware limitations.

 

If you are only targeting full-blown PCs, then the discussion is largely moot, because you'd have to be using exceptions to simulate control flow statements before they became a noticeable performance hotspot...

 

---

 

That said, I'm not a fan of exceptions. They can't be thrown across the boundary between threads, processes, hosts or programming languages. And anywhere in your program, you can can never tell if a function call might result in an arbitrary exception being thrown.

 

They have been termed "invisible gotos", and I don't think that is an unfair characterisation.


Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#17 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 09:54 PM

I do like the concept, when used properly. I agree that it isn't easy to know when a function will throw an exception, unless you check its documentation. However, I'd argue that it's just as hard to tell if it will trigger an assertion, when used in place of exceptions.



#18 Hodgman   Moderators   -  Reputation: 31984

Like
2Likes
Like

Posted 20 February 2013 - 10:07 PM

However, I'd argue that it's just as hard to tell if it will trigger an assertion, when used in place of exceptions.

Every function should be a candidate for triggering an assertion, but that doesn't matter because the caller doesn't need to be aware of this. The caller just needs to be aware of any pre-conditions -- e.g. "index must be smaller than size" -- and know that if these mustn't be violated. I find it cleaner to say that these things can't be violated "or else!" (so that in the retail version you can remove all the error checking code) rather than saying that if they are violated then an error will be returned (because then the error handling is part of the interface, and can't be removed in case a caller relies on it).
 
With exceptions, the caller needs to know what kinds of assertions can be thrown under which circumstances, so that they can either handle them, or add them to their own documentation and pass the buck to their own caller. Failure to do so will result in an unhandled exception, with your program unwinding to main, so callers need to know.
 
With assertions, if something is broken, then you get a crash dump / debug breakpoint so that you can fix your code, which is alerting the developer, not the caller.
 

The only way I can think of eliminating all error checking overhead is to make almost every error a fatal one, where it doesn't get propagated at all, at the expense of being unable to recover from a possibly recoverable error.

Do you have any examples of a recoverable error that you want to support? e.g. in my experience, on consoles if you get a std::bad_alloc, then you've already lost -- if the program needs 600MiB of RAM to run, then it can't run, and needs the attention of the developers, there's nothing a catch block can do to fix the game.
 


My most frequent use-case for exceptions is to indicate something that should not happen in a normal work load has happened (out of memory, data that goes into an internal structure from external code is invalid, attempting to use something that is in a valid but explicitly unusable state). In these cases, I would use a debugger to figure it out, but swallowing exceptions is against my style, so they would only occur in areas that must be fixed; a perfect run should never throw them, but still have the option of handling them just in case, like the extremely rare circumstance of expecting memory allocation to fail if you allocate based on input, and displaying an error asking for a different value, though nothrow new could just as easily be used.

May I ask why you can't just use assert()?


QFE -- for any kind of error that requires the attention of a developer in order to fix it, then exceptions are harmful. When you throw an exception, you're unwinding the call-stack, which is like ordering a clean-up crew to a crime-scene before forensics have arrived. In order to debug the issue, you need to freeze the crime scene as soon after the illegal operation as you can.

Edited by Hodgman, 20 February 2013 - 10:25 PM.


#19 ApochPiQ   Moderators   -  Reputation: 16415

Like
2Likes
Like

Posted 20 February 2013 - 11:06 PM

I still maintain that exceptions and assertions are orthogonal concepts.

 

An assertion says "something happened which emphatically should not be possible" -  a contract violation, or something fundamentally broken in the code of some sort. Pinpointing the cause of this failure and preserving the complete context of the assertion failure is paramount. You can and should dump core - any reasonable user-facing system should automatically submit the complete callstack and (ideally) a minidump to the developer. Any middleware should gracefully fail with one of two kinds of information: a complete description of the nature of the assertion and what, if anything, the caller did to trip it; or a comprehensive location identification and relevant state so that the middleware author can locate, ideally reproduce, and then fix the internal cause of failure.

 

Exceptions are totally different. An exception says "something happened which I cannot handle in this current control flow context." The implicit assumption is that someone higher up the call stack can deal with the situation. If you do not comprehensively catch exceptions when they can possibly be thrown (accidental oversight aside), you are using exceptions wrong.

 

Earlier in this thread, someone made the remark that having a "disabled" exception call abort() is a reasonable tradeoff. I think this reflects a completely misguided interpretation of how exceptions are meant to be used. Exceptions should be thought of as recoverable in almost all circumstances. If you want a recoverable error, the right thing to do is to catch the exception at the appropriate contextual site and recover. If you want to kill the program, you should dump core and immediately exit, period. Unwinding the stack in this scenario is a sin and you should be dragged behind the metaphorical woodshed and beaten to within an inch of your life if you ship software that unwinds the stack and then abnormally terminates. Put another way, having a try/catch in main() is a crime against humanity.

 

If we're talking about a middleware product, for instance, I should also get consistency across platforms; if I compile your code with exceptions off, I shouldn't get abnormal exits left and right when recoverable errors occur. By the same token, if I'm used to your code dumping core when I do something wrong, I shouldn't port to a new platform and suddenly get your exceptions getting thrown instead.

 

In fact, if this is for a C++ middleware product, exceptions shouldn't even be considered an option. The boundaries and limitations on exception propagation have been hinted at already, but it cannot be overstated that these will cause your users to have painful problems. Even header-only libraries should avoid C++ exceptions because you can't guarantee that the client code is exception safe (or even has them turned on).

 

Genuine "oh shit" moments should assert and dump core, end of story. Recoverable errors should be propagated by return or out-parameter or some other easily retrievable status field, depending on your preferred style.

 

If this is not middleware, it's really up to your preferences, but please don't abuse exceptions, especially not in C++.

 

If this is not C++, then we need to re-frame the discussion, because other languages have occasionally managed to not suck at exceptions as badly as C++ does :-)



#20 Ectara   Crossbones+   -  Reputation: 3066

Like
0Likes
Like

Posted 20 February 2013 - 11:31 PM


Failure to do so will result in an unhandled exception, with your program unwinding to main, so callers need to know.


I definitely agree with this.

 

 


Do you have any examples of a recoverable error that you want to support?

My guess would be benign ones like out_of_range. I haven't designed any exceptions yet, because I'm still on the fence about what they would be used for, if anything, so I have a hard time answering that one.

To be honest, I have a hard time deciding which path I should go, because I'm hearing conflicting viewpoints in this thread, and around various other blogs, fora, and reference sites. Some people say to never throw, and to die immediately in every bad circumstance. Others say that most talks of side-effects of exceptions are ill-informed, and there's nothing wrong with using them for performance. I would personally subscribe to a three-pronged approach, but then there is a problem that code that interacts with this library might not have exceptions enabled, and thus I can't use exceptions for recoverable things, because this will result in abortion. Then there's the problem that unwinding the stack for fatal problems is nonsensical, so using them in place of assertions makes no sense. So, if I can't guarantee that exceptions can be used, they seem to get underfoot. However, having the execution terminate at the first sign of unexpected trouble seems like an unfriendly policy for other code that uses this library.

So, I'll entertain this thought pattern, too. If assertions are used for everything that isn't useful and expected behavior, how does this affect the usage of constructors and RAII principles? Does this not affect one's methodologies if they swap from using exceptions if a fatal error occurs in a constructor? I also feel like this would create the strange requirement that a custom allocator class should trigger an assertion if it fails to allocate memory, like new would if exceptions are turned off.


Edited by Ectara, 20 February 2013 - 11:36 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS