Jump to content

February 2017 »

2627 28     

Recent Entries

- - - - -

Setting Thread Affinity on Windows in D

4: Adsense

When working with D's standard library, it is sometimes necessary to work around missing declarations in the core.sys.windows.windows module. It's a fairly big module as is, but it isn't all-inclusive. If you are doing any heavy-duty Windows development, you'll likely want a third-party Win32 API binding. But if you just need to call a function or two, that's overkill.

A good example of this is the need to lock the timing thread to one core when dealing with the Windows timer on a multi-core machine. This is a well-known solution to the problem of erratic timing results returned by QueryPerformanceCounter. If you are using a library like SDL, which uses QPC under the hood for its timing calls, this is something you ought to consider. Unfortunately, Phobos is currently missing declarations for a couple of function calls and one type that you need to lock the timing thread down.

For many cases, this can be overcome by adding the appropriate declarations where you need them. In other cases, you'll find (particularly when using DMD) that the Win32 import libraries that ship with the compiler are outdated. In that case, you'll either need to generate them yourself or load the function symbols manually via LoadLibrary and friends. Luckily, for setting the thread affinity the solution is easy. Here's a complete example, ripped right out of the module where I use it.

		import core.sys.windows.windows;
		import std.windows.syserror;

	    // Declarations missing from the windows module.
		alias size_t DWORD_PTR;
			DWORD SetThreadAffinityMask(HANDLE,DWORD);
			BOOL GetProcessAffinityMask(HANDLE,DWORD_PTR*,DWORD_PTR*);

		void setThreadAffinity()
			void doThrow(string msg)
				auto err = GetLastError();
				throw new Exception(msg ~ sysErrorString(err));

			DWORD_PTR procMask, sysMask;
			if(!GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask))
				doThrow("GetProcessAffinityMask failure: ");

			DWORD_PTR mask = 1;
			if(mask & procMask)
				if(!SetThreadAffinityMask(GetCurrentThread(), mask))
					doThrow("SetThreadAffinityMask failure: ");
				throw new Exception("Unexpected affinity mask mismatch.");

Drop this into module or class scope and add the following to an init method somewhere in the same module (or another module if you move setThreadAffinity out of the private block).

version(Windows) setThreadAffinity();

Notice also that I imported std.windows.syserror, which exposes the sysErrorString function. I'm sure I wasn't the only one who overlooked that module. After years of using D, I only noticed it recently. If you're going to be making Win32 API calls, it will come in handy.

Note: GameDev.net moderates comments.