| /* |
| * Copyright (c) 2008-2011 Apple Inc. All rights reserved. |
| * |
| * @APPLE_APACHE_LICENSE_HEADER_START@ |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * @APPLE_APACHE_LICENSE_HEADER_END@ |
| */ |
| |
| #include <dispatch/dispatch.h> |
| #include <unistd.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <assert.h> |
| #ifdef __APPLE__ |
| #include <libkern/OSAtomic.h> |
| #endif |
| #include <math.h> |
| #include <bsdtests.h> |
| #include "dispatch_test.h" |
| |
| #ifndef NSEC_PER_SEC |
| #define NSEC_PER_SEC 1000000000 |
| #endif |
| |
| #if TARGET_OS_EMBEDDED |
| #define LOOP_COUNT 50000 |
| #else |
| #define LOOP_COUNT 200000 |
| #endif |
| |
| static void test_group_notify(void*); |
| |
| static dispatch_group_t |
| create_group(size_t count, unsigned int delay) |
| { |
| size_t i; |
| |
| dispatch_group_t group = dispatch_group_create(); |
| |
| for (i = 0; i < count; ++i) { |
| dispatch_queue_t queue = dispatch_queue_create(NULL, NULL); |
| assert(queue); |
| |
| dispatch_group_async(group, queue, ^{ |
| if (delay) { |
| fprintf(stderr, "sleeping...\n"); |
| sleep(delay); |
| fprintf(stderr, "done.\n"); |
| } |
| }); |
| |
| dispatch_release(queue); |
| } |
| return group; |
| } |
| |
| static void |
| test_group(void *ctxt __attribute__((unused))) |
| { |
| long res; |
| dispatch_group_t group; |
| |
| group = create_group(100, 0); |
| test_ptr_notnull("dispatch_group_async", group); |
| |
| dispatch_group_wait(group, DISPATCH_TIME_FOREVER); |
| |
| // should be OK to re-use a group |
| dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{}); |
| dispatch_group_wait(group, DISPATCH_TIME_FOREVER); |
| |
| dispatch_release(group); |
| group = NULL; |
| |
| group = create_group(3, 7); |
| test_ptr_notnull("dispatch_group_async", group); |
| |
| res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC)); |
| test_long("dispatch_group_wait", !res, 0); |
| |
| // retry after timeout (this time succeed) |
| res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC)); |
| test_long("dispatch_group_wait", res, 0); |
| |
| dispatch_release(group); |
| group = NULL; |
| |
| group = create_group(100, 0); |
| test_ptr_notnull("dispatch_group_async", group); |
| |
| dispatch_group_notify(group, dispatch_get_main_queue(), ^{ |
| dispatch_test_current("Notification Received", dispatch_get_main_queue()); |
| dispatch_async_f(dispatch_get_main_queue(), NULL, test_group_notify); |
| }); |
| |
| dispatch_release(group); |
| group = NULL; |
| } |
| |
| static long completed; |
| |
| static void |
| test_group_notify2(long cycle, dispatch_group_t tested) |
| { |
| static dispatch_queue_t rq, nq; |
| static dispatch_once_t once; |
| dispatch_once(&once, ^{ |
| rq = dispatch_queue_create("release", 0); |
| dispatch_suspend(rq); |
| nq = dispatch_queue_create("notify", 0); |
| }); |
| dispatch_resume(rq); |
| |
| // n=4 works great for a 4CPU Mac Pro, this might work for a wider range of |
| // systems. |
| const int n = 1 + arc4random() % 8; |
| dispatch_group_t group = dispatch_group_create(); |
| dispatch_queue_t qa[n]; |
| |
| dispatch_group_enter(group); |
| for (int i = 0; i < n; i++) { |
| char buf[48]; |
| sprintf(buf, "T%ld-%d", cycle, i); |
| qa[i] = dispatch_queue_create(buf, 0); |
| } |
| |
| __block float eh = 0; |
| for (int i = 0; i < n; i++) { |
| dispatch_queue_t q = qa[i]; |
| dispatch_group_async(group, q, ^{ |
| // Seems to trigger a little more reliably with some work being |
| // done in this block |
| assert(cycle && "cycle must be non-zero"); |
| eh = (float)sin(M_1_PI / cycle); |
| }); |
| } |
| dispatch_group_leave(group); |
| |
| dispatch_group_notify(group, nq, ^{ |
| completed = cycle; |
| dispatch_group_leave(tested); |
| }); |
| |
| // Releasing qa's queues here seems to avoid the race, so we are arranging |
| // for the current iteration's queues to be released on the next iteration. |
| dispatch_sync(rq, ^{}); |
| dispatch_suspend(rq); |
| for (int i = 0; i < n; i++) { |
| dispatch_queue_t q = qa[i]; |
| dispatch_async(rq, ^{ dispatch_release(q); }); |
| } |
| dispatch_release(group); |
| } |
| |
| static void |
| test_group_notify(void *ctxt __attribute__((unused))) |
| { |
| // <rdar://problem/11445820> |
| dispatch_group_t tested = dispatch_group_create(); |
| test_ptr_notnull("dispatch_group_create", tested); |
| long i; |
| for (i = 1; i <= LOOP_COUNT; i++) { |
| if (!(i % (LOOP_COUNT/10))) { |
| fprintf(stderr, "#%ld\n", i); |
| } |
| dispatch_group_enter(tested); |
| test_group_notify2(i, tested); |
| if (dispatch_group_wait(tested, dispatch_time(DISPATCH_TIME_NOW, |
| NSEC_PER_SEC))) { |
| break; |
| } |
| } |
| test_long("dispatch_group_notify", i - 1, LOOP_COUNT); |
| test_stop(); |
| } |
| |
| int |
| main(void) |
| { |
| dispatch_test_start("Dispatch Group"); |
| dispatch_async_f(dispatch_get_main_queue(), NULL, test_group); |
| dispatch_main(); |
| |
| return 0; |
| } |