Bool type and reading and writing operation atomicity


(Dale Myers) #1

Put simply, are reading and writing to Bools both atomic operations in Swift (3)?

Obviously, something reading and then assigning assuming the value doesn't change between is not correct, but would something like the following be fine:

    var myBool = false

    // Thread 1
    while true {
        if randomEvent() {
            myBool = true
        }
    }
    
    // Thread 2
    while true {
        if myBool {
            print("Was set to true")
        } else {
            print("Not set")
        }
    }

I understand that in thread 2, the value of myBool can change between the check and the print statements, but that's fine. What I want to confirm for my team is that `myBool` can never be in some weird "third" state?

From what I can tell, behind the scenes, Bool uses a Builtin.Int1 for storage. I can't find the definition for this type anywhere so I can't check what it is really doing. My assumption is that it uses something like a C++ unsigned char behind the scenes, which would be fine with the above code on x86 and ARM as far as I'm aware.

Can someone help me figure out if I'm right or wrong in my assumptions?

Thanks,

Dale


(Joe Groff) #2

Nothing in Swift is formally atomic. You'll need to use external APIs that provide atomic operations, such as OSAtomic* on Apple platforms, with raw memory if you need to do that sort of thing in Swift today.

If you naively ported this code to C or C++, it would also formally have a data race, since unless `myBool` is std::atomic (C++) or _Atomic (C11), the compiler will assume that `myBool` is not concurrently accessed and emit nonatomic loads/stores or optimize accordingly. When Swift does gain proper support for atomics, it will almost certainly be something you have to opt in to as well.

-Joe

···

On Jan 16, 2017, at 3:27 AM, Dale Myers via swift-users <swift-users@swift.org> wrote:

Put simply, are reading and writing to Bools both atomic operations in Swift (3)?

Obviously, something reading and then assigning assuming the value doesn't change between is not correct, but would something like the following be fine:

   var myBool = false

   // Thread 1
   while true {
       if randomEvent() {
           myBool = true
       }
   }

   // Thread 2
   while true {
       if myBool {
           print("Was set to true")
       } else {
           print("Not set")
       }
   }

I understand that in thread 2, the value of myBool can change between the check and the print statements, but that's fine. What I want to confirm for my team is that `myBool` can never be in some weird "third" state?

From what I can tell, behind the scenes, Bool uses a Builtin.Int1 for storage. I can't find the definition for this type anywhere so I can't check what it is really doing. My assumption is that it uses something like a C++ unsigned char behind the scenes, which would be fine with the above code on x86 and ARM as far as I'm aware.

Can someone help me figure out if I'm right or wrong in my assumptions?


(Johannes Weiss) #3

Hi,

Put simply, are reading and writing to Bools both atomic operations in Swift (3)?

you don't get a guarantee for that as Swift at the moment lacks both, a memory and a concurrency model.

Obviously, something reading and then assigning assuming the value doesn't change between is not correct, but would something like the following be fine:

   var myBool = false

   // Thread 1
   while true {
       if randomEvent() {
           myBool = true
       }
   }

   // Thread 2
   while true {
       if myBool {
           print("Was set to true")
       } else {
           print("Not set")
       }
   }

I understand that in thread 2, the value of myBool can change between the check and the print statements, but that's fine. What I want to confirm for my team is that `myBool` can never be in some weird "third" state?

As far as I know, you don't get a guarantee for that. But worse (and just like in C/C++/...) you most importantly don't get the guarantee that Thread 2 will ever see the change of Thread 1. The compiler could also optimise the code in Thread 2 to

while true {
   // if false {
   // print("Was set to true")
   // } else {
          print("Not set")
   // }
}

ie. the compiler could 'prove' that `myBool` is never written (legally) and therefore delete all the code that assumes myBool != false.

I'm not saying the compiler is doing this today but AFAIK it could do so if it wanted.

From what I can tell, behind the scenes, Bool uses a Builtin.Int1 for storage. I can't find the definition for this type anywhere so I can't check what it is really doing. My assumption is that it uses something like a C++ unsigned char behind the scenes, which would be fine with the above code on x86 and ARM as far as I'm aware.

In C/C++ you'd need the new _Atomic variables to get that guarantee.

[...]

Again, I'm not saying the compiler does do that but AFAIK it could do so legally. But one of the compiler people can answer these questions much better than I can.

Cheers,
  Johannes


(Dale Myers) #4

Compiler optimisations are something I completely ignored in my reasoning, which as has been pointed out was clearly a mistake.

We will switch to using something that provides atomic operations.

Thanks!

···

