Home > database >  Swift thread safe array
Swift thread safe array

Time:10-27

I want to create thread-safe array for piping data between threads

public class SyncArray<T> {
  
  public var dataArray = [T]()
  private var semaphore = DispatchSemaphore(value: 1)
  
  public init() {
    
  }
  
  private func wait() { semaphore.wait() }
  private func signal() { semaphore.signal() }
  
  public func count() -> Int {
    var count = 0;
    wait(); defer { signal() }
    count = dataArray.count
    return count;
  }
  
  public func unshift() -> T? {
    var firstEl:T? = nil
    wait(); defer { signal() }
    if(self.count() > 0){
      firstEl = dataArray.removeFirst()
    }
    return firstEl;
  }
  
  public func pop() -> T? {
    var lastEl:T? = nil
    wait(); defer { signal() }
    if(self.count() > 0){
      lastEl = dataArray.popLast()
    }
    return lastEl;
  }
  
  public func append(value: T) -> Void {
    wait(); defer { signal() }
    dataArray.append(value)
  }
}

Pipe data

   
    let buff = SyncArray<Container>()
   
    DispatchQueue.global().async {
      do {
        let dataSource = getDataSource()
        for i in 0 ..< dataSource.length{
          buff.append(value: dataSource[i])
        }
    }
    
    DispatchQueue.global().async {
      while(true) {
        let data = buff.unshift()
      }
    }

The idea is to pipe data between threads. For some reason buff.append and buff.unshift deadlocks eachother

i tried allso

 public func count() -> Int {
    wait();
    count = dataArray.count
    signal()
    return count;
  }

Same result. Please, advise what am I doing wrong. I feel the fix should be super simple. Thanks!

CodePudding user response:

Your problem is that unshift calls count. unshift is already holding the semaphore, but the first thing that count does is call wait, which causes a deadlock. You have the same problem in popLast.

Since you already have exclusive access to the array you can simply use its isEmpty property.

public func unshift() -> T? {
    var firstEl:T? = nil
    wait(); defer { signal() }
    if !dataArray.isEmpty {
      firstEl = dataArray.removeFirst()
    }
    return firstEl;
  }

public func pop() -> T? {
    var lastEl:T? = nil
    wait(); defer { signal() }
    if !dataArray.isEmpty {
      lastEl = dataArray.popLast()
    }
    return lastEl;
  }

You could also replace your DispatchSemaphore with a NSRecursiveLock since you don't need the counting behaviour of a semaphore. NSRecursiveLock can be locked multiple times by the same thread without causing a deadlock.

  • Related