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 }