Previous page

Next page

Locate page in Contents

Asynchronous Functions

An asynchronous operation is executed in its own thread. An asynchronous function that started the operation returns to the caller immediately without waiting for the operation to complete. The results of the operation can be verified later when needed. Asynchronous functions return PRL_HANDLE, which is a pointer to an integer and is a handle of type PHT_JOB. The handle is used as a reference to the asynchronous job executed in the background. The general procedure for calling an asynchronous function is as follows:

  1. Register an event handler (callback function).
  2. Call an asynchronous function.
  3. Analyze the results of events (jobs) within the callback function.
  4. Handle the appropriate event in the callback function.
  5. Un-register the event handler when it is no longer needed.

The Callback Function (Event Handler)

Asynchronous functions return data to the caller by means of an event handler (or callback function). The callback function could be called at any time, depending on how long the asynchronous function takes to complete. The callback function must have a specific signature. The prototype can be found in PrlApi.h and is as follows:

typedef PRL_METHOD_PTR(PRL_EVENT_HANDLER_PTR) (

            PRL_HANDLE hEvent,

            PRL_VOID_PTR data

            );

The following is an example of the callback function implementation:

static PRL_RESULT OurCallback(PRL_HANDLE handle, void *pData)

{

    // Event handler code...

  

    // You must always release the handle before exiting.

    PrlHandle_Free(handle);

}

A handle received by the callback function can be of type PHT_EVENT or PHT_JOB. The type can be determined using the PrlHandle_GetType function. The PHT_EVENT type indicates that the callback was called by a system event. If the type is PHT_JOB then the callback was called by an asynchronous job started by the program.

To handle system events within a callback function:

  1. Get the event type using PrlEvent_GetType.
  2. Examine the event type. If it is relevant, a handle of type PHT_EVENT_PARAMETER can be extracted using PrlEvent_GetParam.
  3. Convert the PHT_EVENT_PARAMETER handle to the appropriate handle type using PrlEvtPrm_ToHandle.

To handle jobs within a callback function:

  1. Get the job type using PrlJob_GetType. A job type can be used to identify the function that started the job and to determine the type of the result it contains. For example, a job of type PJOC_SRV_GET_VM_LIST is started by PrlSrv_GetVmList function call, which returns a list of virtual machines.
  2. Examine the job type. If it is relevant, proceed to the next step.
  3. Get the job return code using PrlJob_GetRetCode. If it doesn't contain an error, proceed to the next step.
  4. Get the result (a handle of type PHT_RESULT) from the job handle using PrlJob_GetResult.
  5. Get a handle to the result using PrlResult_GetParam. Note that some functions return a list (ie. there can be more than a single parameter in the result). For example, PrlSrv_GetVmList returns a list of available virtual machines. In such cases, use PrlResult_GetParamCount and PrlResult_GetParamByIndex.
  6. Implement code to use the handle obtained in step 5.

Note: You must always free the handle that was passed to the callback function before exiting, regardless of whether you actually used it or not. Failure to do so will result in a memory leak.

The following skeleton code demonstrates implementation of the above steps. In this example, the objective is to handle events of type PET_DSP_EVT_HOST_STATISTICS_UPDATED that are generated by a call to function PrlSrv_SubscribeToHostStatistics, and to obtain the result from a job of type PJOC_SRV_GET_VM_LIST.

static PRL_RESULT OurCallbackFunction(PRL_HANDLE hHandle, PRL_VOID_PTR pUserData)

