Simultaneous access to global tuple members

SE-0176 Enforce Exclusive Access to Memory (https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md) states that "Accesses to different stored properties of a struct or different elements of a tuple are allowed to overlap."

And indeed this compiles and runs as expected (Xcode 9 beta 4):

    // main.swift

    func foo() {
        var tuple = (1, 2)
        swap(&tuple.0, &tuple.1)
        print(tuple)
    }

    foo()

However, if the tuple is a global variable, it crashes with a runtime exception:

    // main.swift

    var tuple = (1, 2)
    swap(&tuple.0, &tuple.1)
    print(tuple)

    Simultaneous accesses to 0x100401cb0, but modification requires exclusive access.
    Previous access (a modification) started at tupleaccess`main + 76 (0x1000015cc).
    Current access (a modification) started at:
    0 tupleaccess 0x0000000100335b60 swift_beginAccess + 605
    1 tupleaccess 0x0000000100001580 main + 110
    2 libdyld.dylib 0x00007fffa7284234 start + 1

Is there anything special with simultaneous access to members of a global tuple, or is this a bug?

Regards, Martin

Not a bug. The very next sentence after the one you quoted: “However, note that modifying part of a value type still requires exclusive access to the entire value”. The first example in that section (https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md#value-types) is similar to your example; it trips the dynamic enforcement mechanism for the same reason. The second example shows a possible workaround.

Sincerely,
Guillaume Lessard

···

On Aug 8, 2017, at 01:11, Martin R via swift-users <swift-users@swift.org> wrote:

SE-0176 Enforce Exclusive Access to Memory (https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md) states that "Accesses to different stored properties of a struct or different elements of a tuple are allowed to overlap."

[snip]

Is there anything special with simultaneous access to members of a global tuple, or is this a bug?

In that example the tuple is a (stored) property of a class, not a global variable. And why does it crash for a global variable, but not for a local variable in a function?

(Sorry if I am overlooking something obvious.)

Martin

···

On 8. Aug 2017, at 17:50, Guillaume Lessard <glessard@tffenterprises.com> wrote:

On Aug 8, 2017, at 01:11, Martin R via swift-users <swift-users@swift.org> wrote:

SE-0176 Enforce Exclusive Access to Memory (https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md) states that "Accesses to different stored properties of a struct or different elements of a tuple are allowed to overlap."

[snip]

Is there anything special with simultaneous access to members of a global tuple, or is this a bug?

Not a bug. The very next sentence after the one you quoted: “However, note that modifying part of a value type still requires exclusive access to the entire value”. The first example in that section (https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md#value-types) is similar to your example; it trips the dynamic enforcement mechanism for the same reason. The second example shows a possible workaround.

Sincerely,
Guillaume Lessard

In the case of a local variable in a function, the compiler can statically prove that there is no simultaneous access, and using `swap` is allowed. With a global variable, the compiler can’t statically prove exclusive access.
(it seems silly with your simple example, but the system doesn’t try static enforcement with global variables.)

Here’s another modification which traps at runtime:

···

On Aug 8, 2017, at 09:10, Martin R <martinr448@gmail.com> wrote:

In that example the tuple is a (stored) property of a class, not a global variable. And why does it crash for a global variable, but not for a local variable in a function?

***
import Dispatch

func foo() {
  var tuple = (1, 2)

  let q = DispatchQueue(label: "queue")
  q.async { swap(&tuple.0, &tuple.1) }
  q.sync {}

  print(tuple)
}

foo()
***

Guillaume Lessard

Thank you for the clarification.

···

On 8. Aug 2017, at 18:39, Guillaume Lessard <glessard@tffenterprises.com> wrote:

On Aug 8, 2017, at 09:10, Martin R <martinr448@gmail.com> wrote:

In that example the tuple is a (stored) property of a class, not a global variable. And why does it crash for a global variable, but not for a local variable in a function?

In the case of a local variable in a function, the compiler can statically prove that there is no simultaneous access, and using `swap` is allowed. With a global variable, the compiler can’t statically prove exclusive access.
(it seems silly with your simple example, but the system doesn’t try static enforcement with global variables.)

Here’s another modification which traps at runtime:

***
import Dispatch

func foo() {
var tuple = (1, 2)

let q = DispatchQueue(label: "queue")
q.async { swap(&tuple.0, &tuple.1) }
q.sync {}

print(tuple)
}

foo()
***

Guillaume Lessard