Skip to content

Crash: Variable is not retained in await statement in Release mode. #61658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
WarWithinMe opened this issue Oct 21, 2022 · 3 comments
Open
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. crash Bug: A crash, i.e., an abnormal termination of software optimized only Flag: An issue whose reproduction requires optimized compilation run-time crash Bug → crash: Swift code crashed during execution

Comments

@WarWithinMe
Copy link

WarWithinMe commented Oct 21, 2022

Describe the bug
In Release mode, a variable might not get retained if it's being used in an await statement.
In Debug mode, the crash does not exist.

Steps To Reproduce
Minimal reproduce code:

import UIKit

class Data {
  var obj = NSObject()
}

actor Consumer {
  func consume(_ data: Data) { print(data) }
}

class View: UIView {
  var consumer = Consumer()

  var data: Data? {
    didSet {

      // 1. Crash. data is released before consumer.consume(), then consumer prints an already released object.
      //    This only happens in Release mode
      Task {
        if let data {
          await consumer.consume(data)
        }
      }

      // 2. Works fine, if data is used before await.
//      Task {
//        if let data {
//          print(data)
//          await consumer.consume(data)
//        }
//      }

      // 3. Works fine, if there's another statement before await.
//      Task {
//        if let data {
//          print("ok")
//          await consumer.consume(data)
//        }
//      }

      // 4. Works fine, if there's another task.
//      Task {
//        if let data {
//          Task {
//            await consumer.consume(data)
//          }
//        }
//      }
    }
  }
}

class ViewController: UIViewController {
  override func loadView() {
    self.view = View(frame: .init(x: 0, y: 0, width: 100, height: 100))
  }
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    Task {
      while (true) {
        try await Task.sleep(nanoseconds: 1000)
        (view as! View).data = .init()
      }
    }
  }
}

Expected behavior
The data object shƒould stay valid until after the await statement.

Environment (please fill out the following information)

  • OS: [e.g. macOS 11.0] ios 16
  • Xcode Version/Tag/Branch: Xcode-14.1.0-Release.Candidate

Additional context
Tested with swiftlang-5.7.1.135.2 clang-1400.0.29.51

Minimal Project to Reproduce
Test.zip

@WarWithinMe WarWithinMe added the bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. label Oct 21, 2022
@LucianoPAlmeida LucianoPAlmeida added the run-time crash Bug → crash: Swift code crashed during execution label Oct 22, 2022
@AnthonyLatsis AnthonyLatsis added the optimized only Flag: An issue whose reproduction requires optimized compilation label Oct 22, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 2022
@saagarjha
Copy link
Contributor

Shorter example:

import Foundation

class Data {
	var obj = NSObject()
}

actor Consumer {
	func consume(_ data: Data) {
		print(data)
	}
}

var consumer = Consumer()

var data: Data? {
	didSet {
		Task {
			if let data {
				await consumer.consume(data)
			}
		}
	}
}

Task {
	while true {
		data = .init()
	}
}

RunLoop.main.run()

This crashes regardless of optimization level. I think there is something broken in how objects are captured across an await in an actor-isolated context.

@eeckstein
Copy link
Contributor

This looks like a race condition. The global variable data is set and read without going through the consumer actor.

@saagarjha
Copy link
Contributor

Ah yes, you're right. I guess my code probably has some other issue then, or the race is less obvious there…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. crash Bug: A crash, i.e., an abnormal termination of software optimized only Flag: An issue whose reproduction requires optimized compilation run-time crash Bug → crash: Swift code crashed during execution
Projects
None yet
Development

No branches or pull requests

5 participants