6

I'm porting code written under Visual Studio 2012 to compile with Visual Studio 2015. The code builds OK with Windows 2012.

I have an issue with some code that calls InterlockedIncrement64. It builds OK for an x64 target, but fails where the target is Win32 and the calling code is managed (i.e. compiled with /clr), yielding:

error C3861: 'InterlockedIncrement64': identifier not found

Looking in winnt.h, it seems that InterlockedIncrement64 is undefined when the target is Win32 and _MANAGED is defined.

I can rearrange the code such that InterlockedIncrement64 isn't called for managed code, but I'm still curious to know why this change in behavior has come with Visual Studio 2015.

Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
Mark Short
  • 75
  • 5
  • 3
    Can't you just use the managed version [`System::Threading::Interlocked::Increment(Int64)`](https://msdn.microsoft.com/library/zs86dyzy%28v=vs.110%29.aspx) instead? – Christian.K May 31 '16 at 15:01
  • read this, its on the first paragraph on the [documentation website](https://msdn.microsoft.com/en-us/library/windows/desktop/ms683615(v=vs.85).aspx) : *"To operate on 32-bit values, use the InterlockedIncrement function."*. You really should read the documentation if you're dealing with anything windows-related. Everything else will get you into trouble / leave you clueless. Its kinda mandatory. 64bit functions may or may not be linked / available for 32bit targets - always use the 32bit-ones in that case. – specializt May 31 '16 at 15:15
  • Thanks for the suggestion. The code with InterlockedIncrement64 is an inline function in a header file. The managed code needs to include that header file, but doesn't call the inline function. So my fix is to enclose the inline function with #ifndef _MANAGED. – Mark Short May 31 '16 at 15:18
  • inline functions in **header files**? You're doing it wrong. Verily. – specializt May 31 '16 at 15:23
  • 2
    @specializt *Where else* would you put inline functions? The principal use for the inline keyword is to allow functions to be defined in header files... – Cody Gray - on strike May 31 '16 at 16:08
  • thats .... *horribly* wrong, you should never trust your current sources again. `inline` simply is a keyword for *automatic code multiplication* of sorts *(yes thats a bad choice of words, i know)*, it inserts functions at each and every place they're used, hence function calls (and possibly context switches) are avoided, increasing performance. This [microsoft explanation](https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx) is ... somewhat reliable. Header files should **never** contain actual function or method bodies - under no circumstance, that'd defeat the purpose. – specializt May 31 '16 at 16:33
  • 2
    @specializt Not exactly. One meaning of the inline keyword is as a hint to the compiler that the function should be expanded inline at each call site. But that's essentially an obsolete meaning. Optimizers ignore that hint in 99% of cases nowadays because they are smarter than the programmer who write it. They use heuristics to decide which functions to inline, regardless of whether or not you've annotated them as such. Now, the second meaning of the inline keyword is all that matters, which is in bypassing the one-definition rule (ODR), allowing function bodies to appear in header files. – Cody Gray - on strike Jun 01 '16 at 05:10
  • Aside from that, @specializt, even under the first meaning of the inline keyword (as an optimization hint), you have no choice but to put the function's body in the header file. Using inline on a function *declaration* makes no sense, and neither does using it on a function body appearing in a code file. It would amount to an impossible request. The compiler can't *see* that definition from other translation units and therefore will not be able to inline it. You have absolutely no choice but to put inline functions in header files. – Cody Gray - on strike Jun 01 '16 at 05:13
  • you basically confirmed what is written all over the internet and even what i wrote and added your own, additional definition. I have never read or seen about your claim about headers and inline - and to be honest it doesnt make much sense, its kinda pointless to point out that header files contain declarations -- which is basically what you said. I would like to see some sources, especially for the "99%" claim which is ... utterly made up, there is even `forceinline` for microsoft-specific code, there is no "second meaning" to a `keyword`, that doesnt even make any sense – specializt Jun 01 '16 at 09:25
  • also : `inline` does not make "function bodies to appear in header files", thats completely nonsense - it *will* make functions being inlined in each `translation unit` seperately – specializt Jun 01 '16 at 09:33

1 Answers1

4

As the name implies, InterlockedIncrement64 is an atomic increment operation for a LONGLONG, and it needs memory to be 64 bit aligned.

Given that you can't set memory alignment in managed code and it may be used for managed class members then this limitation makes sense (to me): "...otherwise, this function will behave unpredictably on multiprocessor x86 systems and any non-x86 systems.". Think about this:

::InterlockedIncrement64(&_memberVariable);

If _memberVariable is allocated in managed world, then it won't be 64-bit aligned (though it may happen by chance), and this code will always fail for Win32. It's simpler to remove this function when _MANAGED is defined.

Workaround: check #ifdef _MANAGED and call Interlocked::Increment instead, or drop atomicity (!) but include a memory barrier after increment.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • 1
    I'd say a *better* workaround would be to call the managed API designed for this purpose, `Interlocked::Increment`. You get all the benefits without any of the disadvantages. "When in Rome..." and all that. – Cody Gray - on strike Jun 01 '16 at 05:16
  • 1
    It is one of the methods directly recognized by the jitter. The variable will be aligned in 64-bit mode so the x64 jitter directly generates LOCK XADD. But not in 32-bit mode so x86 jitter generates a call to a helper function inside the CLR, [COMInterlocked::ExchangeAdd64()](https://github.com/dotnet/coreclr/blob/master/src/vm/comutilnative.cpp#L2574). Nothing to do with COM btw. – Hans Passant Jun 01 '16 at 06:54
  • @HansPassant thanks, I just started writing a test app to see generated assembly but you already gave me the answer! Now my perplexity really arises: finally ExchangeAdd64() will call InterlockedExchangeAdd64 which still needs to be 64 bit aligned but 32 bit jitter will align at 32 bit... – Adriano Repetti Jun 01 '16 at 07:02
  • Windows doesn't know beans about the CLR. Different division, twenty managers removed in the organizational chart before you hit somebody that keeps an eye on both. And besides, this happens at runtime. – Hans Passant Jun 01 '16 at 07:07
  • Yes but if InterlockedIncrement64() gives unpredictable results (on 32 bit when not 64 bit aligned) then also InterlockedExchangeAdd64() will. Well, OK...happens... – Adriano Repetti Jun 01 '16 at 07:09
  • It works the other way around as well, the jitter does not know that much about Windows. It runs on many operating systems. Nor does a language ever make an assumption about the OS, a macro would only ever be useful to C++/CLI. Since the plumbing is already there, a macro just isn't useful. – Hans Passant Jun 01 '16 at 07:18