Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 1054057
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 16, 20262026-05-16T17:20:46+00:00 2026-05-16T17:20:46+00:00

Reading this question , I wanted to test if I could demonstrate the non-atomicity

  • 0

Reading this question, I wanted to test if I could demonstrate the non-atomicity of reads and writes on a type for which the atomicity of such operations is not guaranteed.

private static double _d;

[STAThread]
static void Main()
{
    new Thread(KeepMutating).Start();
    KeepReading();
}

private static void KeepReading()
{
    while (true)
    {
        double dCopy = _d;

        // In release: if (...) throw ...
        Debug.Assert(dCopy == 0D || dCopy == double.MaxValue); // Never fails
    }
}

private static void KeepMutating()
{
    Random rand = new Random();
    while (true)
    {
        _d = rand.Next(2) == 0 ? 0D : double.MaxValue;
    }
}

To my surprise, the assertion refused to fail even after a full three minutes of execution.
What gives?

  1. The test is incorrect.
  2. The specific timing characteristics of the test make it unlikely/impossible that the assertion will fail.
  3. The probability is so low that I have to run the test for much longer to make it likely that it will trigger.
  4. The CLR provides stronger guarantees about atomicity than the C# spec.
  5. My OS/hardware provides stronger guarantees than the CLR.
  6. Something else?

Of course, I don’t intend to rely on any behaviour that is not explicitly guaranteed by the spec, but I would like a deeper understanding of the issue.

FYI, I ran this on both Debug and Release (changing Debug.Assert to if(..) throw) profiles in two separate environments:

  1. Windows 7 64-bit + .NET 3.5 SP1
  2. Windows XP 32-bit + .NET 2.0

EDIT: To exclude the possibility of John Kugelman’s comment “the debugger is not Schrodinger-safe” being the problem, I added the line someList.Add(dCopy); to the KeepReading method and verified that this list was not seeing a single stale value from the cache.

EDIT:
Based on Dan Bryant’s suggestion: Using long instead of double breaks it virtually instantly.

  • 1 1 Answer
  • 2 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-16T17:20:47+00:00Added an answer on May 16, 2026 at 5:20 pm

    You might try running it through CHESS to see if it can force an interleaving that breaks the test.

    If you take a look at the x86 diassembly (visible from the debugger), you might also see if the jitter is generating instructions that preserve atomicity.


    EDIT: I went ahead and ran the disassembly (forcing target x86). The relevant lines are:

                    double dCopy = _d;
    00000039  fld         qword ptr ds:[00511650h] 
    0000003f  fstp        qword ptr [ebp-40h]
    
                    _d = rand.Next(2) == 0 ? 0D : double.MaxValue;
    00000054  mov         ecx,dword ptr [ebp-3Ch] 
    00000057  mov         edx,2 
    0000005c  mov         eax,dword ptr [ecx] 
    0000005e  mov         eax,dword ptr [eax+28h] 
    00000061  call        dword ptr [eax+1Ch] 
    00000064  mov         dword ptr [ebp-48h],eax 
    00000067  cmp         dword ptr [ebp-48h],0 
    0000006b  je          00000079 
    0000006d  nop 
    0000006e  fld         qword ptr ds:[002423D8h] 
    00000074  fstp        qword ptr [ebp-50h] 
    00000077  jmp         0000007E 
    00000079  fldz 
    0000007b  fstp        qword ptr [ebp-50h] 
    0000007e  fld         qword ptr [ebp-50h] 
    00000081  fstp        qword ptr ds:[00159E78h] 
    

    It uses a single fstp qword ptr to perform the write operation in both cases. My guess is that the Intel CPU guarantees atomicity of this operation, though I haven’t found any documentation to support this. Any x86 gurus who can confirm this?


    UPDATE:

    This fails as expected if you use Int64, which uses the 32-bit registers on the x86 CPU rather than the special FPU registers. You can see this below:

                    Int64 dCopy = _d;
    00000042  mov         eax,dword ptr ds:[001A9E78h] 
    00000047  mov         edx,dword ptr ds:[001A9E7Ch] 
    0000004d  mov         dword ptr [ebp-40h],eax 
    00000050  mov         dword ptr [ebp-3Ch],edx 
    

    UPDATE:

    I was curious if this would fail if I forced non-8byte alignment of the double field in memory, so I put together this code:

        [StructLayout(LayoutKind.Explicit)]
        private struct Test
        {
            [FieldOffset(0)]
            public double _d1;
    
            [FieldOffset(4)]
            public double _d2;
        }
    
        private static Test _test;
    
        [STAThread]
        static void Main()
        {
            new Thread(KeepMutating).Start();
            KeepReading();
        }
    
        private static void KeepReading()
        {
            while (true)
            {
                double dummy = _test._d1;
                double dCopy = _test._d2;
    
                // In release: if (...) throw ...
                Debug.Assert(dCopy == 0D || dCopy == double.MaxValue); // Never fails
            }
        }
    
        private static void KeepMutating()
        {
            Random rand = new Random();
            while (true)
            {
                _test._d2 = rand.Next(2) == 0 ? 0D : double.MaxValue;
            }
        }
    

    It does not fail and the generated x86 instructions are essentially the same as before:

                    double dummy = _test._d1;
    0000003e  mov         eax,dword ptr ds:[03A75B20h] 
    00000043  fld         qword ptr [eax+4] 
    00000046  fstp        qword ptr [ebp-40h] 
                    double dCopy = _test._d2;
    00000049  mov         eax,dword ptr ds:[03A75B20h] 
    0000004e  fld         qword ptr [eax+8] 
    00000051  fstp        qword ptr [ebp-48h] 
    

    I experimented with swapping _d1 and _d2 for usage with dCopy/set and also tried a FieldOffset of 2. All generated the same basic instructions (with different offsets above) and all did not fail after several seconds (likely billions of attempts). I’m cautiously confident, given these results, that at least the Intel x86 CPUs provide atomicity of double load/store operations, regardless of alignment.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

While reading this question I wanted to test the input in GCC to see
I was reading this question (which you do not have to read because I
I was just reading this question and wanted to try the alias method rather
I have my own answer to this question, which I'll post, but I wanted
I was reading this question Deleting a const pointer and wanted to know more
I was reading this question , I wanted to ask more about the code
After reading this question, I need to clear up some things. IQueryable<Customer> custs =
After reading this question , i saw the answer by Naveen containing a link
I was reading this question, but didn't understand some part of the aix's answer
Now that it's answered: Don't bother reading this question, it's a bit lengthy and

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.