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 }