Reputation: 1585
What I'm expecting to happen:
Since the process affinity is restricted to a single core, all the threads of the current process have to compete for the execution time.
Since thread a
has a higher priority than thread b
and it never blocks, thread b
is never executed.
What I'm seeing:
Thread b
gets spuriously executed.
Why is this happening? Is there a way to enforce this execution logic with WinAPI?
#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;
DWORD WINAPI a_function(LPVOID lpParam)
{
while (true) {
}
return 0;
}
DWORD WINAPI b_function(LPVOID lpParam)
{
while (true) {
cout << "b" << endl;
}
return 0;
}
int main()
{
DWORD affinityMask = 1u;
BOOL res = SetProcessAffinityMask(GetCurrentProcess(), affinityMask);
if (!res) {
return -1;
}
HANDLE a_handle = CreateThread(nullptr, 0, a_function, nullptr, CREATE_SUSPENDED, nullptr);
HANDLE b_handle = CreateThread(nullptr, 0, b_function, nullptr, CREATE_SUSPENDED, nullptr);
SetThreadPriority(a_handle, THREAD_PRIORITY_NORMAL);
SetThreadPriority(b_handle, THREAD_PRIORITY_BELOW_NORMAL);
ResumeThread(a_handle);
ResumeThread(b_handle);
WaitForSingleObject(a_handle, INFINITE);
return 0;
}
Output:
b
b
b
b
b
Note: I'm not using ResumeThread()
/ SuspendThread()
because :
"The SuspendThread function is not intended to be used for thread synchronization because it does not control the point in the code at which the thread's execution is suspended." (source)
Upvotes: 1
Views: 1291
Reputation: 6393
Priorities are not absolute. As soon as you use multiple threads, and all of the threads are in a runable state (that is, they are not suspended or waiting on a synchronization primitive), they will run sooner or later. Priorities only ensure that threads with a higher priority get scheduled with less latency / larger time slices, but not that they get scheduled exclusively before any other class of thread gets a shot.
The rationale is something quite similar to what you constructed: Spinlocks. If thread a
was a consumer, and thread b
was a producer, then never running b
would send a
into a deadlock. That problem is commonly known as a Priority Inversion, because b
would technically have priority over a
as a
is waiting on b
, but the configured priorities, and the non-use of kernel synchronization objects doesn't reflect this dependency.
That's not just a hypothetical scenario, but actually quite common. You could argue that you wouldn't notice on a multi-threaded system, but the rationale is once again that you can't risk breaking the software just by restricting it to a single CPU core, or because all cores get occupied by high priority threads.
Upvotes: 3