Very simplified, it can work one way or another:
- Create a periodic expected timer with some reasonable short wait time (maybe 100 ms). Get the "last" value for each corresponding process by calling
GetProcessTimes once. - Forever cycle, timer lock.
- Every time you wake up:
- if
GetProcessAffinityMask returns 0, call SetProcessAffinityMask(old_value) . This means that we paused this process at our last iteration, now we give it a chance to start again. - else call
GetProcessTimes to get the "current" value - call
GetSystemTimeAsFileTime - calculate delta by subtracting the last from the current
cpu_usage = (deltaKernelTime + deltaUserTime) / (deltaTime)- if it is more than you want the call
old_value = GetProcessAffinityMask , followed by SetProcessAffinityMask(0) , which will disable this process.
This is basically a very primitive version of the scheduler that runs in the kernel, implemented in userland. It lays the process of "sleeping" for a short period of time if it has used more processor time than what you think is right. A more complex measurement may be required, perhaps after a second or 5 seconds (and probably desirable).
You may be tempted to suspend all threads in the process. However, it is important not to play with priorities and not to use SuspendThread unless you know exactly what the program is doing, as this can easily lead to deadlocks and other unpleasant side effects. Consider, for example, pausing a thread containing a critical section while another thread is still running and trying to get the same object. Or imagine that your process unloaded in the middle of a suspension of a dozen threads, leaving half of them running and the other half dead.
Setting the proximity mask to zero, on the other hand, simply means that from now on, no thread in the process receives more time fragments on any processor. Resetting affinity gives - atomically, at the same time - all threads can start again.
Unfortunately, SetProcessAffinityMask does not return the old mask as SetThreadAffinityMask , at least according to the documentation. Therefore, an additional call to Get... needed.
Damon source share