Operation Completion Samples

This section shows example code on how to use operation completion callback function in the HCS APIs.

The examples in this page use HcsCreateComputeSystem as the HCS function to track, and the following global definitions:

// JSON document that represents an "Empty" virtual machine.
// Essentially, just the bare minimum necessary to create a virtual machine
// that doesn't have any guest nor storage attached to it.
constexpr auto c_EmptyVM = LR"(
{
    "SchemaVersion": {
        "Major": 2,
        "Minor": 1
    },
    "Owner": "Sample",
    "VirtualMachine": {
        "Chipset": {
            "Uefi": {}
        },
        "ComputeTopology": {
            "Memory": {
                "Backing": "Physical",
                "SizeInMB": 1024
            },
            "Processor": {
                "Count": 2
            }
        }
    },
    "ShouldTerminateOnLastHandleClosed": true
})";

using unique_hcs_operation = wil::unique_any<HCS_OPERATION, decltype(&HcsCloseOperation), HcsCloseOperation>;
using unique_hcs_system = wil::unique_any<HCS_SYSTEM, decltype(&HcsCloseComputeSystem), HcsCloseComputeSystem>;

Wait for operation synchronously

The first, most common, pattern is to wait for an operation explicitly to achieve a synchronous behavior without complex custom waitable objects.

unique_hcs_operation operation{ HcsCreateOperation(nullptr, nullptr) };
unique_hcs_system system;
THROW_IF_FAILED(HcsCreateComputeSystem(L"Test", c_EmptyVM, operation.get(), nullptr, &system));
wil::unique_hlocal_string resultDoc;
THROW_IF_FAILED_MSG(HcsWaitForOperationResult(operation.get(), INFINITE, &resultDoc), "%ws", resultDoc.get());

Use an HCS operation callback

This pattern relies on client code creating an operation with a callback, and use that as the synchronization mechanism to know when the operation has completed. Note in the example code that we don't store anywhere the returned HCS_OPERATION handle when creating it with HcsCreateOperation since it can be closed from within the callback!

// Use a windows event object as the context so that we can wait on it
wil::unique_event callbackWait;
callbackWait.create();
unique_hcs_system system;
THROW_IF_FAILED(HcsCreateComputeSystem(
    L"Test",
    c_EmptyVM,
    HcsCreateOperation(
        callbackWait.get(),
        [](HCS_OPERATION operation, void* context)
        {
            WI_ASSERT(HcsGetOperationType(operation) == HcsOperationTypeCreate);
            wil::unique_hlocal_string resultDoc;
            THROW_IF_FAILED_MSG(HcsGetOperationResult(operation, &resultDoc), "%ws", resultDoc.get());
            HcsCloseOperation(operation);
            SetEvent(context);
        }),
    nullptr,
    &system));
callbackWait.wait(INFINITE);

Use event callback's operation completion

This pattern relies on the compute system or compute process event callback to get notified when the operation has been completed. This requires client code to use HcsEventOptionEnableOperationCallbacks as the event option when calling HcsSetComputeSystemCallback or HcsSetProcessCallback. The following example uses event callback for compute system.

When this pattern is used, it's not allowed to use operation callbacks. Any attempt to do that will fail the function with HCS_E_OPERATION_SYSTEM_CALLBACK_ALREADY_SET.

// Use a windows event object as the context so that we can wait on it
wil::unique_event callbackWait;
callbackWait.create();
unique_hcs_system system;
THROW_IF_FAILED(HcsCreateComputeSystem(L"Test", c_EmptyVM, HcsCreateOperation(nullptr, nullptr), nullptr, &system));
THROW_IF_FAILED(HcsSetComputeSystemCallback(
    system.get(),
    HcsEventOptionEnableOperationCallbacks,
    callbackWait.get(),
    [](HCS_EVENT* event, void* context)
    {
        WI_ASSERT(event->Type == HcsEventOperationCallback);
        wil::unique_hlocal_string resultDoc;
        THROW_IF_FAILED_MSG(HcsGetOperationResult(event->Operation, &resultDoc), "%ws", resultDoc.get());
        HcsCloseOperation(event->Operation);
        SetEvent(context);
    }));
callbackWait.wait(INFINITE);

// However, once the system callback has been set, using an operation with callback fails!
callbackWait.create();
HRESULT result = HcsStartComputeSystem(
    system.get(),
    HcsCreateOperation(nullptr, [](HCS_OPERATION operation, void* context){}),
    nullptr);
THROW_HR_IF(E_UNEXPECTED, result != HCS_E_OPERATION_SYSTEM_CALLBACK_ALREADY_SET);