diff --git a/README.md b/README.md index dec93a2..dd275f4 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ dub add birchwood Birchwood depends on the following D libraries: -* `libsnooze` (atleast 1.0.0-beta) -* `eventy` (atleast 0.4.0) -* `dlog` (atleast 0.3.19) +* `libsnooze` (at least 1.2.0-beta) +* `eventy` (at least 0.4.0) +* `dlog` (at least 0.3.19) ## Usage diff --git a/dub.json b/dub.json index b7a3312..4973ba2 100644 --- a/dub.json +++ b/dub.json @@ -7,7 +7,7 @@ "dependencies": { "dlog": ">=0.3.19", "eventy": ">=0.4.0", - "libsnooze": ">=1.0.0-beta" + "libsnooze": ">=1.2.0-beta" }, "description": "A sane IRC framework for the D language", "license": "LGPL-3.0", diff --git a/source/birchwood/client/client.d b/source/birchwood/client/client.d index a527e28..95e8509 100644 --- a/source/birchwood/client/client.d +++ b/source/birchwood/client/client.d @@ -10,7 +10,7 @@ import std.container.slist : SList; import core.sync.mutex : Mutex; import core.thread : Thread, dur; import std.string; -import eventy : EventyEvent = Event, Engine, EventType, Signal; +import eventy : EventyEvent = Event, Engine, EventType, Signal, EventyException; import birchwood.config; import birchwood.client.exceptions : BirchwoodException, ErrorType; import birchwood.protocol.messages : Message, encodeMessage, decodeMessage, isValidText; @@ -19,6 +19,8 @@ import birchwood.client.receiver : ReceiverThread; import birchwood.client.sender : SenderThread; import birchwood.client.events; +import libsnooze.exceptions : SnoozeError; + import dlog; package __gshared Logger logger; @@ -209,7 +211,7 @@ public class Client : Thread * Params: * nickname = the nickname to request * Throws: - * BirchwoodException on invalid nickname + * `BirchwoodException` on invalid nickname */ public void nick(string nickname) { @@ -244,7 +246,7 @@ public class Client : Thread * Params: * channel = the channel to join * Throws: - * BirchwoodException on invalid channel name + * `BirchwoodException` on invalid channel name */ public void joinChannel(string channel) { @@ -275,7 +277,8 @@ public class Client : Thread * Params: * channels = the channels to join * Throws: - * BirchwoodException on invalid channel name + * `BirchwoodException` on invalid channel name or + * if the list is empty */ public void joinChannel(string[] channels) { @@ -344,7 +347,8 @@ public class Client : Thread * Params: * channels = the list of channels to part from * Throws: - * BirchwoodException if the channels list is empty + * `BirchwoodException` if the channels list is empty + * or there are illegal characters present */ public void leaveChannel(string[] channels) { @@ -414,14 +418,24 @@ public class Client : Thread * * Params: * channel = the channel to leave + * Throws: + * `BirchwoodException` if the channel name + * is invalid */ public void leaveChannel(string channel) { - // TODO: Add check for valid and non-empty channel names - - /* Leave the channel */ - Message leaveMessage = new Message("", "PART", channel); - sendMessage(leaveMessage); + /* Ensure the channel name contains only valid characters */ + if(isValidText(channel)) + { + /* Leave the channel */ + Message leaveMessage = new Message("", "PART", channel); + sendMessage(leaveMessage); + } + /* If invalid characters were present */ + else + { + throw new BirchwoodException(ErrorType.ILLEGAL_CHARACTERS); + } } /** @@ -431,7 +445,8 @@ public class Client : Thread * message = The message to send * recipients = The receipients of the message * Throws: - * BirchwoodException if the recipients list is empty + * `BirchwoodException` if the recipients list is empty + * or illegal characters are present */ public void directMessage(string message, string[] recipients) { @@ -504,6 +519,9 @@ public class Client : Thread * Params: * message = The message to send * recipients = The receipient of the message + * Throws: + * `BirchwoodException` if the receipient's nickname + * is invalid or there are illegal characters present */ public void directMessage(string message, string recipient) { @@ -537,7 +555,7 @@ public class Client : Thread * message = The message to send * recipients = The receipients of the message * Throws: - * BirchwoodException if the channels list is empty + * `BirchwoodException` if the channels list is empty */ public void channelMessage(string message, string[] channels) { @@ -610,6 +628,9 @@ public class Client : Thread * Params: * message = The message to send * channel = The channel to send the message to + * Throws: + * `BirchwoodException` if the message or channel name + * contains illegal characters */ public void channelMessage(string message, string channel) { @@ -625,13 +646,11 @@ public class Client : Thread } else { - //TODO: Invalid channel name throw new BirchwoodException(ErrorType.INVALID_CHANNEL_NAME); } } else { - //TODO: Illegal characters throw new BirchwoodException(ErrorType.ILLEGAL_CHARACTERS); } } @@ -649,8 +668,12 @@ public class Client : Thread } /** - * Initialize the event handlers - */ + * Initialize the event handlers + * + * Throws: + * `EventyException` on error registering + * the signals and event types + */ private void initEvents() { /* TODO: For now we just register one signal type for all messages */ @@ -775,7 +798,8 @@ public class Client : Thread * Connects to the server * * Throws: - * BirchwoodException if there is an error connecting + * `BirchwoodException` if there is an error connecting + * or something failed internally */ public void connect() { @@ -793,22 +817,12 @@ public class Client : Thread /* Register default handler */ initEvents(); - // /** - // * Initialize the ready events for both the - // * receive and send queue managers, then after - // * doing so start both managers and spin for - // * both of them to enter a ready state (i.e. - // * they have ensured a waiting-pipe pair for - // * libsnooze exists) - // */ - /* Set the running status to true */ running = true; /* Start the receive queue and send queue managers */ this.receiver.start(); this.sender.start(); - // while(!receiver.isReady() || !sender.isReady()) {} /* Start the socket read-decode loop */ this.start(); @@ -820,6 +834,14 @@ public class Client : Thread { throw new BirchwoodException(ErrorType.CONNECT_ERROR); } + catch(EventyException e) + { + throw new BirchwoodException(ErrorType.INTERNAL_FAILURE, e.toString()); + } + catch(SnoozeError e) + { + throw new BirchwoodException(ErrorType.INTERNAL_FAILURE, e.toString()); + } } // TODO: Do actual liveliness check here else diff --git a/source/birchwood/client/exceptions.d b/source/birchwood/client/exceptions.d index 7258c38..36b7456 100644 --- a/source/birchwood/client/exceptions.d +++ b/source/birchwood/client/exceptions.d @@ -15,6 +15,16 @@ import std.conv : to; */ public enum ErrorType { + /** + * This could occur from errors with `Eventy` + * when setting up the signal handlers and + * event types. It can also occur if `libsnooze` + * has an error which would occur when calling + * `ensure(Thread)` for the `Receiver` and `Sender` + * threads + */ + INTERNAL_FAILURE, + /** * If the provided connection information * is invalid, such as incorrect hostname, diff --git a/source/birchwood/client/receiver.d b/source/birchwood/client/receiver.d index a847660..a0bafa2 100644 --- a/source/birchwood/client/receiver.d +++ b/source/birchwood/client/receiver.d @@ -48,7 +48,6 @@ public final class ReceiverThread : Thread * to be processed and received */ private Event receiveEvent; - // private bool hasEnsured; /** * The associated IRC client @@ -61,16 +60,19 @@ public final class ReceiverThread : Thread * * Params: * client = the Client to associate with + * Throws: + * `SnoozeError` on failure to construct an + * `Event` or ensure ourselves */ this(Client client) { super(&recvHandlerFunc); this.client = client; - this.receiveEvent = new Event(); // TODO: Catch any libsnooze error here - this.recvQueueLock = new Mutex(); + this.receiveEvent = new Event(); + this.recvQueueLock = new Mutex(); + this.receiveEvent.ensure(this); } - // TODO: Rename to `receiveQ` /** * Enqueues the raw message into the receieve queue * for eventual processing @@ -89,10 +91,6 @@ public final class ReceiverThread : Thread /* Unlock queue */ recvQueueLock.unlock(); - // TODO: Add a "register" function which can initialize pipes - // ... without needing a wait, we'd need a ready flag though - // ... for receiver's thread start - /** * Wake up all threads waiting on this event * (if any, and if so it would only be the receiver) @@ -116,21 +114,6 @@ public final class ReceiverThread : Thread { // TODO: We could look at libsnooze wait starvation or mutex racing (future thought) - - // // Do a once-off call to `ensure()` here which then only runs once and - // // ... sets a `ready` flag for the Client to spin on. This ensures that - // // ... when the first received messages will be able to cause a wait - // // ... to immediately unblock rather than letting wait() register itself - // // ... and then require another receiveQ call to wake it up and process - // // ... the initial n messages + m new ones resulting in the second call - // if(hasEnsured == false) - // { - // receiveEvent.ensure(); - // hasEnsured = true; - // } - - // TODO: See above notes about libsnooze behaviour due - // ... to usage in our context try { receiveEvent.wait(); @@ -152,9 +135,7 @@ public final class ReceiverThread : Thread } continue; } - - - + /* Lock the receieve queue */ recvQueueLock.lock(); diff --git a/source/birchwood/client/sender.d b/source/birchwood/client/sender.d index 0de1df0..2af5a1e 100644 --- a/source/birchwood/client/sender.d +++ b/source/birchwood/client/sender.d @@ -40,7 +40,6 @@ public final class SenderThread : Thread * to be processed and sent */ private Event sendEvent; - // private bool hasEnsured; /** * The associated IRC client @@ -53,16 +52,19 @@ public final class SenderThread : Thread * * Params: * client = the Client to associate with + * Throws: + * `SnoozeError` on failure to construct an + * `Event` or ensure ourselves */ this(Client client) { super(&sendHandlerFunc); this.client = client; - this.sendEvent = new Event(); // TODO: Catch any libsnooze error here + this.sendEvent = new Event(); this.sendQueueLock = new Mutex(); + this.sendEvent.ensure(this); } - // TODO: Rename to `sendQ` /** * Enqueues the raw message into the send queue * for eventual sending @@ -81,10 +83,6 @@ public final class SenderThread : Thread /* Unlock queue */ sendQueueLock.unlock(); - // TODO: Add a "register" function which can initialize pipes - // ... without needing a wait, we'd need a ready flag though - // ... for sender's thread start - /** * Wake up all threads waiting on this event * (if any, and if so it would only be the sender) @@ -92,7 +90,6 @@ public final class SenderThread : Thread sendEvent.notifyAll(); } - /** * The send queue worker function */ @@ -100,24 +97,10 @@ public final class SenderThread : Thread { while(client.running) { - // // Do a once-off call to `ensure()` here which then only runs once and - // // ... sets a `ready` flag for the Client to spin on. This ensures that - // // ... when the first sent messages will be able to cause a wait - // // ... to immediately unblock rather than letting wait() register itself - // // ... and then require another sendQ call to wake it up and process - // // ... the initial n messages + m new ones resulting in the second call - // if(hasEnsured == false) - // { - // sendEvent.ensure(); - // hasEnsured = true; - // } - // TODO: We could look at libsnooze wait starvation or mutex racing (future thought) /* TODO: handle normal messages (xCount with fakeLagInBetween) */ - // TODO: See above notes about libsnooze behaviour due - // ... to usage in our context try { sendEvent.wait(); @@ -141,13 +124,6 @@ public final class SenderThread : Thread } - - - // TODO: After the above call have a once-off call to `ensure()` here - // ... which then only runs once and sets a `ready` flag for the Client - // ... to spin on - - /* Lock queue */ sendQueueLock.lock();