Uploaded image for project: 'Swift'
  1. Swift
  2. SR-13628

Deadlock when using the print function

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: None
    • Labels:
      None
    • Environment:

      MacBook Pro late 2013.

      macOS Catalina 10.15.6

      Swift 5.3

      Description

      I found this really strange bug in which the print function causes a deadlock when called within a dispatch queue.

      The deadlock occurs at the line 

      print("AuthorizationManager.description: BEFORE queue")
      

      in AuthorizationManager.description, which is indirectly called by

      print(self)
      

      Intriguingly, changing the above line to any of the following prevents the deadlock:

       print(self.description)
       print("\(self)")
       print(self, to: &self.someString)
      

      Furthermore, removing the line

      print("Refreshing")
      

      also prevents the deadlock.

      I suspect that this bug is related to the fact that the print function is synchronized.

      I also tested out other locking mechanisms instead of DispatchQueue, including NSLockNSRecursiveLock and even this fancy implementation from swift-log, and I experienced the exact same bug.

       

      I posted this issue on the swift forums, and a commenter summarized the cause of the bug as follows:

      So I found that for a dead lock to happen, you need to repeatedly and concurrently do the following:

      1. You run a block in dispatchQueue,
      2. The block prints anything [In this case it is the line `print("Refreshing")`],
      3. The block runs print(X) on a separated concurrent queue, where
        • X is CustomStringConvertible that uses dispatchQueue inside description.

       

      Here is the code that causes the issue:

      import Foundation
      
      class AuthorizationManager: CustomStringConvertible {
          
          let dispatchQueue = DispatchQueue(label: "AuthorizationManager")
      
          var description: String {
              print("AuthorizationManager.description: BEFORE queue")
              // MARK: - Deadlock Occurs Here -
              return dispatchQueue.sync {
                  print("AuthorizationManager.description: INSIDE queue")
                  return "I am an authorization manager"
              }
          }
      
          func refreshTokens() {
              dispatchQueue.sync {
                  print("Refreshing")
              }
              DispatchQueue.global().async {
                  print(self)
                  
              }
          }
          
      }
      
      /// Entry Point.
      func testDeadlock2() {
      
          let authorizationManager = AuthorizationManager()
          
          for i in 0..<100 {
              print(i)
              authorizationManager.refreshTokens()
          }
          
          print("\n--- Finished ---\n")
          
      }

       
       

        Attachments

          Activity

            People

            Assignee:
            Unassigned Unassigned
            Reporter:
            Peter_Schorn Peter Schorn
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated: