Skip to content
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

[Concurrency] Add "async" operation for continuing work asynchronously. #37007

Merged
merged 5 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,11 @@ SIMPLE_DECL_ATTR(_implicitSelfCapture, ImplicitSelfCapture,
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove,
115)

SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext,
OnParam | UserInaccessible |
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove,
116)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
19 changes: 17 additions & 2 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,18 @@ class alignas(8) Expr {
Kind : 2
);

SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1,
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1,
/// True if closure parameters were synthesized from anonymous closure
/// variables.
HasAnonymousClosureVars : 1,

/// True if "self" can be captured implicitly without requiring "self."
/// on each member reference.
ImplicitSelfCapture : 1
ImplicitSelfCapture : 1,

/// True if this @Sendable async closure parameter should implicitly
/// inherit the actor context from where it was formed.
InheritActorContext : 1
);

SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16,
Expand Down Expand Up @@ -3876,6 +3880,7 @@ class ClosureExpr : public AbstractClosureExpr {
setParameterList(params);
Bits.ClosureExpr.HasAnonymousClosureVars = false;
Bits.ClosureExpr.ImplicitSelfCapture = false;
Bits.ClosureExpr.InheritActorContext = false;
}

SourceRange getSourceRange() const;
Expand Down Expand Up @@ -3914,6 +3919,16 @@ class ClosureExpr : public AbstractClosureExpr {
Bits.ClosureExpr.ImplicitSelfCapture = value;
}

/// Whether this closure should implicitly inherit the actor context from
/// where it was formed. This only affects @Sendable async closures.
bool inheritsActorContext() const {
return Bits.ClosureExpr.InheritActorContext;
}

void setInheritsActorContext(bool value = true) {
Bits.ClosureExpr.InheritActorContext = value;
}

/// Determine whether this closure expression has an
/// explicitly-specified result type.
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3382,6 +3382,7 @@ struct ParameterListInfo {
SmallBitVector unsafeSendable;
SmallBitVector unsafeMainActor;
SmallBitVector implicitSelfCapture;
SmallBitVector inheritActorContext;

public:
ParameterListInfo() { }
Expand Down Expand Up @@ -3414,6 +3415,10 @@ struct ParameterListInfo {
/// 'self' to be implicit, without requiring "self.".
bool isImplicitSelfCapture(unsigned paramIdx) const;

/// Whether the given parameter is a closure that should inherit the
/// actor context from the context in which it was created.
bool inheritsActorContext(unsigned paramIdx) const;

/// Whether there is any contextual information set on this parameter list.
bool anyContextualInfo() const;

Expand Down
4 changes: 4 additions & 0 deletions include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,10 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_reportUnexpectedExecutor(
const unsigned char *file, uintptr_t fileLength, bool fileIsASCII,
uintptr_t line, ExecutorRef executor);

SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
JobPriority swift_task_getCurrentThreadPriority(void);

}

#pragma clang diagnostic pop
Expand Down
2 changes: 2 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,8 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
PrintWithColorRAII(OS, ClosureModifierColor) << " single-expression";
if (E->allowsImplicitSelfCapture())
PrintWithColorRAII(OS, ClosureModifierColor) << " implicit-self";
if (E->inheritsActorContext())
PrintWithColorRAII(OS, ClosureModifierColor) << " inherits-actor-context";

if (E->getParameters()) {
OS << '\n';
Expand Down
13 changes: 12 additions & 1 deletion lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ ParameterListInfo::ParameterListInfo(
unsafeSendable.resize(params.size());
unsafeMainActor.resize(params.size());
implicitSelfCapture.resize(params.size());
inheritActorContext.resize(params.size());

// No parameter owner means no parameter list means no default arguments
// - hand back the zeroed bitvector.
Expand Down Expand Up @@ -1025,6 +1026,10 @@ ParameterListInfo::ParameterListInfo(
if (param->getAttrs().hasAttribute<ImplicitSelfCaptureAttr>()) {
implicitSelfCapture.set(i);
}

if (param->getAttrs().hasAttribute<InheritActorContextAttr>()) {
inheritActorContext.set(i);
}
}
}

Expand Down Expand Up @@ -1061,9 +1066,15 @@ bool ParameterListInfo::isImplicitSelfCapture(unsigned paramIdx) const {
: false;
}

bool ParameterListInfo::inheritsActorContext(unsigned paramIdx) const {
return paramIdx < inheritActorContext.size()
? inheritActorContext[paramIdx]
: false;
}

bool ParameterListInfo::anyContextualInfo() const {
return unsafeSendable.any() || unsafeMainActor.any() ||
implicitSelfCapture.any();
implicitSelfCapture.any() || inheritActorContext.any();
}

/// Turn a param list into a symbolic and printable representation that does not
Expand Down
49 changes: 44 additions & 5 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5710,17 +5710,25 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,

/// Apply the contextually Sendable flag to the given expression,
static void applyContextualClosureFlags(
Expr *expr, bool sendable, bool forMainActor, bool implicitSelfCapture) {
Expr *expr, bool sendable, bool forMainActor, bool implicitSelfCapture,
bool inheritActorContext) {
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
closure->setUnsafeConcurrent(sendable, forMainActor);
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
closure->setInheritsActorContext(inheritActorContext);
return;
}

if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
applyContextualClosureFlags(
captureList->getClosureBody(), sendable, forMainActor,
implicitSelfCapture);
implicitSelfCapture, inheritActorContext);
}

