Skip to content

Edit Stream Cancellation Message#488

Merged
Faraclas merged 3 commits intoni:masterfrom
Faraclas:grpc/cancel-with-method
Jan 7, 2026
Merged

Edit Stream Cancellation Message#488
Faraclas merged 3 commits intoni:masterfrom
Faraclas:grpc/cancel-with-method

Conversation

@Faraclas
Copy link
Copy Markdown
Collaborator

@Faraclas Faraclas commented Jan 6, 2026

Server-Side Cancellation Error Code Propagation

Overview

This document describes the changes made to the gRPC-LabVIEW DLL to properly propagate custom error codes from SetCallStatus through to the GetRequestData function, enabling LabVIEW applications to distinguish between normal call completion and server-initiated cancellation.

Problem Statement

Previously, when GetRequestData exited from a blocking call, it would always return -2 regardless of whether the call completed normally or was forcefully cancelled via SetCallStatus. This made it impossible for LabVIEW code to differentiate between:

  • Normal call completion (client finished sending data)
  • Server-side cancellation (via SetCallStatus)
  • Timeout or other server-initiated termination

While SetCallStatus could force the DLL to exit its blocking state, the calling code in LabVIEW had no way to know why the call ended.

Solution

The solution adds error code propagation from SetCallStatus to GetRequestData by:

  1. Adding a getter method to retrieve the call's status code
  2. Checking this status code in GetRequestData before returning the generic -2 error
  3. Returning the custom error code (in the format -(1000 + statusCode)) when a non-OK status has been set

Files Modified

1. src/grpc_server.h

Change: Added public method declaration to CallData class

grpc::StatusCode GetCallStatusCode();

Location: Line 161, in the public section of the CallData class declaration

Purpose: Provides public access to the internal _callStatus error code

2. src/event_data.cc

Change: Implemented the GetCallStatusCode() method

grpc::StatusCode CallData::GetCallStatusCode()
{
    return _callStatus.error_code();
}

Location: After the SetCallStatusError methods (around line 81-86)

Purpose: Returns the status code from the internal _callStatus member variable

3. src/grpc_interop.cc

Change: Modified GetRequestData to check for custom error status before returning -2

Original code:

if (data->_call->IsActive() && data->_call->ReadNext())
{
    // ... data copying logic ...
    return 0;
}
return -2;

New code:

if (data->_call->IsActive() && data->_call->ReadNext())
{
    // ... data copying logic ...
    return 0;
}
// Check if a custom error status was set via SetCallStatus
auto statusCode = data->_call->GetCallStatusCode();
if (statusCode != grpc::StatusCode::OK)
{
    return -(1000 + statusCode);
}
return -2;

Location: In the GetRequestData function, lines 390-396

Purpose: Before returning the generic -2 error, checks if a custom status was set via SetCallStatus and returns that error code instead

How It Works

Normal Flow (No Custom Error)

  1. GetRequestData is called and blocks waiting for data
  2. Call completes normally (client finishes, no errors)
  3. IsActive() returns false and ReadNext() returns false
  4. GetCallStatusCode() returns grpc::StatusCode::OK (0)
  5. Function returns -2 (normal completion)

Custom Error Flow (Server-Side Cancellation)

  1. GetRequestData is called and blocks waiting for data
  2. LabVIEW calls SetCallStatus(id, errorCode, "message") from another thread
  3. This sets _callStatus to the specified error code and breaks the blocking call
  4. IsActive() returns false and ReadNext() returns false
  5. GetCallStatusCode() returns the custom error code (e.g., grpc::StatusCode::CANCELLED = 1)
  6. Function returns -(1000 + errorCode) (e.g., -1001 for CANCELLED)

Usage in LabVIEW

Example: Detecting Server-Side Cancellation

// In one thread: Wait for data
errorCode = GetRequestData(callID, requestDataCluster)

if errorCode = 0 then
    // Success: New data received
    ProcessData(requestDataCluster)
else if errorCode = -2 then
    // Normal completion: Call ended naturally
    HandleCallComplete()
else if errorCode = -1001 then  // -(1000 + CANCELLED)
    // Server-side cancellation: SetCallStatus was called
    HandleCancellation()
else
    // Other error
    HandleError(errorCode)
end if

Example: Cancelling from Another Thread

// In timeout or user cancellation thread:
SetCallStatus(callID, CANCELLED, "User requested cancellation")

// This will cause GetRequestData (in another thread) to exit with -1001

Error Code Reference

The error codes follow the pattern: -(1000 + grpc::StatusCode)

Common error codes:

  • 0: Success (data received)
  • -1: Invalid call ID
  • -2: Normal completion (no custom error)
  • -1001: -(1000 + CANCELLED) - Server-side cancellation
  • -1002: -(1000 + UNKNOWN) - Unknown error
  • -1004: -(1000 + DEADLINE_EXCEEDED) - Timeout
  • -1013: -(1000 + INTERNAL) - Internal error

See gRPC Status Codes for complete list.

Benefits

  1. Distinguish cancellation types: LabVIEW can now differentiate between client-initiated and server-initiated cancellation
  2. Better error handling: Custom error codes can provide specific context about why a call ended
  3. Timeout support: Enables proper timeout handling by setting DEADLINE_EXCEEDED status
  4. Backward compatible: Existing code that doesn't use SetCallStatus will still receive -2 as before

Testing

The .DLL was built and tested on an existing grpc-labview project. Previously the stream would exit with -2 on the server regardless of the reason. Now it exits with the reason you give it.

@Faraclas Faraclas merged commit 332c486 into ni:master Jan 7, 2026
5 checks passed
@Faraclas Faraclas deleted the grpc/cancel-with-method branch January 9, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants