0

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!

2
  • I'd put a wrapper around NSLock Commented Oct 26, 2021 at 19:44
  • @Rob do you know how to correctly wrap shared resources in NSLock? my previous version was through three NSLocks but i got SIGSEGV error because append and pop used different locks Commented Oct 26, 2021 at 19:58

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.