1/**
2 * \file
3 * \brief Barrier client API implementation
4 *
5 * Implementation of a double barrier using the get/set API.
6 */
7
8/*
9 * Copyright (c) 2011, ETH Zurich.
10 * All rights reserved.
11 *
12 * This file is distributed under the terms in the attached LICENSE file.
13 * If you do not find this file, copies can be found by writing to:
14 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
15 */
16
17#include <barrelfish/barrelfish.h>
18
19#include <if/octopus_defs.h>
20#include <if/octopus_thc.h>
21
22#include <octopus/init.h>
23#include <octopus/barrier.h>
24#include <octopus/getset.h>
25#include <octopus/trigger.h>
26
27#include "common.h"
28
29/**
30 * \brief Client enters a barrier. Blocks until all clients have entered the
31 * barrier.
32 *
33 * Each client creates a (sequential record) based on the provided name.
34 * Once a client sees the specified amount (wait_for) of records it
35 * creates a record that wakes up all waiting clients.
36 *
37 * \param[in] name Name of the barrier.
38 * \param[out] barrier_record Record created for each client.
39 * \param[in] wait_for Number of clients entering the barrier.
40 */
41errval_t oct_barrier_enter(const char* name, char** barrier_record, size_t wait_for)
42{
43    errval_t err;
44    errval_t exist_err;
45    char** names = NULL;
46    uint64_t mode = 0;
47    octopus_trigger_id_t tid;
48    size_t current_barriers = 0;
49    octopus_trigger_t t = oct_mktrigger(OCT_ERR_NO_RECORD, octopus_BINDING_RPC,
50            OCT_ON_SET, NULL, NULL);
51
52    err = oct_set_get(SET_SEQUENTIAL, barrier_record,
53            "%s_ { barrier: '%s' }", name, name);
54    *barrier_record = strdup(*barrier_record);
55    if (*barrier_record == NULL) {
56        return LIB_ERR_MALLOC_FAIL;
57    }
58    err = oct_get_names(&names, &current_barriers, "_ { barrier: '%s' }",
59            name);
60    oct_free_names(names, current_barriers);
61    if (err_is_fail(err)) {
62        return err;
63    }
64    //debug_printf("current_barriers: %lu wait_for: %lu\n", current_barriers,
65    //        wait_for);
66
67    if (current_barriers != wait_for) {
68        struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
69        err = cl->call_seq.exists(cl, name, t, &tid, &exist_err);
70        if (err_is_fail(err)) {
71            return err;
72        }
73        err = exist_err;
74
75        if (err_is_ok(err)) {
76            // Barrier already exists
77        }
78        if (err_no(err) == OCT_ERR_NO_RECORD) {
79            // Wait until barrier record is created
80            err = cl->recv.trigger(cl, NULL, NULL, &mode, NULL, NULL);
81            assert(mode & OCT_REMOVED);
82
83            err = SYS_ERR_OK;
84        }
85        else {
86            // Some other error happend, return it
87        }
88    }
89    else {
90        // We are the last to enter the barrier,
91        // wake up the others
92        err = oct_set(name);
93    }
94
95    return err;
96}
97
98/**
99 * \brief Leave a barrier. Blocks until all involved parties have
100 * called oct_barrier_leave().
101 *
102 * Client deletes its barrier record. In case the client
103 * was the last one we delete the special record which
104 * wakes up all other clients.
105 *
106 * \param barrier_record Clients own record as provided by
107 * oct_barrier_enter.
108 */
109errval_t oct_barrier_leave(const char* barrier_record)
110{
111    errval_t exist_err;
112    errval_t err;
113    char* rec_name = NULL;
114    char* barrier_name = NULL;
115    char** names = NULL;
116    size_t remaining_barriers = 0;
117    uint64_t mode = 0;
118    octopus_trigger_id_t tid;
119    octopus_trigger_t t = oct_mktrigger(SYS_ERR_OK, octopus_BINDING_RPC,
120            OCT_ON_DEL, NULL, NULL);
121
122    //debug_printf("leaving: %s\n", barrier_record);
123    err = oct_read(barrier_record, "%s { barrier: %s }", &rec_name,
124            &barrier_name);
125    if (err_is_ok(err)) {
126        err = oct_del(rec_name);
127        if (err_is_fail(err)) {
128            goto out;
129        }
130
131        err = oct_get_names(&names, &remaining_barriers, "_ { barrier: '%s' }",
132                barrier_name);
133        oct_free_names(names, remaining_barriers);
134
135        //debug_printf("remaining barriers is: %lu\n", remaining_barriers);
136
137        if (err_is_ok(err)) {
138            struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
139            err = cl->call_seq.exists(cl, barrier_name, t, &tid, &exist_err);
140            if (err_is_fail(err)) {
141                goto out;
142            }
143            err = exist_err;
144
145            if (err_is_ok(err)) {
146                // Wait until everyone has left the barrier
147                err = cl->recv.trigger(cl, NULL, NULL, &mode, NULL, NULL);
148                assert(mode & OCT_REMOVED);
149            }
150            else if (err_no(err) == OCT_ERR_NO_RECORD) {
151                // barrier already deleted
152                err = SYS_ERR_OK;
153            }
154        }
155        else if (err_no(err) == OCT_ERR_NO_RECORD) {
156            // We are the last one to leave the barrier,
157            // wake-up all others
158            err = oct_del("%s", barrier_name);
159        }
160        else {
161            // Just return the error
162        }
163    }
164
165out:
166    free(rec_name);
167    free(barrier_name);
168    return err;
169}
170