tristanable/source/tristanable/manager.d

261 lines
4.8 KiB
D
Raw Normal View History

2020-09-29 18:18:53 +01:00
module tristanable.manager;
2020-09-29 18:13:36 +01:00
import std.socket : Socket;
import core.sync.mutex : Mutex;
import bmessage : bSendMessage = sendMessage;
2020-09-29 18:18:53 +01:00
import tristanable.queue : Queue;
import core.thread : Thread, Duration, dur;
2020-10-16 16:11:26 +01:00
import tristanable.watcher;
2021-09-08 10:14:03 +01:00
import std.container.dlist;
import tristanable.exceptions;
2020-09-29 18:13:36 +01:00
2021-09-09 15:09:27 +01:00
/**
* Manager
*
* This is the core class that is to be instantiated
* that represents an instance of the tristanable
* framework. It is passed a Socket from which it
* reads from (using a bformat block reader).
*
* It contains a Watcher which does the reading and
* appending to respective queues (the user need not
* worry about this factum).
*
* The functions provided allow users to wait in a
* tight loop to dequeue ("receive" in a blcoking mannger)
* from a specified queue.
*/
2020-09-29 10:57:25 +01:00
public final class Manager
{
/* All queues */
2021-09-08 10:14:03 +01:00
private DList!(Queue) queues;
2020-09-29 10:57:25 +01:00
private Mutex queuesLock;
/* TODO Add drop queue? */
2020-09-29 18:13:36 +01:00
/**
* The remote host
*/
private Socket socket;
2020-10-16 16:11:26 +01:00
private Watcher watcher;
2022-05-17 13:42:19 +01:00
private bool isAlive;
2020-10-16 16:11:26 +01:00
2020-09-29 18:13:36 +01:00
private bool onRecvQueue;
2020-09-29 18:13:36 +01:00
/**
* Constructs a new Manager with the given
* endpoint Socket
*
*/
this(Socket socket, Duration timeOut = dur!("msecs")(100), bool newSys = false, bool onRecvQueue = false)
2020-09-29 10:57:25 +01:00
{
2021-09-09 15:18:16 +01:00
/* TODO: Make sure the socket is in STREAM mode */
2020-09-29 18:13:36 +01:00
/* Set the socket */
this.socket = socket;
/* Set whether this should have on-receive queue funtionality */
this.onRecvQueue = onRecvQueue;
2020-09-29 18:13:36 +01:00
/* Initialize the queues mutex */
queuesLock = new Mutex();
/* Initialize the watcher */
watcher = new Watcher(this, socket, timeOut, newSys);
2020-09-29 10:57:25 +01:00
}
/**
* Starts the session (watcher)
*/
public void start()
{
/* Set this session as alive */
this.isAlive = true;
/* Start the watcher */
watcher.start();
}
public bool isOnRecvQueue()
{
return onRecvQueue;
}
2020-09-29 18:13:36 +01:00
public Queue getQueue(ulong tag)
{
Queue matchingQueue;
queuesLock.lock();
foreach(Queue queue; queues)
{
if(queue.getTag() == tag)
{
matchingQueue = queue;
break;
}
}
queuesLock.unlock();
return matchingQueue;
}
2020-09-29 10:57:25 +01:00
2021-09-08 12:46:05 +01:00
/* TODO: Probably remove this or keep it */
public bool isValidTag(ulong tag)
{
return !(getQueue(tag) is null);
}
/**
* Returns a new queue with a new ID,
* if all IDs are used then it returns
* null
*
* Use this if you don't care about reserving
* queues IDs and just want a throwaway queue
2021-09-08 12:46:05 +01:00
*
* FIXME: All tags in use, this won't handle it
*/
public Queue generateQueue()
{
/* Newly generated queue */
Queue newQueue;
queuesLock.lock();
ulong curGuess = 0;
bool bad = true;
reguess: while(bad)
{
if(isValidTag(curGuess))
{
curGuess++;
continue reguess;
}
bad = false;
}
/* Create the new queue with the free id found */
2022-05-17 13:42:19 +01:00
newQueue = new Queue(this, curGuess);
2021-09-08 12:42:25 +01:00
/* Add the queue (recursive mutex) */
addQueue(newQueue);
queuesLock.unlock();
return newQueue;
}
public Queue[] getQueues()
{
Queue[] queues;
queuesLock.lock();
foreach(Queue queue; this.queues)
{
queues ~= queue;
}
queuesLock.unlock();
return queues;
}
2021-09-09 17:02:45 +01:00
/**
* Removes the given Queue, `queue`, from the manager
*
* Throws a TristanableException if the id of the
* queue wanting to be removed is not in use by any
* queue already added
*/
public void removeQueue(Queue queue)
{
queuesLock.lock();
/* Make sure such a tag exists */
if(isValidTag(queue.getTag()))
{
queues.linearRemoveElement(queue);
}
else
{
/* Unlock queue before throwing an exception */
queuesLock.unlock();
throw new TristanableException(this, "Cannot remove a queue with an id not in use");
}
queuesLock.unlock();
}
2021-09-09 17:02:45 +01:00
/**
* Adds the given Queue, `queue`, to the manager
*
* Throws a TristanableException if the id of the
* queue wanting to be added is already in use by
* another already added queue
*/
2020-09-29 18:13:36 +01:00
public void addQueue(Queue queue)
2020-09-29 10:57:25 +01:00
{
2020-09-29 18:13:36 +01:00
queuesLock.lock();
/* Make sure such a tag does not exist already */
if(!isValidTag(queue.getTag()))
2020-09-29 18:13:36 +01:00
{
queues ~= queue;
}
else
{
/* Unlock queue before throwing an exception */
queuesLock.unlock();
throw new TristanableException(this, "Cannot add queue with id already in use");
2020-09-29 18:13:36 +01:00
}
queuesLock.unlock();
2020-09-29 10:57:25 +01:00
}
2021-09-08 21:09:48 +01:00
public Socket getSocket()
{
return socket;
}
2022-05-17 13:42:19 +01:00
/**
* Called by the Watcher thread when there is a socket
* error such that the `isValid` status field that
* the Queue operations check can be checked to see
* if the Queue calls should unblock due to a dead
* socket
* FIXME: End user should not be able to call this
*/
void invalidate()
{
isAlive = false;
}
bool isInvalid()
{
return !isAlive;
2022-05-17 13:42:19 +01:00
}
2021-09-08 12:46:05 +01:00
/**
* TODO: Comment
* TODO: Testing
*/
2020-09-29 18:13:36 +01:00
public void shutdown()
{
/* TODO: Implement me */
2021-09-08 10:19:05 +01:00
/* Make the loop stop whenever it does */
watcher.shutdown();
/* Wait for the thread to end */
watcher.join();
2020-09-29 18:13:36 +01:00
}
2020-09-29 10:57:25 +01:00
}