Dex – 1: Terminology
Description: Reference for Dex-1: Terminology

#### Terminology

# Futures

A future is a container that will eventually contain a result or an error.

Programmers often use the words “future” and “promise” interchangeably. Libdex tries, when possible, to follow the academic nomenclature for futures. That is to say that a future is the interface and promise is a type of future.

Futures exist in one of three states. The first state is `DEX_FUTURE_STATUS_PENDING`. A future exists in this state until it has either rejected or resolved.

The second state is `DEX_FUTURE_STATUS_RESOLVED`. A future reaches this state when it has successfully obtained a value.

The last third state is `DEX_FUTURE_STATUS_REJECTED`. If there was a failure to obtain a value a future will be in this state and contain a `GError` representing such failure.

# Promises and More

A promise is a type of future that allows the creator to set the resolved value or error. This is a common type of future to use when you are integrating with things that are not yet integrated with Libdex.

Other types of futures also exist.

    /* resolve to "true" */
    DexPromise *good = dex_promise_new ();
    dex_promise_resolve_boolean (good, TRUE);

    /* reject with error */
    DexPromise *bad = dex_promise_new ();
    dex_promise_reject (good,
                        g_error_new (G_IO_ERROR,
                                     G_IO_ERROR_FAILED,
                                     "Failed"));

## Static Futures

Sometimes you already know the result of a future upfront. The `DexStaticFuture` is used in this case. Various constructors for `DexFuture` will help you create one.

For example, `dex_future_new_take_object()` will create a static future for a `GObject`-derived instance.

    DexFuture *future = dex_future_new_for_int (123);

## Blocks

One of the most commonly used types of futures in Libdex is a `DexBlock`.

a `DexBlock` is a callback that is run to process the result of a future. The block itself is also a future meaning that you can chain these blocks together into robust processing groups.

### "Then" Blocks

The first type of block is a “then” block which is created using `dex_future_then()`. These blocks will only be run if the dependent future resolves with a value. Otherwise, the rejection of the dependent future is propagated to the block.

    static DexFuture *
    further_processing (DexFuture *future,
                        gpointer   user_data)
    {
      const GValue *result = dex_promise_get_value (future, NULL);

      return dex_ref (future);
    }

### "Catch" Blocks

Since some futures may fail, there is value in being able to “catch” the failure and resolve it. Use `dex_future_catch()` to handle the result of a rejected future and resolve or reject it further.

    static DexFuture *
    catch_rejection (DexFuture *future,
                     gpointer   user_data)
    {
      g_autoptr(GError) error = NULL;

      dex_future_get_value (future, &error);

      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
        return dex_future_new_true ();

      return dex_ref (future);
    }

### "Finally" Blocks

There may be times when you want to handle completion of a future whether it resolved or rejected. For this case, use a “finally” block by calling `dex_future_finally()`.

### Infinite Loops

If you find you have a case where you want a `DexBlock` to loop indefinitely, you can use the “_loop” variants of the block APIs.

See `dex_future_then_loop()`, `dex_future_catch_loop()`, or `dex_future_finally_loop()`. This is generally useful when your block’s callback will begin the next stage of work as the result of the callback.

## Future Sets

A `DexFutureSet` is a type of future that contains the result of multiple futures. This is an extremely useful construct because it allows you to do work concurrently and then process the results in a sort of “reduce” phase.

For example, you could make a request to a database, cache server, and a timeout and process the first that completes.

There are multiple types of future sets based on the type of problem you want to solve.

`dex_future_all()` can be used to resolve when all dependent futures have resolved, otherwise it will reject with error once they are complete. If you want to reject as soon as the first item rejects, `dex_future_all_race()` will get you that behavior.

Other useful `DexFutureSet` constructors include `dex_future_any()` and `dex_future_first()`.

    /* Either timeout or propagate result of cache/db query */
    return dex_future_first (dex_timeout_new_seconds (60),
                             dex_future_any (query_db_server (),
                                             query_cache_server (),
                                             NULL),
                             NULL);

## Cancellable

Many programmers who use GTK and GIO are familiar with `GCancellable`. Libdex has something similar in the form of `DexCancellable`. However, in the Libdex case, `DexCancellable` is a future.

It allows for convenient grouping with other futures to perform cancellation when the `dex_cancellable_cancel()` method is called.

It can also integrate with `GCancellable` when created using `dex_cancellable_new_from_cancellable()`.

A `DexCancellable` will only ever reject.

    DexFuture *future = dex_cancellable_new ();

    dex_cancellable_cancel (DEX_CANCELLABLE (future));

## Timeouts

A timeout may be represented as a future. In this case, the timeout will reject after a time period has passed.

A `DexTimeout` will only ever reject.

This future is implemented on top of `GMainContext` via API like `g_timeout_add()`.

    DexFuture *future = dex_timeout_new_seconds (60);

If you want to bound another future with a timeout, prefer the timeout wrapper constructors. They pass through the wrapped future’s resolved value or rejection when it completes first, and reject with `DEX_ERROR_TIMED_OUT` when the timeout wins.

    DexFuture *future = dex_future_with_timeout_seconds (query_db_server (), 60);

The wrapped future is discarded when the timeout wins. If the underlying work must continue independently, keep and disown a separate reference before wrapping or keep that work separate from the timeout-bound result.

Since: 1.2

## Unix Signals

Libdex can represent unix signals as a future. That is to say that the future will resolve to an integer of the signal number when that signal has been raised.

This is implemented using [func@GLibUnix@signal_source_new] and comes with all the same restrictions.

## Delayed

Sometimes you may run into a case where you want to gate the result of a future until a specific moment.

For this case, `DexDelayed` allows you to wrap another future and decide when to “uncork” the result.

    DexFuture *delayed = dex_delayed_new (dex_future_new_true ());

    dex_delayed_release (DEX_DELAYED (delayed));

## Fibers

Another type of future is a “fiber”.

More care will be spent on fibers later on but suffice to say that the result of a fiber is easily consumable as a future via `DexFiber`.

    DexFuture *future = dex_scheduler_spawn (NULL, 0, my_fiber, state, state_free);

## Coroutines

Another type of future is a stackless “coroutine”, `DexCoroutine`.

Use `dex_scheduler_spawn_coroutine()` to create a coroutine for work that is mostly waiting on other futures.

The coroutine function receives a `DexCoroutineContext` and must use the `DEX_COROUTINE_BEGIN` / `DEX_COROUTINE_END` pair together with `DEX_COROUTINE_SUSPEND_*` macros to yield and resume across waits.

Coroutines are often useful for lower-overhead state-machine style async code. See Coroutines for practical guidance.

# Cancellation Propagation

Futures within your application will inevitably depend on other futures.

If all of the futures depending on a future have been released, the dependent future will have the opportunity to cancel itself. This allows for cascading cancellation so that unnecessary work may be elided.

You can use `dex_future_disown()` to ensure that a future will continue to be run even if the dependent futures are released.

# Schedulers

Libdex requires much processing that needs to be done on the main loop of a thread. This is generally handled by a `DexScheduler`.

The main thread of an application has the default scheduler which is a `DexMainScheduler`.

Libdex also has a managed thread pool of schedulers via the `DexThreadPoolScheduler`.

Schedulers manage short tasks, executing `DexBlock` when they are ready, finalizing objects on their owning thread, and running fibers.

Schedulers integrate with the current threads `GMainContext` via `GSource` making it easy to use Libdex with GTK and Clutter-based applications.

# Channels

Channels are a higher-level construct built on futures that allow passing work between producers and consumers. They are akin to Go channels in that they have a read and a write side. However, they are much more focused on integrating well with `DexFuture`.