Job Objects
A job object allows groups of processes to be managed as a unit. Job objects are namable, securable, sharable objects that control attributes of the processes associated with them. Operations performed on the job object affect all processes associated with the job object. To create a job object, use the CreateJobObject() function. When the job is created, there are no associated processes. To associate a process with a job, use the AssignProcessToJobObject() function. A process can be associated only with a single job. After you associate a process with a job, the association cannot be broken. By default, processes created using CreateProcess() by a process associated with a job are associated with the job; however, processes created using Win32_Process.Create() are not associated with the job. If a job has the extended limit JOB_OBJECT_LIMIT_BREAKAWAY_OK and a process associated with the job was created with the CREATE_BREAKAWAY_FROM_JOB flag, its child processes are not associated with the job. If the job has the extended limit JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK, no child processes are associated with the job.
To determine if a process is running in a job, use the IsProcessInJob() function.
A job can enforce limits on each associated process, such as the working set size, process priority, end-of-job time limit, and so on. To set limits for a job object, use the SetInformationJobObject() function. If a process associated with a job attempts to increase its working set size or process priority, the function calls are silently ignored. The job object also records basic accounting information for all its associated processes, including those that have terminated. To retrieve this accounting information, use the QueryInformationJobObject() function. To terminate all processes currently associated with a job object, use the TerminateJobObject() function. To obtain a handle for an existing job object, use the OpenJobObject() function and specify the name given to the object when it was created. Only named job objects can be opened. To close a job object handle, use the CloseHandle() function. The job is destroyed when its last handle has been closed and all associated processes have been terminated. However, if the job has the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag specified, closing the last job object handle terminates all associated processes and then destroys the job object itself. If a tool is to manage a process tree that uses job objects, both the tool and the members of the process tree must cooperate. Use one of the following options:
User-Mode Scheduling
User-mode scheduling (UMS) is a light-weight mechanism that applications can use to schedule their own threads. An application can switch between UMS threads in user mode without involving the system scheduler and regain control of the processor if a UMS thread blocks in the kernel. UMS threads differ from fibers in that each UMS thread has its own thread context instead of sharing the thread context of a single thread. The ability to switch between threads in user mode makes UMS more efficient than thread pools for managing large numbers of short-duration work items that require few system calls.
UMS is recommended for applications with high performance requirements that need to efficiently run many threads concurrently on multiprocessor or multicore systems. To take advantage of UMS, an application must implement a scheduler component that manages the application's UMS threads and determines when they should run. Developers should consider whether their application performance requirements justify the work involved in developing such a component. Applications with moderate performance requirements might be better served by allowing the system scheduler to schedule their threads. UMS is available starting with 64-bit versions of Windows 7 and Windows Server 2008 R2. This feature is not available on 32-bit versions of Windows.
UMS Scheduler
An application's UMS scheduler is responsible for creating, managing, and deleting UMS threads and determining which UMS thread to run. An application's scheduler performs the following tasks:
UMS Scheduler Thread
A UMS scheduler thread is an ordinary thread that has converted itself to UMS by calling the EnterUmsSchedulingMode() function. The system scheduler determines when the UMS scheduler thread runs based on its priority relative to other ready threads. The processor on which the scheduler thread runs is influenced by the thread's affinity, same as for non-UMS threads. The caller of EnterUmsSchedulingMode() specifies a completion list and a UmsSchedulerProc() entry point function to associate with the UMS scheduler thread. The system calls the specified entry point function when it is finished converting the calling thread to UMS. The scheduler entry point function is responsible for determining the appropriate next action for the specified thread. An application might create one UMS scheduler thread for each processor that will be used to run UMS threads. The application might also set the affinity of each UMS scheduler thread for a specific logical processor, which tends to exclude unrelated threads from running on that processor, effectively reserving it for that scheduler thread. Be aware that setting thread affinity in this way can affect overall system performance by starving other processes that may be running on the system.
UMS Worker Threads, Thread Contexts, and Completion Lists
A UMS worker thread is created by calling CreateRemoteThreadEx() with the PROC_THREAD_ATTRIBUTE_UMS_THREAD attribute and specifying a UMS thread context and a completion list. A UMS thread context represents the UMS thread state of a worker thread and is used to identify the worker thread in UMS function calls. It is created by calling CreateUmsThreadContext(). A completion list is created by calling the CreateUmsCompletionList() function. A completion list receives UMS worker threads that have completed execution in the kernel and are ready to run in user mode. Only the system can queue worker threads to a completion list. New UMS worker threads are automatically queued to the completion list specified when the threads were created. Previously blocked worker threads are also queued to the completion list when they are no longer blocked. Each UMS scheduler thread is associated with a single completion list. However, the same completion list can be associated with any number of UMS scheduler threads, and a scheduler thread can retrieve UMS contexts from any completion list for which it has a pointer. Each completion list has an associated event that is signaled by the system when it queues one or more worker threads to an empty list. The GetUmsCompletionListEvent() function retrieves a handle to the event for a specified completion list. An application can wait on more than one completion list event along with other events that make sense for the application.
UMS Scheduler Entry Point Function
An application's scheduler entry point function is implemented as a UmsSchedulerProc() function. The system calls the application's scheduler entry point function at the following times:
The Reason parameter of the UmsSchedulerProc() function specifies the reason that the entry point function was called. If the entry point function was called because a new UMS scheduler thread was created, the SchedulerParam parameter contains data specified by the caller of EnterUmsSchedulingMode(). If the entry point function was called because a UMS worker thread yielded, the SchedulerParam parameter contains data specified by the caller of UmsThreadYield(). If the entry point function was called because a UMS worker thread blocked in the kernel, the SchedulerParam parameter is NULL. The scheduler entry point function is responsible for determining the appropriate next action for the specified thread. For example, if a worker thread is blocked, the scheduler entry point function might run the next available ready UMS worker thread.
When the scheduler entry point function is called, the application's scheduler should attempt to retrieve all of the items in its associated completion list by calling the DequeueUmsCompletionListItems() function. This function retrieves a list of UMS thread contexts that have finished processing in the kernel and are ready to run in user mode. The application's scheduler should not run UMS threads directly from this list because this can cause unpredictable behavior in the application. Instead, the scheduler should retrieve all UMS thread contexts by calling the GetNextUmsListItem() function once for each context, insert the UMS thread contexts in the scheduler’s ready thread queue, and only then run UMS threads from the ready thread queue. If the scheduler does not need to wait on multiple events, it should call DequeueUmsCompletionListItems() with a non-zero timeout parameter so the function waits on the completion list event before returning. If the scheduler does need to wait on multiple completion list events, it should call DequeueUmsCompletionListItems() with a timeout parameter of zero so the function returns immediately, even if the completion list is empty. In this case, the scheduler can wait explicitly on completion list events, for example, by using WaitForMultipleObjects().
UMS Thread Execution
A newly created UMS worker thread is queued to the specified completion list and does not begin running until the application's UMS scheduler selects it to run. This differs from non-UMS threads, which the system scheduler automatically schedules to run unless the caller explicitly creates the thread suspended. The scheduler runs a worker thread by calling ExecuteUmsThread() with the worker thread's UMS context. A UMS worker thread runs until it yields by calling the UmsThreadYield() function, blocks, or terminates.
UMS Best Practices
Applications that implement UMS should follow these best practices: