Waiting until two async blocks are executed before starting another block
Asked Answered
P

10

203

When using GCD, we want to wait until two async blocks are executed and done before moving on to the next steps of execution. What is the best way to do that?

We tried the following, but it doesn't seem to work:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
});


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
});

// wait until both the block1 and block2 are done before start block3
// how to do that?

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
});
Peng answered 10/8, 2012 at 21:40 Comment(1)
See my answer for Swift 5 that offers up to six different ways to solve your problem.Hackler
F
321

Use dispatch groups: see here for an example, "Waiting on Groups of Queued Tasks" in the "Dispatch Queues" chapter of Apple's iOS Developer Library's Concurrency Programming Guide

Your example could look something like this:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
    NSLog(@"Block1");
    [NSThread sleepForTimeInterval:5.0];
    NSLog(@"Block1 End");
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
    NSLog(@"Block2");
    [NSThread sleepForTimeInterval:8.0];
    NSLog(@"Block2 End");
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
    NSLog(@"Block3");
});

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group);

and could produce output like this:

2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3
Foot answered 10/8, 2012 at 22:5 Comment(8)
Cool. will the async tasks/blocks, once associated with the group, be executed sequentially or concurrently? I mean, assume that block1 and block2 are associated with a group now, will block2 wait until block1 is done before it can start executing?Peng
That's up to you. dispatch_group_async is just like dispatch_asyncwith a group parameter added. So if you use different queues for block1 and block2 or schedule them on the same concurrent queue, they can run concurrently; if you schedule them on the same serial queue, they'll run serially. It's no different from scheduling the blocks without groups.Arras
Does this also apply to executing web service post?Milliary
Do you notice that the time is not equal to the sleep time set in your block? why it would be like this?Lib
In ARC just remove dispatch_release(group);Vitrification
what will happen if I does not write this line [NSThread sleepForTimeInterval:8.0];Ailssa
AND how does it distinguish that this is block 2 and the other one is block 1? how can we prioritize them?Noodle
ARC forbids explicit message send of 'release'Midwife
N
286

Expanding on Jörn Eyrich answer (upvote his answer if you upvote this one), if you do not have control over the dispatch_async calls for your blocks, as might be the case for async completion blocks, you can use the GCD groups using dispatch_group_enter and dispatch_group_leave directly.

In this example, we're pretending computeInBackground is something we cannot change (imagine it is a delegate callback, NSURLConnection completionHandler, or whatever), and thus we don't have access to the dispatch calls.

// create a group
dispatch_group_t group = dispatch_group_create();

// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);     // pair 1 enter
[self computeInBackground:1 completion:^{
    NSLog(@"1 done");
    dispatch_group_leave(group); // pair 1 leave
}];

// again... (and again...)
dispatch_group_enter(group);     // pair 2 enter
[self computeInBackground:2 completion:^{
    NSLog(@"2 done");
    dispatch_group_leave(group); // pair 2 leave
}];

// Next, setup the code to execute after all the paired enter/leave calls.
//
// Option 1: Get a notification on a block that will be scheduled on the specified queue:
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"finally!");
});

// Option 2: Block an wait for the calls to complete in code already running
// (as cbartel points out, be careful with running this on the main/UI queue!):
//
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // blocks current thread
// NSLog(@"finally!");

In this example, computeInBackground:completion: is implemented as:

- (void)computeInBackground:(int)no completion:(void (^)(void))block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"%d starting", no);
        sleep(no*2);
        block();
    });
}

Output (with timestamps from a run):

12:57:02.574  2 starting
12:57:02.574  1 starting
12:57:04.590  1 done
12:57:06.590  2 done
12:57:06.591  finally!
Narcoanalysis answered 3/1, 2014 at 18:47 Comment(6)
@Narcoanalysis The above code waits on the main thread. I believe this will block the main thread and cause the UI to be unresponsive until the whole group is complete. I recommend moving the wait to a background thread. For example, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)Bligh
@cbartel, good catch! I've updated the example code to reflect your comment. Many times you need the callback to be on the main queue--in that case though the dispatch_queue_notify is likely better (unless the blocking time is guaranteed to be short).Offing
Where can I release the group ( i.e. dispatch_release(group) )? I'm unsure if it's safe to release in dispatch_group_notify. But since that's the code that is run after the group is complete, I'm unsure where to release.Rooky
If you are using ARC then you do not need to call dispatch_release: #8619132Offing
If not using ARC, then "it is valid to release the group after setting a notification block" developer.apple.com/library/mac/documentation/Darwin/Reference/…Offing
Nice post which further explains that: commandshift.co.uk/blog/2014/03/19/…Franci
H
126

With Swift 5.1, Grand Central Dispatch offers many ways to solve your problem. According to your needs, you may choose one of the seven patterns shown in the following Playground snippets.


#1. Using DispatchGroup, DispatchGroup's notify(qos:flags:queue:execute:) and DispatchQueue's async(group:qos:flags:execute:)

The Apple Developer Concurrency Programming Guide states about DispatchGroup:

Dispatch groups are a way to block a thread until one or more tasks finish executing. You can use this behavior in places where you cannot make progress until all of the specified tasks are complete. For example, after dispatching several tasks to compute some data, you might use a group to wait on those tasks and then process the results when they are done.

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

queue.async(group: group) {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async(group: group) {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

group.notify(queue: queue) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

#2. Using DispatchGroup, DispatchGroup's wait(), DispatchGroup's enter() and DispatchGroup's leave()

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

group.enter()
queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    group.leave()
}

group.enter()
queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    group.leave()
}

queue.async {
    group.wait()
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

Note that you can also mix DispatchGroup wait() with DispatchQueue async(group:qos:flags:execute:) or mix DispatchGroup enter() and DispatchGroup leave() with DispatchGroup notify(qos:flags:queue:execute:).


#3. Using Dispatch​Work​Item​Flags barrier and DispatchQueue's async(group:qos:flags:execute:)

Grand Central Dispatch Tutorial for Swift 4: Part 1/2 article from Raywenderlich.com gives a definition for barriers:

Dispatch barriers are a group of functions acting as a serial-style bottleneck when working with concurrent queues. When you submit a DispatchWorkItem to a dispatch queue you can set flags to indicate that it should be the only item executed on the specified queue for that particular time. This means that all items submitted to the queue prior to the dispatch barrier must complete before the DispatchWorkItem will execute.

Usage:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

queue.async(flags: .barrier) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

#4. Using DispatchWorkItem, Dispatch​Work​Item​Flags's barrier and DispatchQueue's async(execute:)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .barrier) {
    print("#3 finished")
}

queue.async(execute: dispatchWorkItem)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

#5. Using DispatchSemaphore, DispatchSemaphore's wait() and DispatchSemaphore's signal()

Soroush Khanlou wrote the following lines in The GCD Handbook blog post:

Using a semaphore, we can block a thread for an arbitrary amount of time, until a signal from another thread is sent. Semaphores, like the rest of GCD, are thread-safe, and they can be triggered from anywhere. Semaphores can be used when there’s an asynchronous API that you need to make synchronous, but you can’t modify it.

Apple Developer API Reference also gives the following discussion for DispatchSemaphore init(value:​) initializer:

Passing zero for the value is useful for when two threads need to reconcile the completion of a particular event. Passing a value greater than zero is useful for managing a finite pool of resources, where the pool size is equal to the value.

Usage:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 0)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    semaphore.signal()
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    semaphore.signal()
}

