1 module grpc.common.call;
2 import grpc.logger;
3 import interop.headers;
4 import grpc.common.cq;
5 import grpc.core.utils;
6 import grpc.core.resource;
7 import grpc.common.metadata;
8 import grpc.common.byte_buffer;
9 import grpc.core.sync.mutex;
10 import core.memory : GC;
11 import core.lifetime;
12 
13 struct CallContext {
14 @safe @nogc:
15     shared(Mutex) mutex;
16     grpc_call** call;
17     CallDetails details;
18     MetadataArray metadata;
19     ByteBuffer data;
20     MonoTime timestamp;
21 
22     static CallContext create() @trusted {
23         auto call = cast(grpc_call**)gpr_zalloc((grpc_call**).sizeof); 
24         return CallContext(cast(shared)Mutex.create(),
25                            call,
26                            CallDetails.create(),
27                            MetadataArray.create(),
28                            ByteBuffer.create());
29     }
30 
31     @disable this(this);
32 
33     ~this() @trusted {
34         destroy(data);
35         gpr_free(cast(void*)call);
36     }
37 }
38 
39 struct CallDetails {
40 @safe @nogc:
41     private {
42         shared(Mutex) mutex;
43         SharedResource _details;
44     }
45 
46     inout(grpc_call_details)* handle() inout @trusted nothrow {
47         return cast(typeof(return)) _details.handle;
48     }
49 
50     @property string method() @trusted {
51         mutex.lock;
52         scope(exit) mutex.unlock;
53         return slice_to_string(handle.method); 
54     }
55 
56     @property string host() @trusted {
57         mutex.lock;
58         scope(exit) mutex.unlock;
59         return slice_to_string(handle.host);
60     }
61 
62     @property uint flags() {
63         mutex.lock;
64         scope(exit) mutex.unlock;
65         uint flags = handle.flags;
66         return flags;
67     }
68 
69     @property Duration deadline() @trusted {
70         mutex.lock;
71         scope(exit) mutex.unlock;
72         Duration d = timespectodur(handle.deadline);
73         return d;
74     }
75 
76     static CallDetails create() @trusted {
77         grpc_call_details* details;
78 
79         if ((details = cast(grpc_call_details*)gpr_zalloc((grpc_call_details).sizeof)) != null) {
80             static bool release(shared(void)* ptr) @trusted nothrow {
81                 grpc_call_details_destroy(cast(grpc_call_details*)ptr);
82                 gpr_free(cast(void*)ptr);
83                 return true;
84             }
85             
86             grpc_call_details_init(details);
87             CallDetails obj = CallDetails(cast(shared)Mutex.create(), SharedResource(cast(shared)details, &release));
88             return obj;
89         } else {
90             assert(0, "memory allocation failed");
91         }
92 
93     }
94 
95     ~this() @trusted {
96         destroy(_details);
97     }
98 
99 
100     @disable this(this);
101 }
102 
103 // since we handle the memory for the CQ tags manually (and since there should only ever be very few tags)
104 // which actually exist, this should be never cleaned up by the garbage collector as it contains VERY important
105 // things
106 
107 struct Tag {
108 @safe @nogc:
109     void* method;
110     string methodName;
111     ubyte[16] metadata;
112     CallContext ctx;
113 
114     static Tag* opCall() @trusted {
115         Tag* obj = cast(Tag*)gpr_zalloc((Tag).sizeof);
116         DEBUG!"new tag created at: %x size: %d"(obj, (Tag).sizeof);
117         assert(obj != null, "memory allocation fail");
118         obj.ctx = CallContext.create();
119         return obj;
120     }
121     
122     static void free(Tag* tag) @trusted {
123         destroy(tag.ctx);
124         gpr_free(tag);
125     }
126 
127     ~this() {
128         assert(0); // the tag should NEVER have it's destructor called, you MUST call free on it
129     }
130 
131 
132     @disable this(this);
133 }