1 module grpc.core.resource; 2 import core.lifetime; 3 import interop.headers; 4 import grpc.core.sync.mutex; 5 import core.atomic : atomicOp; 6 7 struct SharedResource 8 { 9 @safe @nogc: 10 alias bool function(shared(void)*) nothrow Release; 11 static immutable ReleaseException = new Exception("Failed to release resource"); 12 13 this(shared(void)* ptr, Release release) @trusted 14 { 15 assert(ptr); 16 m_payload.refCount = 1; 17 m_payload.handle = ptr; 18 m_payload.release = release; 19 mu = cast(shared)Mutex.create(); 20 } 21 22 this(this) @trusted 23 { 24 if (m_payload != shared(Payload).init) { 25 incRefCount(); 26 } 27 } 28 29 ~this() 30 { 31 detach(); 32 } 33 34 void opAssign(SharedResource rhs) @trusted 35 { 36 detach(); 37 m_payload = rhs.m_payload; 38 rhs.m_payload = Payload.init; 39 } 40 41 bool detach() @trusted 42 { 43 if (m_payload != shared(Payload).init) { 44 scope(exit) { 45 m_payload = shared(Payload).init; 46 } 47 48 if (decRefCount() < 1 && m_payload.handle != null) { 49 return m_payload.release(m_payload.handle); 50 } 51 } 52 return false; 53 } 54 55 void forceRelease() @trusted 56 { 57 if (m_payload != shared(Payload).init) { 58 scope(exit) { 59 m_payload = shared(Payload).init; 60 } 61 62 decRefCount(); 63 if (m_payload.handle != null) { 64 scope(exit) m_payload.handle = null; 65 if (!m_payload.release(m_payload.handle)) { 66 throw ReleaseException; 67 } 68 } 69 } 70 } 71 72 inout(void)* handle() inout @trusted nothrow shared 73 { 74 mu.lock; 75 scope(exit) mu.unlock; 76 77 if (m_payload != shared(Payload).init) { 78 return cast(typeof(return)) m_payload.handle; 79 } else { 80 return null; 81 } 82 } 83 84 inout(void)* handle() inout @trusted nothrow 85 { 86 mu.lock; 87 scope(exit) mu.unlock; 88 89 if (m_payload != shared(Payload).init) { 90 return cast(typeof(return)) m_payload.handle; 91 } else { 92 return null; 93 } 94 } 95 96 private: 97 void incRefCount() @trusted nothrow 98 { 99 assert (m_payload != shared(Payload).init && m_payload.refCount > 0); 100 atomicOp!"+="(m_payload.refCount, 1); 101 } 102 103 int decRefCount() @trusted nothrow 104 { 105 assert (m_payload != shared(Payload).init && m_payload.refCount > 0); 106 return atomicOp!"-="(m_payload.refCount, 1); 107 } 108 109 struct Payload 110 { 111 int refCount; 112 void* handle; 113 Release release; 114 } 115 shared(Mutex) mu; 116 shared(Payload) m_payload; 117 118 invariant() 119 { 120 () @trusted { 121 assert (m_payload == shared(Payload).init || 122 (m_payload.refCount > 0 && m_payload.release !is null), "failed invariant"); 123 } (); 124 } 125 }