1 /** 2 * Send queue management 3 */ 4 module birchwood.client.sender; 5 6 import core.thread : Thread, dur; 7 8 import std.container.slist : SList; 9 import core.sync.mutex : Mutex; 10 11 // TODO: Examine the below import which seemingly fixes stuff for libsnooze 12 import libsnooze.clib; 13 import libsnooze; 14 15 import birchwood.client; 16 17 version(unittest) 18 { 19 import std.stdio : writeln; 20 } 21 22 /** 23 * Manages the send queue 24 */ 25 public final class SenderThread : Thread 26 { 27 /** 28 * The send queue 29 */ 30 private SList!(ubyte[]) sendQueue; 31 32 /** 33 * The send queue's lock 34 */ 35 private Mutex sendQueueLock; 36 37 /** 38 * The libsnooze event to await on which 39 * when we wake up signals a new message 40 * to be processed and sent 41 */ 42 private Event sendEvent; 43 44 /** 45 * The associated IRC client 46 */ 47 private Client client; 48 49 /** 50 * Constructs a new sender thread with the associated 51 * client 52 * 53 * Params: 54 * client = the Client to associate with 55 * Throws: 56 * `SnoozeError` on failure to construct an 57 * `Event` or ensure ourselves 58 */ 59 this(Client client) 60 { 61 super(&sendHandlerFunc); 62 this.client = client; 63 this.sendEvent = new Event(); 64 this.sendQueueLock = new Mutex(); 65 this.sendEvent.ensure(this); 66 } 67 68 /** 69 * Enqueues the raw message into the send queue 70 * for eventual sending 71 * 72 * Params: 73 * encodedMessage = the message to enqueue 74 */ 75 public void sq(ubyte[] encodedMessage) 76 { 77 /* Lock queue */ 78 sendQueueLock.lock(); 79 80 /* Add to queue */ 81 sendQueue.insertAfter(sendQueue[], encodedMessage); 82 83 /* Unlock queue */ 84 sendQueueLock.unlock(); 85 86 /** 87 * Wake up all threads waiting on this event 88 * (if any, and if so it would only be the sender) 89 */ 90 sendEvent.notifyAll(); 91 } 92 93 /** 94 * The send queue worker function 95 */ 96 private void sendHandlerFunc() 97 { 98 while(client.running) 99 { 100 // TODO: We could look at libsnooze wait starvation or mutex racing (future thought) 101 102 /* TODO: handle normal messages (xCount with fakeLagInBetween) */ 103 104 try 105 { 106 sendEvent.wait(); 107 } 108 catch(InterruptedException e) 109 { 110 version(unittest) 111 { 112 writeln("wait() interrupted"); 113 } 114 continue; 115 } 116 catch(FatalException e) 117 { 118 // TODO: This should crash and end 119 version(unittest) 120 { 121 writeln("wait() had a FATAL error!!!!!!!!!!!"); 122 } 123 continue; 124 } 125 126 127 /* Lock queue */ 128 sendQueueLock.lock(); 129 130 foreach(ubyte[] message; sendQueue[]) 131 { 132 client.socket.send(message); 133 Thread.sleep(dur!("seconds")(client.connInfo.getFakeLag())); 134 } 135 136 /* Empty the send queue */ 137 sendQueue.clear(); 138 139 /* Unlock queue */ 140 sendQueueLock.unlock(); 141 } 142 } 143 144 /** 145 * Stops the send queue manager 146 */ 147 public void end() 148 { 149 // TODO: See above notes about libsnooze behaviour due 150 // ... to usage in our context 151 sendEvent.notifyAll(); 152 } 153 }