# `linda`

Linda is a classic coordination model for process communication. This library provides a Linda tuple-space implementation. Requires both multi-threading and sockets support. It supports SWI-Prolog, Trealla Prolog, and XVM.

The tuple-space is a shared blackboard where processes can:

- Write tuples using the `out/1-2` predicates

- Read tuples (without removing) using the `rd/1-2` and `rd_noblock/1-2` predicates

- Remove tuples using the `in/1-2` and `in_noblock/1-2` predicates

The blocking predicates (`in/1-2`, `rd/1-2`) suspend the process until a matching tuple becomes available. The non-blocking variants (`in_noblock/1-2`, `rd_noblock/1-2`) fail immediately if no matching tuple is found. There are also additional predicates for list operations. Tuples are matched using unification.

## API documentation

Open the ../../apis/library_index.html#linda link in a web browser.

## Loading

To load this library, load the `loader.lgt` file:

    | ?- logtalk_load(linda(loader)).

## Testing

To test this library predicates, load the `tester.lgt` file:

    | ?- logtalk_load(linda(tester)).

## Usage

The main library entities are the `linda_server` and `linda_client` categories. These categories provide a clean separation between server and client code. For backward compatibility with the previous version of this library, a `linda` object importing both categories is also provided. The usage examples that follow use this object. An object importing the `linda_server` category must use the `threaded/0` directive.

### Starting a server

To start a Linda server that prints its address:

    | ?- linda::linda.
    % Server started at localhost:54321
    ...

To start a server with a callback when it starts:

    | ?- linda::linda([(Host:Port)-format('Server at ~w:~w~n', [Host, Port])]).

The server runs until all clients disconnect after a shutdown request.

### Connecting a client

To connect to a server:

    | ?- linda::linda_client(localhost:54321).

### Tuple operations

Write a tuple:

    | ?- linda::out(message(hello, world)).

Read a tuple (blocking):

    | ?- linda::rd(message(X, Y)).
    X = hello,
    Y = world

Remove a tuple (blocking):

    | ?- linda::in(message(X, Y)).
    X = hello,
    Y = world

Non-blocking operations:

    | ?- linda::rd_noblock(message(X, Y)).
    no   % if no matching tuple exists

    | ?- linda::in_noblock(message(X, Y)).
    no   % if no matching tuple exists

Wait for one of several tuples:

    | ?- linda::in([task(1, X), task(2, X), done], Tuple).

Alternative syntax using `in_list/2`:

    | ?- linda::in_list([task(1, X), task(2, X), done], Tuple).

Similarly for reading:

    | ?- linda::rd_list([status(ready), status(waiting)], Status).

Collect all matching tuples atomically (read without removing):

    | ?- linda::findall_rd_noblock(N, counter(N), Counters).

Collect and remove all matching tuples atomically:

    | ?- linda::findall_in_noblock(X, item(X), Items).

### Disconnecting

Close the client connection:

    | ?- linda::close_client.

Request server shutdown (server stops accepting new connections):

    | ?- linda::shutdown_server, linda::close_client.

### Timeout control

Set a timeout for blocking operations:

    | ?- linda::linda_timeout(Old, 5:0).  % 5 seconds timeout

Disable timeout (wait forever):

    | ?- linda::linda_timeout(_, off).

## Examples

### Producer-Consumer

Producer (client 1):

    producer :-
        produce(X),
        linda::out(item(X)),
        producer.

Consumer (client 2):

    consumer :-
        linda::in(item(X)),
        consume(X),
        consumer.

### Critical Region

    critical_section :-
        linda::in(mutex),  % acquire lock
        do_critical_work,
        linda::out(mutex). % release lock

Initialize the mutex:

    | ?- linda::out(mutex).

### Synchronization

Wait for a signal:

    | ?- linda::in(ready).  % blocks until someone does out(ready)

Send a signal:

    | ?- linda::out(ready).

## API Summary

### Server predicates

- `linda` - Start server on automatic port

- `linda(Options)` - Start server with options

### Client predicates

- `linda_client(Address)` - Connect to server at `Host:Port`

- `close_client` - Close connection

- `shutdown_server` - Request server shutdown

- `linda_timeout(Old, New)` - Get/set timeout

### Tuple predicates (single/default server)

- `out(Tuple)` - Add tuple to space

- `in(Tuple)` - Remove matching tuple (blocking)

- `in_noblock(Tuple)` - Remove matching tuple (non-blocking)

- `in(TupleList, Tuple)` - Remove one of several tuples (blocking)

- `in_list(TupleList, Tuple)` - Remove one of several tuples (blocking)

- `rd(Tuple)` - Read matching tuple (blocking)

- `rd_noblock(Tuple)` - Read matching tuple (non-blocking)

- `rd(TupleList, Tuple)` - Read one of several tuples (blocking)

- `rd_list(TupleList, Tuple)` - Read one of several tuples (blocking)

- `findall_rd_noblock(Template, Tuple, List)` - Collect all matching tuples (atomic read)

- `findall_in_noblock(Template, Tuple, List)` - Collect and remove all matching tuples (atomic remove)

### Tuple predicates (multiple servers)

Variants of the predicates above with an additional first argument for the server address or the server alias (an atom; default is `blackboard`).

## Known issues

Recent versions of macOS seem to disable the mapping of `localhost` to `127.0.0.1`. This issue may prevent some functionality from working. This can be fixed either by editing the `/etc/hosts` file or by using `'127.0.0.1'` as the host argument instead of `localhost`.

## See also

- SICStus Prolog Linda documentation: https://sicstus.sics.se/sicstus/docs/latest4/html/sicstus.html/lib_002dlinda.html

- Original Linda papers:

  - Carreiro, N. & Gelernter, D. (1989). Linda in Context.

  - Carreiro, N. & Gelernter, D. (1989). How to Write Parallel Programs: A Guide to the Perplexed.