From 3e7132b996dc0ba5be47dd44fbf81a638b8b2d31 Mon Sep 17 00:00:00 2001
From: Michael Delago <michaeldelago98@gmail.com>
Date: Fri, 21 Jun 2024 19:48:37 -0400
Subject: [PATCH 1/3] attempt initial impl

---
 client.cc                   | 23 +++++++++++++++++++++--
 client.lisp                 | 17 ++++++++++++-----
 shared.lisp                 |  3 +++
 tests/integration-test.lisp | 26 ++++++++++++++++++++++++++
 4 files changed, 62 insertions(+), 7 deletions(-)

diff --git a/client.cc b/client.cc
index 6a933c8..470ab64 100644
--- a/client.cc
+++ b/client.cc
@@ -21,6 +21,17 @@
 namespace lisp {
 namespace lisp_grpc {
 
+// Creates a gpr_timespec equal to the 'time_s' in seconds.
+// This gpr_timespec can then be used as a deadline for a grpc call.
+//
+// Initial implmentation pulled from grpc tests:
+// https://github.com/grpc/grpc/blob/9a606ec6180812fbb2582646b1aeab1dfeb13475/test/core/test_util/test_config.cc#L78
+gpr_timespec grpc_timeout_seconds_to_deadline(size_t time_s) {
+  return gpr_time_add(
+      gpr_now(GPR_CLOCK_MONOTONIC),
+      gpr_time_from_millis(static_cast<size_t>(1e3) * time_s, GPR_TIMESPAN));
+}
+
 extern "C" {
 
 // Creates a grpc_call* given a 'channel', which manages the
@@ -30,11 +41,19 @@ extern "C" {
 // grpc_completion_queue_pluck or grpc_completion_queue_next is called.
 grpc_call* lisp_grpc_channel_create_call(grpc_channel* channel,
                                          const char* call_name,
-                                         grpc_completion_queue* cq) {
+                                         grpc_completion_queue* cq,
+                                         size_t* deadline_seconds) {
+  gpr_timespec send_deadline;
+  if (deadline_seconds != nullptr) {
+    send_deadline = grpc_timeout_seconds_to_deadline(*deadline_seconds);
+  } else {
+    deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  }
+
   return grpc_channel_create_call(
       channel, nullptr, GRPC_PROPAGATE_DEFAULTS, cq,
       grpc_slice_from_copied_string(call_name), nullptr,
-      gpr_inf_future(GPR_CLOCK_MONOTONIC), nullptr);
+      send_deadline, nullptr);
 }
 
 // Prepares ops for completion queue pluck/next
diff --git a/client.lisp b/client.lisp
index d81431e..0111574 100644
--- a/client.lisp
+++ b/client.lisp
@@ -45,12 +45,19 @@ additional args ARGS for client information. If CREDS is passed then a secure
 channel will be created using CREDS else an insecure channel will be used."
   (c-grpc-client-new-channel creds target args))
 
-(defun service-method-call (channel call-name cq)
+(defun service-method-call (channel call-name cq &optional (deadline-seconds *call-deadline*))
   "A wrapper to create a grpc_call pointer that will be used to call CALL-NAME
-on the CHANNEL provided and store the result in the completion queue CQ."
-  (cffi:foreign-funcall "lisp_grpc_channel_create_call"
-                        :pointer channel :string call-name :pointer cq
-                        :pointer))
+on the CHANNEL provided and store the result in the completion queue CQ. A
+deadline can be set with DEADLINE-SECONDS."
+  (let ((deadline-pointer (if deadline-seconds
+                              (cffi:foreign-alloc :size :initial-element deadline-seconds)
+                              (cffi:null-pointer))))
+    (prog1
+        (cffi:foreign-funcall "lisp_grpc_channel_create_call"
+                              :pointer channel :string call-name :pointer cq :pointer deadline-pointer
+                              :pointer)
+      (unless (cffi:null-pointer-p deadline-pointer)
+        (cffi:foreign-free deadline-pointer)))))
 
 
 ;; Auxiliary Functions
diff --git a/shared.lisp b/shared.lisp
index 5cca9cd..9bd60c1 100644
--- a/shared.lisp
+++ b/shared.lisp
@@ -21,6 +21,9 @@
 (defvar *completion-queue* nil "The global completion queue used to
 manage grpc calls.")
 
+(defvar *call-deadline* nil
+  "The deadline for a grpc call. The default, nil, specifies an infinite deadline")
+
 ;; gRPC Enums
 (cffi:defcenum grpc-security-level
   "Security levels of grpc transport security. It represents an inherent
diff --git a/tests/integration-test.lisp b/tests/integration-test.lisp
index 445931c..866fa4d 100644
--- a/tests/integration-test.lisp
+++ b/tests/integration-test.lisp
@@ -75,3 +75,29 @@ Parameters
              (assert-true (string=  actual-client-response expected-client-response))
              (bordeaux-threads:join-thread thread)))))
   (grpc:shutdown-grpc))