if (auto identity = dyn_cast<IdentityExpr>(expr)) {
applyContextualClosureFlags(
identity->getSubExpr(), sendable, forMainActor,
implicitSelfCapture, inheritActorContext);
}
}

Expand Down Expand Up @@ -5958,9 +5966,10 @@ Expr *ExprRewriter::coerceCallArguments(
bool isMainActor = paramInfo.isUnsafeMainActor(paramIdx) ||
(isUnsafeSendable && apply && isMainDispatchQueue(apply->getFn()));
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx);
applyContextualClosureFlags(
arg, isUnsafeSendable && contextUsesConcurrencyFeatures(dc),
isMainActor, isImplicitSelfCapture);
isMainActor, isImplicitSelfCapture, inheritsActorContext);

// If the types exactly match, this is easy.
auto paramType = param.getOldType();
Expand Down Expand Up @@ -6113,6 +6122,20 @@ static bool isClosureLiteralExpr(Expr *expr) {
return (isa<CaptureListExpr>(expr) || isa<ClosureExpr>(expr));
}

/// Whether we should propagate async down to a closure.
static bool shouldPropagateAsyncToClosure(Expr *expr) {
if (auto IE = dyn_cast<IdentityExpr>(expr))
return shouldPropagateAsyncToClosure(IE->getSubExpr());

if (auto CLE = dyn_cast<CaptureListExpr>(expr))
return shouldPropagateAsyncToClosure(CLE->getClosureBody());

if (auto CE = dyn_cast<ClosureExpr>(expr))
return CE->inheritsActorContext();

return false;
}

/// If the expression is an explicit closure expression (potentially wrapped in
/// IdentityExprs), change the type of the closure and identities to the
/// specified type and return true. Otherwise, return false with no effect.
Expand Down Expand Up @@ -6976,8 +6999,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
}
}

// If we have a ClosureExpr, then we can safely propagate the 'concurrent'
// bit to the closure without invalidating prior analysis.
// If we have a ClosureExpr, then we can safely propagate @Sendable
// to the closure without invalidating prior analysis.
auto fromEI = fromFunc->getExtInfo();
if (toEI.isSendable() && !fromEI.isSendable()) {
auto newFromFuncType = fromFunc->withExtInfo(fromEI.withConcurrent());
Expand All @@ -6991,6 +7014,22 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
}
}

// If we have a ClosureExpr, then we can safely propagate the 'async'
// bit to the closure without invalidating prior analysis.
fromEI = fromFunc->getExtInfo();
if (toEI.isAsync() && !fromEI.isAsync() &&
shouldPropagateAsyncToClosure(expr)) {
auto newFromFuncType = fromFunc->withExtInfo(fromEI.withAsync());
if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
fromFunc = newFromFuncType->castTo<FunctionType>();

// Propagating the 'concurrent' bit might have satisfied the entire
// conversion. If so, we're done, otherwise keep converting.
if (fromFunc->isEqual(toType))
return expr;
}
}

