27
27
#include " llvm/ADT/PointerIntPair.h"
28
28
#include " TaskPrivate.h"
29
29
30
- // Uncomment to enable helpful debug spew to stderr
31
- // #define SWIFT_TASK_PRINTF_DEBUG 1
32
-
33
30
using namespace swift ;
34
31
35
32
// / Should we yield the thread?
@@ -74,6 +71,11 @@ class ExecutorTrackingInfo {
74
71
// / The active executor.
75
72
ExecutorRef ActiveExecutor = ExecutorRef::generic();
76
73
74
+ // / Whether this context allows switching. Some contexts do not;
75
+ // / for example, we do not allow switching from swift_job_run
76
+ // / unless the passed-in executor is generic.
77
+ bool AllowsSwitching = true ;
78
+
77
79
// / The tracking info that was active when this one was entered.
78
80
ExecutorTrackingInfo *SavedInfo;
79
81
@@ -114,21 +116,27 @@ class ExecutorTrackingInfo {
114
116
return ActiveExecutor;
115
117
}
116
118
117
- static ExecutorRef getActiveExecutorInThread () {
118
- if (auto activeInfo = ActiveInfoInThread.get ())
119
- return activeInfo->getActiveExecutor ();
120
- return ExecutorRef::generic ();
119
+ void setActiveExecutor (ExecutorRef newExecutor) {
120
+ ActiveExecutor = newExecutor;
121
121
}
122
122
123
- void leave () {
124
- ActiveInfoInThread.set (SavedInfo);
123
+
124
+ bool allowsSwitching () const {
125
+ return AllowsSwitching;
126
+ }
127
+
128
+ // / Disallow switching in this tracking context. This should only
129
+ // / be set on a new tracking info, before any jobs are run in it.
130
+ void disallowSwitching () {
131
+ AllowsSwitching = false ;
125
132
}
126
-
127
- static ExecutorRef getActiveExecutorOnThread () {
128
- if (auto active = ActiveInfoInThread.get ())
129
- return active->getActiveExecutor ();
130
133
131
- swift_unreachable (" no active executor?!" );
134
+ static ExecutorTrackingInfo *current () {
135
+ return ActiveInfoInThread.get ();
136
+ }
137
+
138
+ void leave () {
139
+ ActiveInfoInThread.set (SavedInfo);
132
140
}
133
141
};
134
142
@@ -152,16 +160,6 @@ SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(
152
160
153
161
} // end anonymous namespace
154
162
155
- SWIFT_CC (swift)
156
- static void swift_job_runImpl(Job *job, ExecutorRef executor) {
157
- ExecutorTrackingInfo trackingInfo;
158
- trackingInfo.enterAndShadow (executor);
159
-
160
- runJobInEstablishedExecutorContext (job);
161
-
162
- trackingInfo.leave ();
163
- }
164
-
165
163
void swift::runJobInEstablishedExecutorContext (Job *job) {
166
164
_swift_tsan_acquire (job);
167
165
@@ -199,9 +197,11 @@ AsyncTask *swift::_swift_task_clearCurrent() {
199
197
200
198
SWIFT_CC (swift)
201
199
static ExecutorRef swift_task_getCurrentExecutorImpl() {
202
- auto result = ExecutorTrackingInfo::getActiveExecutorOnThread ();
200
+ auto currentTracking = ExecutorTrackingInfo::current ();
201
+ auto result = (currentTracking ? currentTracking->getActiveExecutor ()
202
+ : ExecutorRef::generic ());
203
203
#if SWIFT_TASK_PRINTF_DEBUG
204
- fprintf (stderr, " %p getting current executor %p\n " , pthread_self (), ( void *) result.getRawValue ());
204
+ fprintf (stderr, " [%p] getting current executor %p\n " , pthread_self (), result.getIdentity ());
205
205
#endif
206
206
return result;
207
207
}
@@ -885,7 +885,7 @@ static Job *preprocessQueue(JobRef first,
885
885
886
886
void DefaultActorImpl::giveUpThread (RunningJobInfo runner) {
887
887
#if SWIFT_TASK_PRINTF_DEBUG
888
- fprintf (stderr, " %p %p.giveUpThread \n " , pthread_self (), this );
888
+ fprintf (stderr, " [%p] giving up thread for actor %p \n " , pthread_self (), this );
889
889
#endif
890
890
auto oldState = CurrentState.load (std::memory_order_acquire);
891
891
assert (oldState.Flags .getStatus () == Status::Running);
@@ -936,8 +936,8 @@ void DefaultActorImpl::giveUpThread(RunningJobInfo runner) {
936
936
}
937
937
938
938
#if SWIFT_TASK_PRINTF_DEBUG
939
- # define LOG_STATE_TRANSITION fprintf (stderr, " %p transition from %zx to %zx in %p.%s \n " , \
940
- pthread_self (), oldState.Flags.getOpaqueValue(), newState.Flags.getOpaqueValue(), this , __FUNCTION__)
939
+ # define LOG_STATE_TRANSITION fprintf (stderr, " [%p] actor %p transitioned from %zx to %zx (%s) \n " , \
940
+ pthread_self (), this, oldState.Flags.getOpaqueValue(), newState.Flags.getOpaqueValue(), __FUNCTION__)
941
941
#else
942
942
# define LOG_STATE_TRANSITION ((void )0 )
943
943
#endif
@@ -1141,6 +1141,34 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned,
1141
1141
}
1142
1142
}
1143
1143
1144
+ SWIFT_CC (swift)
1145
+ static void swift_job_runImpl (Job *job, ExecutorRef executor) {
1146
+ ExecutorTrackingInfo trackingInfo;
1147
+
1148
+ // swift_job_run is a primary entrypoint for executors telling us to
1149
+ // run jobs. Actor executors won't expect us to switch off them
1150
+ // during this operation. But do allow switching if the executor
1151
+ // is generic.
1152
+ if (!executor.isGeneric ()) trackingInfo.disallowSwitching ();
1153
+
1154
+ trackingInfo.enterAndShadow (executor);
1155
+
1156
+ runJobInEstablishedExecutorContext (job);
1157
+
1158
+ trackingInfo.leave ();
1159
+
1160
+ // Give up the current executor if this is a switching context
1161
+ // (which, remember, only happens if we started out on a generic
1162
+ // executor) and we've switched to a default actor.
1163
+ auto currentExecutor = trackingInfo.getActiveExecutor ();
1164
+ if (trackingInfo.allowsSwitching () && currentExecutor.isDefaultActor ()) {
1165
+ // Use an underestimated priority; if this means we create an
1166
+ // extra processing job in some cases, that's probably okay.
1167
+ auto runner = RunningJobInfo::forOther (JobPriority (0 ));
1168
+ asImpl (currentExecutor.getDefaultActor ())->giveUpThread (runner);
1169
+ }
1170
+ }
1171
+
1144
1172
// / The primary function for processing an actor on a thread. Start
1145
1173
// / processing the given default actor as the active default actor on
1146
1174
// / the current thread, and keep processing whatever actor we're
@@ -1154,15 +1182,18 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned,
1154
1182
static void processDefaultActor (DefaultActorImpl *currentActor,
1155
1183
RunningJobInfo runner) {
1156
1184
#if SWIFT_TASK_PRINTF_DEBUG
1157
- fprintf (stderr, " %p processDefaultActor %p\n " , pthread_self (), currentActor);
1185
+ fprintf (stderr, " [%p] processDefaultActor %p\n " , pthread_self (), currentActor);
1158
1186
#endif
1159
1187
DefaultActorImpl *actor = currentActor;
1160
1188
1161
- // Register that we're processing a default actor in this frame.
1189
+ // If we actually have work to do, we'll need to set up tracking info.
1190
+ // Optimistically assume that we will; the alternative (an override job
1191
+ // took over the actor first) is rare.
1162
1192
ExecutorTrackingInfo trackingInfo;
1163
- auto activeTrackingInfo = trackingInfo.enterOrUpdate (
1193
+ trackingInfo.enterAndShadow (
1164
1194
ExecutorRef::forDefaultActor (asAbstract (currentActor)));
1165
1195
1196
+ // Remember whether we've already taken over the actor.
1166
1197
bool threadIsRunningActor = false ;
1167
1198
while (true ) {
1168
1199
assert (currentActor);
@@ -1171,12 +1202,13 @@ static void processDefaultActor(DefaultActorImpl *currentActor,
1171
1202
if (shouldYieldThread ())
1172
1203
break ;
1173
1204
1174
- // Claim another job from the current actor.
1205
+ // Try to claim another job from the current actor, taking it over
1206
+ // if we haven't already.
1175
1207
auto job = currentActor->claimNextJobOrGiveUp (threadIsRunningActor,
1176
1208
runner);
1177
1209
1178
1210
#if SWIFT_TASK_PRINTF_DEBUG
1179
- fprintf (stderr, " %p processDefaultActor %p claimed job %p\n " , pthread_self (), currentActor, job);
1211
+ fprintf (stderr, " [%p] processDefaultActor %p claimed job %p\n " , pthread_self (), currentActor, job);
1180
1212
#endif
1181
1213
1182
1214
// If we failed to claim a job, we have nothing to do.
@@ -1187,15 +1219,18 @@ static void processDefaultActor(DefaultActorImpl *currentActor,
1187
1219
break ;
1188
1220
}
1189
1221
1222
+ // This thread now owns the current actor.
1223
+ threadIsRunningActor = true ;
1224
+
1190
1225
// Run the job.
1191
1226
runJobInEstablishedExecutorContext (job);
1192
1227
1193
1228
// The current actor may have changed after the job.
1194
1229
// If it's become nil, or not a default actor, we have nothing to do.
1195
- auto currentExecutor = activeTrackingInfo-> getActiveExecutor ();
1230
+ auto currentExecutor = trackingInfo. getActiveExecutor ();
1196
1231
1197
1232
#if SWIFT_TASK_PRINTF_DEBUG
1198
- fprintf (stderr, " %p processDefaultActor %p current executor now %p\n " , pthread_self (), currentActor, ( void *) currentExecutor.getRawValue ());
1233
+ fprintf (stderr, " [%p] processDefaultActor %p current executor now %p\n " , pthread_self (), currentActor, currentExecutor.getIdentity ());
1199
1234
#endif
1200
1235
1201
1236
if (!currentExecutor.isDefaultActor ()) {
@@ -1205,17 +1240,15 @@ static void processDefaultActor(DefaultActorImpl *currentActor,
1205
1240
break ;
1206
1241
}
1207
1242
currentActor = asImpl (currentExecutor.getDefaultActor ());
1208
-
1209
- // Otherwise, we know that we're running the actor on this thread.
1210
- threadIsRunningActor = true ;
1211
1243
}
1212
1244
1213
- if (activeTrackingInfo == &trackingInfo)
1214
- trackingInfo.leave ();
1245
+ // Leave the tracking info.
1246
+ trackingInfo.leave ();
1215
1247
1216
1248
// If we still have an active actor, we should give it up.
1217
- if (currentActor)
1249
+ if (threadIsRunningActor && currentActor) {
1218
1250
currentActor->giveUpThread (runner);
1251
+ }
1219
1252
1220
1253
swift_release (actor);
1221
1254
}
@@ -1386,7 +1419,14 @@ void swift::swift_MainActor_register(HeapObject *actor) {
1386
1419
/* ****************************************************************************/
1387
1420
1388
1421
// / Can the current executor give up its thread?
1389
- static bool canGiveUpThreadForSwitch (ExecutorRef currentExecutor) {
1422
+ static bool canGiveUpThreadForSwitch (ExecutorTrackingInfo *trackingInfo,
1423
+ ExecutorRef currentExecutor) {
1424
+ assert (trackingInfo || currentExecutor.isGeneric ());
1425
+
1426
+ // Some contexts don't allow switching at all.
1427
+ if (trackingInfo && !trackingInfo->allowsSwitching ())
1428
+ return false ;
1429
+
1390
1430
// We can certainly "give up" a generic executor to try to run
1391
1431
// a task for an actor.
1392
1432
if (currentExecutor.isGeneric ())
@@ -1439,31 +1479,38 @@ static bool tryAssumeThreadForSwitch(ExecutorRef newExecutor,
1439
1479
// / continue to run the given task on it.
1440
1480
SWIFT_CC (swiftasync)
1441
1481
static void runOnAssumedThread (AsyncTask *task, ExecutorRef executor,
1482
+ ExecutorTrackingInfo *oldTracking,
1442
1483
RunningJobInfo runner) {
1443
- // Set that this actor is now the active default actor on this thread,
1444
- // and set up tracking info if there isn't any already.
1445
- ExecutorTrackingInfo trackingInfo;
1446
- auto activeTrackingInfo = trackingInfo.enterOrUpdate (executor);
1484
+ // If there's alreaady tracking info set up, just change the executor
1485
+ // there and tail-call the task. We don't want these frames to
1486
+ // potentially accumulate linearly.
1487
+ if (oldTracking) {
1488
+ oldTracking->setActiveExecutor (executor);
1447
1489
1448
- // If one already existed, we should just tail-call the task; we don't
1449
- // want these frames to potentially accumulate linearly.
1450
- if (activeTrackingInfo != &trackingInfo) {
1451
1490
// FIXME: force tail call
1452
1491
return task->runInFullyEstablishedContext ();
1453
1492
}
1454
1493
1455
- // Otherwise, run the new task.
1494
+ // Otherwise, set up tracking info.
1495
+ ExecutorTrackingInfo trackingInfo;
1496
+ trackingInfo.enterAndShadow (executor);
1497
+
1498
+ // Run the new task.
1456
1499
task->runInFullyEstablishedContext ();
1457
1500
1458
1501
// Leave the tracking frame, and give up the current actor if
1459
1502
// we have one.
1460
1503
//
1461
- // In principle, we could execute more tasks here, but that's probably
1462
- // not a reasonable thing to do in an assumed context rather than a
1463
- // dedicated actor-processing job.
1504
+ // In principle, we could execute more tasks from the actor here, but
1505
+ // that's probably not a reasonable thing to do in an assumed context
1506
+ // rather than a dedicated actor-processing job.
1464
1507
executor = trackingInfo.getActiveExecutor ();
1465
1508
trackingInfo.leave ();
1466
1509
1510
+ #if SWIFT_TASK_PRINTF_DEBUG
1511
+ fprintf (stderr, " [%p] leaving assumed thread, current executor is %p\n " , pthread_self (), executor.getIdentity ());
1512
+ #endif
1513
+
1467
1514
if (executor.isDefaultActor ())
1468
1515
asImpl (executor.getDefaultActor ())->giveUpThread (runner);
1469
1516
}
@@ -1472,9 +1519,12 @@ SWIFT_CC(swiftasync)
1472
1519
static void swift_task_switchImpl (SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
1473
1520
TaskContinuationFunction *resumeFunction,
1474
1521
ExecutorRef newExecutor) {
1475
- auto currentExecutor = ExecutorTrackingInfo::getActiveExecutorInThread ();
1522
+ auto trackingInfo = ExecutorTrackingInfo::current ();
1523
+ auto currentExecutor =
1524
+ (trackingInfo ? trackingInfo->getActiveExecutor ()
1525
+ : ExecutorRef::generic ());
1476
1526
#if SWIFT_TASK_PRINTF_DEBUG
1477
- fprintf (stderr, " %p switch %p -> %p\n " , pthread_self (), ( void *) currentExecutor.getRawValue (), ( void *) newExecutor.getRawValue ());
1527
+ fprintf (stderr, " [%p] trying to switch from executor %p to %p\n " , pthread_self (), currentExecutor.getIdentity (), newExecutor.getIdentity ());
1478
1528
#endif
1479
1529
1480
1530
// If the current executor is compatible with running the new executor,
@@ -1499,16 +1549,22 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
1499
1549
// If the current executor can give up its thread, and the new executor
1500
1550
// can take over a thread, try to do so; but don't do this if we've
1501
1551
// been asked to yield the thread.
1502
- if (canGiveUpThreadForSwitch (currentExecutor) &&
1552
+ if (canGiveUpThreadForSwitch (trackingInfo, currentExecutor) &&
1503
1553
!shouldYieldThread () &&
1504
1554
tryAssumeThreadForSwitch (newExecutor, runner)) {
1555
+ #if SWIFT_TASK_PRINTF_DEBUG
1556
+ fprintf (stderr, " [%p] switch succeeded, task %p assumed thread for executor %p\n " , pthread_self (), task, newExecutor.getIdentity ());
1557
+ #endif
1505
1558
giveUpThreadForSwitch (currentExecutor, runner);
1506
1559
// FIXME: force tail call
1507
- return runOnAssumedThread (task, newExecutor, runner);
1560
+ return runOnAssumedThread (task, newExecutor, trackingInfo, runner);
1508
1561
}
1509
1562
1510
1563
// Otherwise, just asynchronously enqueue the task on the given
1511
1564
// executor.
1565
+ #if SWIFT_TASK_PRINTF_DEBUG
1566
+ fprintf (stderr, " [%p] switch failed, task %p enqueued on executor %p\n " , pthread_self (), task, newExecutor.getIdentity ());
1567
+ #endif
1512
1568
swift_task_enqueue (task, newExecutor);
1513
1569
}
1514
1570
@@ -1519,7 +1575,7 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
1519
1575
SWIFT_CC (swift)
1520
1576
static void swift_task_enqueueImpl (Job *job, ExecutorRef executor) {
1521
1577
#if SWIFT_TASK_PRINTF_DEBUG
1522
- fprintf (stderr, " %p enqueue %p \n " , pthread_self (), ( void *) executor.getRawValue ());
1578
+ fprintf (stderr, " [%p] enqueue job %p on executor %p \n " , pthread_self (), job, executor.getIdentity ());
1523
1579
#endif
1524
1580
1525
1581
assert (job && " no job provided" );
0 commit comments