|
| 1 | +// RUN: rm -rf %t |
| 2 | +// RUN: mkdir -p %t |
| 3 | +// RUN: %target-build-swift %s -o %t/a.out -enforce-exclusivity=checked |
| 4 | +// |
| 5 | +// RUN: %target-run %t/a.out |
| 6 | +// REQUIRES: executable_test |
| 7 | + |
| 8 | +// Tests for traps at run time when enforcing exclusive access. |
| 9 | + |
| 10 | +import StdlibUnittest |
| 11 | + |
| 12 | +struct X { |
| 13 | + var i = 7 |
| 14 | +} |
| 15 | + |
| 16 | +/// Calling this function will begin a read access to the variable referred to |
| 17 | +/// in the first parameter that lasts for the duration of the call. Any |
| 18 | +/// accesses in the closure will therefore be nested inside the outer read. |
| 19 | +func readAndPerform<T>(_ _: UnsafePointer<T>, closure: () ->()) { |
| 20 | + closure() |
| 21 | +} |
| 22 | + |
| 23 | +/// Begin a modify access to the first paraemter and call the closure inside it. |
| 24 | +func modifyAndPerform<T>(_ _: UnsafeMutablePointer<T>, closure: () ->()) { |
| 25 | + closure() |
| 26 | +} |
| 27 | + |
| 28 | +var globalX = X() |
| 29 | + |
| 30 | +var ExclusiveAccessTestSuite = TestSuite("ExclusiveAccess") |
| 31 | + |
| 32 | +ExclusiveAccessTestSuite.test("Read") { |
| 33 | + let l = globalX // no-trap |
| 34 | + _blackHole(l) |
| 35 | +} |
| 36 | + |
| 37 | +// It is safe for a read access to overlap with a read. |
| 38 | +ExclusiveAccessTestSuite.test("ReadInsideRead") { |
| 39 | + readAndPerform(&globalX) { |
| 40 | + let l = globalX // no-trap |
| 41 | + _blackHole(l) |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +ExclusiveAccessTestSuite.test("ModifyInsideRead") |
| 46 | + .skip(.custom( |
| 47 | + { _isFastAssertConfiguration() }, |
| 48 | + reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| 49 | + .crashOutputMatches("modify/read access conflict detected on address") |
| 50 | + .code |
| 51 | +{ |
| 52 | + readAndPerform(&globalX) { |
| 53 | + expectCrashLater() |
| 54 | + globalX = X() |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +ExclusiveAccessTestSuite.test("ReadInsideModify") |
| 59 | + .skip(.custom( |
| 60 | + { _isFastAssertConfiguration() }, |
| 61 | + reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| 62 | + .crashOutputMatches("read/modify access conflict detected on address") |
| 63 | + .code |
| 64 | +{ |
| 65 | + modifyAndPerform(&globalX) { |
| 66 | + expectCrashLater() |
| 67 | + let l = globalX |
| 68 | + _blackHole(l) |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +ExclusiveAccessTestSuite.test("ModifyInsideModify") |
| 73 | + .skip(.custom( |
| 74 | + { _isFastAssertConfiguration() }, |
| 75 | + reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| 76 | + .crashOutputMatches("modify/modify access conflict detected on address") |
| 77 | + .code |
| 78 | +{ |
| 79 | + modifyAndPerform(&globalX) { |
| 80 | + expectCrashLater() |
| 81 | + globalX.i = 12 |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +var globalOtherX = X() |
| 86 | + |
| 87 | +// It is safe for two modifications of different variables |
| 88 | +// to overlap. |
| 89 | +ExclusiveAccessTestSuite.test("ModifyInsideModifyOfOther") { |
| 90 | + modifyAndPerform(&globalOtherX) { |
| 91 | + globalX.i = 12 // no-trap |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +// The access durations for these two modifications do not overlap |
| 96 | +ExclusiveAccessTestSuite.test("ModifyFollowedByModify") { |
| 97 | + globalX = X() |
| 98 | + _blackHole(()) |
| 99 | + |
| 100 | + globalX = X() // no-trap |
| 101 | +} |
| 102 | + |
| 103 | +ExclusiveAccessTestSuite.test("ClosureCaptureModifyModify") { |
| 104 | + var x = X() |
| 105 | + modifyAndPerform(&x) { |
| 106 | + // FIXME: This should be caught dynamically. |
| 107 | + x.i = 12 |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | + |
| 114 | +runAllTests() |
0 commit comments