blob: c500e8771857bb6188354425d11a1ca00780dd96 [file] [log] [blame]
/*
* 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;
}