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 }