Archive for October, 2006

Signal Retraction

Qt 4 introduced easier ways to queue method calls until the next eventloop cycle. Now you can connect signals and slots together with QueuedConnection. You can use the connectionless QMetaObject::invokeMethod() with the QueuedConnection mode as well. In the old days, a delayed method call took more work. Usually you would store argument values somewhere, use a QTimer to invoke a special slot at a later time, and this slot would then take the original argument values and call the method you wanted to call in the first place. With Qt 4, you can get this stuff in one line.

Anyway, delayed calls are often a good thing. They are most useful for assisting with delayed signals (which can also aid in making your object signal-safe). In fact, if you wanted to go overboard, you could emit all of your signals late. This would make your object pretty darn safe, just not very optimized.

There is a price to all of this delayed business though. One major issue is that the state of the object may have changed between the emit and the time of receipt. Maybe a certain object expects you to inspect one of its properties from within a slot connected to one of its signals. If you were to make this a QueuedConnection, it might not be possible to use this object correctly. Of course, given that the connection mode is the user’s choice, hopefully no new objects are being written that rely on DirectConnection. If they are, they’d better be documented as such.

Unfortunately, there are some cases where getting the state right just isn’t possible, at least not in a clean way. This is when you have object re-use or object deletion.

Object Re-use

Take, for example, QTcpSocket. Imagine you set up your signals using QueuedConnection, then call connectToHost(”server1″…), abort(), connectToHost(”server2″…) (in that order), and server1 is up but server2 is down. There is a race condition, whereby connected() might be emitted (for server1), and then error() (for server2), but your application receives both of these signals after calling the second connectToHost(). Since you cannot associate these signals with a particular “usage” of QTcpSocket, the assumption is that you were able to connect to server2. In fact, if it takes some time before the connection to server2 results in an error, your application might be confused for awhile.

One solution to this problem might have been to have connectToHost return a context id, which would then be present in any subsequent signals. This is the natural approach to an asynchronous system. Of course, this is also ugly and would defeat much of the clean fun of Qt programming, so we won’t give this any further consideration. :)

Anyhow, this problem is not limited to making QueuedConnection signals with QTcpSocket. It is conceivable that QTcpSocket might use some queued stuff internally. Indeed, I’ve read the code and I’ve seen QMetaObject::invokeMethod() in there. This has the same kind of potential, where actions might be queued, and they are still in the queue even if you reset the object for a new use.

The answer? Don’t use QueuedConnection on a stateful object (like QTcpSocket), and delete the object if you want to cleanly re-use it. Any queued business will die with the object. Yes, this means abort() is useless to you.

Object Deletion

Deleting an object will stop any internal queued stuff.

However (and this is a big one), “externally” queued stuff, such as a signal/slot connection that you made using QueuedConnection, will not be stopped. Qt only verifies the signal and slot connection during the time of emit. When it is time for the slot to be called, only the receiver needs to still exist. This means that if you are using QueuedConnection, it is entirely possible that you may delete an object, only to later have one of your slots be called because the sender emitted a queued signal just before you deleted it. Fortunately, Qt does zero out the sender() if it no longer exists at the time of the slot.

The answer? Don’t use QueuedConnection on a stateful object if you want to be able to delete it without it haunting you.

Signal Retraction

Sometimes, you need to queue an action internally. We already went over the benefits. The problem is that by doing queued things, your object is not easily trusted for re-use. If someone wants to re-use your object, then they are better off deleting it and starting fresh. Can we improve the situation? Yes! With Signal Retraction.

Signal retraction is where a queued outbound signal is canceled when the object is re-used. For example, if QTcpSocket practiced signal retraction, then calling abort() would cancel all queued activities, such that we wouldn’t receive the connected() signal from the first usage. This ensures that the signals received are safely coupled with the usage that caused them.

So how the heck do you retract a signal? By any means possible, I don’t care how you do it. Go digging in the event queue and destroy the event if you want. :) Personally, I tend to use QTimers with a timeout of 0, and emit my signal when the timeout() occurs. I use a real QTimer object, not singleShot(), so that I can call stop() on it if my object needs to reset, thus preventing the emit.

Here’s an example using QTimer:

class MyObject : public QObject
{
        Q_OBJECT
public:
        bool ready;
        QTimer readyTrigger;

        MyObject() : readyTrigger(this)
        {
                ready = false;
                connect(&readyTrigger, SIGNAL(timeout()),
                        SLOT(ready_timeout()));
                readyTrigger.setSingleShot(true);
        }

