Skip to content

Capture upload to blob result and bubble up after completion in core #2615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 38 additions & 29 deletions iothub_client/src/iothub_client_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ typedef struct UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA_TAG
{
IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback;
IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx;
bool isCallbackInvokedWithError;
IOTHUB_CLIENT_FILE_UPLOAD_RESULT blobStorageUploadResult;
}UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA;

typedef struct INVOKE_METHOD_SAVED_DATA_TAG
Expand Down Expand Up @@ -2299,6 +2299,7 @@ static HTTPWORKER_THREAD_INFO* allocateUploadToBlob(const char* destinationFileN
threadInfo->workerThreadType = HTTPWORKER_THREAD_UPLOAD_TO_BLOB;
threadInfo->iotHubClientHandle = iotHubClientHandle;
threadInfo->context = context;
threadInfo->uploadBlobMultiblockSavedData.blobStorageUploadResult = FILE_UPLOAD_ERROR;

if (mallocAndStrcpy_s(&threadInfo->destinationFileName, destinationFileName) != 0)
{
Expand Down Expand Up @@ -2420,42 +2421,49 @@ IOTHUB_CLIENT_RESULT IoTHubClientCore_UploadToBlobAsync(IOTHUB_CLIENT_CORE_HANDL
return result;
}

// This callback wrapper allows iothub_client_core to track if the upload to blob callback has
// been fired with a FILE_UPLOAD_ERROR already. That is because IoTHubClientCore_LL_UploadMultipleBlocksToBlob
// will fire the user callback with error only in specific situations (i.e., uploading to Azure Storage).
// All other errors within IoTHubClientCore_LL_UploadMultipleBlocksToBlob do not cause the callback to be invoked,
// only signaling the upload error through the return code. However, the return code is not exposed to the user application
// This callback wrapper allows iothub_client_core to trap the final result from the upload to blob callback.
// That is because IoTHubClientCore_LL_UploadMultipleBlocksToBlob
// will fire the user callback with succes or error only in specific situations (i.e., uploading to Azure Storage).
// All other stages within IoTHubClientCore_LL_UploadMultipleBlocksToBlob do not cause the callback to be invoked,
// only signaling the final upload result through the return code. However, the return code is not exposed to the user application
// by iothub_client_core. So iothub_client_core must invoke the user callback so the user can get a feedback that
// an upload has failed if IoTHubClientCore_LL_UploadMultipleBlocksToBlob has not done so.
// an upload has succeeded or failed.
static void uploadToBlobMultiblockCallbackWrapper(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context)
{
HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)context;

if (result == FILE_UPLOAD_ERROR)
if (data == NULL || size == NULL)
{
threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError = true;
// Capture the last callback invocation and save result.
threadInfo->uploadBlobMultiblockSavedData.blobStorageUploadResult = result;
}
else
{
threadInfo->uploadBlobMultiblockSavedData.getDataCallback(result, data, size, threadInfo->context);
}

threadInfo->uploadBlobMultiblockSavedData.getDataCallback(result, data, size, threadInfo->context);
}

// This callback wrapper allows iothub_client_core to track if the upload to blob callback has
// been fired with a FILE_UPLOAD_ERROR already. That is because IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx
// will fire the user callback with error only in specific situations (i.e., uploading to Azure Storage).
// All other errors within IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx do not cause the callback to be invoked,
// only signaling the upload error through the return code. However, the return code is not exposed to the user application
// This callback wrapper allows iothub_client_core to trap the final result from the upload to blob callback.
// That is because IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx
// will fire the user callback with succes or error only in specific situations (i.e., uploading to Azure Storage).
// All other stages within IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx do not cause the callback to be invoked,
// only signaling the final upload result through the return code. However, the return code is not exposed to the user application
// by iothub_client_core. So iothub_client_core must invoke the user callback so the user can get a feedback that
// an upload has failed if IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx has not done so.
// an upload has succeeded or failed.
static IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT uploadToBlobMultiblockCallbackWrapperEx(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context)
{
HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)context;

if (result == FILE_UPLOAD_ERROR)
if (data == NULL || size == NULL)
{
threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError = true;
// Capture the last callback invocation and save result.
threadInfo->uploadBlobMultiblockSavedData.blobStorageUploadResult = result;
return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK;
}
else
{
return threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx(result, data, size, threadInfo->context);
}

return threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx(result, data, size, threadInfo->context);
}

static int uploadMultipleBlock_thread(void* data)
Expand All @@ -2464,26 +2472,27 @@ static int uploadMultipleBlock_thread(void* data)
IOTHUB_CLIENT_CORE_LL_HANDLE llHandle = threadInfo->iotHubClientHandle->IoTHubClientLLHandle;

IOTHUB_CLIENT_RESULT result;
IOTHUB_CLIENT_FILE_UPLOAD_RESULT finalUploadToBlobResult;

srand((unsigned int)get_time(NULL));

if (threadInfo->uploadBlobMultiblockSavedData.getDataCallback != NULL)
{
result = IoTHubClientCore_LL_UploadMultipleBlocksToBlob(llHandle, threadInfo->destinationFileName, uploadToBlobMultiblockCallbackWrapper, threadInfo);

if (result != IOTHUB_CLIENT_OK && !threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError)
{
threadInfo->uploadBlobMultiblockSavedData.getDataCallback(FILE_UPLOAD_ERROR, NULL, NULL, threadInfo->context);
}
// See comments in uploadToBlobMultiblockCallbackWrapper for further details.
finalUploadToBlobResult = (result != IOTHUB_CLIENT_OK ? FILE_UPLOAD_ERROR : threadInfo->uploadBlobMultiblockSavedData.blobStorageUploadResult);

threadInfo->uploadBlobMultiblockSavedData.getDataCallback(finalUploadToBlobResult, NULL, NULL, threadInfo->context);
}
else
{
result = IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx(llHandle, threadInfo->destinationFileName, uploadToBlobMultiblockCallbackWrapperEx, threadInfo);

if (result != IOTHUB_CLIENT_OK && !threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError)
{
(void)threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx(FILE_UPLOAD_ERROR, NULL, NULL, threadInfo->context);
}
// See comments in uploadToBlobMultiblockCallbackWrapperEx for further details.
finalUploadToBlobResult = (result != IOTHUB_CLIENT_OK ? FILE_UPLOAD_ERROR : threadInfo->uploadBlobMultiblockSavedData.blobStorageUploadResult);

(void)threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx(finalUploadToBlobResult, NULL, NULL, threadInfo->context);
}
(void)markThreadReadyToBeGarbageCollected(threadInfo);

Expand Down
Loading