// If we have a ClosureExpr, then we can safely propagate a global actor
// to the closure without invalidating prior analysis.
fromEI = fromFunc->getExtInfo();
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
IGNORED_ATTR(UnsafeSendable)
IGNORED_ATTR(UnsafeMainActor)
IGNORED_ATTR(ImplicitSelfCapture)
IGNORED_ATTR(InheritActorContext)
#undef IGNORED_ATTR

void visitAlignmentAttr(AlignmentAttr *attr) {
Expand Down
30 changes: 19 additions & 11 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,19 +773,26 @@ static bool isAsyncCall(const ApplyExpr *call) {
/// features.
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc);

/// Determine whether this closure is escaping.
static bool isSendableClosure(const AbstractClosureExpr *closure) {
/// Determine whether this closure should be treated as Sendable.
///
/// \param forActorIsolation Whether this check is for the purposes of
/// determining whether the closure must be non-isolated.
static bool isSendableClosure(
const AbstractClosureExpr *closure, bool forActorIsolation) {
if (auto explicitClosure = dyn_cast<ClosureExpr>(closure)) {
if (forActorIsolation && explicitClosure->inheritsActorContext())
return false;

if (explicitClosure->isUnsafeSendable())
return true;
}

if (auto type = closure->getType()) {
if (auto fnType = type->getAs<AnyFunctionType>())
if (fnType->isSendable())
return true;
}

if (auto explicitClosure = dyn_cast<ClosureExpr>(closure)) {
if (explicitClosure->isUnsafeSendable())
return true;
}

return false;
}

Expand Down Expand Up @@ -2113,7 +2120,7 @@ namespace {
}

if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
if (isSendableClosure(closure)) {
if (isSendableClosure(closure, /*forActorIsolation=*/true)) {
return diag::actor_isolated_from_concurrent_closure;
}

Expand Down Expand Up @@ -2310,8 +2317,9 @@ namespace {
}
}

// Sendable closures are always actor-independent.
if (isSendableClosure(closure))
// Sendable closures are actor-independent unless the closure has
// specifically opted into inheriting actor isolation.
if (isSendableClosure(closure, /*forActorIsolation=*/true))
return ClosureActorIsolation::forIndependent();

// A non-escaping closure gets its isolation from its context.
Expand Down Expand Up @@ -2362,7 +2370,7 @@ bool ActorIsolationChecker::mayExecuteConcurrentlyWith(
while (useContext != defContext) {
// If we find a concurrent closure... it can be run concurrently.
if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
if (isSendableClosure(closure))
if (isSendableClosure(closure, /*forActorIsolation=*/false))
return true;
}

Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,7 @@ namespace {
UNINTERESTING_ATTR(UnsafeSendable)
UNINTERESTING_ATTR(UnsafeMainActor)
UNINTERESTING_ATTR(ImplicitSelfCapture)
UNINTERESTING_ATTR(InheritActorContext)
#undef UNINTERESTING_ATTR

void visitAvailableAttr(AvailableAttr *attr) {
Expand Down
12 changes: 12 additions & 0 deletions stdlib/public/Concurrency/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "swift/ABI/Actor.h"
#include "llvm/ADT/PointerIntPair.h"
#include "TaskPrivate.h"
#include <dispatch/dispatch.h>

#if defined(__APPLE__)
#include <asl.h>
Expand Down Expand Up @@ -259,6 +260,17 @@ static bool isExecutingOnMainThread() {
#endif
}

JobPriority swift::swift_task_getCurrentThreadPriority() {
if (isExecutingOnMainThread())
return JobPriority::UserInitiated;

#if defined(__APPLE__)
return static_cast<JobPriority>(qos_class_self());
#else
return JobPriority::Unspecified;
#endif
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, nice :)


SWIFT_CC(swift)
static bool swift_task_isCurrentExecutorImpl(ExecutorRef executor) {
if (auto currentTracking = ExecutorTrackingInfo::current()) {
Expand Down
Loading