+
+(deftest test-client-server-deadline (server-suite)
+  (grpc:init-grpc)
+  (unwind-protect
+       (let* ((expected-client-response "Hello World Back")
+              (hostname "localhost")
+              (method-name "xyz")
+              (port-number 8000)
+              (sem (bordeaux-threads:make-semaphore))
+              (thread (bordeaux-threads:make-thread
+                       (lambda () (run-server sem hostname method-name
+                                              port-number)))))
+         (bordeaux-threads:wait-on-semaphore sem)
+         (grpc:with-insecure-channel
+             (channel
+              (concatenate 'string hostname ":" (write-to-string port-number)))
+           (let* ((grpc::*call-deadline* 3)
+                  (message "Hello World")
+                  (response (grpc:grpc-call channel method-name
+                                            (flexi-streams:string-to-octets message)
+                                            nil nil))
+                  (actual-client-response (flexi-streams:octets-to-string
+                                           (car response))))
+             (assert-true (string=  actual-client-response expected-client-response))
+             (bordeaux-threads:join-thread thread)))))
+  (grpc:shutdown-grpc))

From 40561108797b83c11c6c5c2064d873b2410a839e Mon Sep 17 00:00:00 2001
From: Michael Delago <michaeldelago98@gmail.com>
Date: Fri, 21 Jun 2024 21:59:48 -0400
Subject: [PATCH 2/3] its definitely doing it

---
 client.cc                   |  2 +-
 grpc.lisp                   |  3 ++-
 tests/integration-test.lisp | 33 ++++++++++++++++++++++++++++++---
 3 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/client.cc b/client.cc
index 470ab64..f0018a9 100644
--- a/client.cc
+++ b/client.cc
@@ -47,7 +47,7 @@ grpc_call* lisp_grpc_channel_create_call(grpc_channel* channel,
   if (deadline_seconds != nullptr) {
     send_deadline = grpc_timeout_seconds_to_deadline(*deadline_seconds);
   } else {
-    deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+    send_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
   }
 
   return grpc_channel_create_call(
diff --git a/grpc.lisp b/grpc.lisp
index 80b19b4..dfa7a43 100644
--- a/grpc.lisp
+++ b/grpc.lisp
@@ -18,4 +18,5 @@
    #:with-insecure-channel
    #:with-ssl-channel
    #:grpc-call
-   #:check-server-status))
+   #:check-server-status
+   #:*call-deadline*))
diff --git a/tests/integration-test.lisp b/tests/integration-test.lisp
index 866fa4d..30ce587 100644
--- a/tests/integration-test.lisp
+++ b/tests/integration-test.lisp
@@ -40,8 +40,8 @@ Parameters
         :utf-8))
      :action
      (lambda (message call)
-       (declare (ignore call))
-       (format t "~% response: ~A ~%" message)
+       (format t "~% call: ~A ~%" call)
+       (format t " response: ~A ~%" message)
        (concatenate 'string
                     message
                     " Back"))))
@@ -76,7 +76,34 @@ Parameters
              (bordeaux-threads:join-thread thread)))))
   (grpc:shutdown-grpc))
 
-(deftest test-client-server-deadline (server-suite)
+(deftest test-client-server-deadline-can-set (server-suite)
+  (grpc:init-grpc)
+  (unwind-protect
+       (let* ((expected-client-response "Hello World Back")
+              (hostname "localhost")
+              (method-name "xyz")
+              (port-number 8000)
+              (sem (bordeaux-threads:make-semaphore))
+              (thread (bordeaux-threads:make-thread
+                       (lambda () (run-server sem hostname method-name
+                                              port-number)))))
+         (bordeaux-threads:wait-on-semaphore sem)
+         (grpc:with-insecure-channel
+             (channel
+              (concatenate 'string hostname ":" (write-to-string port-number)))
+           (let* ((grpc::*call-deadline* 3)
+                  (message "Hello World")
+                  (response (grpc:grpc-call channel method-name
+                                            (flexi-streams:string-to-octets message)
+                                            nil nil))
+                  (actual-client-response (flexi-streams:octets-to-string
+                                           (car response))))
+             (assert-true (string=  actual-client-response expected-client-response))
+             (bordeaux-threads:join-thread thread)))))
+  (grpc:shutdown-grpc))
+
+;; TODO(michaeldelago): make it fail if the deadline gets exceeded
+(deftest test-client-server-deadline-can-fail (server-suite)
   (grpc:init-grpc)
   (unwind-protect
        (let* ((expected-client-response "Hello World Back")

From 8b142ed20ec838c0e8480ceb71671d0852c500e2 Mon Sep 17 00:00:00 2001
From: Michael Delago <michaeldelago98@gmail.com>
Date: Sun, 23 Jun 2024 15:16:54 -0400
Subject: [PATCH 3/3] add TODO's to tests

---
 tests/integration-test.lisp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/integration-test.lisp b/tests/integration-test.lisp
index 30ce587..b15657f 100644
--- a/tests/integration-test.lisp
+++ b/tests/integration-test.lisp
@@ -77,6 +77,7 @@ Parameters
   (grpc:shutdown-grpc))
 
 (deftest test-client-server-deadline-can-set (server-suite)
+  "TODO(michaeldelago): The server should receive the deadline and be able to confirm that it's set"
   (grpc:init-grpc)
   (unwind-protect
        (let* ((expected-client-response "Hello World Back")
@@ -102,8 +103,8 @@ Parameters
              (bordeaux-threads:join-thread thread)))))
   (grpc:shutdown-grpc))
 
-;; TODO(michaeldelago): make it fail if the deadline gets exceeded
 (deftest test-client-server-deadline-can-fail (server-suite)
+  "TODO(michaeldelago): The server should return an error if the deadline is too high"
   (grpc:init-grpc)
   (unwind-protect
        (let* ((expected-client-response "Hello World Back")