        // kicks the object into motion.  emit ready() when ready.
        void start()
        {
                ready = true;
                readyTrigger.start();
        }

        // reset the object to the initial state.  no ghost signals.
        void reset()
        {
                ready = false;
                readyTrigger.stop();
        }

signals:
        void ready();

private slots:
        void ready_timeout()
        {
                emit ready();
        }
};

There’s a little bit of delayed signals philosophy going on in there, if you’re confused about the way it is written.

Happy coding!

Comments

Nested Eventloops

To define the title: a nested eventloop is when you invoke the eventloop again rather than returning back to it. This is QCoreApplication::processEvents(), QDialog::exec (), QMessageBox::information(), and the like. In all but the most controlled situations, performing a nested eventloop using these traditional methods is dangerous and should be avoided. On the other hand, restricting event processing to a specific subset of objects can be safe. Unfortunately, Qt doesn’t provide a direct and general way to perform this kind of “scoped” eventloop.

First, let’s discuss why the traditional nested eventloop functions are bad. Spinning the eventloop is a big deal. Every object in the application (or, more specifically, in the thread, if your app is multithreaded, but most aren’t) will run when the eventloop is run. This means event handlers being called, possible signals emitted as a result of those events, etc. This can cause unpredictable behaviors. If the application has not “returned to the eventloop” yet, then there may be functions on the stack that are incomplete, and so the entire state of the program is not ready to receive events yet. A form of re-entrancy is also possible, whereby an event handler is called, sometime down the line the eventloop is run again, and then the same event handler is called again. The event handler is now on the stack twice. Did you write your application to work under this kind of condition?

Here is an example of an object that prompts the user for two socket ids, and then emits a signal when either one has data. This is a bit contrived, but it is short, and the problem should be easy to spot.

class MyObject
{
        QSocketNotifier *sn1, *sn2;

        void setup()
        {
                int sockfd1 = get_next_sockfd();
                sn1 = new QSocketNotifier(sockfd1, QSocketNotifier::Read);
                connect(sn1, SIGNAL(activated(int)), SLOT(sn_activated(int)));

                int sockfd2 = get_next_sockfd();
                sn2 = new QSocketNotifier(sockfd2, QSocketNotifier::Read);
                connect(sn2, SIGNAL(activated(int)), SLOT(sn_activated(int)));
        }

        int get_next_sockfd()
        {
                return QInputDialog::getInteger(0, "Prompt", "Socket FD:");
        }

signals:
        void dataReady();

private slots:
        void sn_activated(int)
        {
                delete sn1;
                delete sn2;
                emit dataReady();
        }
}

Do you see it? QInputDialog::getInteger() spins the eventloop. If the first socket has data to read, the second call to getInteger might cause sn_activated(sockfd1) to be called. If this happens, the program will likely crash at the “delete sn2;” line. Now, one obvious fix would be to set sn2 to zero at the top of setup(), but that’s not the point of this discussion. The point is that the use of getInteger() may mean you need to take additional precautions with your code, that you normally wouldn’t have to do. This was a simple example. In a more complex, real-world situation, you may not know which function is spinning a nested eventloop, and the fixes may not be as straightforward.

In simple applications, running a nested eventloop is not a big deal. If you aren’t running any non-GUI objects, then feel free to use QMessageBox::information(). It is convenient, and modality usually protects you from re-entrancy. However, if you do have non-GUI objects, then QMessageBox::information() is a potential disaster. Suppose you have slots listening for network data. Those slots might be called, and your application may not be written to work properly in such conditions.

Ultimately, what this means is that nested eventloops can be safe if you know they will be safe in a given situation. But if you don’t know that they will be safe, then you cannot use them. And if you are a library author, you’re in a pickle, because you cannot know if the application will be safe. Please, if you are writing a library, DO NOT use processEvents(), QMessageBox::information(), etc. If you absolutely must use a nested eventloop in a library, any method leading to that effect should be clearly documented. This way, the application developer can either avoid the function, or design his application to withstand the effects.

You might say that problems with nested eventloops are the result of poor programming at the higher level. That is, the application should be designed to “withstand the effects” of any method potentially running a nested eventloop. This is one of Trolltech’s positions (as of the time of this writing, October 23rd 2006, which I may talk more about later). Anyway, I’m here to tell you that you cannot write such a generic application. What if QString::operator+() invoked a nested eventloop? Good luck trying to make your application withstand such a thing, even if you were told about it up front. But having to account for any random method invoking a nested eventloop? Not possible. It would be like thread programming without mutexes.

