mxcl / PromiseKit

Promises for Swift & ObjC.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to achieve a barrier behavior with PromiseKit and DispatchQueue?

bvirlet opened this issue · comments

Hello,

I have several pieces of work that I need to dispatch on a queue. The queue is a concurrent queue. Some of the work can happen in parallel, but some work must act as a barrier. I'd think the Dispatch's .barrier flag could be useful. The difficulty for me is the interaction with PromiseKit.

With PromiseKit, tasks can be dispatched on queues, but that's just the first block. My tasks are all asynchronous, so I need a way to mark the task as still active in the concurrent queue until the promise resolves.

As a consequence, I'm using DispatchGroup to achieve this; I'm not sure I'm writing this in the best (or even correct) way and would love some advice. Hopefully, this may also help other folks with the same problem.

Barrier task:

// We start on the main queue
self.syncQueue.async(flags: [.barrier]) {
    let group = DispatchGroup()
    group.enter()
    
    syncPromise() # syncPromise() is not described here, it's an asynchronous promise doing some work.
        .ensure(on: .main) {
            group.leave()
            self.endBackgroundTask()
        }
        .done {
            doneBlock(.success)
        }
        .catch { error in
            doneBlock(.failure(error))
        }
    
    group.wait()
}

Concurrent tasks:

func promise() -> Promise<Void> {
    let group = DispatchGroup()

    group.enter()

    return firstly {
        return Promise<Void>()
    }.then(on: syncQueue) { _ -> Promise<Void> in
        // We want this block to keep a work item busy on the sync queue until the promise
        // finishes.

        let promise = self.downloadDocument(documentUID: self.documentUID)
            .ensure {
                group.leave()
            }
        group.wait()

        return promise
    }.ensure {
        self.retainCycle = nil
    }
}

Is this the best way to write this? I suppose there is no way to write this purely with PromiseKit? Am I falling into any traps?

Thanks a lot!


  • PromiseKit 6.13.3

Hi Bruno --

I don't think I'm getting a complete picture of what you need to do, but this line is sticking out to me:

With PromiseKit, tasks can be dispatched on queues, but that's just the first block.

Are you aware that in addition to on:, there is also an optional flags: argument to most promise methods? What prevents you from just dispatching with flags: [.barrier] at whatever step needs to run on its own within the context of the concurrent queue?

Is the DispatchGroup above just a mechanism to achieve a barrier effect, or do you also have an independent need to track the status of promises?

Hi @GarthSnyder,

Thanks for your reply!

The DispatchGroup above is just a mechanism to achieve a barrier effect.

From what I understand, if I use the flags: .barrier parameter for a Promise block, the barrier effect will only last for the duration of the block. In my case, I'd like the barrier effect to last until the promise created in that block resolves.

In other words, if I write this, I want longLastingPromise() to run non-concurrently until it resolves:

then(flags: .barrier) {
   longLastingPromise() // This is a Promise doing some asynchronous work
}.then { 
  // more work
}

If I understand correctly the code of Thenable, the .barrier flag will only apply to the block that creates the longLastingPromise().

Hence my use of the DispatchGroup to keep the block "busy" until the promise resolves. I suppose I could also use a DispatchSemaphore. I'm a bit afraid over overseeing an easier solution by relying on these lower-level constructs.

Hopefully I'm more clear.

Ah, got it, thanks for the clarification. Your understanding is correct.

What you've outlined in the first post above is essentially the implementation of wait() on a promise. Could you use that?

then(on: concurrentQueue, flags: .barrier) {
    Promise.value(longLastingPromise().wait())
}.then { 
    // more work
}

This form blocks the thread on which the barrier runs, but I would imagine that's fine given the context.

Hi @GarthSnyder,

Thanks! I didn't think about wait. This looks much cleaner. I cannot find the Promise constructor you are referring to, though. Is this something in PromiseKit 7?

Oops, sorry, I meant Promise.value(). I corrected above.

Feel free to reopen if you have other concerns.