wiki:PadawanPitfalls
Last modified 21 months ago Last modified on 26.01.2017 14:58:11

Padawan Pitfalls

This page lists several problems that you are likely to encounter if you do not read this. Sounds weird? Read on, it gets even weirder!

Make sure that you have also read about the FawkesGuarantees!

Thread initialization and finalization

Thread initialization and finalization (from now on called inifin) is an important thing. Here are some things that you have to keep in mind:

init() must throw exceptions on error:

It is not enough to just detect that there is an error and log it. If there is anything that will violate the initialization assumptions in loop() you have to throw an exception, any exception will do! Re-throw an exception that you caught, create a new one. The important thing is that it has a meaningful error message (and is derived from Exception). If your init() method throws an exception the thread is never started! Thus you do not have to check in loop() for the resources claimed!

Free resources in init() on error:

If your initialization fails for any reason and you have to throw an exception, make sure that before throwing the exception you cleanup all the resources claimed and you unregister you thread from any facility that you registered it for (for example from the Fawkes Network Hub). The reason for this is that finalize() is not called in that situation (if the thread does not get started), and in the destructor it is not safe to destroy the resources because you don't know if or when init() failed.

finalize() may not throw exceptions:

Catch exceptions in finalize(), log the error and then go on with finalization. Since your thread is about to be stopped anyway you should finalize all you can, if there is an error log it and go on, do not stop the finalization after only half of it is done just because one step failed.

Free resources in finalize() claimed in init():

Any resources you claim in init() have to be freed in finalize(). For example close all BlackBoard interfaces that you opened, unregister from any facility you registered for etc.

Logging

Log as much as needed, and as little as possible:

Logging is a good thing in general, it helps to find bugs and to locate problems while running the software. But for two reasons it is a good things to use logging only sparsely: firstly logging is an expensive operation. Even if it is not that expensive, it's still extra work and consumes resources (IO is in general not that cheap because it is rather slow compared to everything else your plugin can do). Secondly if there are too many trees you don't see the forrest. This means that if you log too much the important stuff goes unseen. Thus report errors, and sparse special events that are good to know and happen rarely (like loading a plugin), log as much as you like during the development phase, but at a tournament make your application shut the f* up.

Network Communication

You need someone to send data to:

If you send data over the network it has to be addressed to someone. For this the [doxygen:FawkesNetworkHub FawkesNetworkHub] provides two different methods: the first is unicast, sending the message to a specific receiver and the second is broadcast, sending the message to all currently connected clients. In general broadcasting should be used rarely, since in most cases not all clients want the information you send. So in most cases you probably want some kind of subscribe/unsubscribe scheme such that a client can turn on and off that you send specific notification messages.

Check the incoming component ID:

If you receive messages for example using the [doxygen:FawkesNetworkClient FawkesNetworkClient] check the component ID for each and every incoming message. If you only look at the message ID it can happen that you receive a message with a message type you know, but for another component. This will lead to chaos and havoc because you interpret the message wrongly. Note that you shall check the component ID even if you only expect messages for one component. Output a warning or call aunt Tilda if you receive a message for an unexpected component ID. We call this defensive programming to eliminate problem because of other's failures before it bugs you. See the navigator GUI as an example.

BlackBoard

I do not get any new data:

The typical cause for this kind of problem that you have forgotten to add a read() call for the interfaces. For a typical interface the data exists three times, once in the writer, once in the blackboard, and once in the reader (this is you). To always have consistent data and to allow for choosing the point in time when to update it, the data is only updated on request. The writer needs to call write() to copy its version (that it may modify without influencing anyone else) to the blackboard. From there, readers can pull in the latest data by calling read(). This also holds true if you use a BlackBoardListener to get notified about data updates, you still have to call read() yourself!