2

how'd you apply thread safe functionality to static functions of a struct

class SingleSome {

    struct Static {
        private static var instance: SingleSome?

        //need barrier sync
        static func getInstance(block: () -> SingleSome) -> SingleSome {
            if instance == nil {
                instance = block()
            }
            return instance!
        }

        static func remove() { //need barrier sync
            instance = nil
        }
    }

}

reason a block was used as param as there could be inherited objects of SingleSome

user2727195
  • 7,122
  • 17
  • 70
  • 118

2 Answers2

3

You can use a private serial queue to ensure that only one thread can be in any of the critical sections at any instant.

class SingleSome {

    struct Static {
        private static let queue = dispatch_queue_create("SingleSome.Static.queue", nil)
        private static var instance: SingleSome?

        static func getInstance(block: () -> SingleSome) -> SingleSome {
            var myInstance: SingleSome?
            dispatch_sync(queue) {
                if self.instance == nil {
                    self.instance = block()
                }
                myInstance = self.instance
            }
            // This return has to be outside the dispatch_sync block,
            // so there's a race condition if I return instance directly.
            return myInstance!
        }

        static func remove() {
            dispatch_sync(queue) {
                self.instance = nil
            }
        }
    }

}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • will there be a problem with memory management since `self` is getting captured inside the block – user2727195 Jan 08 '15 at 18:36
  • No, there is no problem because the block is short-lived. – rob mayoff Jan 08 '15 at 19:12
  • please have a look at the linked question as this question was based on it, and have used `[weak self]` even for short lived blocks, http://stackoverflow.com/questions/27822117/thread-safe-access-to-a-variable-in-a-class?lq=1 – user2727195 Jan 08 '15 at 19:30
  • Since these are static functions, `self` refers to the class object. The class object won't be deallocated, which means you don't need to worry about using `weak self`. – rob mayoff Jan 08 '15 at 19:38
  • just in case, if this `self` was inside a block containing in a class, we are supposed to `[unowned self]` or `[weak self]`, right? – user2727195 Jan 08 '15 at 20:46
  • 2
    I don't know what "inside a block containing a class" means. In general you want to think about whether you want a strong or weak reference. The rule isn't “always use weak”. The rule is “use strong if you want `self` to live as least as long as the block, and weak if `self` should be allowed to die before the block dies.” – rob mayoff Jan 08 '15 at 21:42
  • I see, so either weak or strong reference are there just to hold on to the `self` for the lifetime of the block, and if block ends, this strong or weak reference dies as well (please confirm), and let's say there's one single line of code inside the block and is executed immediately, I can choose not to have single or weak declaration (because the object is live cause it called it, and the captured self will die once block ends, and there won't be memory leak if the caller object dies after the block ends, please also confirm this) – user2727195 Jan 08 '15 at 22:27
  • by immediate call I mean `dispatch_sync` compared to an async call `dispatch_async` – user2727195 Jan 08 '15 at 23:14
0

Use a semaphore, dispatch_sync isn't appropriate because you need a synchronous return value from getInstance:

class SingleSome {

    struct Static {
        private static var instance: SingleSome?
        private static let lock = dispatch_semaphore_create(1)

        //need barrier sync
        static func getInstance(block: () -> SingleSome) -> SingleSome {
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER)
            var value = instance
            if value == nil {
                instance = block()
                value = instance
            }
            dispatch_semaphore_signal(lock)
            return value!
        }

        static func remove() { //need barrier sync
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER)
            instance = nil
            dispatch_semaphore_signal(lock)
        }
    }

}

Also note that as written this is subject to deadlocks if block results in either remove or getInstance being called as dispatch_semaphore_t is not thread recursive.

David Berry
  • 40,941
  • 12
  • 84
  • 95