# Chapter 34. Advanced igraph programming

## 1. Using igraph in multi-threaded programs

The igraph library is considered thread-safe if it has been compiled with thread-local storage enabled, i.e. the `IGRAPH_ENABLE_TLS` setting was toggled to `ON` and the current platform supports this feature. To check whether an igraph build is thread-safe, use the `IGRAPH_THREAD_SAFE` macro. When linking to external versions of igraph's dependencies, it is the responsibility of the user to check that these dependencies were also compiled to be thread-safe.

### 1.1. `IGRAPH_THREAD_SAFE` — Specifies whether igraph was built in thread-safe mode.

    #define IGRAPH_THREAD_SAFE

This macro is defined to 1 if the current build of the igraph library is built in thread-safe mode, and 0 if it is not. A thread-safe igraph library attempts to use thread-local data structures instead of global ones, but note that this is not (and can not) be guaranteed for third-party libraries that igraph links to.

### 1.2. Thread-safe ARPACK library

Note that igraph is only thread-safe if it was built with the internal ARPACK library, i.e. the one that comes with igraph. The standard ARPACK library is not thread-safe.

### 1.3. Thread-safety of random number generators

The default random number generator that igraph uses is not guaranteed to be thread-safe. You need to set a different random number generator instance for every thread that you want to use igraph from. This is especially important if you set the seed of the random number generator to ensure reproducibility; sharing a random number generator between threads would break reproducibility as the order in which the various threads are scheduled is random, and therefore they would still receive random numbers in an unpredictable order from the shared random number generator.

## 2. Progress handlers

### 2.1. About progress handlers

It is often useful to report the progress of some long calculation, to allow the user to follow the computation and guess the total running time. A couple of igraph functions support this at the time of writing, hopefully more will support it in the future.

To see the progress of a computation, the user has to install a progress handler, as there is none installed by default. If an igraph function supports progress reporting, then it calls the installed progress handler periodically, and passes a percentage value to it, the percentage of computation already performed. To install a progress handler, you need to call `igraph_set_progress_handler()`. Currently there is a single pre-defined progress handler, called `igraph_progress_handler_stderr()`.

### 2.2. Setting up progress handlers

#### 2.2.1. `igraph_progress_handler_t` — Type of progress handler functions

    typedef igraph_error_t igraph_progress_handler_t(const char *message, igraph_real_t percent,
                                          void *data);

This is the type of the igraph progress handler functions. There is currently one such predefined function, `igraph_progress_handler_stderr()`, but the user can write and set up more sophisticated ones.

Arguments:

`message`: | A string describing the function or algorithm that is reporting the progress. Current igraph functions always use the name `message` argument if reporting from the same function.
`percent`: | Numeric, the percentage that was completed by the algorithm or function.
`data`: | User-defined data. Current igraph functions that report progress pass a null pointer here. Users can write their own progress handlers and functions with progress reporting, and then pass some meaningfull context here.

Returns:

`` | If the return value of the progress handler is not `IGRAPH_SUCCESS`, then `igraph_progress()` returns the error code from the progress handler intact. The `IGRAPH_PROGRESS()` macro also frees all allocated memory.

#### 2.2.2. `igraph_set_progress_handler` — Install a progress handler, or remove the current handler.

    igraph_progress_handler_t *
    igraph_set_progress_handler(igraph_progress_handler_t new_handler);

There is a single simple predefined progress handler: `igraph_progress_handler_stderr()`.

Arguments:

`new_handler`: | Pointer to a function of type `igraph_progress_handler_t`, the progress handler function to install. To uninstall the current progress handler, this argument can be a null pointer.

Returns:

`` | Pointer to the previously installed progress handler function.

Time complexity: O(1).

#### 2.2.3. `igraph_progress_handler_stderr` — A simple predefined progress handler.

    igraph_error_t igraph_progress_handler_stderr(const char *message, igraph_real_t percent,
                                       void* data);

This simple progress handler first prints `message`, and then the percentage complete value in a short message to standard error.

Arguments:

`message`: | A string describing the function or algorithm that is reporting the progress. Current igraph functions always use the same `message` argument if reporting from the same function.
`percent`: | Numeric, the percentage that was completed by the algorithm or function.
`data`: | User-defined data. Current igraph functions that report progress pass a null pointer here. Users can write their own progress handlers and functions with progress reporting, and then pass some meaningfull context here.

Returns:

`` | This function always returns with `IGRAPH_SUCCESS`.

Time complexity: O(1).

### 2.3. Invoking the progress handler