{

    PRL_JOB_OPERATION_CODE nJobType = PJOC_UNKNOWN; // job type

    PRL_HANDLE_TYPE nHandleType = PHT_ERROR; // handle type

    PRL_HANDLE hVm = PRL_INVALID_HANDLE; // virtual machine handle

    PRL_HANDLE hParam = PRL_INVALID_HANDLE; // event parameter

    PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result

    PRL_UINT32 nParamsCount = -1; // parameter count

    PRL_UINT32 nParamIndex = -1; // parameter index

    PRL_RESULT err = PRL_ERR_UNINITIALIZED; // error

    

    // Check the type of the received handle.

    PrlHandle_GetType(hHandle, &nHandleType);

    

    if (nHandleType == PHT_EVENT) // Event handle

    {

        PRL_EVENT_TYPE EventType;

        PrlEvent_GetType(hHandle, &EventType);

    

        // Check if the event type is a statistics update.

        if (EventType == PET_DSP_EVT_HOST_STATISTICS_UPDATED)

        {

            // Get handle to PHT_EVENT_PARAMETER.

            PRL_HANDLE hEventParameters = PRL_INVALID_HANDLE;

            PrlEvent_GetParam(hHandle, 0, &hEventParameters);

    

            // Get handle to PHT_SYSTEM_STATISTICS.

            PRL_HANDLE hServerStatistics = PRL_INVALID_HANDLE;

            PrlEvtPrm_ToHandle(hEventParameters, &hServerStatistics);

            

            // Code goes here to extract the statistics data

            // using hServerStatistics.

            

            PrlHandle_Free(hServerStatistics);

            PrlHandle_Free(hEventParameters);

        }

    }

    else if (nHandleType == PHT_JOB) // Job handle

    {

        // Get the job type.

        PrlJob_GetOpCode(hHandle, &nJobType);

        

        // Check if the job type is PJOC_SRV_GET_VM_LIST.

        if (nJobType == PJOC_SRV_GET_VM_LIST)

        {

            // Check the job return code.

            PRL_RESULT nJobRetCode;

            PrlJob_GetRetCode(hHandle, &nJobRetCode);

            if (PRL_FAILED(nJobRetCode))

            {

                fprintf(stderr, "[B]%.8X: %s\n", nJobRetCode,

                prl_result_to_string(nJobRetCode));

                PrlHandle_Free(hHandle);

                return nJobRetCode;

            }          

            

            err = PrlJob_GetResult(hHandle, &hJobResult);

    

            // if (err != PRL_ERR_SUCCESS), process the error here.

    

            // Determine the number of parameters in the result.

            PrlResult_GetParamsCount(hJobResult, &nParamsCount);

            

            // Iterate through the parameter list.

            for(nParamIndex = 0; nParamIndex < nParamsCount ; nParamIndex++)

            {

                // Obtain a virtual machine handle (PHT_VIRTUAL_MACHINE).

                PrlResult_GetParamByIndex(hJobResult, nParamIndex, &hVm);

    

                // Code goes here to obtain virtual machine info from hVm.

    

                // Free the handle when done using it.

                PrlHandle_Free(hVm);

            }

            PrlHandle_Free(hJobResult);

        }

    }

    

    PrlHandle_Free(hHandle);

    return PRL_ERR_SUCCESS;

}

Registering / Unregistering an Event Handler

The PrlSrv_RegEventHandler function is used to register an event handler, PrlSrv_UnregEventHandler is used to unregister an event handler.

Note: When an event handler is registered, it will receive all of the events/jobs regardless of their origin. It is the responsibility of the program to identify the type of the event and to handle each one accordingly.

// Register an event handler.

ReturnDataClass rd; // some user-defined class.

PrlSrv_RegEventHandler(hServer, OurCallbackFunction, &rd);

    

// Make a call to an asynchronous function here.

// OurCallbackFunction will be called by the background thread

// as soon as the job is completed, and code within

// OurCallbackFunction can populate the ReturnDataClass instance.

// For example, we can make the following call here:

  

hJob = PrlSrv_GetVmList(hServer);

PrlHandle_Free(hJob);

  

// Please note that we still have to obtain the

// job object (hJob above) and free it; otherwise

// we will have memory leaks.

  

// Unregister the event handler when it is no longer needed.

PrlSrv_UnregEventHandler(hServer, OurCallbackFunction, &rd);

Calling Asynchronous Functions Synchronously

It is possible to call an asynchronous function synchronously by using the PrlJob_Wait function. The function takes two parameters: a PHT_JOB handle and a timeout value in milliseconds. Once you call the function, the main thread will be suspended and the function will wait for the asynchronous job to complete. The function will return when the job is completed or when timeout value is reached, whichever comes first. The following code snippet illustrates how to call an asynchronous function PrlServer_Login synchronously:

// Log in (PrlSrv_Login is asynchronous).

PRL_HANDLE hJob = PrlSrv_Login(

        hServer,

        szHostnameOrIpAddress,

        szUsername,

        szPassword,

        0,

        0,

        0,

        PSL_LOW_SECURITY);

  

// Wait for a maximum of 10 seconds for

// asynchronous function PrlSrv_Login to complete.

ret = PrlJob_Wait(hJob, 10000);

if (PRL_FAILED(ret))

{

    fprintf(stderr, "PrlJob_Wait for PrlSrv_Login returned with error: %s\n",

        prl_result_to_string(ret));

    PrlHandle_Free(hJob);

    PrlHandle_Free(hServer);

    return -1;

}

    

// Analyse the result of the PrlServer_Login call.

PRL_RESULT nJobResult;

ret = PrlJob_GetRetCode(hJob, &nJobResult);

if (PRL_FAILED(nJobResult))

{

    PrlHandle_Free(hJob);

    PrlHandle_Free(hServer);

    printf("Login job returned with error: %s\n",

        prl_result_to_string(nJobResult));

    return -1;

}

else

{

    printf("login successfully performed\n");

}