This is the last entry in the stack trace and presumably where the
EXC_BAD_ACCESS error is triggered:
0 swift::ResolveAsSymbolicReference::operator()(swift::Demangle::__runtime::SymbolicReferenceKind, swift::Demangle::__runtime::Directness, int, void const*)
I'm creating an AWS Swift Lambda. There are
async methods which I'm using and decided to run in a
Task instead of implementing
AsyncLambdaHandler has a method requirement with a default implementation, but only on for macOS 12+, and I wasn't immediately sure how I'd implement it myself. So I ran the
async methods in the
Task and the program immediately crashed ("EXC_BAD_ACCESS"). It didn't even get to the line which instantiates the
Task; it just crashes right on the line before it. But I presume the
Task is the problem because when I comment out instantiation of the task and the usage of the
async code, no crash occurs.
I've created a minimal reproducible example of the error here. There is a sample payload in the README.md, that should work using a POST request, but it will not when using the
Task. Task creation — and only
Task creation — is in the method
API.handle(context:event:callback:) in API.swift.
Nothing is holding on to the
Task as you're mixing async code and callbacks, so I suspect the function is going out of scope, cleaning itself up then the task gets executed and tries to call a callback in a scope that no longer exists, hence the bad access error
But that's always the case with AWS Swift Lambda's — they are executed asynchronously. That's why they're given the closure because it's not expected that by time that
LambdaHandler.handle(context:event:callback:) returns that the request will have been completed. So if it is expected that the callback may be executed in a scope that no longer exists anyway, then why would it matter if it is done from within a closure given to a
Task or a closure given to some method that invokes it on completion of some other asynchronous task like a HTTP request.
Also, in all the examples I saw of
Task being used I've never seen it held onto. I also placed a breakpoint in the task and it's never reached; although I do know that that's not always a sign that the code at the breakpoint is not executed, it is more often than not though.
Which examples have you seen that create a
Task? The issue is that you're mixing async styles. All the examples of using the completion handler show it being called synchronously, e.g. whatever work that you're doing will be blocking. If you don't want to block and you can't use async/await you'll need to use
EventLoopFutures where you return a promise instead of invoking the completion handler
No examples of creating a
Task in the context of AWS Swift Lambda — just general Swift use cases of using
await. That's part of the problem, there's barely any examples of AWS Swift Lambda at all.
Furthermore, the documentation for
Lambda.InitializationContext.eventLoop explicitly states
Most importantly the
EventLoop must never be blocked.
Which makes it seem as if blocking, synchronous code should not be ran on invocations of the Lambda lest its
EventLoop be blocked.
Thanks for the clarity though, it does make sense at the moment that you'd use
EventLoopFutures when you're responding to the Lambda request asynchronously rather than invoking a given callback at some later point in the future, maybe.
I would have just used
AsyncLambdaHandler, but all the code regarding it is marked
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *), and I'm on macOS 11.6.5; so it makes it seem like it is unsafe to use in spite of my OS supporting Swift 5.5.
You could always build it in a Linux container using VSCode Remote Dev etc. It's not available on macOS 11 as the concurrency runtime isn't fully implemented on there
I tried going the pure
AsyncLambdaHandler approach, but I'm still getting an
EXC_BAD_ACCESS error. I created a minimal reproducible example of the error. Unlike with the first example, this one doesn't event get to the waiting phase of the lambda — it crashes (same error — even looks like the same culprit as before in the stack trace) as soon as the Lambda starts to spin up.
From what I've read it appears that Swift _Concurrency was backported to macOS 10.15 — I'm running macOS 11.6.5 — and yet it seems like the problem I'm facing is caused by using _Concurrency API's.
Some of the concurrency runtime was backported but not everything so I'm not sure what's going on. It could be a compiler bug in the way the back porting is working or it's just not supported
I'm just trying to figure out whether this is something that I'm doing wrong or something wrong with Swift/Xcode. If it's something I'm doing wrong, then I will put in the effort to fix it for the benefits of being able to use the concurrency API's. If it's not something I'm doing wrong, then I need to stop trying to incorporate Swift Concurrency into my Lambda now and start trying to find ways to work around the dependencies that have already incorporated it into their API's.
If everything wasn't backported, then why would Xcode include this in their release notes for version 13.2:
- You can now use Swift Concurrency in applications that deploy to macOS Catalina 10.15, iOS 13, tvOS 13, and watchOS 6 or newer. This support includes
await , actors, global actors, structured concurrency, and the task APIs. (70738378)
Are applications that deploy to macOS 10.15 Catalina, but are not necessarily Mac apps (the Lambda I'm trying to run from Xcode) not included under that umbrella?
I'm not 100% sure on the specific requirements for concurrency stuff and the back deployment stuff but we've deliberately not enabled it in Vapor as it requires a lot of code duplication and there's no guarantee that it will be work reliably. I still think you're best bet is remote dev with VSCode if you can't upgrade to macOS 12 and want to use async stuff