queue.async {
    semaphore.wait()
    semaphore.wait()    
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

#6. Using OperationQueue and Operation's addDependency(_:)

The Apple Developer API Reference states about Operation​Queue:

Operation queues use the libdispatch library (also known as Grand Central Dispatch) to initiate the execution of their operations.

Usage:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let operationQueue = OperationQueue()

let blockOne = BlockOperation {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

let blockTwo = BlockOperation {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let blockThree = BlockOperation {
    print("#3 finished")
}

blockThree.addDependency(blockOne)
blockThree.addDependency(blockTwo)

operationQueue.addOperations([blockThree, blockTwo, blockOne], waitUntilFinished: false)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */

#7. Using OperationQueue and OperationQueue's addBarrierBlock(_:) (requires iOS 13)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let operationQueue = OperationQueue()

let blockOne = BlockOperation {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

let blockTwo = BlockOperation {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

operationQueue.addOperations([blockTwo, blockOne], waitUntilFinished: false)
operationQueue.addBarrierBlock {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */
Hackler answered 25/3, 2017 at 23:0 Comment(1)
Is there a solution for async calls without using group.enter() and group.leave() for each (and without semaphores)? Like If I need to wait for an async request to a server, then after that wait for a second async request and so on. I've read this article avanderlee.com/swift/asynchronous-operations but I don's see a simple usage of it comparing to BlockOperationQuadriplegia
K
57

Another GCD alternative is a barrier:

dispatch_queue_t queue = dispatch_queue_create("com.company.app.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{ 
    NSLog(@"start one!\n");  
    sleep(4);  
    NSLog(@"end one!\n");
});

dispatch_async(queue, ^{  
    NSLog(@"start two!\n");  
    sleep(2);  
    NSLog(@"end two!\n"); 
});

dispatch_barrier_async(queue, ^{  
    NSLog(@"Hi, I'm the final block!\n");  
});

Just create a concurrent queue, dispatch your two blocks, and then dispatch the final block with barrier, which will make it wait for the other two to finish.

Kerikeriann answered 25/10, 2013 at 3:27 Comment(2)
Is there any problem if I did not use sleep(4);Ailssa
No, of course, there is no problem with that. In fact, you practically never want to sleep()! I only added those sleep() calls for pedagogic reasons, to make the blocks run long enough so that you can see that they run concurrently. In this trivial example, in the absence of sleep(), these two blocks may run so quickly that the dispatched block might start and finish before you have a chance to empirically observe the concurrent execution. But don’t sleep() in your own code.Kerikeriann
K
40

I know you asked about GCD, but if you wanted, NSOperationQueue also handles this sort of stuff really gracefully, e.g.:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 3");
}];

NSOperation *operation;

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 1");
    sleep(7);
    NSLog(@"Finishing 1");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 2");
    sleep(5);
    NSLog(@"Finishing 2");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

[queue addOperation:completionOperation];
Kerikeriann answered 26/1, 2013 at 19:43 Comment(12)
This is fine when the code inside of your NSBlockOperation is synchronous. But what if it's not, and you want to trigger completion when your async operation is done?Rennie
@GregMaletic In that case, I make a NSOperation subclass that is concurrent and set isFinished when the asynchronous process completes. Then the dependencies work fine.Kerikeriann
@GregMaletic See #18429511 and #17427355 for examples.Kerikeriann
Thanks for the suggestion! I decided to go with GCD plus a dispatch_semaphore to notify when the async operations terminate.Rennie
@GregMaletic Yeah, you can use that, too (as long as dispatch_semaphore_wait is not taking place on the main queue and as long as your signals and waits are balanced). As long as your don't block the main queue, a semaphore approach is fine, if you don't need the flexibility of operations (e.g. having the ability to cancel them, ability to control degree of concurrency, etc).Kerikeriann
@Rob: Can you please elaborate or update your answer with example of running the code above with asynchronous blocks? I would like to wait for operation1 & operation2 (both are async.) before executing operation 3. ThanksTheosophy
@Kerikeriann I posted a question so you can get credit. Thanks. #19920271Theosophy
@Theosophy You either should (a) use semaphores; (b) use the above NSOperation approach, but use synchronous renditions of your operations; or (c) wrap your asynchronous operation in a concurrent NSOperation that will only signal isFinished when the asynchronous request is done. See my answer posted at that other question.Kerikeriann
And how do you make the priority between task one and two?Noodle
@Noodle - If you need task one to finish before task two starts, add a dependency between those tasks. Or if the queue is always only performing one task at a time, make it a serial queue by setting maxConcurrentOperationCount to 1. You can set priority of operations, too, both the qualityOfService and queuePriority, but these have a far more subtle impact on task priority than the dependencies and/or degree of queue concurrency.Kerikeriann
I see, and yes my tasks should be serial and be done on main thread so I guess Ill do what you said, Thank you @KerikeriannNoodle
Whether the tasks should be serial and whether you do it on the main thread are two different questions. You can have serial background queues, too (e.g. an operation queue with a maxConcurrentQueueCount of 1). And you presumably had a reason to move these tasks off of the main thread to start with...Kerikeriann
T
4

Answers above are all cool, but they all missed one thing. group executes tasks(blocks) in the thread where it entered when you use dispatch_group_enter/dispatch_group_leave.

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      dispatch_async(demoQueue, ^{
        dispatch_group_t demoGroup = dispatch_group_create();
        for(int i = 0; i < 10; i++) {
          dispatch_group_enter(demoGroup);
          [self testMethod:i
                     block:^{
                       dispatch_group_leave(demoGroup);
                     }];
        }

        dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
          NSLog(@"All group tasks are done!");
        });
      });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

this runs in the created concurrent queue demoQueue. If i dont create any queue, it runs in main thread.

- (IBAction)buttonAction:(id)sender {
    dispatch_group_t demoGroup = dispatch_group_create();
    for(int i = 0; i < 10; i++) {
      dispatch_group_enter(demoGroup);
      [self testMethod:i
                 block:^{
                   dispatch_group_leave(demoGroup);
                 }];
    }

    dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
      NSLog(@"All group tasks are done!");
    });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

