1 // Copyright 2022 Garrett D'Amore
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE or https://www.boost.org/LICENSE_1_0.txt)
5 
6 module dcell.evqueue;
7 
8 import core.sync.condition;
9 import core.sync.mutex;
10 import core.time;
11 import std.concurrency;
12 
13 import dcell.event;
14 
15 package class EventQueue
16 {
17     this()
18     {
19         mx = new Mutex();
20         cv = new Condition(mx);
21     }
22 
23     Event receive(this Q)(Duration dur)
24             if (is(Q == EventQueue) || is(Q == shared EventQueue))
25     {
26         Event ev;
27         synchronized (mx)
28         {
29             while ((q.length == 0) && !closed)
30             {
31                 if (!cv.wait(dur))
32                 {
33                     return (ev);
34                 }
35             }
36 
37             if (closed)
38             {
39                 return Event(EventType.closed);
40             }
41             ev = q[0];
42             q = q[1 .. $];
43         }
44 
45         return ev;
46     }
47 
48     Event receive(this Q)() if (is(Q == EventQueue) || is(Q == shared EventQueue))
49     {
50         Event ev;
51         synchronized (mx)
52         {
53             while ((q.length == 0) && !closed)
54             {
55                 cv.wait();
56             }
57             if (closed)
58             {
59                 return Event(EventType.closed);
60             }
61 
62             ev = q[0];
63             q = q[1 .. $];
64         }
65 
66         return ev;
67     }
68 
69     void close(this Q)() if (is(Q == EventQueue) || is(Q == shared EventQueue))
70     {
71         synchronized (mx)
72         {
73             closed = true;
74             cv.notifyAll();
75         }
76     }
77 
78     void send(this Q)(Event ev) if (is(Q == EventQueue) || is(Q == shared EventQueue))
79     {
80         synchronized (mx)
81         {
82             if (!closed) // cannot send any more events after close
83             {
84                 q ~= ev;
85                 cv.notify();
86             }
87         }
88     }
89 
90 private:
91 
92     Mutex mx;
93     Condition cv;
94     Event[] q;
95     bool closed;
96 }
97 
98 unittest
99 {
100     import core.thread;
101     auto eq = new EventQueue();
102 
103     eq.send(Event(EventType.key));
104     assert(eq.receive().type  == EventType.key);
105 
106     assert(eq.receive(msecs(10)).type == EventType.none);
107 
108     spawn(function(shared EventQueue eq){
109         Thread.sleep(msecs(50));
110         eq.send(Event(EventType.mouse));
111     }, cast(shared EventQueue)eq);
112     assert(eq.receive().type == EventType.mouse);
113     eq.close();
114     assert(eq.receive().type == EventType.closed);
115 }