-----Original Message-----
From: johannesweiss@apple.com [mailto:johannesweiss@apple.com]
Sent: 17 January 2017 18:37
To: Dale Myers <dalemy@microsoft.com>
Cc: swift-users@swift.org
Subject: Re: [swift-users] Bool type and reading and writing operation atomicity

Hi,

Put simply, are reading and writing to Bools both atomic operations in Swift (3)?

you don't get a guarantee for that as Swift at the moment lacks both, a memory and a concurrency model.

Obviously, something reading and then assigning assuming the value doesn't change between is not correct, but would something like the following be fine:

   var myBool = false

   // Thread 1
   while true {
       if randomEvent() {
           myBool = true
       }
   }

   // Thread 2
   while true {
       if myBool {
           print("Was set to true")
       } else {
           print("Not set")
       }
   }

I understand that in thread 2, the value of myBool can change between the check and the print statements, but that's fine. What I want to confirm for my team is that `myBool` can never be in some weird "third" state?

As far as I know, you don't get a guarantee for that. But worse (and just like in C/C++/...) you most importantly don't get the guarantee that Thread 2 will ever see the change of Thread 1. The compiler could also optimise the code in Thread 2 to

while true {
   // if false {
   // print("Was set to true")
   // } else {
          print("Not set")
   // }
}

ie. the compiler could 'prove' that `myBool` is never written (legally) and therefore delete all the code that assumes myBool != false.

I'm not saying the compiler is doing this today but AFAIK it could do so if it wanted.

From what I can tell, behind the scenes, Bool uses a Builtin.Int1 for storage. I can't find the definition for this type anywhere so I can't check what it is really doing. My assumption is that it uses something like a C++ unsigned char behind the scenes, which would be fine with the above code on x86 and ARM as far as I'm aware.

In C/C++ you'd need the new _Atomic variables to get that guarantee.

[...]

Again, I'm not saying the compiler does do that but AFAIK it could do so legally. But one of the compiler people can answer these questions much better than I can.

Cheers,
  Johannes


(Zhao Xin) #5

You can use the GCD semaphore.
https://developer.apple.com/reference/dispatch/dispatchsemaphore

Or it is always better to consider of using queues if you can.

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/ThreadMigration/ThreadMigration.html#//apple_ref/doc/uid/TP40008091-CH105-SW1

Zhaoxin

···

On Wed, Jan 18, 2017 at 5:53 PM, Dale Myers via swift-users < swift-users@swift.org> wrote:

Compiler optimisations are something I completely ignored in my reasoning,
which as has been pointed out was clearly a mistake.

We will switch to using something that provides atomic operations.

Thanks!

-----Original Message-----
From: johannesweiss@apple.com [mailto:johannesweiss@apple.com]
Sent: 17 January 2017 18:37
To: Dale Myers <dalemy@microsoft.com>
Cc: swift-users@swift.org
Subject: Re: [swift-users] Bool type and reading and writing operation
atomicity

Hi,

> Put simply, are reading and writing to Bools both atomic operations in
Swift (3)?

you don't get a guarantee for that as Swift at the moment lacks both, a
memory and a concurrency model.

> Obviously, something reading and then assigning assuming the value
doesn't change between is not correct, but would something like the
following be fine:
>
> var myBool = false
>
> // Thread 1
> while true {
> if randomEvent() {
> myBool = true
> }
> }
>
> // Thread 2
> while true {
> if myBool {
> print("Was set to true")
> } else {
> print("Not set")
> }
> }
>
>
> I understand that in thread 2, the value of myBool can change between
the check and the print statements, but that's fine. What I want to confirm
for my team is that `myBool` can never be in some weird "third" state?

As far as I know, you don't get a guarantee for that. But worse (and just
like in C/C++/...) you most importantly don't get the guarantee that Thread
2 will ever see the change of Thread 1. The compiler could also optimise
the code in Thread 2 to

while true {
   // if false {
   // print("Was set to true")
   // } else {
          print("Not set")
   // }
}

ie. the compiler could 'prove' that `myBool` is never written (legally)
and therefore delete all the code that assumes myBool != false.

I'm not saying the compiler is doing this today but AFAIK it could do so
if it wanted.

> From what I can tell, behind the scenes, Bool uses a Builtin.Int1 for
storage. I can't find the definition for this type anywhere so I can't
check what it is really doing. My assumption is that it uses something like
a C++ unsigned char behind the scenes, which would be fine with the above
code on x86 and ARM as far as I'm aware.

In C/C++ you'd need the new _Atomic variables to get that guarantee.

> [...]

Again, I'm not saying the compiler does do that but AFAIK it could do so
legally. But one of the compiler people can answer these questions much
better than I can.

Cheers,
  Johannes
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users