#### 2.3.1. `IGRAPH_PROGRESS` — Report the progress of a calculation from an igraph function (macro variant).

    #define IGRAPH_PROGRESS(message, percent, data)

The standard way to report progress from an igraph function

Arguments:

`message`: | A string, a textual message that references the calculation under progress.
`percent`: | Numeric scalar, the percentage that is complete.
`data`: | User-defined data, this can be used in user-defined progress handler functions, from user-written igraph functions.

Returns:

`` | If the return value of the progress handler is not `IGRAPH_SUCCESS`, then `igraph_progress()` returns the error code from the progress handler intact. The `IGRAPH_PROGRESS()` macro also frees all allocated memory.

#### 2.3.2. `IGRAPH_PROGRESSF` — Report the progress of a calculation from an igraph function, printf-like (macro variant).

    #define IGRAPH_PROGRESSF(args)

This is the more flexible version of `IGRAPH_PROGRESS()`, having a printf-like syntax. As this macro takes variable number of arguments, they must be all supplied as a single argument, enclosed in parentheses. `igraph_progressf()` is then called with the given arguments.

Arguments:

`args`: | The arguments to pass to `igraph_progressf()`.

Returns:

`` | If the progress handler returns with a value other than `IGRAPH_SUCCESS`, then the function that called this macro returns as well, with the same error code, after cleaning up all allocated memory as needed.

#### 2.3.3. `igraph_progress` — Report the progress of a calculation from an igraph function.

    igraph_error_t igraph_progress(const char *message, igraph_real_t percent, void *data);

Note that the usual way to report progress is the `IGRAPH_PROGRESS` macro, as that takes care of the return value of the progress handler.

Arguments:

`message`: | A string describing the function or algorithm that is reporting the progress. Current igraph functions always use the name `message` argument if reporting from the same function.
`percent`: | Numeric, the percentage that was completed by the algorithm or function.
`data`: | User-defined data. Current igraph functions that report progress pass a null pointer here. Users can write their own progress handlers and functions with progress reporting, and then pass some meaningfull context here.

Returns:

`` | Error code from the progress handler function, or `IGRAPH_SUCCESS` if no progress handler function was registered.

Time complexity: O(1).

#### 2.3.4. `igraph_progressf` — Report the progress of a calculation from an igraph function, printf-like.

    igraph_error_t igraph_progressf(const char *message, igraph_real_t percent, void *data,
                         ...);

This is a more flexible version of `igraph_progress()`, with a printf-like template string. First the template string is filled with the additional arguments and then `igraph_progress()` is called.

Note that there is an upper limit for the length of the `message` string, currently 1000 characters.

Arguments:

`message`: | A string describing the function or algorithm that is reporting the progress. For this function this is a template string, using the same syntax as the standard `libc` `printf` function.
`percent`: | Numeric, the percentage that was completed by the algorithm or function.
`data`: | User-defined data. Current igraph functions that report progress pass a null pointer here. Users can write their own progress handlers and functions with progress reporting, and then pass some meaningfull context here.
`...`: | Additional argument that were specified in the `message` argument.

Returns:

`` | Error code from the progress handler function, or `IGRAPH_SUCCESS` if no progress handler function was registered. \return

### 2.4. Writing progress handlers

To write a new progress handler, one needs to create a function of type `igraph_progress_handler_t`. The new progress handler can then be installed with the `igraph_set_progress_handler()` function.

One can assume that the first progress handler call from a calculation will be call with zero as the `percentage` argument, and the last call from a function will have 100 as the `percentage` argument. Note, however, that if an error happens in the middle of a computation, then the 100 percent call might be omitted.

### 2.5. Writing igraph functions with progress reporting

If you want to write a function that uses igraph and supports progress reporting, you need to include `igraph_progress()` calls in your function, usually via the `IGRAPH_PROGRESS()` macro.

It is good practice to always include a call to `igraph_progress()` with a zero `percentage` argument, before the computation; and another call with 100 `percentage` value after the computation is completed.

It is also good practice not to call `igraph_progress()` too often, as this would slow down the computation. It might not be worth to support progress reporting in functions with linear or log-linear time complexity, as these are fast, even with a large amount of data. For functions with quadratic or higher time complexity make sure that the time complexity of the progress reporting is constant or at least linear. In practice this means having at most O(n) progress checks and at most 100 `igraph_progress()` calls.

### 2.6. Multi-threaded programs

In multi-threaded programs, each thread has its own progress handler, if thread-local storage is supported and igraph is thread-safe. See the `IGRAPH_THREAD_SAFE` macro for checking whether an igraph build is thread-safe.

