Change proc_mgmt/pending_clients.h to use reply queues instead of hashtables.
[barrelfish] / usr / proc_mgmt / spawnd_state.c
1 /*
2  * \brief Spawnd state internals for the process manager.
3  *
4  * Copyright (c) 2017, ETH Zurich.
5  * All rights reserved.
6  *
7  * This file is distributed under the terms in the attached LICENSE file.
8  * If you do not find this file, copies can be found by writing to:
9  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
10  */
11
12 #include <barrelfish/barrelfish.h>
13
14 #include "spawnd_state.h"
15
16 static struct spawnd_state *spawnds[MAX_COREID];
17
18 errval_t spawnd_state_alloc(coreid_t core_id, struct spawn_binding *b)
19 {
20     spawnds[core_id] = (struct spawnd_state*) malloc(
21             sizeof(struct spawnd_state));
22     if (spawnds[core_id] == NULL) {
23         return LIB_ERR_MALLOC_FAIL;
24     }
25
26     spawnds[core_id]->b = b;
27     spawnds[core_id]->core_id = core_id;
28     spawnds[core_id]->sendq.head = NULL;
29     spawnds[core_id]->sendq.tail = NULL;
30     spawnds[core_id]->recvq.head = NULL;
31     spawnds[core_id]->recvq.tail = NULL;
32
33     b->st = spawnds[core_id];
34
35     return SYS_ERR_OK;
36 }
37
38 void spawnd_state_free(coreid_t core_id)
39 {
40     if (spawnds[core_id] != NULL) {
41         free(spawnds[core_id]);
42     }
43 }
44
45 inline bool spawnd_state_exists(coreid_t core_id)
46 {
47     return spawnds[core_id] != NULL;
48 }
49
50 inline struct spawnd_state *spawnd_state_get(coreid_t core_id)
51 {
52     return spawnds[core_id];
53 }
54
55 /**
56  * \brief Enqueue on a waitset queue.
57  *
58  * \param q    Pointer to queue to enqueue on
59  * \param m    Pointer to element to enqueue
60  *
61  * \return true if queue was empty, false if not.
62  */
63 static bool enqueue(struct msg_queue *q, struct msg_queue_elem *m)
64 {
65     assert(m->next == NULL);
66
67     // Enqueue on the queue
68     if(q->tail != NULL) {
69         q->tail->next = m;
70     } else {
71         assert(q->head == NULL);
72         q->head = m;
73     }
74     q->tail = m;
75
76     return q->head == q->tail ? true : false;
77 }
78
79 /**
80  * \brief Dequeues from a waitset queue.
81  *
82  * \param q    Pointer to queue to dequeue from
83  *
84  * \return the newly dequeued element.
85  */
86 static struct msg_queue_elem *dequeue(struct msg_queue *q)
87 {
88     // Queue should have at least one element
89     assert(q->head != NULL && q->tail != NULL);
90
91     struct msg_queue_elem *e = q->head;
92     q->head = e->next;
93     if(q->head == NULL) {
94         q->tail = NULL;
95     }
96
97     return e;
98 }
99
100 /**
101  * \brief Enqueue an element on a waitset queue IN FRONT.
102  *
103  * \param q    Pointer to queue to enqueue on
104  * \param m    Pointer to element to enqueue
105  *
106  * \return true if queue was empty, false if not.
107  */
108 static bool enqueue_at_front(struct msg_queue *q, struct msg_queue_elem *m)
109 {
110     assert(m->next == NULL);
111     if(q->tail == NULL) {
112         assert(q->head == NULL);
113         q->head = m;
114         q->tail = m;
115     } else {
116         m->next = q->head;
117         q->head = m;
118     }
119     return q->head == q->tail ? true : false;
120 }
121
122 static void spawnd_send_handler(void *arg)
123 {
124     struct spawnd_state *spawnd = (struct spawnd_state*) arg;
125     struct msg_queue *q = &spawnd->sendq;
126
127     // Dequeue next element from the queue
128     struct msg_queue_elem *m = (struct msg_queue_elem*) dequeue(q);
129
130     assert(m->cont != NULL);
131     if (m->cont(m)) {
132         // Send continuation succeeded, need to enqueue a receive.
133         struct msg_queue_elem *recvm = (struct msg_queue_elem*) malloc(
134                 sizeof(struct msg_queue_elem));
135         recvm->st = m->st;
136         recvm->next = NULL;
137         enqueue(&spawnd->recvq, recvm);
138     } else {
139         // Send continuation failed, need to re-enqueue message.
140         // TODO(razvan): Re-enqueuing at the front of the queue, to preserve
141         // original message order. Could a different strategy be preferrable?
142         enqueue_at_front(q, m);
143     }
144
145     if (q->head != NULL) {
146         // Queue is non-empty, therefore re-register.
147         errval_t err = spawnd->b->register_send(spawnd->b, spawnd->b->waitset,
148                                                 MKCONT(spawnd_send_handler,
149                                                        arg));
150         if (err_is_fail(err)) {
151             DEBUG_ERR(err, "regitering for spawnd send");
152             return;
153         }
154     }
155 }
156
157 errval_t spawnd_state_enqueue_send(struct spawnd_state *spawnd,
158                                    struct msg_queue_elem *msg)
159 {
160     msg->next = NULL;
161
162     // If queue was empty, enqueue on waitset
163     if(enqueue(&spawnd->sendq, msg)) {
164         return spawnd->b->register_send(spawnd->b, spawnd->b->waitset,
165                                         MKCONT(spawnd_send_handler, spawnd));
166     } else {
167         return SYS_ERR_OK;
168     }
169 }
170
171 void *spawnd_state_dequeue_recv(struct spawnd_state *spawnd)
172 {
173     struct msg_queue_elem *m = dequeue(&spawnd->recvq);
174     assert(m != NULL);
175     return m->st;
176 }