using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Google.GRPC.Core;
namespace Google.GRPC.Core.Internal
{
// TODO: we need to make sure that the delegates are not collected before invoked.
internal delegate void EventCallbackDelegate(IntPtr eventPtr);
///
/// grpc_call from
///
internal class CallSafeHandle : SafeHandleZeroIsInvalid
{
const UInt32 GRPC_WRITE_BUFFER_HINT = 1;
[DllImport("libgrpc.so")]
static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_invoke_old")]
static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq,
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback,
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback,
UInt32 flags);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")]
static extern GRPCCallError grpc_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_cancel(CallSafeHandle call);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_start_write_status_old")]
static extern GRPCCallError grpc_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")]
static extern GRPCCallError grpc_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")]
static extern GRPCCallError grpc_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc_csharp_ext.so")]
static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call,
byte[] buffer, UIntPtr length,
IntPtr tag, UInt32 flags);
[DllImport("libgrpc_csharp_ext.so", EntryPoint = "grpc_call_start_write_from_copied_buffer")]
static extern void grpc_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call,
byte[] buffer, UIntPtr length,
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback,
UInt32 flags);
[DllImport("libgrpc.so")]
static extern void grpc_call_destroy(IntPtr call);
private CallSafeHandle()
{
}
///
/// Creates a client call.
///
public static CallSafeHandle Create(ChannelSafeHandle channel, string method, string host, Timespec deadline)
{
return grpc_channel_create_call_old(channel, method, host, deadline);
}
public void Invoke(CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, bool buffered)
{
AssertCallOk(grpc_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered)));
}
public void Invoke(CompletionQueueSafeHandle cq, bool buffered, EventCallbackDelegate metadataReadCallback, EventCallbackDelegate finishedCallback)
{
AssertCallOk(grpc_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered)));
}
public void ServerAccept(CompletionQueueSafeHandle cq, IntPtr finishedTag)
{
AssertCallOk(grpc_call_server_accept_old(this, cq, finishedTag));
}
public void ServerAccept(CompletionQueueSafeHandle cq, EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_server_accept_old_CALLBACK(this, cq, callback));
}
public void ServerEndInitialMetadata(UInt32 flags)
{
AssertCallOk(grpc_call_server_end_initial_metadata_old(this, flags));
}
public void StartWrite(byte[] payload, IntPtr tag, bool buffered)
{
grpc_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered));
}
public void StartWrite(byte[] payload, bool buffered, EventCallbackDelegate callback)
{
grpc_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered));
}
public void StartWriteStatus(Status status, IntPtr tag)
{
AssertCallOk(grpc_call_start_write_status_old(this, status.StatusCode, status.Detail, tag));
}
public void StartWriteStatus(Status status, EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback));
}
public void WritesDone(IntPtr tag)
{
AssertCallOk(grpc_call_writes_done_old(this, tag));
}
public void WritesDone(EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_writes_done_old_CALLBACK(this, callback));
}
public void StartRead(IntPtr tag)
{
AssertCallOk(grpc_call_start_read_old(this, tag));
}
public void StartRead(EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_start_read_old_CALLBACK(this, callback));
}
public void Cancel()
{
AssertCallOk(grpc_call_cancel(this));
}
public void CancelWithStatus(Status status)
{
AssertCallOk(grpc_call_cancel_with_status(this, status.StatusCode, status.Detail));
}
protected override bool ReleaseHandle()
{
grpc_call_destroy(handle);
return true;
}
private static void AssertCallOk(GRPCCallError callError)
{
Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
}
private static UInt32 GetFlags(bool buffered) {
return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;
}
}
}