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