and there's a third way to make tasks executed in another thread:

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      //  dispatch_async(demoQueue, ^{
      __weak ViewController* weakSelf = self;
      dispatch_group_t demoGroup = dispatch_group_create();
      for(int i = 0; i < 10; i++) {
        dispatch_group_enter(demoGroup);
        dispatch_async(demoQueue, ^{
          [weakSelf testMethod:i
                         block:^{
                           dispatch_group_leave(demoGroup);
                         }];
        });
      }

      dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
        NSLog(@"All group tasks are done!");
      });
      //  });
    }

Of course, as mentioned you can use dispatch_group_async to get what you want.

Teniers answered 24/2, 2017 at 9:58 Comment(0)
R
3

The first answer is essentially correct, but if you want the very simplest way to accomplish the desired result, here's a stand-alone code example demonstrating how to do it with a semaphore (which is also how dispatch groups work behind the scenes, JFYI):

#include <dispatch/dispatch.h>
#include <stdio.h>

main()
{
        dispatch_queue_t myQ = dispatch_queue_create("my.conQ", DISPATCH_QUEUE_CONCURRENT);
        dispatch_semaphore_t mySem = dispatch_semaphore_create(0);

        dispatch_async(myQ, ^{ printf("Hi I'm block one!\n"); sleep(2); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ printf("Hi I'm block two!\n"); sleep(4); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER); printf("Hi, I'm the final block!\n"); });
        dispatch_main();
}
Rataplan answered 11/8, 2012 at 22:1 Comment(2)
Two observations: 1. You're missing a dispatch_semaphore_wait. You have two signals, so you need two waits. As is, your "completion" block will start as soon as the first block signals the semaphore, but before the other block finishes; 2. Given this was an iOS question, I'd discourage the use of dispatch_main.Kerikeriann
I agree with Rob. This is not a valid solution. The dispatch_semaphore_wait will unblock as soon as either of the dispatch_semaphore_signal methods are called. The reason this may appear to work is that the printf for blocks 'one' and 'two' occur immediately, and the printf for the 'finally' occurs after a wait--thus after the block one has slept for 2 seconds. If you put the printf after the sleep calls, you'll get the output for 'one', then 2 seconds later for 'finally', then 2 seconds later for 'two'.Offing
K
0

Swift 4.2 example:

let group = DispatchGroup.group(count: 2)
group.notify(queue: DispatchQueue.main) {
     self.renderingLine = false
     // all groups are done
}
DispatchQueue.main.async {
    self.renderTargetNode(floorPosition: targetPosition, animated: closedContour) {
        group.leave()
        // first done
    }
    self.renderCenterLine(position: targetPosition, animated: closedContour) {
        group.leave()
        // second done
    }
 }
Koehn answered 19/1, 2019 at 19:44 Comment(1)
group.leave() caused crashCartan
L
0

Accepted answer in swift:

let group = DispatchGroup()

group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block1
    print("Block1")
    Thread.sleep(forTimeInterval: 5.0)
    print("Block1 End")
})


group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block2
    print("Block2")
    Thread.sleep(forTimeInterval: 8.0)
    print("Block2 End")
})

dispatch_group_notify(group, DispatchQueue.global(qos: .default), {
    // block3
    print("Block3")
})

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group)
Lalitta answered 18/7, 2019 at 3:34 Comment(0)
H
-4

Not to say other answers are not great for certain circumstances, but this is one snippet I always user from Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


    if (signInDoneSel) {
        [self performSelector:signInDoneSel];
    }

}
Hazel answered 8/5, 2016 at 6:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.