Handling Ctrl-C in applications

Problem

Any C/C++ application needs to handle signals like SIGINT (Ctrl-C) gracefully. WireAPI captures signals, so these don't get passed to the application. If you write a naive application you can find that your code never exits when you use Ctrl-C - and you need to explicitly kill the process with a SIGTERM (kill -9).

The following example shows the problem. You can compile this and run it (with a broker running locally), then press Ctrl-C. The program never exits:

#include "base.h"
#include "amq_client_connection.h"
#include "amq_client_session.h"

int main (int argc, char *argv [])
{
    amq_client_connection_t
        *connection;
    icl_longstr_t
        *auth_data;

    //  Initialise system
    icl_system_initialise (argc, argv);

    //  Open a connection
    auth_data = amq_client_connection_auth_plain ("guest", "guest");
    connection = amq_client_connection_new (
        "localhost", "/", auth_data, "loop example", 0, 30000);
    assert (connection);
    icl_longstr_destroy (&auth_data);

    FOREVER {
        sleep (1);
    }
    //  Close the connection
    amq_client_connection_destroy (&connection);

    //  Uninitialise system
    icl_system_terminate ();

    exit (0);
}

Explanation

If you trace the code, you will see that it simply loops for ever in this block:

    FOREVER {
        sleep (1);
    }

To make the code work properly we need to do two things:

  • Check the health of the connection and exit the loop if the connection dies.
  • Call the WireAPI connection wait method instead of doing a sleep() call.

Let's take these one at a time. First, checking the health of the connection:

    FOREVER {
        sleep (1);
        if (!connection->alive)
            break;
    }

When we compile and run the code now, we see that it does actually exit when we press Ctrl-C. What is going on? In fact WireAPI is detecting the interrupt, correctly shutting down, and setting the connection alive flag to zero.

However, we see that the application still sleeps for a second between checks. So when we press Ctrl-C it responds only after a short delay (average half a second, of course).

WireAPI provides a smart sleep method, called wait, which does several useful things:

  1. It pauses the application.
  2. It returns as soon as something interesting happens - e.g. a message arrives, or the connection closes.
  3. In single-threaded builds, it lets WireAPI operate.

In fact, without doing wait calls, your application won't work properly in a single-threaded environment since the sleep() calls will block the whole application, including WireAPI.

When we add the wait call - using a 1-second wait - we get this:

    FOREVER {
        amq_client_connection_wait (connection, 1000);
        if (!connection->alive)
            break;
    }

The program now works properly and responds immediately to a Ctrl-C.

Comments

Add a New Comment

Edit | Files | Tags | Source | Print

rating: +1+x

Author

Pieter Hintjens <moc.xitami|hp#moc.xitami|hp>

All tutorials

Performance Tests: This tutorial explains how to do OpenAMQ performance tests using 0MQ performance testing framework.

Broker federation: How to setup a geographically distributed federation of OpenAMQ brokers and how to define dataflows between individual geographical locations.

The ESB cookbook: How to implement your own Enterprise Service Bus, step by step, using OpenAMQ.

Publishing contents: How to publish contents at high speed without bizarre memory issues.

Tuning OpenAMQ for low latency: How to tune your operating system and hardware to get the lowest latency, i.e. the best response times, from your OpenAMQ broker.

The mandatory and immediate flags: How to use the 'mandatory' and 'immediate' flags on the Basic.Publish method.

Load balancing: How to use OpenAMQ to distribute work between multiple applications, a technique called "load balancing".

Content based routing: How to route messages based on their header properties. It is a fast and simple way to do 'content based routing' without needing to inspect entire messages. We explain the principles and provide worked examples.

Transient or durable subscriptions: How to make subscriptions that are transient (come and go with their consuming applications) or durable (stay around).

The AMQ model: How the AMQ model works: this is a basic backgrounder for anyone starting to use OpenAMQ in their applications.

Developing on Windows: How to build OpenAMQ client applications using MSVC on Windows

Handling Ctrl-C in applications: How to properly detect an interrupt (Ctrl-C) and shut-down gracefully in C/C++ WireAPI applications.