Bake’s client interface

A simple client example

The following code shows how to use the Bake client library to access a target on a remote Bake provider.

#include <assert.h>
#include <stdio.h>
#include <margo.h>
#include <bake-client.h>

int main(int argc, char** argv)
{
    int ret;

    margo_instance_id mid = margo_init("na+sm", MARGO_CLIENT_MODE, 0, 0);
    assert(mid);

    assert(argc == 2 || argc == 3);

    const char* server_addr_str = argv[1];
    const char* target_id_str = argc == 3 ? argv[2] : NULL;

    hg_addr_t server_address;
    hg_return_t hret = margo_addr_lookup(mid, server_addr_str, &server_address);
    assert(hret == HG_SUCCESS);

    bake_client_t client;
    ret = bake_client_init(mid, &client);
    assert(ret == 0);

    bake_provider_handle_t ph;
    ret = bake_provider_handle_create(client, server_address, 42, &ph);
    assert(ret == 0);

    bake_target_id_t tid;
    if(target_id_str) {
        ret = bake_target_id_from_string(target_id_str, &tid);
        assert(ret == 0);
    } else {
        uint64_t num_targets;
        ret = bake_probe(ph, 1, &tid, &num_targets);
        assert(ret == 0);
        assert(num_targets == 1);
    }

    bake_region_id_t rid;
    ret = bake_create(ph, tid, 10, &rid);
    assert(ret == 0);

    char in_buffer[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
    ret = bake_write(ph, tid, rid, 0, in_buffer, 10);
    assert(ret == 0);

    ret = bake_persist(ph, tid, rid, 0, 10);
    assert(ret == 0);

    char out_buffer[10];
    uint64_t bytes_read;
    ret = bake_read(ph, tid, rid, 0, out_buffer, 10, &bytes_read);
    assert(ret == 0);
    assert(memcmp(in_buffer, out_buffer, 10) == 0);

    ret = bake_provider_handle_release(ph);
    assert(ret == 0);

    ret = bake_client_finalize(client);
    assert(ret == 0);

    hret = margo_addr_free(mid, server_address);
    assert(hret == HG_SUCCESS);

    margo_finalize(mid);

    return 0;
}

The bake_client_init function is used to create a bake_client object. This client object is required to then create provider handles using bake_provider_handle_create.

In this example, the target ID is either passed as the program’s second argument, in which case bake_target_id_from_string is used to decode it into a bake_target_id_t object, or it is queried from the provider using the bake_probe function. This function takes the provider handle, the maximum number of targets to list, a pointer to an array of target IDs (here just one target), and a pointer to store the returned numbed of targets.

The bake_create function is used to create a region of 10 bytes in the target. It returns a bake_region_id_t identifying this region. This region ID can be used to write into the region using bake_write, or to read from it using bake_read. Note that in our example, we write and read the full 10 bytes of the region, starting from offset 0, but this is not a requirement. These operations may access any part of the region using different offsets and sizes.

Important

Unless Bake was built with the +sizecheck option, read and write operations will not check that the provided offset and size fall within the requested region. This can lead to corruption of the target if the region is accessed outside of its bounds.

bake_write does not guarantee that the written data is persistent on the target (e.g., should the service crash after the write, the data may not be there upon restarting the service). To ensure persistence or a given segment within a given region, one has to call bake_persist.

Note

Creating a new region, writing the entirety of its content, and persisting it, is a very common pattern. Hence Bake provides the bake_create_write_persist function for this purpose.

Other client functions

The Bake client library provides a number of other functions that may be useful.

On provider handles:

  • bake_provider_handle_ref_incr can be used to increase the internal reference count of the provider handle.

  • bake_provider_handle_get_info retrieves the provider handle’s internal information (client, address, and provider id).

  • bake_provider_handle_get/set_eager_limit access the provider handle’s “eager limit”. This value is the number of bytes under which the client will send data using RPC arguments instead of RDMA transfer.

On regions:

  • bake_get_size gets the size of a given region.

  • bake_get_data can be used if the caller is in the same process as the target bake provider, to obtain a direct pointer to the region’s data. Using this function is however not recommended since it strongly couples the client code with the server code (forcing them to live in the same process space).

  • bake_remove can be used to remove a region. Note that not all backends support removing a region.

  • bake_proxy_write and bake_create_write_persist_proxy are versions of bake_write and bake_create_write_persist that take a bulk handle, a remote address (as a string) and an offset, as the source of the data to be written into the region. These functions are useful in two scenarios: (1) when the data comes from another process and the calling process is only forwarding a bulk handle to the Bake server; (2) if the program needs to send data that is not contiguous in memory, by creating a bulk handle for it manually.

  • bake_proxy_read can similarly be used to read the content of a region into a bulk handle instead of a buffer.