| /* |
| * 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 <stdio.h> |
| #include <dispatch/dispatch.h> |
| #include <dispatch/private.h> |
| #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) |
| #include <unistd.h> |
| #endif |
| #include <stdlib.h> |
| #include <assert.h> |
| #ifdef __APPLE__ |
| #include <TargetConditionals.h> |
| #endif |
| #include <sys/types.h> |
| #ifdef __ANDROID__ |
| #include <linux/sysctl.h> |
| #else |
| #include <sys/sysctl.h> |
| #endif /* __ANDROID__ */ |
| |
| #include <bsdtests.h> |
| #include "dispatch_test.h" |
| |
| static volatile int done; |
| |
| #ifdef DISPATCH_QUEUE_PRIORITY_BACKGROUND // <rdar://problem/7439794> |
| #define USE_BACKGROUND_PRIORITY 1 |
| #else |
| #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN |
| #endif |
| |
| #define QUEUE_PRIORITY_PTHREAD INT_MAX |
| |
| #if DISPATCH_API_VERSION < 20100518 // <rdar://problem/7790099> |
| #define DISPATCH_QUEUE_CONCURRENT NULL |
| #endif |
| |
| #if TARGET_OS_EMBEDDED |
| #define LOOP_COUNT 5000000 |
| const int importance = 24; // priority 55 |
| #else |
| #define LOOP_COUNT 100000000 |
| const int importance = 4; // priority 35 |
| #endif |
| |
| char *labels[] = { "BACKGROUND", "LOW", "DEFAULT", "HIGH", }; |
| int levels[] = { |
| DISPATCH_QUEUE_PRIORITY_BACKGROUND, DISPATCH_QUEUE_PRIORITY_LOW, |
| DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_HIGH, |
| }; |
| #define PRIORITIES (sizeof(levels)/sizeof(*levels)) |
| |
| static union { |
| long count; |
| char padding[64]; |
| } counts[PRIORITIES]; |
| |
| static volatile long iterations; |
| static long total; |
| static size_t prio0, priorities = PRIORITIES; |
| |
| static int |
| n_blocks(void) |
| { |
| static dispatch_once_t pred; |
| static int n; |
| dispatch_once(&pred, ^{ |
| #ifdef __linux__ |
| n = (int)sysconf(_SC_NPROCESSORS_CONF); |
| #else |
| size_t l = sizeof(n); |
| int rc = sysctlbyname("hw.ncpu", &n, &l, NULL, 0); |
| assert(rc == 0); |
| #endif |
| n *= 32; |
| }); |
| return n; |
| } |
| |
| static void |
| histogram(void) |
| { |
| long completed = 0; |
| size_t x, y, i; |
| printf("\n"); |
| for (y = prio0; y < prio0 + priorities; ++y) { |
| printf("%s: %ld\n", labels[y], counts[y].count); |
| completed += counts[y].count; |
| |
| double fraction = (double)counts[y].count / (double)n_blocks(); |
| double value = fraction * (double)80; |
| for (x = 0; x < 80; ++x) { |
| printf("%s", (value > x) ? "*" : " "); |
| } |
| printf("\n"); |
| } |
| |
| test_long("blocks completed", completed, total); |
| for (i = prio0; i < prio0 + priorities; i++) { |
| if (levels[i] == DISPATCH_QUEUE_PRIORITY_HIGH) { |
| test_long_less_than_or_equal("high priority precedence", |
| counts[i-2].count, counts[i].count); |
| } |
| #if USE_BACKGROUND_PRIORITY |
| if (levels[i] == DISPATCH_QUEUE_PRIORITY_BACKGROUND) { |
| test_long_less_than_or_equal("background priority precedence", |
| counts[i].count, counts[i+2].count); |
| } |
| #endif |
| } |
| } |
| |
| static void |
| cpubusy(void* context) |
| { |
| if (done) return; |
| size_t idx; |
| for (idx = 0; idx < LOOP_COUNT; ++idx) { |
| if (done) break; |
| } |
| |
| volatile long *count = context; |
| long iterdone = __sync_sub_and_fetch(&iterations, 1); |
| |
| if (iterdone >= 0) { |
| __sync_add_and_fetch(count, 1); |
| if (!iterdone) { |
| __sync_add_and_fetch(&done, 1); |
| usleep(100000); |
| histogram(); |
| dispatch_time_t delay = DISPATCH_TIME_NOW; |
| dispatch_after(delay, dispatch_get_main_queue(), ^{ |
| test_stop(); |
| }); |
| } |
| } |
| } |
| |
| static void |
| submit_work(dispatch_queue_t queue, void* context) |
| { |
| int i; |
| |
| for (i = n_blocks(); i; --i) { |
| dispatch_async_f(queue, context, cpubusy); |
| } |
| |
| } |
| |
| int |
| main(int argc __attribute__((unused)), char* argv[] __attribute__((unused))) |
| { |
| dispatch_queue_t q[PRIORITIES]; |
| size_t i; |
| |
| #if !USE_BACKGROUND_PRIORITY |
| prio0++; |
| priorities--; |
| #endif |
| |
| iterations = total = ((int)priorities * n_blocks()) / 2; |
| |
| #if USE_SET_TARGET_QUEUE |
| dispatch_test_start("Dispatch Priority (Set Target Queue)"); |
| #else |
| dispatch_test_start("Dispatch Priority"); |
| #endif |
| |
| for (i = prio0; i < prio0 + priorities; i++) { |
| dispatch_queue_t rq = dispatch_get_global_queue(levels[i], 0); |
| #if USE_SET_TARGET_QUEUE |
| q[i] = dispatch_queue_create(labels[i], DISPATCH_QUEUE_CONCURRENT); |
| test_ptr_notnull("q[i]", q[i]); |
| assert(q[i]); |
| dispatch_suspend(q[i]); |
| dispatch_set_target_queue(q[i], rq); |
| if (DISPATCH_QUEUE_CONCURRENT != NULL) { |
| dispatch_queue_set_width(q[i], LONG_MAX); |
| } |
| dispatch_release(rq); |
| #else |
| q[i] = rq; |
| #endif |
| } |
| |
| for (i = prio0; i < prio0 + priorities; i++) { |
| submit_work(q[i], &counts[i].count); |
| } |
| |
| for (i = prio0; i < prio0 + priorities; i++) { |
| dispatch_resume(q[i]); |
| dispatch_release(q[i]); |
| } |
| |
| dispatch_main(); |
| |
| return 0; |
| } |