@@ -1034,6 +1034,11 @@ class DefaultActorImplHeader : public HeapObject {
10341034 // synchronously, no job queue is needed and the lock will handle all priority
10351035 // escalation logic
10361036 Mutex drainLock;
1037+ // Actor can be deinitialized while lock is still being held.
1038+ // We maintain separate reference counter for the lock to make sure that
1039+ // lock is destroyed and memory is freed when both actor is deinitialized
1040+ // and lock is unlocked.
1041+ std::atomic<int > lockReferenceCount;
10371042#else
10381043 // Note: There is some padding that is added here by the compiler in order to
10391044 // enforce alignment. This is space that is available for us to use in
@@ -1130,6 +1135,7 @@ class DefaultActorImpl
11301135 this ->isDistributedRemoteActor = isDistributedRemote;
11311136#if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
11321137 new (&this ->drainLock ) Mutex ();
1138+ lockReferenceCount = 1 ;
11331139#else
11341140 _status ().store (ActiveActorStatus (), std::memory_order_relaxed);
11351141 new (&this ->prioritizedJobs ) PriorityQueue ();
@@ -1152,6 +1158,11 @@ class DefaultActorImpl
11521158 // / Unlock an actor
11531159 bool unlock (bool forceUnlock);
11541160
1161+ #if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
1162+ void retainLock ();
1163+ void releaseLock ();
1164+ #endif
1165+
11551166#if !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
11561167 // / Enqueue a job onto the actor.
11571168 void enqueue (Job *job, JobPriority priority);
@@ -1688,7 +1699,9 @@ void DefaultActorImpl::destroy() {
16881699}
16891700
16901701void DefaultActorImpl::deallocate () {
1691- #if !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
1702+ #if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
1703+ releaseLock ();
1704+ #else
16921705 // If we're running, mark ourselves as ready for deallocation but don't
16931706 // deallocate yet. When we stop running the actor - at unlock() time - we'll
16941707 // do the actual deallocation.
@@ -1704,8 +1717,8 @@ void DefaultActorImpl::deallocate() {
17041717 }
17051718
17061719 assert (oldState.isIdle ());
1707- #endif
17081720 deallocateUnconditional ();
1721+ #endif
17091722}
17101723
17111724void DefaultActorImpl::deallocateUnconditional () {
@@ -1718,6 +1731,7 @@ void DefaultActorImpl::deallocateUnconditional() {
17181731
17191732bool DefaultActorImpl::tryLock (bool asDrainer) {
17201733#if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
1734+ retainLock ();
17211735 this ->drainLock .lock ();
17221736 return true ;
17231737#else /* SWIFT_CONCURRENCY_ACTORS_AS_LOCKS */
@@ -1830,6 +1844,7 @@ bool DefaultActorImpl::unlock(bool forceUnlock)
18301844{
18311845#if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
18321846 this ->drainLock .unlock ();
1847+ releaseLock ();
18331848 return true ;
18341849#else
18351850 bool distributedActorIsRemote = swift_distributed_actor_is_remote (this );
@@ -1919,6 +1934,18 @@ bool DefaultActorImpl::unlock(bool forceUnlock)
19191934#endif
19201935}
19211936
1937+ #if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
1938+ void DefaultActorImpl::retainLock () {
1939+ lockReferenceCount.fetch_add (1 , std::memory_order_acquire);
1940+ }
1941+ void DefaultActorImpl::releaseLock () {
1942+ if (1 == lockReferenceCount.fetch_add (-1 , std::memory_order_release)) {
1943+ drainLock.~Mutex ();
1944+ deallocateUnconditional ();
1945+ }
1946+ }
1947+ #endif
1948+
19221949SWIFT_CC (swift)
19231950static void swift_job_runImpl (Job *job, SerialExecutorRef executor) {
19241951 ExecutorTrackingInfo trackingInfo;
@@ -2257,24 +2284,6 @@ static void swift_task_deinitOnExecutorImpl(void *object,
22572284 DeinitWorkFunction *work,
22582285 SerialExecutorRef newExecutor,
22592286 size_t rawFlags) {
2260- #if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
2261- // To properly support isolated deinit in this mode, we
2262- // need to be able to postpone deallocation of the lock
2263- // until actor is unlocked.
2264- //
2265- // Note that such zombie state probably should be supported regardless of the isolated deinit.
2266- // Theoretically it is possible for last release to happen in the middle of a job isolated to the actor.
2267- // But until isolated consuming parameters are fixed, this seems to be impossible to reproduce.
2268- // See also https://github.com/swiftlang/swift/issues/76083
2269- //
2270- // Alternatively we could lock and unlock before executing deinit body.
2271- // This would be sufficient to wait until all jobs isolated to the actor have finished.
2272- // Any code attempting to take actor's lock while deinit is running is incorrect anyway.
2273- // So it does not matter much if deinit body is executed with lock held or not.
2274- //
2275- // But this workaround applies only to the isolated deinit and does not solve the generic case.
2276- swift_Concurrency_fatalError (0 , " Isolated deinit is not yet supported in actor as locks model" );
2277- #else
22782287 // If the current executor is compatible with running the new executor,
22792288 // we can just immediately continue running with the resume function
22802289 // we were passed in.
@@ -2289,7 +2298,12 @@ static void swift_task_deinitOnExecutorImpl(void *object,
22892298 return work (object); // 'return' forces tail call
22902299 }
22912300
2301+ #if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
2302+ // In this mode taking actor lock is the only possible implementation
2303+ #else
2304+ // Otherwise, it is an optimisation applied when deinitializing default actors
22922305 if (newExecutor.isDefaultActor () && object == newExecutor.getIdentity ()) {
2306+ #endif
22932307 // Try to take the lock. This should always succeed, unless someone is
22942308 // running the actor using unsafe unowned reference.
22952309 if (asImpl (newExecutor.getDefaultActor ())->tryLock (false )) {
@@ -2329,9 +2343,13 @@ static void swift_task_deinitOnExecutorImpl(void *object,
23292343 // Give up the current actor.
23302344 asImpl (newExecutor.getDefaultActor ())->unlock (true );
23312345 return ;
2346+ } else {
2347+ #if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
2348+ assert (false && " Should not enqueue onto default actor in actor as locks model" );
2349+ #endif
23322350 }
2351+ #if !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
23332352 }
2334-
23352353 auto currentTask = swift_task_getCurrent ();
23362354 auto priority = currentTask ? swift_task_currentPriority (currentTask)
23372355 : swift_task_getCurrentThreadPriority ();
0 commit comments