## 3. Status handlers

### 3.1. Status reporting

In addition to the possibility of reporting the progress of an igraph computation via `igraph_progress()`, it is also possible to report simple status messages from within igraph functions, without having to judge how much of the computation was performed already. For this one needs to install a status handler function.

Status handler functions must be of type `igraph_status_handler_t` and they can be installed by a call to `igraph_set_status_handler()`. Currently there is a simple predefined status handler function, called `igraph_status_handler_stderr()`, but the user can define new ones.

igraph functions report their status via a call to the `IGRAPH_STATUS()` or the `IGRAPH_STATUSF()` macro.

### 3.2. Setting up status handlers

#### 3.2.1. `igraph_status_handler_t` — The type of the igraph status handler functions

    typedef igraph_error_t igraph_status_handler_t(const char *message, void *data);

Arguments:

`message`: | The status message.
`data`: | Additional context, with user-defined semantics. Existing igraph functions pass a null pointer here.

Returns:

`` | Error code. The current calculation will abort if you return anything else than `IGRAPH_SUCCESS` here.

#### 3.2.2. `igraph_set_status_handler` — Install of uninstall a status handler function.

    igraph_status_handler_t *
    igraph_set_status_handler(igraph_status_handler_t new_handler);

To uninstall the currently installed status handler, call this function with a null pointer.

Arguments:

`new_handler`: | The status handler function to install.

Returns:

`` | The previously installed status handler function.

Time complexity: O(1).

#### 3.2.3. `igraph_status_handler_stderr` — A simple predefined status handler function.

    igraph_error_t igraph_status_handler_stderr(const char *message, void *data);

A simple status handler function that writes the status message to the standard error.

Arguments:

`message`: | The status message.
`data`: | Additional context, with user-defined semantics. Existing igraph functions pass a null pointer here.

Returns:

`` | Error code.

Time complexity: O(1).

### 3.3. Invoking the status handler

#### 3.3.1. `IGRAPH_STATUS` — Report the status of an igraph function.

    #define IGRAPH_STATUS(message, data)

Typically this function is called only a handful of times from an igraph function. E.g. if an algorithm has three major steps, then it is logical to call it three times, to signal the three major steps.

Arguments:

`message`: | The status message.
`data`: | Additional context, with user-defined semantics. Existing igraph functions pass a null pointer here.

Returns:

`` | If the status handler returns with a value other than `IGRAPH_SUCCESS`, then the function that called this macro returns as well, with the same error code, after cleaning up all allocated memory as needed.

#### 3.3.2. `IGRAPH_STATUSF` — Report the status from an igraph function

    #define IGRAPH_STATUSF(args)

This is the more flexible version of `IGRAPH_STATUS()`, having a printf-like syntax. As this macro takes variable number of arguments, they must be all supplied as a single argument, enclosed in parentheses. `igraph_statusf()` is then called with the given arguments.

Arguments:

`args`: | The arguments to pass to `igraph_statusf()`.

Returns:

`` | If the status handler returns with a value other than `IGRAPH_SUCCESS`, then the function that called this macro returns as well, with the same error code, after cleaning up all allocated memory as needed.

#### 3.3.3. `igraph_status` — Reports status from an igraph function.

    igraph_error_t igraph_status(const char *message, void *data);

It calls the installed status handler function, if there is one. Otherwise it does nothing. Note that the standard way to report the status from an igraph function is the `IGRAPH_STATUS` or `IGRAPH_STATUSF` macro, as these take care of cleaning up allocated memory from the calling function if the status handler returns with an error code.

Arguments:

`message`: | The status message.
`data`: | Additional context, with user-defined semantics. Existing igraph functions pass a null pointer here.

Returns:

`` | Error code from the status handler function, or `IGRAPH_SUCCESS` if no status handler function was registered.

Time complexity: O(1).

#### 3.3.4. `igraph_statusf` — Report status, more flexible printf-like version.

    igraph_error_t igraph_statusf(const char *message, void *data, ...);

This is the more flexible version of `igraph_status()`, that has a syntax similar to the `printf` standard C library function. It substitutes the values of the additional arguments into the `message` template string and calls `igraph_status()`.

Arguments:

`message`: | Status message template string, the syntax is the same as for the `printf` function.
`data`: | Additional context, with user-defined semantics. Existing igraph functions pass a null pointer here.
`...`: | The additional arguments to fill the template given in the `message` argument.

Returns:

`` | Error code from the status handler function, or `IGRAPH_SUCCESS` if no status handler function was registered.