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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
10 */
11
12#include <barrelfish/barrelfish.h>
13
14#include "spawnd_state.h"
15
16static struct spawnd_state *spawnds[MAX_COREID];
17
18/**
19 * \brief Allocates a state structure for a new spawnd binding.
20 *
21 * \param core_id       core where the spawnd newly bound with runs.
22 * \param spawn_binding Flounder binding structure for the spawnd.
23 */
24errval_t spawnd_state_alloc(coreid_t core_id, struct spawn_binding *b)
25{
26    spawnds[core_id] = (struct spawnd_state*) malloc(
27            sizeof(struct spawnd_state));
28    if (spawnds[core_id] == NULL) {
29        return LIB_ERR_MALLOC_FAIL;
30    }
31
32    spawnds[core_id]->b = b;
33    spawnds[core_id]->core_id = core_id;
34    spawnds[core_id]->sendq.head = NULL;
35    spawnds[core_id]->sendq.tail = NULL;
36    spawnds[core_id]->recvq.head = NULL;
37    spawnds[core_id]->recvq.tail = NULL;
38
39    b->st = spawnds[core_id];
40
41    return SYS_ERR_OK;
42}
43
44/**
45 * \brief Returns whether connected to spawnd on the given core.
46 */
47inline bool spawnd_state_exists(coreid_t core_id)
48{
49    return spawnds[core_id] != NULL;
50}
51
52/**
53 * \brief Returns the state element for the spawnd on the given core.
54 */
55inline struct spawnd_state *spawnd_state_get(coreid_t core_id)
56{
57    return spawnds[core_id];
58}
59
60/**
61 * \brief Enqueue on a waitset queue.
62 *
63 * \param q    Pointer to queue to enqueue on
64 * \param m    Pointer to element to enqueue
65 *
66 * \return true if queue was empty, false if not.
67 */
68static bool enqueue(struct msg_queue *q, struct msg_queue_elem *m)
69{
70    assert(m->next == NULL);
71
72    // Enqueue on the queue
73    if(q->tail != NULL) {
74        q->tail->next = m;
75    } else {
76        assert(q->head == NULL);
77        q->head = m;
78    }
79    q->tail = m;
80
81    return q->head == q->tail ? true : false;
82}
83
84/**
85 * \brief Dequeues from a waitset queue.
86 *
87 * \param q    Pointer to queue to dequeue from
88 *
89 * \return the newly dequeued element.
90 */
91static struct msg_queue_elem *dequeue(struct msg_queue *q)
92{
93    // Queue should have at least one element
94    assert(q->head != NULL && q->tail != NULL);
95
96    struct msg_queue_elem *e = q->head;
97    q->head = e->next;
98    if(q->head == NULL) {
99        q->tail = NULL;
100    }
101
102    return e;
103}
104
105/**
106 * \brief Enqueue an element on a waitset queue IN FRONT.
107 *
108 * \param q    Pointer to queue to enqueue on
109 * \param m    Pointer to element to enqueue
110 *
111 * \return true if queue was empty, false if not.
112 */
113static bool enqueue_at_front(struct msg_queue *q, struct msg_queue_elem *m)
114{
115    assert(m->next == NULL);
116    if(q->tail == NULL) {
117        assert(q->head == NULL);
118        q->head = m;
119        q->tail = m;
120    } else {
121        m->next = q->head;
122        q->head = m;
123    }
124    return q->head == q->tail ? true : false;
125}
126
127/**
128 * \brief Event-based handler for sending requests to spawnd.
129 *
130 * This function pops the next request from the send queue of the targeted
131 * spawnd (wrapped inside arg). It attempts to send the request, re-enqueuing
132 * it at front if sending fails. It then re-registers a new send if the queue
133 * still has pending requests.
134 *
135 * \param arg Wrapper over the spawnd_state structure for the target spawnd.
136 */
137static void spawnd_send_handler(void *arg)
138{
139    struct spawnd_state *spawnd = (struct spawnd_state*) arg;
140    struct msg_queue *q = &spawnd->sendq;
141
142    // Dequeue next element from the queue
143    struct msg_queue_elem *m = (struct msg_queue_elem*) dequeue(q);
144
145    assert(m->cont != NULL);
146    if (m->cont(m)) {
147        // Send continuation succeeded, need to enqueue a receive.
148        struct msg_queue_elem *recvm = (struct msg_queue_elem*) malloc(
149                sizeof(struct msg_queue_elem));
150        recvm->st = m->st;
151        recvm->next = NULL;
152        enqueue(&spawnd->recvq, recvm);
153    } else {
154        // Send continuation failed, need to re-enqueue message.
155        enqueue_at_front(q, m);
156    }
157
158    if (q->head != NULL) {
159        // Queue is non-empty, therefore re-register.
160        errval_t err = spawnd->b->register_send(spawnd->b, spawnd->b->waitset,
161                                                MKCONT(spawnd_send_handler,
162                                                       arg));
163        if (err_is_fail(err)) {
164            DEBUG_ERR(err, "regitering for spawnd send");
165            return;
166        }
167    }
168}
169
170/**
171 * \brief Enqueues a new send request event.
172 *
173 * \param spawnd target spawnd to send the request to.
174 * \param msg    request to enqueue.
175 */
176errval_t spawnd_state_enqueue_send(struct spawnd_state *spawnd,
177                                   struct msg_queue_elem *msg)
178{
179    msg->next = NULL;
180
181    // If queue was empty, enqueue on waitset
182    if(enqueue(&spawnd->sendq, msg)) {
183        return spawnd->b->register_send(spawnd->b, spawnd->b->waitset,
184                                        MKCONT(spawnd_send_handler, spawnd));
185    } else {
186        return SYS_ERR_OK;
187    }
188}
189
190/**
191 * \brief Dequeues and returns the next message in a receive queue.
192 *
193 * \param spawnd spawnd instance whose receive queue to pop.
194 */
195void *spawnd_state_dequeue_recv(struct spawnd_state *spawnd)
196{
197    struct msg_queue_elem *m = dequeue(&spawnd->recvq);
198    assert(m != NULL);
199    return m->st;
200}