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 import core.sync.condition : Condition;
11 
12 import birchwood.client;
13 
14 version(unittest)
15 {
16     import std.stdio : writeln;
17 }
18 
19 /** 
20  * Manages the send queue
21  */
22 public final class SenderThread : Thread
23 {
24     /** 
25      * The send queue
26      */
27     private SList!(ubyte[]) sendQueue;
28 
29     /** 
30      * The send queue's lock
31      */
32     private Mutex sendQueueLock;
33 
34     /** 
35      * Condition variable for waking
36      * up send queue reader
37      */
38     private Condition sendQueueCond;
39 
40     /** 
41      * The associated IRC client
42      */
43     private Client client;
44 
45     /** 
46      * Constructs a new sender thread with the associated
47      * client
48      *
49      * Params:
50      *   client = the Client to associate with
51      * Throws:
52      *   `SnoozeError` on failure to construct an
53      * `Event` or ensure ourselves
54      */
55     this(Client client)
56     {
57         super(&sendHandlerFunc);
58         this.client = client;
59         this.sendQueueLock = new Mutex();
60         this.sendQueueCond = new Condition(this.sendQueueLock);
61     }
62 
63     /** 
64      * Enqueues the raw message into the send queue
65      * for eventual sending
66      *
67      * Params:
68      *   encodedMessage = the message to enqueue
69      */
70     public void sq(ubyte[] encodedMessage)
71     {
72         /* Lock queue */
73         sendQueueLock.lock();
74 
75         /* Add to queue */
76         sendQueue.insertAfter(sendQueue[], encodedMessage);
77 
78         /* Wake the sleeping message handler */
79         sendQueueCond.notify();
80 
81         /* Unlock queue */
82         sendQueueLock.unlock();
83     }
84 
85     /** 
86      * The send queue worker function
87      */
88     private void sendHandlerFunc()
89     {
90         while(client.isRunning())
91         {
92             /* TODO: handle normal messages (xCount with fakeLagInBetween) */
93 
94             /* Lock the queue */
95             sendQueueLock.lock();
96 
97             /* Sleep till woken (new message) */
98             sendQueueCond.wait(); // TODO: Check SyncError?
99 
100             foreach(ubyte[] message; sendQueue[])
101             {
102                 client.socket.send(message);
103                 Thread.sleep(dur!("seconds")(client.connInfo.getFakeLag()));
104             }
105 
106             /* Empty the send queue */
107             sendQueue.clear();
108 
109             /* Unlock queue */
110             sendQueueLock.unlock();
111         }
112     }
113 
114     /** 
115      * Stops the send queue manager
116      */
117     public void end()
118     {
119         /* Lock the queue */
120         sendQueueLock.lock();
121 
122         /* Wake up sleeping thread (so it can exit) */
123         sendQueueCond.notify();
124 
125         /* Unlock the queue */
126         sendQueueLock.unlock();
127 
128         // Wait on the manager thread to end
129         join();
130     }
131 }