1 /** 2 * Supporting of dynamic configuration of libpq 3 */ 4 module dpq2.dynloader; 5 6 version(Dpq2_Dynamic): 7 8 import dpq2.connection: Connection; 9 import core.sync.mutex: Mutex; 10 import dpq2.exception: Dpq2Exception; 11 12 class ConnectionFactory 13 { 14 private __gshared Mutex mutex; 15 private __gshared bool instanced; 16 private ReferenceCounter cnt; 17 18 shared static this() 19 { 20 mutex = new Mutex(); 21 } 22 23 //mixin ctorBody; //FIXME: https://issues.dlang.org/show_bug.cgi?id=22627 24 immutable mixin ctorBody; 25 26 // If ctor throws dtor will be called. This is behaviour of current D design. 27 // https://issues.dlang.org/show_bug.cgi?id=704 28 private immutable bool isSucessfulConstructed; 29 30 private mixin template ctorBody() 31 { 32 this() { this(""); } 33 34 /** 35 Params: 36 path = A string containing one or more comma-separated shared 37 library names. 38 */ 39 this(string path) 40 { 41 import std.exception: enforce; 42 43 mutex.lock(); 44 scope(success) instanced = true; 45 scope(exit) mutex.unlock(); 46 47 enforce!Dpq2Exception(!instanced, "Already instanced"); 48 49 cnt = ReferenceCounter(path); 50 assert(ReferenceCounter.instances == 1); 51 52 isSucessfulConstructed = true; 53 } 54 } 55 56 ~this() 57 { 58 mutex.lock(); 59 scope(exit) mutex.unlock(); 60 61 if(isSucessfulConstructed) 62 { 63 assert(instanced); 64 65 cnt.__custom_dtor(); 66 } 67 68 instanced = false; 69 } 70 71 /// This method is need to forbid attempts to create connection without properly loaded libpq 72 /// Accepts same parameters as Connection ctors in static configuration 73 Connection createConnection(T...)(T args) const 74 { 75 mutex.lock(); 76 scope(exit) mutex.unlock(); 77 78 assert(instanced); 79 80 return new Connection(args); 81 } 82 83 void connStringCheck(string connString) const 84 { 85 mutex.lock(); 86 scope(exit) mutex.unlock(); 87 88 assert(instanced); 89 90 import dpq2.connection; 91 92 _connStringCheck(connString); 93 } 94 } 95 96 package struct ReferenceCounter 97 { 98 import core.atomic; 99 import derelict.pq.pq: DerelictPQ; 100 debug import std.experimental.logger; 101 import std.stdio: writeln; 102 103 debug(dpq2_verbose) invariant() 104 { 105 mutex.lock(); 106 scope(exit) mutex.unlock(); 107 108 import std.stdio; 109 writeln("Instances ", instances); 110 } 111 112 private __gshared Mutex mutex; 113 private __gshared ptrdiff_t instances; 114 115 shared static this() 116 { 117 mutex = new Mutex(); 118 } 119 120 this() @disable; 121 this(this) @disable; 122 123 /// Used only by connection factory 124 this(string path) 125 { 126 mutex.lock(); 127 scope(exit) mutex.unlock(); 128 129 assert(instances == 0); 130 131 debug trace("DerelictPQ loading..."); 132 DerelictPQ.load(path); 133 debug trace("...DerelictPQ loading finished"); 134 135 instances++; 136 } 137 138 /// Used by all other objects 139 this(bool) 140 { 141 mutex.lock(); 142 scope(exit) mutex.unlock(); 143 144 assert(instances > 0); 145 146 instances++; 147 } 148 149 // TODO: here is must be a destructor, but: 150 // "This is bug or not? (immutable class containing struct with dtor)" 151 // https://forum.dlang.org/post/spim8c$108b$1@digitalmars.com 152 // https://issues.dlang.org/show_bug.cgi?id=13628 153 void __custom_dtor() const 154 { 155 mutex.lock(); 156 scope(exit) mutex.unlock(); 157 158 assert(instances > 0); 159 160 instances--; 161 162 if(instances == 0) 163 { 164 //TODO: replace writeln by trace? 165 debug trace("DerelictPQ unloading..."); 166 DerelictPQ.unload(); 167 debug trace("...DerelictPQ unloading finished"); 168 } 169 } 170 } 171 172 version (integration_tests): 173 174 void _integration_test() 175 { 176 import std.exception : assertThrown; 177 178 // Some testing: 179 { 180 auto f = new immutable ConnectionFactory(); 181 assert(ConnectionFactory.instanced); 182 assert(ReferenceCounter.instances == 1); 183 f.destroy; 184 } 185 186 assert(ConnectionFactory.instanced == false); 187 assert(ReferenceCounter.instances == 0); 188 189 { 190 auto f = new immutable ConnectionFactory(); 191 192 // Only one instance of ConnectionFactory is allowed 193 assertThrown!Dpq2Exception(new immutable ConnectionFactory()); 194 195 assert(ConnectionFactory.instanced); 196 assert(ReferenceCounter.instances == 1); 197 198 f.destroy; 199 } 200 201 assert(!ConnectionFactory.instanced); 202 assert(ReferenceCounter.instances == 0); 203 204 { 205 import derelict.util.exception: SharedLibLoadException; 206 207 assertThrown!SharedLibLoadException( 208 new immutable ConnectionFactory(`wrong/path/to/libpq.dll`) 209 ); 210 } 211 212 assert(!ConnectionFactory.instanced); 213 assert(ReferenceCounter.instances == 0); 214 } 215 216 // Used only by integration tests facility: 217 private shared ConnectionFactory _connFactory; 218 219 //TODO: Remove cast and immutable, https://issues.dlang.org/show_bug.cgi?id=22627 220 package immutable(ConnectionFactory) connFactory() { return cast(immutable) _connFactory; } 221 222 void _initTestsConnectionFactory() 223 { 224 //TODO: ditto 225 _connFactory = cast(shared) new immutable ConnectionFactory; 226 227 assert(ConnectionFactory.instanced); 228 assert(ReferenceCounter.instances == 1); 229 }