1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/include/fsserver/queue.h Thu Jul 11 17:15:05 2019 +0200
1.3 @@ -0,0 +1,79 @@
1.4 +/*
1.5 + * A queue mechanism for concurrent access to a resource.
1.6 + *
1.7 + * Copyright (C) 2019 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#pragma once
1.26 +
1.27 +#include <l4/sys/types.h>
1.28 +
1.29 +#include <pthread.h>
1.30 +
1.31 +#include <queue>
1.32 +
1.33 +
1.34 +
1.35 +/* Queue operations. */
1.36 +
1.37 +enum { Queue_op_start, Queue_op_yield, Queue_op_end };
1.38 +
1.39 +
1.40 +
1.41 +/* A queue abstraction. */
1.42 +
1.43 +class Queue
1.44 +{
1.45 + /* A queue of threads and the current running thread. */
1.46 +
1.47 + std::queue<l4_cap_idx_t> _queue;
1.48 + l4_cap_idx_t _running = L4_INVALID_CAP;
1.49 +
1.50 + /* The endpoint for interacting with the queue and its own thread. */
1.51 +
1.52 + l4_cap_idx_t _thread_ep;
1.53 + pthread_t _thread;
1.54 +
1.55 +public:
1.56 + explicit Queue()
1.57 + {
1.58 + }
1.59 +
1.60 + /* Initiation and dispatch methods. */
1.61 +
1.62 + virtual long start();
1.63 +
1.64 + virtual void mainloop();
1.65 +
1.66 + /* Queue interaction methods. */
1.67 +
1.68 + virtual long start(l4_cap_idx_t ep);
1.69 +
1.70 + virtual long yield();
1.71 +
1.72 + virtual long end();
1.73 +
1.74 +protected:
1.75 + /* Maintenance and scheduling methods. */
1.76 +
1.77 + virtual void remove();
1.78 +
1.79 + virtual void next();
1.80 +
1.81 + l4_cap_idx_t pop();
1.82 +};
2.1 --- a/lib/src/Makefile Mon Jul 08 21:25:44 2019 +0200
2.2 +++ b/lib/src/Makefile Thu Jul 11 17:15:05 2019 +0200
2.3 @@ -3,7 +3,7 @@
2.4
2.5 TARGET = libfsserver.a libfsserver.so
2.6 PC_FILENAME = libfsserver
2.7 -SRC_CC = accessor.cc file_resource.cc flexpage.cc pager.cc paging.cc pages.cc resource.cc server.cc
2.8 +SRC_CC = accessor.cc file_resource.cc flexpage.cc pager.cc paging.cc pages.cc queue.cc resource.cc server.cc
2.9 REQUIRES_LIBS = libipc libfsclient
2.10
2.11 PRIVATE_INCDIR = $(PKGDIR)/include/fsserver
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/lib/src/queue.cc Thu Jul 11 17:15:05 2019 +0200
3.3 @@ -0,0 +1,188 @@
3.4 +/*
3.5 + * A queue mechanism for concurrent access to a resource.
3.6 + *
3.7 + * Copyright (C) 2019 Paul Boddie <paul@boddie.org.uk>
3.8 + *
3.9 + * This program is free software; you can redistribute it and/or
3.10 + * modify it under the terms of the GNU General Public License as
3.11 + * published by the Free Software Foundation; either version 2 of
3.12 + * the License, or (at your option) any later version.
3.13 + *
3.14 + * This program is distributed in the hope that it will be useful,
3.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.17 + * GNU General Public License for more details.
3.18 + *
3.19 + * You should have received a copy of the GNU General Public License
3.20 + * along with this program; if not, write to the Free Software
3.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
3.22 + * Boston, MA 02110-1301, USA
3.23 + */
3.24 +
3.25 +#include <l4/re/c/util/cap_alloc.h>
3.26 +
3.27 +#include <stdlib.h>
3.28 +#include <stdio.h>
3.29 +
3.30 +#include <pthread-l4.h>
3.31 +
3.32 +#include <ipc/server.h>
3.33 +#include <ipc/util_ipc.h>
3.34 +#include "queue.h"
3.35 +
3.36 +
3.37 +
3.38 +/* Thread main function for a queue. */
3.39 +
3.40 +static void *queue_thread(void *data)
3.41 +{
3.42 + Queue *obj = reinterpret_cast<Queue *>(data);
3.43 +
3.44 + obj->mainloop();
3.45 + return 0;
3.46 +}
3.47 +
3.48 +
3.49 +
3.50 +/* Set up thread. */
3.51 +
3.52 +long Queue::start()
3.53 +{
3.54 + pthread_create(&_thread, NULL, queue_thread, this);
3.55 +
3.56 + long err = ipc_server_new_gate_for_thread(&_thread_ep, pthread_l4_cap(_thread), l4_umword_t(this));
3.57 + if (err)
3.58 + return err;
3.59 +
3.60 + return L4_EOK;
3.61 +}
3.62 +
3.63 +/* Listen for messages. */
3.64 +
3.65 +void Queue::mainloop()
3.66 +{
3.67 + ipc_expect_capabilities(1);
3.68 +
3.69 + while (1)
3.70 + {
3.71 + l4_msgtag_t tag = ipc_server_wait((l4_umword_t) this);
3.72 + long op = l4_msgtag_label(tag);
3.73 +
3.74 + if (l4_ipc_error(tag, l4_utcb()))
3.75 + continue;
3.76 +
3.77 + /* Register the client and either continue or wait to continue. */
3.78 +
3.79 + if (op == Queue_op_start)
3.80 + {
3.81 + l4_cap_idx_t client = L4_INVALID_CAP;
3.82 +
3.83 + if (ipc_import_capability(tag, 0, &client))
3.84 + continue;
3.85 +
3.86 + _queue.push(client);
3.87 +
3.88 + ipc_expect_capability(0);
3.89 +
3.90 + /* Only schedule another client if another is not running and is
3.91 + available. */
3.92 +
3.93 + if (_queue.size() > 1)
3.94 + if (l4_is_invalid_cap(_running))
3.95 + next();
3.96 + }
3.97 +
3.98 + /* Remove the client and schedule another client. */
3.99 +
3.100 + else if (op == Queue_op_end)
3.101 + {
3.102 + remove();
3.103 + next();
3.104 + }
3.105 +
3.106 + /* Yield from the client, scheduling another. */
3.107 +
3.108 + else if (op == Queue_op_yield)
3.109 + {
3.110 + next();
3.111 + }
3.112 + }
3.113 +}
3.114 +
3.115 +/* Remove the running task from the queue, releasing its capability. */
3.116 +
3.117 +void Queue::remove()
3.118 +{
3.119 + if (l4_is_invalid_cap(_running))
3.120 + return;
3.121 +
3.122 + l4re_util_cap_free_um(_running);
3.123 +
3.124 + _running = L4_INVALID_CAP;
3.125 +}
3.126 +
3.127 +/* Schedule another client from the queue. */
3.128 +
3.129 +void Queue::next()
3.130 +{
3.131 + if (_queue.empty())
3.132 + return;
3.133 +
3.134 + if (l4_is_valid_cap(_running))
3.135 + _queue.push(_running);
3.136 +
3.137 + _running = pop();
3.138 +
3.139 + l4_ipc_send(_running, l4_utcb(), l4_msgtag(0, 0, 0, 0), L4_IPC_NEVER);
3.140 +}
3.141 +
3.142 +/* Pop the front of the queue. */
3.143 +
3.144 +l4_cap_idx_t Queue::pop()
3.145 +{
3.146 + l4_cap_idx_t front = _queue.front();
3.147 + _queue.pop();
3.148 + return front;
3.149 +}
3.150 +
3.151 +
3.152 +
3.153 +/* Finish with the queue. */
3.154 +
3.155 +long Queue::end()
3.156 +{
3.157 + l4_msgtag_t tag;
3.158 +
3.159 + tag = l4_msgtag(Queue_op_end, 0, 0, 0);
3.160 + tag = l4_ipc_send(_thread_ep, l4_utcb(), tag, L4_IPC_NEVER);
3.161 +
3.162 + return l4_error(tag);
3.163 +}
3.164 +
3.165 +/* Register with the queue. */
3.166 +
3.167 +long Queue::start(l4_cap_idx_t ep)
3.168 +{
3.169 + l4_msgtag_t tag;
3.170 +
3.171 + if (l4_is_invalid_cap(ep))
3.172 + return -L4_EINVAL;
3.173 +
3.174 + tag = l4_msgtag(Queue_op_start, 0, 1, 0);
3.175 + ipc_export_capability(tag, 0, ep);
3.176 + tag = l4_ipc_call(_thread_ep, l4_utcb(), tag, L4_IPC_NEVER);
3.177 +
3.178 + return l4_error(tag);
3.179 +}
3.180 +
3.181 +/* Yield control to another member of the queue. */
3.182 +
3.183 +long Queue::yield()
3.184 +{
3.185 + l4_msgtag_t tag;
3.186 +
3.187 + tag = l4_msgtag(Queue_op_yield, 0, 0, 0);
3.188 + tag = l4_ipc_call(_thread_ep, l4_utcb(), tag, L4_IPC_NEVER);
3.189 +
3.190 + return l4_error(tag);
3.191 +}