1 /**
2  * Miscellaneous types.
3  *
4  * Copyright: © 2015-2016 Dragos Carp
5  * License: Boost Software License - Version 1.0
6  * Authors: Dragos Carp
7  */
8 module asynchronous.types;
9 
10 import std.algorithm;
11 import std.container.array;
12 import std.exception;
13 import std.traits;
14 
15 struct Coroutine
16 {
17 }
18 
19 template UNSPECIFIED(E)
20 if (is(E == enum))
21 {
22     E UNSPECIFIED()
23     {
24         return cast(E) 0;
25     }
26 }
27 
28 /**
29  * This operation was cancelled.
30  */
31 class CancelledException : Exception
32 {
33     this(string message = null, string file = __FILE__, size_t line = __LINE__,
34         Throwable next = null) @safe pure nothrow
35     {
36         super(message, file, line, next);
37     }
38 }
39 
40 /**
41  * This operation is not allowed in this state.
42  */
43 class InvalidStateException : Exception
44 {
45     this(string message = null, string file = __FILE__, size_t line = __LINE__,
46         Throwable next = null) @safe pure nothrow
47     {
48         super(message, file, line, next);
49     }
50 }
51 
52 /**
53  * This operation exceeded the given deadline.
54  */
55 class TimeoutException : Exception
56 {
57     this(string message = null, string file = __FILE__, size_t line = __LINE__,
58         Throwable next = null) @safe pure nothrow
59     {
60         super(message, file, line, next);
61     }
62 }
63 
64 /**
65  * This operation is not implemented.
66  */
67 class NotImplementedException : Exception
68 {
69     this(string message = null, string file = __FILE__, size_t line = __LINE__,
70         Throwable next = null) @safe pure nothrow
71     {
72         super(message, file, line, next);
73     }
74 }
75 
76 class ResourcePool(T, TArgs...)
77 if (is(T == class))
78 {
79     private Array!ubyte inUseFlags;
80     private Array!T resources;
81 
82     private TArgs tArgs;
83 
84     this(TArgs tArgs)
85     {
86         this.tArgs = tArgs;
87     }
88 
89     T acquire(TResetArgs...)(TResetArgs tResetArgs)
90     out (result)
91     {
92         assert(result !is null);
93     }
94     body
95     {
96         auto count = inUseFlags[].countUntil!"!a";
97         T result = null;
98 
99         if (count >= 0)
100         {
101             inUseFlags[count] = true;
102             result = resources[count];
103         }
104         else
105         {
106             result = new T(tArgs);
107             inUseFlags.insertBack(true);
108             resources.insertBack(result);
109         }
110 
111         static if (hasMember!(T, "reset"))
112             result.reset(tResetArgs);
113 
114         return result;
115     }
116 
117     void release(T t)
118     {
119         auto count = resources[].countUntil!(a => a is t);
120 
121         enforce(count >= 0, "Cannot release non-acquired resource");
122         enforce(inUseFlags[count], "Resource is already released");
123 
124         inUseFlags[count] = false;
125 
126         static if (hasMember!(T, "reset"))
127             resources[count].reset;
128     }
129 
130     @property size_t length() const
131     {
132         return inUseFlags[].count!(a => !!a);
133     }
134 
135     @property size_t capacity() const
136     {
137         return inUseFlags.length;
138     }
139 
140     override string toString() const
141     {
142         import std.format : format;
143 
144         return "%s(length: %s, capacity: %s)".format(typeid(this), length,
145             capacity);
146     }
147 }
148 
149 unittest
150 {
151     import std.algorithm : each, equal, map;
152     import std.range : array, drop, iota, take;
153 
154     static class Foo
155     {
156         int bar;
157         int baz;
158 
159         this(int baz)
160         {
161             this.baz = baz;
162         }
163 
164         void reset(int bar = 0)
165         {
166             this.bar = bar;
167         }
168     }
169 
170     auto fooPool = new ResourcePool!(Foo, int)(10);
171 
172     auto foo = fooPool.acquire(100);
173     assert(foo.bar == 100);
174     assert(foo.baz == 10);
175     fooPool.release(foo);
176     assert(foo.bar == 0);
177     assert(foo.baz == 10);
178 
179     auto foos1 = iota(100).map!(a => fooPool.acquire(a)).array;
180     assert(fooPool.length == 100);
181     assert(fooPool.capacity == 100);
182     assert(foos1.map!(a => a.bar).equal(iota(100)));
183 
184     foos1.take(20).each!(a => fooPool.release(a));
185     foos1 = foos1.drop(20);
186     assert(fooPool.length == 80);
187     assert(fooPool.capacity == 100);
188 
189     auto foos2 = iota(10).map!(a => fooPool.acquire(a)).array;
190     assert(fooPool.length == 90);
191     assert(fooPool.capacity == 100);
192 
193     auto foos3 = iota(20).map!(a => fooPool.acquire(a)).array;
194     assert(fooPool.length == 110);
195     assert(fooPool.capacity == 110);
196 
197     foos1.each!(a => fooPool.release(a));
198     assert(fooPool.length == 30);
199     assert(fooPool.capacity == 110);
200 
201     foos2.each!(a => fooPool.release(a));
202     assert(fooPool.length == 20);
203     assert(fooPool.capacity == 110);
204 
205     foos3.each!(a => fooPool.release(a));
206     assert(fooPool.length == 0);
207     assert(fooPool.capacity == 110);
208 }