Add extra layer of queuing above the Flounder UMP one.
[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]->queue.head = NULL;
29     spawnds[core_id]->queue.tail = NULL;
30
31     return SYS_ERR_OK;
32 }
33
34 void spawnd_state_free(coreid_t core_id)
35 {
36     if (spawnds[core_id] != NULL) {
37         free(spawnds[core_id]);
38     }
39 }
40
41 inline bool spawnd_state_exists(coreid_t core_id)
42 {
43     return spawnds[core_id] != NULL;
44 }
45
46 inline struct spawnd_state *spawnd_state_get(coreid_t core_id)
47 {
48     return spawnds[core_id];
49 }
50
51 /**
52  * \brief Enqueue on a waitset queue.
53  *
54  * \param q    Pointer to queue to enqueue on
55  * \param m    Pointer to element to enqueue
56  *
57  * \return true if queue was empty, false if not.
58  */
59 static bool enqueue_send(struct msg_queue *q, struct msg_queue_elem *m)
60 {
61     assert(m->next == NULL);
62
63     // Enqueue on the queue
64     if(q->tail != NULL) {
65         q->tail->next = m;
66     } else {
67         assert(q->head == NULL);
68         q->head = m;
69     }
70     q->tail = m;
71
72     return q->head == q->tail ? true : false;
73 }
74
75 /**
76  * \brief Dequeues from a waitset queue.
77  *
78  * \param q    Pointer to queue to dequeue from
79  *
80  * \return the newly dequeued element.
81  */
82 static struct msg_queue_elem *dequeue_send(struct msg_queue *q)
83 {
84     // Queue should have at least one element
85     assert(q->head != NULL && q->tail != NULL);
86
87     struct msg_queue_elem *e = q->head;
88     q->head = e->next;
89     if(q->head == NULL) {
90         q->tail = NULL;
91     }
92
93     return e;
94 }
95
96 /**
97  * \brief Enqueue an element on a waitset queue IN FRONT.
98  *
99  * \param q    Pointer to queue to enqueue on
100  * \param m    Pointer to element to enqueue
101  *
102  * \return true if queue was empty, false if not.
103  */
104 static bool enqueue_send_at_front(struct msg_queue *q, struct msg_queue_elem *m)
105 {
106     assert(m->next == NULL);
107     if(q->tail == NULL) {
108         assert(q->head == NULL);
109         q->head = m;
110         q->tail = m;
111     } else {
112         m->next = q->head;
113         q->head = m;
114     }
115     return q->head == q->tail ? true : false;
116 }
117
118 static void spawnd_send_handler(void *arg)
119 {
120     struct spawnd_state *spawnd = (struct spawnd_state*) arg;
121     struct msg_queue *q = &spawnd->queue;
122
123     // Dequeue next element from the queue
124     struct msg_queue_elem *m = (struct msg_queue_elem*) dequeue_send(q);
125
126     assert(m->cont != NULL);
127     if (!m->cont(m)) {
128         // Flounder TX busy, need to re-enqueue message.
129         // TODO(razvan): Re-enqueuing at the front of the queue, to preserve
130         // original message order. Could a different strategy be preferrable?
131         enqueue_send_at_front(q, m);
132     }
133
134     if (q->head != NULL) {
135         // Queue is non-empty, therefore re-register.
136         errval_t err = spawnd->b->register_send(spawnd->b, spawnd->b->waitset,
137                                                 MKCONT(spawnd_send_handler,
138                                                        arg));
139         if (err_is_fail(err)) {
140             DEBUG_ERR(err, "regitering for spawnd send");
141             return;
142         }
143     }
144 }
145
146 errval_t spawnd_state_enqueue_send(struct spawnd_state *spawnd,
147                                    struct msg_queue_elem *msg)
148 {
149     msg->next = NULL;
150
151     // If queue was empty, enqueue on waitset
152     if(enqueue_send(&spawnd->queue, msg)) {
153         return spawnd->b->register_send(spawnd->b, spawnd->b->waitset,
154                                         MKCONT(spawnd_send_handler, spawnd));
155     } else {
156         return SYS_ERR_OK;
157     }
158 }