We need assurances, as programmers, of what functions are going to do. We need to know what global variables they might modify (consider functions that return static data). We need to rely on their signatures being correct at runtime (return value, arguments). If we cannot have any expectations about the methods we call, then it would not be possible to write programs. If a method may run a nested eventloop, the user of the method must be informed about it.

In short, avoid using nested eventloops. This concept joins Signal Safety and Delayed Signals as another essential Qt programming practice for clean and predictable code.

Comments

In-place builds

It may not be widely known that Psi (as well as QCA and Qt) can be built and used “in-place”. This means you configure and make, but there is no ‘make install’ step. This can be convenient if you don’t want to install these packages system-wide.

Of course, you can always just set a prefix within your home directory (or other user-writable area) when building sources if you don’t wish to install system-wide. However, in-place builds are extra convenient for the developer, as it saves the trouble of having to do ‘make install’ every time something is changed.

So while this is not anything new for Psi development, and nor is the idea particularly revolutionary, it might be something you didn’t know you could do and so I’m documenting it here. Personally, I develop everything in-place.

Qt:

Qt 3 is/was always built in-place. You extract it where you want it to end up, configure and make. There is no ‘make install’ step. With Qt 4, they changed it to behave more like other packages. Now the prefix defaults to somewhere in /usr/local, and there’s a ‘make install’. However, the old way is still supported if you pass the directory of where you extracted Qt to -prefix. That is:

$ tar zxvf qt-x11-opensource-src-[version].tar.gz
$ cd qt-x11-opensource-src-[version]
$ ./configure -prefix $PWD
$ make

This is not a hack or a way to trick Qt. It is actually a supported feature, and when Qt is done configuring you’ll notice it says:

Qt is now configured for building. Just run 'make'.
Once everything is built, Qt is installed.
You should not run 'make install'.

Another feature of Qt 4 is that qmake is aware of the installation location. This allows it to locate the necessary files it needs without QTDIR. Additionally, all subtools (e.g. moc) are invoked using their full path. This means you don’t even need the Qt 4 bin directory in your PATH. Finally, if you’re using gcc, qmake also uses rpath when linking. This means you don’t need Qt 4’s lib directory to be specified anywhere either. No need for /etc/ld.so.conf or LD_LIBRARY_PATH. You simply build Qt, go into your project directory, and run qmake.

$ cd /path/to/myproject
$ /path/to/qt/bin/qmake
$ make

QCA:

Like Qt, QCA (as of 2.0 beta2, I believe) supports in-place builds by pointing prefix to $PWD.

$ ./configure --prefix=$PWD

Note: If you’re building against an in-place Qt, and you didn’t put Qt’s bin directory in your PATH, then you’re going to have to tell configure where Qt is. Do this with –qtdir. For example:

$ ./configure --prefix=$PWD --qtdir=/path/to/qt

Unlike Qt, QCA doesn’t use rpath. This may change in the future, but for now you will need to put QCA’s lib directory in your LD_LIBRARY_PATH if you want to use it in-place:

$ export LD_LIBRARY_PATH=$PWD/lib

There’s actually one hidden secret you must do next, and it is sort of cheating. You need to copy crypto.prf into your qmake feature directory. This is so projects that need QCA can find QCA.

$ cp crypto.prf /path/to/qt/mkspecs/features

Fortunately, this file doesn’t ever change, so if you later modify QCA in-place, there’s no need to copy this file again.

QConf:

With the latest development version (as of October 22nd, 2006), qconf can be run without doing ‘make install’. In fact, you can run it from another location, and it locates its data files relative to the executable. This allows it to work similar to qmake, in that you can invoke it by specifying the full path. Unlike qmake, however, this is not dependent on the prefix. That is, you can configure qconf with the default prefix of /usr/local, and you can still run qconf from anywhere without installing it.

$ ./configure --qtdir=/path/to/qt
$ make
$ cd /path/to/myproject
$ /path/to/qconf
$ ./configure ...

Psi:

Psi can be built and used in-place. Once compiled, just run it.

$ ./configure --qtdir=/path/to/qt
$ make
$ ./psi

That’s it!

You can build and use all the Psi tools without mucking with your system, and without ever doing ‘make install’ either.

Comments (2)

Bad Behavior has blocked 557 access attempts in the last 7 days.