swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.59k stars 10.36k forks source link

`Task.yield()` and `Task.sleep()` do not respect task executor preference #74395

Open stephencelis opened 5 months ago

stephencelis commented 5 months ago

Description

From this discussion: https://forums.swift.org/t/task-executor-preference-and-deterministic-scheduling/72448

Reproduction

final class MainExecutor: TaskExecutor {
  func enqueue(_ job: consuming ExecutorJob) {
    job.runSynchronously(
      isolatedTo: MainActor.sharedUnownedExecutor,
      taskExecutor: asUnownedTaskExecutor()
    )
  }
}

func testSerializedExecution_YieldEveryOtherValue() async {
  let xs = Mutex<[Int]>([])
  await withTaskExecutorPreference(MainExecutor()) {
    await withTaskGroup(of: Void.self) { group in
      for x in 1...1000 {
        group.addTask { @Sendable in  // Explicit `@Sendable` required in Xcode 16 due to new `sending` keyword
          if x.isMultiple(of: 2) { await Task.yield() }
          xs.withLock { $0.append(x) }
        }
      }
    }
  }
  XCTAssertEqual(
   (0...499).map { $0 * 2 + 1 } + (1...500).map { $0 * 2 },
    xs.withLock { $0 }
  )
}

Expected behavior

I expect the test to pass.

Environment

swift-driver version: 1.109.2 Apple Swift version 6.0 (swiftlang-6.0.0.3.300 clang-1600.0.20.10) Target: arm64-apple-macosx14.0

Additional information

No response

stephencelis commented 5 months ago

/cc @FranzBusch @ktoso

ktoso commented 5 months ago

Thanks for the report! This is tracked internally as rdar://126047740

ktoso commented 4 months ago

We're tracking this as rdar://126047740

stephencelis commented 1 month ago

@ktoso Sadly the above doesn't compile anymore in Xcode 16 (Mutex can't be referenced in the task group for some reason?), but if you swap it out for a LockIsolated you'll find a regression in withTaskExecutorPreference that causes the test to crash (EXC_BAD_ACCESS). Lowering the number of iterations from 1000 to 10 gets something that doesn't crash, for what it's worth.

Edit: Mutex works with an explicit @Sendable annotation in the group's addTask closure.