6a9bcec145d65a66f422fc0ee2dd808d3f53f86f
[barrelfish] / lib / octopus / client / barriers.c
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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
15  */
16
17 #include <barrelfish/barrelfish.h>
18
19 #include <octopus/init.h>
20 #include <octopus/barrier.h>
21 #include <octopus/getset.h>
22 #include <octopus/trigger.h>
23
24 #include "common.h"
25
26 /**
27  * \brief Client enters a barrier. Blocks until all clients have entered the
28  * barrier.
29  *
30  * Each client creates a (sequential record) based on the provided name.
31  * Once a client sees the specified amount (wait_for) of records it
32  * creates a record that wakes up all waiting clients.
33  *
34  * \param[in] name Name of the barrier.
35  * \param[out] barrier_record Record created for each client.
36  * \param[in] wait_for Number of clients entering the barrier.
37  */
38 errval_t oct_barrier_enter(const char* name, char** barrier_record, size_t wait_for)
39 {
40     errval_t err;
41     errval_t exist_err;
42     char* record = NULL;
43     char** names = NULL;
44     uint64_t mode = 0;
45     uint64_t state = 0;
46     uint64_t fn = 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             DIST_ON_SET, NULL, NULL);
51
52     err = oct_set_get(SET_SEQUENTIAL, barrier_record,
53             "%s_ { barrier: '%s' }", name, name);
54     err = oct_get_names(&names, &current_barriers, "_ { barrier: '%s' }",
55             name);
56     oct_free_names(names, current_barriers);
57     if (err_is_fail(err)) {
58         return err;
59     }
60     //debug_printf("current_barriers: %lu wait_for: %lu\n", current_barriers,
61     //        wait_for);
62
63     if (current_barriers != wait_for) {
64         struct octopus_thc_client_binding_t* cl = oct_get_thc_client();
65         err = cl->call_seq.exists(cl, name, t, &tid, &exist_err);
66         if (err_is_fail(err)) {
67             return err;
68         }
69         err = exist_err;
70
71         if (err_is_ok(err)) {
72             // Barrier already exists
73         }
74         if (err_no(err) == OCT_ERR_NO_RECORD) {
75             // Wait until barrier record is created
76             err = cl->recv.trigger(cl, &tid, &fn, &mode, &record, &state);
77             free(record);
78             assert(mode & DIST_REMOVED);
79
80             err = SYS_ERR_OK;
81         }
82         else {
83             // Some other error happend, return it
84         }
85     }
86     else {
87         // We are the last to enter the barrier,
88         // wake up the others
89         err = oct_set(name);
90     }
91
92     return err;
93 }
94
95 /**
96  * \brief Leave a barrier. Blocks until all involved parties have
97  * called oct_barrier_leave().
98  *
99  * Client deletes its barrier record. In case the client
100  * was the last one we delete the special record which
101  * wakes up all other clients.
102  *
103  * \param barrier_record Clients own record as provided by
104  * oct_barrier_enter.
105  */
106 errval_t oct_barrier_leave(const char* barrier_record)
107 {
108     errval_t exist_err;
109     errval_t err;
110     char* rec_name = NULL;
111     char* barrier_name = NULL;
112     char* record = NULL;
113     char** names = NULL;
114     size_t remaining_barriers = 0;
115     uint64_t mode = 0;
116     uint64_t state = 0;
117     uint64_t fn = 0;
118     octopus_trigger_id_t tid;
119     octopus_trigger_t t = oct_mktrigger(SYS_ERR_OK, octopus_BINDING_RPC,
120             DIST_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, &tid, &fn, &mode, &record, &state);
148                 assert(mode & DIST_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
165 out:
166     free(record);
167     free(rec_name);
168     free(barrier_name);
169     return err;
170 }