Creating and Submitting a Job

A job contains one or more tasks to run on nodes in the cluster. To create a job, call the IScheduler::CreateJob method.

The job template that you specify for the job defines the initial default values and constraints for many of the job properties. Any changes that you make to the properties must be made within the constraints of the template. After updating the properties and specifying the job resources, add one or more tasks to the job.

To create a task, call the ISchedulerJob::CreateTask method. At a minimum, the task must specify the command to run. If the task requires specific resources or is dependent on other tasks, set those properties, too. To add the task to the job, call the ISchedulerJob::AddTask method.

After you have defined the job (and added all the tasks to the job), call the IScheduler::SubmitJob method to add the job to the scheduling queue. The scheduler will then schedule your job to run based on the job's required resources and priority. If the job is not ready to run, you can call the IScheduler::AddJob method to save the job in the scheduler until it is ready to run.

The ISchedulerJob object provides the following two events to which you can subscribe:

The events provide notification when the state of the job or task has changed, respectively. There is no notification when the state of the job is JobState_Configuring (the first state of the job). Note that the callback does not block the job from continuing; by the time you handle the notification, the state of the job or task may have already transitioned to a new state.

The following C# example shows how to create a job using the default job template, add a task to the job, submit the job to the scheduling queue, and receive notification when the state of the job or tasks in the job change. For details on handling the events, see Implementing the Event Handlers for Job Events in C#.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Hpc.Scheduler;
using Microsoft.Hpc.Scheduler.Properties;

namespace Creating_and_Submitting_a_Job
{
    class Program
    {
        private static ManualResetEvent manualEvent = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            IScheduler scheduler = new Scheduler();
            ISchedulerJob job = null;
            ISchedulerTask task = null;

            try
            {
                scheduler.Connect("localhost");

                // Create a job and add a task to the job.
                job = scheduler.CreateJob();
                task = job.CreateTask();
                task.CommandLine = "<COMMANDGOESHERE>";
                job.AddTask(task);

                // Specify the events that you want to receive.
                job.OnJobState += JobStateCallback;
                job.OnTaskState += TaskStateCallback;

                // Start the job.
                scheduler.SubmitJob(job, @"<domain\username>", null);

                // Blocks so the events get delivered. One of your event
                // handlers need to set this event.
                manualEvent.WaitOne();

                Console.Write("\nPress Enter to quit");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        // Add the code from the Implementing the Event Handlers for Job Events in C# topic here.
    }
}

The following C++ example shows how to create a job using the default job template, add a task to the job, submit the job to the scheduling queue, and receive notification when the state of the job or tasks in the job change. For details on handling the events, see Implementing the Event Handlers for Job Events in C++. This example assumes that the pScheduler variable is a valid pointer to an IScheduler instance.

// Created in CreateJob and set in CJobEventHandler::OnJobState
// when the state of the job changes to Finished, Failed, or Canceled.
HANDLE g_JobDoneEvent = NULL;
void CreateJob(IScheduler* pScheduler)
{
    HRESULT hr = S_OK;
    ISchedulerJob* pJob = NULL;
    ISchedulerTask* pTask = NULL;
    IConnectionPointContainer* pCPContainer = NULL;
    static IConnectionPoint* pConnectionPoint = NULL;
    IStringCollection* pNodes = NULL;
    static DWORD cookie = 0;
    CJobEventHandler *pHandler = NULL;
    IUnknown* punk = NULL;

    hr = pScheduler->CreateJob(&pJob);
    if (FAILED(hr))
    {
        wprintf(L"pScheduler->CreateJob failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->CreateTask(&pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->CreateTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->put_CommandLine(_bstr_t(L"<COMMANDGOESHERE>"));
    if (FAILED(hr))
    {
        wprintf(L"pTask->put_CommandLine failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->AddTask(pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->AddTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    // To subscribe to events, you need to get the connection point container and then
    // find the connection point for the ISchedulerJobEvents interface.
    hr = pJob->QueryInterface(__uuidof(IConnectionPointContainer), reinterpret_cast<void **> (&pCPContainer));
    if (FAILED(hr))
    {
        wprintf(L"pCommand->QueryInterface(__uuidof(IConnectionPointContainer) failed with 0x%x\n", hr);
        goto cleanup;
    }

    hr = pCPContainer->FindConnectionPoint(__uuidof(ISchedulerJobEvents), &pConnectionPoint);
    if (FAILED(hr))
    {
        wprintf(L"pCPContainer->FindConnectionPoint failed with 0x%x\n", hr);
        goto cleanup;
    }

    // Create an instance of your event handler implementation.
    pHandler = new CJobEventHandler();

    if (pHandler)
    {
        // Get the IUnknown interface for your implementation and pass it to the Advise method.
        hr = pHandler->QueryInterface(__uuidof(IUnknown), reinterpret_cast<void **> (&punk));

        // Subscribe to the events that the SchedulerJob object fires.
        // Use the cookie when done to remove the subscription.
        hr = pConnectionPoint->Advise(punk, &cookie);
        if (FAILED(hr))
        {
            wprintf(L"pConnectionPoint->Advise failed with 0x%x\n", hr);
            goto cleanup;
        }
    }
    else
    {
        wprintf(L"Failed to instantiate CCommandEventHandler\n");
    }

    g_JobDoneEvent = CreateEvent (NULL, TRUE, FALSE, NULL);

    // Run the job
    hr = pScheduler->SubmitJob(pJob, _bstr_t(L"<domain\\username>"), NULL);
    if (FAILED(hr))
    {
        wprintf(L"pScheduler->SubmitJob failed with 0x%x.\n", hr);
        goto cleanup;
    }


    // Loop while the job is running. Stop when the job finishes, fails,
    // or is canceled. The wait object is set in the CJobEventHandler::OnJobState
    // callback.
    while (true)
    {
        DWORD dwWait = MsgWaitForMultipleObjects (
            1, &g_JobDoneEvent, FALSE, INFINITE, QS_ALLINPUT);

        if (dwWait == WAIT_OBJECT_0)
        {
            wprintf(L"\nJob finished\n");
            goto cleanup;
        }
        else if (dwWait == WAIT_OBJECT_0 + 1)
        {
            MSG msg;  
            while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
            { 
                DispatchMessage(&msg); 
            } 

        }
    }

cleanup:

    if (pJob)
        pJob->Release();

    if (pTask)
        pTask->Release();

    if (pHandler)
        pHandler->Release();

    if (punk)
        punk->Release();

    if (pCPContainer)
        pCPContainer->Release();

    if (pConnectionPoint)
    {
        if (cookie)
            hr = pConnectionPoint->Unadvise(cookie);

        pConnectionPoint->Release();
    }
}

To determine the status of the job, you should subscribe to receive event notification when the state of the job or the state of a task within the job changes.