File: mainactor.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (145 lines) | stat: -rw-r--r-- 4,024 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// RUN: %target-run-simple-swift(-parse-as-library  -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s

// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: libdispatch

// rdar://76038845
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime
// UNSUPPORTED: single_threaded_concurrency

import Dispatch

/// @returns true iff the expected answer is actually the case, i.e., correct.
/// If the current queue does not match expectations, this function may return
/// false or just crash the program with non-zero exit code, depending on SDK.
func checkIfMainQueue(expectedAnswer expected: Bool) -> Bool {
  if #available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *) {
    dispatchPrecondition(condition: expected ? .onQueue(DispatchQueue.main) 
                                             : .notOnQueue(DispatchQueue.main))
  }
  return true
}

actor A {
  func onCorrectQueue(_ count : Int) -> Int {
    if checkIfMainQueue(expectedAnswer: false) {
      print("on actor instance's queue")
      return count + 1
    }
    print("ERROR: not on actor instance's queue")
    return -10
  }
}

@MainActor func checkAnotherFn(_ count : Int) -> Int {
  if checkIfMainQueue(expectedAnswer: true) {
    print("on main queue again!")
    return count + 1
  } else {
    print("ERROR: left the main queue?")
    return -10
  }
}

@MainActor func enterMainActor(_ initialCount : Int) async -> Int {
  if checkIfMainQueue(expectedAnswer: true) {
    print("hello from main actor!")
  } else {
    print("ERROR: not on correct queue!")
  }

  // try calling a function on another actor.
  let count = await A().onCorrectQueue(initialCount)

  guard checkIfMainQueue(expectedAnswer: true) else {
    print("ERROR: did not switch back to main actor!")
    return -10
  }

  return checkAnotherFn(count) + 1
}

@Sendable func someFunc() async -> Int {
  // NOTE: the "return" counter is just to make sure we're properly returning values.
  // the expected number should be equal to the number of "plus-one" expressions.
  // since there are no loops or duplicate function calls
  return await enterMainActor(0) + 1
}

@MainActor func mainActorFn() -> Int {
  checkIfMainQueue(expectedAnswer: true)
  return 10
}

@MainActor
struct S {
  static var bacteria: Int = mainActorFn()
}

@MainActor
class C {
  static var bacteria: Int = mainActorFn()
  lazy var amoeba: Int = mainActorFn()
  nonisolated init() {}
}

// CHECK: starting
// CHECK-NOT: ERROR
// CHECK: Hello from the main function
// CHECK-NOT: ERROR
// CHECK: hello from main actor!
// CHECK-NOT: ERROR
// CHECK: on actor instance's queue
// CHECK-NOT: ERROR
// CHECK: on main queue again!
// CHECK-NOT: ERROR
// CHECK: finished with return counter = 4

// CHECK: detached task not on main queue
// CHECK: on main queue again
// CHECK: detached task hopped back

@main struct RunIt {
  static func main() async {
    print("starting")
    if checkIfMainQueue(expectedAnswer: true) {
      print("Hello from the main function")
    } else {
      print("ERROR: not on the main queue")
    }
    let result = await someFunc()
    print("finished with return counter = \(result)")

    // Check actor hopping with MainActor.run.
    let task = Task.detached {
      if checkIfMainQueue(expectedAnswer: false) {
        print("detached task not on main queue")
      } else {
        print("ERROR: detached task is on the main queue?")
      }

      _ = await MainActor.run {
        checkAnotherFn(1)
      }

      if checkIfMainQueue(expectedAnswer: false) {
        print("detached task hopped back")
      } else {
        print("ERROR: detached task is on the main queue?")
      }
    }
    _ = await task.value

    // Check that initializers for stored properties are on the right actor
    let t1 = Task.detached { () -> Int in
      let c = C()
      return await c.amoeba
    }
    let t2 = Task.detached { () -> Int in
      return await S.bacteria + C.bacteria
    }
    _ = await t1.value + t2.value
  }
}