Add back test cases

This commit is contained in:
Thomas A 2022-03-26 14:41:39 -07:00
parent 5490af73c1
commit 4ae5219973
62 changed files with 11290 additions and 0 deletions

44
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,44 @@
*.o
*.lo
*.la
*.log
*.trs
.libs
bsdtestharness
bsdtestsummarize
dispatch
dispatch_after
dispatch_api
dispatch_apply
dispatch_c99
dispatch_cascade
dispatch_concur
dispatch_context_for_key
dispatch_data
dispatch_debug
dispatch_drift
dispatch_group
dispatch_io
dispatch_io_net
dispatch_overcommit
dispatch_pingpong
dispatch_plusplus
dispatch_priority
dispatch_priority2
dispatch_queue_finalizer
dispatch_read
dispatch_read2
dispatch_readsync
dispatch_select
dispatch_sema
dispatch_starfish
dispatch_suspend_timer
dispatch_timer
dispatch_timer_bit31
dispatch_timer_bit63
dispatch_timer_set_time
dispatch_timer_short
dispatch_timer_timeout
dispatch_vnode
leaks-wrapper

199
tests/CMakeLists.apple.txt Normal file
View File

@ -0,0 +1,199 @@
if(CMAKE_SYSTEM_NAME STREQUAL Windows)
execute_process(COMMAND
"${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/private"
"${CMAKE_CURRENT_BINARY_DIR}/dispatch")
execute_process(COMMAND
"${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/leaks-wrapper.sh"
"${CMAKE_CURRENT_BINARY_DIR}/leaks-wrapper")
else()
execute_process(COMMAND
"${CMAKE_COMMAND}" -E create_symlink "${PROJECT_SOURCE_DIR}/private"
"${CMAKE_CURRENT_BINARY_DIR}/dispatch")
execute_process(COMMAND
"${CMAKE_COMMAND}" -E create_symlink "${CMAKE_CURRENT_SOURCE_DIR}/leaks-wrapper.sh"
"${CMAKE_CURRENT_BINARY_DIR}/leaks-wrapper")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt")
endif()
add_library(bsdtests
STATIC
bsdtests.c
dispatch_test.c)
target_include_directories(bsdtests
PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}
PUBLIC
# bsdtests.h needs config_ac.h
${PROJECT_BINARY_DIR})
if (WIN32)
target_sources(bsdtests
PRIVATE
generic_win_port.c)
target_compile_definitions(bsdtests
PUBLIC
_CRT_NONSTDC_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS
_USE_MATH_DEFINES)
target_link_libraries(bsdtests
PUBLIC
bcrypt)
endif ()
add_executable(bsdtestharness
bsdtestharness.c)
target_include_directories(bsdtestharness
PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_SOURCE_DIR})
target_link_libraries(bsdtestharness
PRIVATE
bsdtests
dispatch)
function(add_unit_test name)
set(options DISABLED_TEST)
set(single_value_args)
set(multiple_value_args SOURCES)
cmake_parse_arguments(AUT "${options}" "${single_value_args}" "${multiple_value_args}" ${ARGN})
if(AUT_DISABLED_TEST)
return()
endif()
add_executable(${name} ${AUT_SOURCES})
target_include_directories(${name}
PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_SOURCE_DIR})
if(ENABLE_SWIFT)
# For testing in swift.org CI system; make deadlines lenient by default
# to reduce probability of test failures due to machine load.
target_compile_options(${name} PRIVATE -DLENIENT_DEADLINES=1)
endif()
target_include_directories(${name}
SYSTEM BEFORE PRIVATE
"${BlocksRuntime_INCLUDE_DIR}")
if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC")
target_compile_options(${name} PRIVATE -Xclang -fblocks)
target_compile_options(${name} PRIVATE /W3 -Wno-deprecated-declarations)
else()
target_compile_options(${name} PRIVATE -fblocks)
target_compile_options(${name} PRIVATE -Wall -Wno-deprecated-declarations)
endif()
# Without this flag, cross-compiling static test executables for Android armv7
# fails with the multiple definition errors seen in android/ndk#176, so I
# pulled in this workaround noted there. The tests build and run with this
# flag applied.
if(NOT BUILD_SHARED_LIBS AND CMAKE_SYSTEM_NAME STREQUAL Android AND
CMAKE_SYSTEM_PROCESSOR STREQUAL armv7-a)
target_link_options(${name} PRIVATE "LINKER:--allow-multiple-definition")
endif()
target_link_libraries(${name}
PRIVATE
dispatch
Threads::Threads
BlocksRuntime::BlocksRuntime)
target_link_libraries(${name} PRIVATE bsdtests)
add_test(NAME ${name}
COMMAND bsdtestharness $<TARGET_FILE:${name}>)
set_tests_properties(${name}
PROPERTIES
TIMEOUT 120
DEPENDS bsdtestharness
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
if(NOT leaks_EXECUTABLE)
set_tests_properties(${name}
PROPERTIES
ENVIRONMENT NOLEAKS=1)
endif()
endfunction()
# Tests that reliably pass on all platforms
set(DISPATCH_C_TESTS
apply
api
debug
queue_finalizer
overcommit
context_for_key
after
timer
timer_short
timer_timeout
sema
timer_bit31
timer_bit63
timer_set_time
data
io_muxed
io_net
io_pipe
io_pipe_close
select)
# Tests that usually pass, but occasionally fail.
# Excluded by default for purposes of Swift CI
if(EXTENDED_TEST_SUITE)
# When dispatch_group is reenabled here, also remove the if(EXTENDED_TEST_SUITE) condition below
list(APPEND DISPATCH_C_TESTS
priority
concur
group
read
read2
starfish
suspend_timer
pingpong
drift
readsync
cascade
io)
# an oddball; dispatch_priority.c compiled with -DUSE_SET_TARGET_QUEUE=1
add_unit_test(dispatch_priority2 SOURCES dispatch_priority.c)
target_compile_options(dispatch_priority2 PRIVATE -DUSE_SET_TARGET_QUEUE=1)
endif()
# add C tests for platform-specific functionality when applicable
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
list(APPEND DISPATCH_C_TESTS
deadname
proc
vm
vnode)
endif()
foreach(test ${DISPATCH_C_TESTS})
add_unit_test(dispatch_${test}
SOURCES
dispatch_${test}.c)
endforeach()
set_tests_properties(dispatch_io_pipe PROPERTIES TIMEOUT 15)
set_tests_properties(dispatch_io_pipe_close PROPERTIES TIMEOUT 5)
# test dispatch API for various C/CXX language variants
add_unit_test(dispatch_c99 SOURCES dispatch_c99.c)
add_unit_test(dispatch_plusplus SOURCES dispatch_plusplus.cpp)
# test-specific link options
if(WIN32)
target_link_libraries(dispatch_io_muxed PRIVATE WS2_32)
target_link_libraries(dispatch_io_net PRIVATE WS2_32)
else()
# When dispatch_group is reenabled above, remove this
if(EXTENDED_TEST_SUITE)
target_link_libraries(dispatch_group PRIVATE m)
endif()
target_link_libraries(dispatch_timer_short PRIVATE m)
endif()
# test-specific compile options
set_target_properties(dispatch_c99 PROPERTIES C_STANDARD 99)

115
tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,115 @@
add_darling_static_library(dispatch_bsdtests
FAT
SOURCES
bsdtests.c
dispatch_test.c
)
include_directories(
"${CMAKE_CURRENT_SOURCE_DIR}"
)
add_compile_definitions(
HAVE_POSIX_SPAWNP=1
)
add_darling_executable(dispatch_bsdtestharness
bsdtestharness.c
)
target_link_libraries(dispatch_bsdtestharness
dispatch_bsdtests
)
install(TARGETS dispatch_bsdtestharness DESTINATION libexec/darling/usr/libexec/test/dispatch)
set(TEST_COMMANDS "")
function(dispatch_add_unit_test name)
cmake_parse_arguments(UNIT_TEST "" "" "SOURCES" ${ARGN})
add_darling_executable(dispatch_test_${name} ${UNIT_TEST_SOURCES})
target_link_libraries(dispatch_test_${name} dispatch_bsdtests)
set(TEST_COMMANDS "${TEST_COMMANDS}\nrun-test ${name}" PARENT_SCOPE)
install(TARGETS dispatch_test_${name} DESTINATION libexec/darling/usr/libexec/test/dispatch)
endfunction()
# reliable tests
set(DISPATCH_C_TESTS
apply
api
debug
queue_finalizer
overcommit
context_for_key
after
timer
timer_short
timer_timeout
sema
timer_bit31
timer_bit63
timer_set_time
data
io_muxed
io_net
io_pipe
io_pipe_close
select
)
# slightly less reliable tests
list(APPEND DISPATCH_C_TESTS
priority
concur
group
read
read2
starfish
suspend_timer
pingpong
drift
readsync
cascade
io
)
# Darwin-specific tests
list(APPEND DISPATCH_C_TESTS
deadname
proc
vm
vnode
)
foreach(test ${DISPATCH_C_TESTS})
dispatch_add_unit_test(${test} SOURCES dispatch_${test}.c)
endforeach()
#
# tests that require special setup
#
dispatch_add_unit_test(priority2 SOURCES dispatch_priority.c)
target_compile_definitions(dispatch_test_priority2 PRIVATE
USE_SET_TARGET_QUEUE=1
)
dispatch_add_unit_test(c99 SOURCES dispatch_c99.c)
set_target_properties(dispatch_test_c99 PROPERTIES
C_STANDARD 99
)
dispatch_add_unit_test(plusplus SOURCES dispatch_plusplus.cpp)
#
# configure and install runner scripts
#
configure_file(darling/run-all.sh.in "${CMAKE_CURRENT_BINARY_DIR}/darling/run-all.sh" @ONLY)
install(
PROGRAMS
"${CMAKE_CURRENT_BINARY_DIR}/darling/run-all.sh"
darling/run-single.sh
DESTINATION libexec/darling/usr/libexec/test/dispatch
)

728
tests/Foundation/bench.mm Normal file
View File

@ -0,0 +1,728 @@
/*
* 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 <Foundation/Foundation.h>
#include <libkern/OSAtomic.h>
#ifdef __ANDROID__
#include <linux/sysctl.h>
#else
#if !defined(__linux__)
#include <sys/sysctl.h>
#endif
#endif /* __ANDROID__ */
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <math.h>
#ifdef __BLOCKS__
#include <Block.h>
#endif
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
//#define BENCH_SLOW 1
extern "C" {
__private_extern__ void func(void);
#ifdef __BLOCKS__
__private_extern__ void (^block)(void);
#endif
static void backflip(void *ctxt);
static void backflip_done(void);
}
@interface BasicObject : NSObject
{
}
- (void) method;
@end
@implementation BasicObject
- (void) method
{
}
@end
class BasicClass {
public:
virtual void virtfunc(void) {
};
};
static void *
force_a_thread(void *arg)
{
pause();
abort();
return arg;
}
static volatile int32_t global;
static volatile int64_t w_global;
#if TARGET_OS_EMBEDDED
static const size_t cnt = 5000000;
#else
static const size_t cnt = 200000000;
#endif
static const size_t cnt2 = cnt/100;
static uint64_t bfs;
static long double loop_cost;
static long double cycles_per_nanosecond;
static mach_timebase_info_data_t tbi;
static void __attribute__((noinline))
print_result(uint64_t s, const char *str)
{
uint64_t d, e = mach_absolute_time();
long double dd;
d = e - s;
if (tbi.numer != tbi.denom) {
d *= tbi.numer;
d /= tbi.denom;
}
dd = (__typeof__(dd))d / (__typeof__(dd))cnt;
dd -= loop_cost;
if (loop_cost == 0.0) {
loop_cost = dd;
}
dd *= cycles_per_nanosecond;
dd = roundl(dd * 200.0)/200.0;
printf("%-45s%15.3Lf cycles\n", str, dd);
}
#if BENCH_SLOW || !TARGET_OS_EMBEDDED
static void __attribute__((noinline))
print_result2(uint64_t s, const char *str)
{
uint64_t d, e = mach_absolute_time();
long double dd;
d = e - s;
if (tbi.numer != tbi.denom) {
d *= tbi.numer;
d /= tbi.denom;
}
dd = (__typeof__(dd))d / (__typeof__(dd))cnt2;
dd -= loop_cost;
dd *= cycles_per_nanosecond;
printf("%-45s%15.3Lf cycles\n", str, dd);
}
#endif
#if defined(__i386__) || defined(__x86_64__)
static inline uint64_t
rdtsc(void)
{
uint32_t lo, hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return (uint64_t)hi << 32 | lo;
}
#endif
static struct fml {
struct fml *fml_next;
} *fixed_malloc_lifo_head;
struct fml *fixed_malloc_lifo(void);// __attribute__((noinline));
void fixed_free_lifo(struct fml *fml);// __attribute__((noinline));
struct fml *
fixed_malloc_lifo(void)
{
struct fml *fml_r = fixed_malloc_lifo_head;
if (fml_r) {
fixed_malloc_lifo_head = fml_r->fml_next;
return fml_r;
} else {
return (struct fml *)malloc(32);
}
}
void
fixed_free_lifo(struct fml *fml)
{
fml->fml_next = fixed_malloc_lifo_head;
fixed_malloc_lifo_head = fml;
}
int
main(void)
{
pthread_mutex_t plock = PTHREAD_MUTEX_INITIALIZER;
OSSpinLock slock = OS_SPINLOCK_INIT;
BasicObject *bo;
BasicClass *bc;
pthread_t pthr_pause;
dispatch_queue_t q, mq;
kern_return_t kr;
#if BENCH_SLOW
semaphore_t sem;
#endif
uint64_t freq;
uint64_t s;
size_t freq_len = sizeof(freq);
size_t bf_cnt = cnt;
unsigned i;
int r;
printf("\n====================================================================\n");
printf("[TEST] dispatch benchmark\n");
printf("[PID] %d\n", getpid());
printf("====================================================================\n\n");
r = sysctlbyname("hw.cpufrequency", &freq, &freq_len, NULL, 0);
assert(r != -1);
assert(freq_len == sizeof(freq));
cycles_per_nanosecond = (long double)freq / (long double)NSEC_PER_SEC;
#if BENCH_SLOW
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
assert(pool);
#endif
/* Malloc has different logic for threaded apps. */
r = pthread_create(&pthr_pause, NULL, force_a_thread, NULL);
assert(r == 0);
kr = mach_timebase_info(&tbi);
assert(kr == 0);
#if defined(__i386__) || defined(__x86_64__)
assert(tbi.numer == tbi.denom); /* This will fail on PowerPC. */
#endif
bo = [[BasicObject alloc] init];
assert(bo);
bc = new BasicClass();
assert(bc);
q = dispatch_queue_create("com.apple.bench-dispatch", NULL);
assert(q);
mq = dispatch_get_main_queue();
assert(mq);
printf("%-45s%15Lf\n\n", "Cycles per nanosecond:", cycles_per_nanosecond);
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("");
}
print_result(s, "Empty loop:");
printf("\nLoop cost subtracted from the following:\n\n");
#if TARGET_OS_EMBEDDED
s = mach_absolute_time();
for (i = cnt; i; i--) {
mach_absolute_time();
}
print_result(s, "mach_absolute_time():");
#else
s = mach_absolute_time();
for (i = cnt2; i; i--) {
mach_absolute_time();
}
print_result2(s, "mach_absolute_time():");
#endif
#if defined(__i386__) || defined(__x86_64__)
s = mach_absolute_time();
for (i = cnt; i; i--) {
rdtsc();
}
print_result(s, "rdtsc():");
#endif
#if BENCH_SLOW
s = mach_absolute_time();
for (i = cnt2; i; i--) {
pthread_t pthr;
void *pr;
r = pthread_create(&pthr, NULL, (void *(*)(void *))func, NULL);
assert(r == 0);
r = pthread_join(pthr, &pr);
assert(r == 0);
}
print_result2(s, "pthread create+join:");
s = mach_absolute_time();
for (i = cnt2; i; i--) {
kr = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0);
assert(kr == 0);
kr = semaphore_destroy(mach_task_self(), sem);
assert(kr == 0);
}
print_result2(s, "Mach semaphore create/destroy:");
kr = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0);
assert(kr == 0);
s = mach_absolute_time();
for (i = cnt2; i; i--) {
kr = semaphore_signal(sem);
assert(kr == 0);
}
print_result2(s, "Mach semaphore signal:");
kr = semaphore_destroy(mach_task_self(), sem);
assert(kr == 0);
s = mach_absolute_time();
for (i = cnt; i; i--) {
free(malloc(32));
}
print_result(s, "free(malloc(32)):");
s = mach_absolute_time();
for (i = cnt / 2; i; i--) {
void *m1 = malloc(32);
void *m2 = malloc(32);
free(m1);
free(m2);
}
print_result(s, "Avoiding the MRU cache of free(malloc(32)):");
s = mach_absolute_time();
for (i = cnt; i; i--) {
fixed_free_lifo(fixed_malloc_lifo());
}
print_result(s, "per-thread/fixed free(malloc(32)):");
s = mach_absolute_time();
for (i = cnt; i; i--) {
assert(strtoull("18446744073709551615", NULL, 0) == ~0ull);
}
print_result(s, "strtoull(\"18446744073709551615\") == ~0ull:");
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
func();
}
print_result(s, "Empty function call:");
#ifdef __BLOCKS__
s = mach_absolute_time();
for (i = cnt; i; i--) {
block();
}
print_result(s, "Empty block call:");
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
bc->virtfunc();
}
print_result(s, "Empty C++ virtual call:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
[bo method];
}
print_result(s, "Empty ObjC call:");
#if BENCH_SLOW
s = mach_absolute_time();
for (i = cnt2; i; i--) {
[bo description];
}
print_result2(s, "\"description\" ObjC call:");
[pool release];
pool = NULL;
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("nop");
}
print_result(s, "raw 'nop':");
#if defined(__i386__) || defined(__x86_64__)
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("pause");
}
print_result(s, "raw 'pause':");
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("mfence");
}
print_result(s, "Atomic mfence:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("lfence");
}
print_result(s, "Atomic lfence:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("sfence");
}
print_result(s, "Atomic sfence:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
uint64_t sidt_rval;
__asm__ __volatile__ ("sidt %0" : "=m" (sidt_rval));
}
print_result(s, "'sidt' instruction:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
long prev;
__asm__ __volatile__ ("cmpxchg %1,%2"
: "=a" (prev) : "r" (0l), "m" (global), "0" (1l));
}
print_result(s, "'cmpxchg' without the 'lock' prefix:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
global = 0;
__asm__ __volatile__ ("mfence" ::: "memory");
}
print_result(s, "Store + mfence:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
unsigned long _clbr;
#ifdef __LP64__
__asm__ __volatile__ ("cpuid" : "=a" (_clbr)
: "0" (0) : "rbx", "rcx", "rdx", "cc", "memory");
#else
#ifdef __llvm__
__asm__ __volatile__ ("cpuid" : "=a" (_clbr) : "0" (0)
: "ebx", "ecx", "edx", "cc", "memory" );
#else // gcc does not allow inline i386 asm to clobber ebx
__asm__ __volatile__ ("pushl %%ebx\n\tcpuid\n\tpopl %%ebx"
: "=a" (_clbr) : "0" (0) : "ecx", "edx", "cc", "memory" );
#endif
#endif
}
print_result(s, "'cpuid' instruction:");
#elif defined(__arm__)
#include <arm/arch.h>
#if !defined(_ARM_ARCH_7) && defined(__thumb__)
#error "GCD requires instructions unvailable in ARMv6 Thumb1"
#endif
#ifdef _ARM_ARCH_7
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("yield");
}
print_result(s, "raw 'yield':");
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
#ifdef _ARM_ARCH_7
__asm__ __volatile__ ("dmb ish" : : : "memory");
#else
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory");
#endif
}
print_result(s, "'dmb ish' instruction:");
#ifdef _ARM_ARCH_7
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("dmb ishst" : : : "memory");
}
print_result(s, "'dmb ishst' instruction:");
#endif
#ifdef _ARM_ARCH_7
s = mach_absolute_time();
for (i = cnt; i; i--) {
__asm__ __volatile__ ("str %[_r], [%[_p], %[_o]]" :
: [_p] "p" (&global), [_o] "M" (0), [_r] "r" (0) : "memory");
__asm__ __volatile__ ("dmb ishst" : : : "memory");
}
print_result(s, "'str + dmb ishst' instructions:");
#endif
#ifdef _ARM_ARCH_7
s = mach_absolute_time();
for (i = cnt; i; i--) {
uintptr_t prev;
uint32_t t;
do {
__asm__ __volatile__ ("ldrex %[_r], [%[_p], %[_o]]"
: [_r] "=&r" (prev) \
: [_p] "p" (&global), [_o] "M" (0) : "memory");
__asm__ __volatile__ ("strex %[_t], %[_r], [%[_p], %[_o]]"
: [_t] "=&r" (t) \
: [_p] "p" (&global), [_o] "M" (0), [_r] "r" (0) : "memory");
} while (t);
}
print_result(s, "'ldrex + strex' instructions:");
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
#ifdef _ARM_ARCH_7
__asm__ __volatile__ ("dsb ish" : : : "memory");
#else
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0) : "memory");
#endif
}
print_result(s, "'dsb ish' instruction:");
#if BENCH_SLOW
s = mach_absolute_time();
for (i = cnt; i; i--) {
register long _swtch_pri __asm__("ip") = -59;
__asm__ __volatile__ ("svc 0x80" : : "r" (_swtch_pri) : "r0", "memory");
}
print_result(s, "swtch_pri syscall:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
register long _r0 __asm__("r0") = 0, _r1 __asm__("r1") = 1, _r2 __asm__("r2") = 1;
register long _thread_switch __asm__("ip") = -61;
__asm__ __volatile__ ("svc 0x80" : "+r" (_r0)
: "r" (_r1), "r" (_r2), "r" (_thread_switch): "memory");
}
print_result(s, "thread_switch syscall:");
#endif
#endif // __arm__
#if BENCH_SLOW
s = mach_absolute_time();
for (i = cnt; i; i--) {
pthread_yield_np();
}
print_result(s, "pthread_yield_np():");
s = mach_absolute_time();
for (i = cnt2; i; i--) {
usleep(0);
}
print_result2(s, "usleep(0):");
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
__sync_lock_test_and_set(&global, 0);
}
print_result(s, "Atomic xchg:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
__sync_val_compare_and_swap(&global, 1, 0);
}
print_result(s, "Atomic cmpxchg:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
__sync_fetch_and_add(&global, 1);
}
print_result(s, "Atomic increment:");
{
global = 0;
volatile int32_t *g = &global;
s = mach_absolute_time();
for (i = cnt; i; i--) {
uint32_t result;
__sync_and_and_fetch(g, 1);
result = *g;
if (result) {
abort();
}
}
print_result(s, "Atomic and-and-fetch, reloading result:");
}
{
global = 0;
volatile int32_t *g = &global;
s = mach_absolute_time();
for (i = cnt; i; i--) {
uint32_t result;
result = __sync_and_and_fetch(g, 1);
if (result) {
abort();
}
}
print_result(s, "Atomic and-and-fetch, using result:");
}
global = 0;
s = mach_absolute_time();
for (i = cnt; i; i--) {
OSAtomicIncrement32Barrier(&global);
}
print_result(s, "OSAtomicIncrement32Barrier:");
global = 0;
s = mach_absolute_time();
for (i = cnt; i; i--) {
OSAtomicIncrement32(&global);
}
print_result(s, "OSAtomicIncrement32:");
w_global = 0;
s = mach_absolute_time();
for (i = cnt; i; i--) {
OSAtomicIncrement64Barrier(&w_global);
}
print_result(s, "OSAtomicIncrement64Barrier:");
w_global = 0;
s = mach_absolute_time();
for (i = cnt; i; i--) {
OSAtomicIncrement64(&w_global);
}
print_result(s, "OSAtomicIncrement64:");
global = 0;
s = mach_absolute_time();
for (i = cnt; i; i--) {
while (!__sync_bool_compare_and_swap(&global, 0, 1)) {
do {
#if defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__ ("pause");
#elif defined(__arm__) && defined _ARM_ARCH_7
__asm__ __volatile__ ("yield");
#endif
} while (global);
}
global = 0;
}
print_result(s, "Inlined spin lock/unlock:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
OSSpinLockLock(&slock);
OSSpinLockUnlock(&slock);
}
print_result(s, "OSSpinLock/Unlock:");
s = mach_absolute_time();
for (i = cnt; i; i--) {
r = pthread_mutex_lock(&plock);
assert(r == 0);
r = pthread_mutex_unlock(&plock);
assert(r == 0);
}
print_result(s, "pthread lock/unlock:");
#ifdef __BLOCKS__
s = mach_absolute_time();
for (i = cnt; i; i--) {
dispatch_sync(q, ^{ });
}
print_result(s, "dispatch_sync:");
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
dispatch_sync_f(q, NULL, (void (*)(void *))func);
}
print_result(s, "dispatch_sync_f:");
#ifdef __BLOCKS__
s = mach_absolute_time();
for (i = cnt; i; i--) {
dispatch_barrier_sync(q, ^{ });
}
print_result(s, "dispatch_barrier_sync:");
#endif
s = mach_absolute_time();
for (i = cnt; i; i--) {
dispatch_barrier_sync_f(q, NULL, (void (*)(void *))func);
}
print_result(s, "dispatch_barrier_sync_f:");
s = mach_absolute_time();
dispatch_apply_f(cnt,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
NULL, (void (*)(void *, size_t))func);
s += loop_cost; // cancel out the implicit subtraction done by the next line
print_result(s, "dispatch_apply_f():");
// do a "double backflip" to hit the fast-path of the enqueue/dequeue logic
bfs = mach_absolute_time();
dispatch_async_f(dispatch_get_main_queue(), &bf_cnt, backflip);
dispatch_async_f(dispatch_get_main_queue(), &bf_cnt, backflip);
dispatch_main();
}
__attribute__((noinline))
void
backflip_done(void)
{
print_result(bfs, "dispatch_async_f():");
exit(EXIT_SUCCESS);
}
void
backflip(void *ctxt)
{
size_t *bf_cnt = (size_t *)ctxt;
if (--(*bf_cnt)) {
return dispatch_async_f(dispatch_get_main_queue(), ctxt, backflip);
}
backflip_done();
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2009-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>
#import <Foundation/Foundation.h>
#include <libkern/OSAtomic.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#if __OBJC_GC__
const size_t final = 50000, desclen = 538892;
#else
const size_t final = 1000, desclen = 8892;
#endif
NSAutoreleasePool *pool = nil;
static void
work(void* ctxt __attribute__((unused)))
{
pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *a = [NSMutableArray array];
OSSpinLock sl = OS_SPINLOCK_INIT, *l = &sl;
dispatch_apply(final, dispatch_get_global_queue(0, 0), ^(size_t i){
NSDecimalNumber *n = [NSDecimalNumber decimalNumberWithDecimal:
[[NSNumber numberWithInteger:i] decimalValue]];
OSSpinLockLock(l);
[a addObject:n];
OSSpinLockUnlock(l);
});
test_long("count", [a count], final);
test_long("description length", [[a description] length], desclen);
a = nil;
[pool drain];
test_stop_after_delay((void*)(intptr_t)1);
}
int
main(void)
{
dispatch_test_start("Dispatch Apply GC"); // <rdar://problem/7455071>
dispatch_async_f(dispatch_get_main_queue(), (void*)(intptr_t)1, work);
CFRunLoopRun();
return 0;
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2009-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>
#import <Foundation/Foundation.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#if __OBJC_GC__
const size_t final = 50000, desclen = 538892;
#else
const size_t final = 1000, desclen = 8892;
#endif
NSAutoreleasePool *pool = nil;
static void
work(void* ctxt __attribute__((unused)))
{
pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *a = [NSMutableArray array];
dispatch_group_t g = dispatch_group_create();
dispatch_group_async(g, dispatch_get_global_queue(0, 0), ^{
NSUInteger i;
for (i = 0; i < final; i++) {
NSDecimalNumber *n = [NSDecimalNumber decimalNumberWithDecimal:
[[NSNumber numberWithInteger:i] decimalValue]];
dispatch_sync(dispatch_get_main_queue(), ^{
[a addObject:n];
});
}
});
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
test_long("count", [a count], final);
test_long("description length", [[a description] length], desclen);
[pool drain];
test_stop_after_delay((void*)(intptr_t)1);
});
dispatch_release(g);
}
int
main(void)
{
dispatch_test_start("Dispatch Sync GC"); // <rdar://problem/7458685>
dispatch_async_f(dispatch_get_main_queue(), NULL, work);
CFRunLoopRun();
return 0;
}

View File

@ -0,0 +1,73 @@
/*
* 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 <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <Foundation/Foundation.h>
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
@interface MYOperation : NSOperation
{
}
@end
@implementation MYOperation
- (id) init
{
self = [super init];
return self;
}
- (void)main
{
test_stop();
}
@end
int
main(void)
{
dispatch_test_start("NSOperation");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
test_ptr_notnull("NSOperationQueue", queue);
MYOperation *operation = [[MYOperation alloc] init];
test_ptr_notnull("NSOperation", operation);
[queue addOperation:operation];
[operation release];
[[NSRunLoop mainRunLoop] run];
[pool release];
return 0;
}

279
tests/bsdtestharness.c Normal file
View File

@ -0,0 +1,279 @@
/*
* 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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <spawn.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <generic_win_port.h>
#include <Psapi.h>
#include <Windows.h>
#endif
#include <signal.h>
#ifdef __APPLE__
#include <mach/clock_types.h>
#include <mach-o/arch.h>
#endif
#include <bsdtests.h>
#if !defined(_WIN32)
extern char **environ;
#endif
int
main(int argc, char *argv[])
{
int res;
pid_t pid = 0;
if (argc < 2) {
fprintf(stderr, "usage: %s [...]\n", argv[0]);
exit(1);
}
#ifdef HAVE_POSIX_SPAWNP
short spawnflags = 0;
#ifdef __APPLE__
spawnflags |= POSIX_SPAWN_START_SUSPENDED;
#if TARGET_OS_EMBEDDED
spawnflags |= POSIX_SPAWN_SETEXEC;
#endif
#endif
posix_spawnattr_t attr;
res = posix_spawnattr_init(&attr);
assert(res == 0);
res = posix_spawnattr_setflags(&attr, spawnflags);
assert(res == 0);
#endif
uint64_t to = 0;
char *tos = getenv("BSDTEST_TIMEOUT");
if (tos) {
to = strtoul(tos, NULL, 0);
to *= NSEC_PER_SEC;
}
#ifdef __APPLE__
char *arch = getenv("BSDTEST_ARCH");
if (arch) {
const NXArchInfo *ai = NXGetArchInfoFromName(arch);
if (ai) {
res = posix_spawnattr_setbinpref_np(&attr, 1, (cpu_type_t*)&ai->cputype, NULL);
assert(res == 0);
}
}
#endif
int i;
char** newargv = calloc((size_t)argc, sizeof(void*));
for (i = 1; i < argc; ++i) {
newargv[i-1] = argv[i];
}
newargv[i-1] = NULL;
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
#ifdef HAVE_POSIX_SPAWNP
#ifdef __APPLE__
if (spawnflags & POSIX_SPAWN_SETEXEC) {
pid = fork();
}
#endif
if (!pid) {
res = posix_spawnp(&pid, newargv[0], NULL, &attr, newargv, environ);
if (res) {
errno = res;
perror(newargv[0]);
exit(EXIT_FAILURE);
}
}
#elif defined(__unix__)
(void)res;
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child process
if (execve(newargv[0], newargv, environ) == -1) {
perror(newargv[0]);
_Exit(EXIT_FAILURE);
}
}
#elif defined(_WIN32)
(void)res;
WCHAR *cmdline = argv_to_command_line(newargv);
if (!cmdline) {
fprintf(stderr, "argv_to_command_line() failed\n");
exit(EXIT_FAILURE);
}
STARTUPINFOW si = {.cb = sizeof(si)};
PROCESS_INFORMATION pi;
BOOL created = CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
DWORD error = GetLastError();
free(cmdline);
if (!created) {
print_winapi_error("CreateProcessW", error);
exit(EXIT_FAILURE);
}
pid = (pid_t)pi.dwProcessId;
#else
#error "bsdtestharness not implemented on this platform"
#endif
//fprintf(stderr, "pid = %d\n", pid);
assert(pid > 0);
#if defined(__linux__)
int status;
struct rusage usage;
struct timeval tv_stop, tv_wall;
int res2 = wait4(pid, &status, 0, &usage);
(void)res2;
gettimeofday(&tv_stop, NULL);
tv_wall.tv_sec = tv_stop.tv_sec - tv_start.tv_sec;
tv_wall.tv_sec -= (tv_stop.tv_usec < tv_start.tv_usec);
tv_wall.tv_usec = labs(tv_stop.tv_usec - tv_start.tv_usec);
assert(res2 != -1);
test_long("Process exited", (WIFEXITED(status) && WEXITSTATUS(status) && WEXITSTATUS(status) != 0xff) || WIFSIGNALED(status), 0);
printf("[PERF]\twall time: %ld.%06ld\n", tv_wall.tv_sec, tv_wall.tv_usec);
printf("[PERF]\tuser time: %ld.%06ld\n", usage.ru_utime.tv_sec, usage.ru_utime.tv_usec);
printf("[PERF]\tsystem time: %ld.%06ld\n", usage.ru_stime.tv_sec, usage.ru_stime.tv_usec);
printf("[PERF]\tmax resident set size: %ld\n", usage.ru_maxrss);
printf("[PERF]\tpage faults: %ld\n", usage.ru_majflt);
printf("[PERF]\tswaps: %ld\n", usage.ru_nswap);
printf("[PERF]\tvoluntary context switches: %ld\n", usage.ru_nvcsw);
printf("[PERF]\tinvoluntary context switches: %ld\n", usage.ru_nivcsw);
exit((WIFEXITED(status) && WEXITSTATUS(status)) || WIFSIGNALED(status));
#elif defined(_WIN32)
if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0) {
print_winapi_error("WaitForSingleObject", GetLastError());
exit(EXIT_FAILURE);
}
struct timeval tv_stop, tv_wall;
gettimeofday(&tv_stop, NULL);
tv_wall.tv_sec = tv_stop.tv_sec - tv_start.tv_sec;
tv_wall.tv_sec -= (tv_stop.tv_usec < tv_start.tv_usec);
tv_wall.tv_usec = labs(tv_stop.tv_usec - tv_start.tv_usec);
DWORD status;
if (!GetExitCodeProcess(pi.hProcess, &status)) {
print_winapi_error("GetExitCodeProcess", GetLastError());
exit(EXIT_FAILURE);
}
FILETIME create_time, exit_time, kernel_time, user_time;
if (!GetProcessTimes(pi.hProcess, &create_time, &exit_time, &kernel_time, &user_time)) {
print_winapi_error("GetProcessTimes", GetLastError());
exit(EXIT_FAILURE);
}
struct timeval utime, stime;
filetime_to_timeval(&utime, &user_time);
filetime_to_timeval(&stime, &kernel_time);
PROCESS_MEMORY_COUNTERS counters;
if (!GetProcessMemoryInfo(pi.hProcess, &counters, sizeof(counters))) {
print_winapi_error("GetProcessMemoryInfo", GetLastError());
exit(EXIT_FAILURE);
}
test_long("Process exited", status == 0 || status == 0xff, 1);
printf("[PERF]\twall time: %ld.%06ld\n", tv_wall.tv_sec, tv_wall.tv_usec);
printf("[PERF]\tuser time: %ld.%06ld\n", utime.tv_sec, utime.tv_usec);
printf("[PERF]\tsystem time: %ld.%06ld\n", stime.tv_sec, stime.tv_usec);
printf("[PERF]\tmax working set size: %zu\n", counters.PeakWorkingSetSize);
printf("[PERF]\tpage faults: %lu\n", counters.PageFaultCount);
exit(status ? EXIT_FAILURE : EXIT_SUCCESS);
#else
dispatch_queue_t main_q = dispatch_get_main_queue();
dispatch_source_t tmp_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, main_q);
assert(tmp_ds);
dispatch_source_set_event_handler(tmp_ds, ^{
int status;
struct rusage usage;
struct timeval tv_stop, tv_wall;
gettimeofday(&tv_stop, NULL);
tv_wall.tv_sec = tv_stop.tv_sec - tv_start.tv_sec;
tv_wall.tv_sec -= (tv_stop.tv_usec < tv_start.tv_usec);
tv_wall.tv_usec = abs(tv_stop.tv_usec - tv_start.tv_usec);
int res2 = wait4(pid, &status, 0, &usage);
assert(res2 != -1);
test_long("Process exited", (WIFEXITED(status) && WEXITSTATUS(status) && WEXITSTATUS(status) != 0xff) || WIFSIGNALED(status), 0);
printf("[PERF]\twall time: %ld.%06d\n", tv_wall.tv_sec, tv_wall.tv_usec);
printf("[PERF]\tuser time: %ld.%06d\n", usage.ru_utime.tv_sec, usage.ru_utime.tv_usec);
printf("[PERF]\tsystem time: %ld.%06d\n", usage.ru_stime.tv_sec, usage.ru_stime.tv_usec);
printf("[PERF]\tmax resident set size: %ld\n", usage.ru_maxrss);
printf("[PERF]\tpage faults: %ld\n", usage.ru_majflt);
printf("[PERF]\tswaps: %ld\n", usage.ru_nswap);
printf("[PERF]\tvoluntary context switches: %ld\n", usage.ru_nvcsw);
printf("[PERF]\tinvoluntary context switches: %ld\n", usage.ru_nivcsw);
exit((WIFEXITED(status) && WEXITSTATUS(status)) || WIFSIGNALED(status));
});
dispatch_resume(tmp_ds);
if (!to) {
#if TARGET_OS_EMBEDDED
to = 180LL * NSEC_PER_SEC;
#else
to = 90LL * NSEC_PER_SEC;
#endif
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, to), main_q, ^{
kill(pid, SIGKILL);
fprintf(stderr, "Terminating unresponsive process (%0.1lfs)\n", (double)to / NSEC_PER_SEC);
});
signal(SIGINT, SIG_IGN);
tmp_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGINT, 0, main_q);
assert(tmp_ds);
dispatch_source_set_event_handler(tmp_ds, ^{
fprintf(stderr, "Terminating process due to signal\n");
kill(pid, SIGKILL);
});
dispatch_resume(tmp_ds);
if (spawnflags & POSIX_SPAWN_SETEXEC) {
usleep(USEC_PER_SEC/10);
}
kill(pid, SIGCONT);
dispatch_main();
#endif
return 0;
}

536
tests/bsdtests.c Normal file
View File

@ -0,0 +1,536 @@
/*
* 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@
*/
#if defined(__linux__) || defined(__FreeBSD__)
// for asprintf
#define _GNU_SOURCE 1
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <errno.h>
#include <string.h>
#ifdef __APPLE__
#include <crt_externs.h>
#include <mach/mach_error.h>
#include <spawn.h>
#include <sys/wait.h>
#endif
#if defined(_WIN32)
#include <generic_win_port.h>
#endif
#include <inttypes.h>
#include "bsdtests.h"
static int _test_exit_code;
#define _test_print(_file, _line, _desc, \
_expr, _fmt1, _val1, _fmt2, _val2) do { \
const char* _exprstr; \
char _linestr[BUFSIZ]; \
_linestr[0] = 0; \
if (!(_expr)) { \
_exprstr = "FAIL"; \
_test_exit_code = 0xff; \
if (_file && _file[0] != '\0') { \
snprintf(_linestr, sizeof(_linestr), \
" (%s:%ld)", _file, _line); \
} \
} else { \
_exprstr = "PASS"; \
} \
if (_fmt2 == 0) { \
fprintf(stdout, "\n" \
"[BEGIN] %s\n" \
"\tValue: " _fmt1 "\n" \
"[%s] %s%s\n", \
_desc, \
_val1, \
_exprstr, \
_desc, \
_linestr); \
} else { \
fprintf(stdout, "\n" \
"[BEGIN] %s\n" \
"\tActual: " _fmt1 "\n" \
"\tExpected: " _fmt2 "\n" \
"[%s] %s%s\n", \
_desc, \
_val1, \
_val2, \
_exprstr, \
_desc, \
_linestr); \
} \
if (!_expr && _file && _file[0] != '\0') { \
fprintf(stdout, "\t%s:%ld\n", _file, _line); \
} \
fflush(stdout); \
} while (0);
#define GENERATE_DESC \
char desc[BUFSIZ]; \
va_list args; \
\
va_start(args, format); \
vsnprintf(desc, sizeof(desc), format, args); \
va_end(args);
void
_test_ptr_null(const char* file, long line, const char* desc, const void* ptr)
{
_test_print(file, line, desc,
(ptr == NULL), "%p", ptr, "%p", (void*)0);
}
void
test_ptr_null_format(void *ptr, const char *format, ...)
{
GENERATE_DESC
_test_ptr_null(NULL, 0, desc, ptr);
}
void
_test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr)
{
_test_print(file, line, desc,
(ptr != NULL), "%p", ptr, "%p", ptr ?: (void*)~0);
}
void
test_ptr_notnull_format(const void *ptr, const char *format, ...)
{
GENERATE_DESC
_test_ptr_notnull(NULL, 0, desc, ptr);
}
void
_test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected)
{
_test_print(file, line, desc,
(actual == expected), "%p", actual, "%p", expected);
}
void
test_ptr_format(const void* actual, const void* expected, const char* format, ...)
{
GENERATE_DESC
_test_ptr(NULL, 0, desc, actual, expected);
}
void _test_ptr_not(const char* file, long line, const char* desc, const void* actual, const void* expected)
{
_test_print(file, line, desc,
(actual != expected), "%p", actual, "!%p", expected);
}
void test_ptr_not_format(const void *actual, const void* expected, const char *format, ...)
{
GENERATE_DESC
_test_ptr_not(NULL, 0, desc, actual, expected);
}
void
_test_uint32(const char* file, long line, const char* desc, uint32_t actual, uint32_t expected)
{
_test_print(file, line, desc,
(actual == expected), "%u", actual, "%u", expected);
}
void
test_uint32_format(uint32_t actual, uint32_t expected, const char *format, ...)
{
GENERATE_DESC
_test_uint32(NULL, 0, desc, actual, expected);
}
void
_test_int32(const char* file, long line, const char* desc, int32_t actual, int32_t expected)
{
_test_print(file, line, desc,
(actual == expected), "%d", actual, "%d", expected);
}
void
test_int32_format(int32_t actual, int32_t expected, const char* format, ...)
{
GENERATE_DESC
_test_int32(NULL, 0, desc, actual, expected);
}
void
_test_long(const char* file, long line, const char* desc, long actual, long expected)
{
_test_print(file, line, desc,
(actual == expected), "%ld", actual, "%ld", expected);
}
void
test_long_format(long actual, long expected, const char* format, ...)
{
GENERATE_DESC
_test_long(NULL, 0, desc, actual, expected);
}
void
_test_sizet(const char* file, long line, const char* desc, size_t actual, size_t expected)
{
_test_print(file, line, desc,
(actual == expected), "%zd", actual, "%zd", expected);
}
void
test_sizet_format(size_t actual, size_t expected, const char* format, ...)
{
GENERATE_DESC
_test_sizet(NULL, 0, desc, actual, expected);
}
void
_test_uint64(const char* file, long line, const char* desc, uint64_t actual, uint64_t expected)
{
_test_print(file, line, desc,
(actual == expected), "%" PRIu64, actual, "%" PRIu64, expected);
}
void
test_uint64_format(uint64_t actual, uint64_t expected, const char* format, ...)
{
GENERATE_DESC
_test_uint64(NULL, 0, desc, actual, expected);
}
void
_test_int64(const char* file, long line, const char* desc, int64_t actual, int64_t expected)
{
_test_print(file, line, desc,
(actual == expected), "%" PRId64, actual, "%" PRId64, expected);
}
void
test_int64_format(int64_t actual, int64_t expected, const char* format, ...)
{
GENERATE_DESC
_test_int64(NULL, 0, desc, actual, expected);
}
void
_test_long_less_than(const char* file, long line, const char* desc, long actual, long expected_max)
{
_test_print(file, line, desc, (actual < expected_max), "%ld", actual, "<%ld", expected_max);
}
void
test_long_less_than_format(long actual, long expected_max, const char* format, ...)
{
GENERATE_DESC
_test_long_less_than(NULL, 0, desc, actual, expected_max);
}
void
_test_long_less_than_or_equal(const char* file, long line, const char* desc, long actual, long expected_max)
{
_test_print(file, line, desc, (actual <= expected_max), "%ld", actual, "<=%ld", expected_max);
}
void
test_long_less_than_or_equal_format(long actual, long expected_max, const char* format, ...)
{
GENERATE_DESC
_test_long_less_than_or_equal(NULL, 0, desc, actual, expected_max);
}
void
_test_long_greater_than_or_equal(const char* file, long line, const char* desc, long actual, long expected_min)
{
_test_print(file, line, desc, (actual >= expected_min), "%ld", actual, ">=%ld", expected_min);
}
void
test_long_greater_than_or_equal_format(long actual, long expected_max, const char* format, ...)
{
GENERATE_DESC
_test_long_greater_than_or_equal(NULL, 0, desc, actual, expected_max);
}
void
_test_sizet_less_than(const char* file, long line, const char* desc, size_t actual, size_t expected_max)
{
_test_print(file, line, desc, (actual < expected_max), "%zd", actual, "<%zd", expected_max);
}
void
test_sizet_less_than_format(size_t actual, size_t expected_max, const char* format, ...)
{
GENERATE_DESC
_test_sizet_less_than(NULL, 0, desc, actual, expected_max);
}
void
_test_sizet_less_than_or_equal(const char* file, long line, const char* desc, size_t actual, size_t expected_max)
{
_test_print(file, line, desc, (actual <= expected_max), "%zd", actual, "<=%zd", expected_max);
}
void
test_sizet_less_than_or_equal_format(size_t actual, size_t expected_max, const char* format, ...)
{
GENERATE_DESC
_test_sizet_less_than_or_equal(NULL, 0, desc, actual, expected_max);
}
void
_test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected)
{
_test_print(file, line, desc, (val < max_expected), "%f", val, "<%f", max_expected);
}
void
test_double_less_than_format(double val, double max_expected, const char* format, ...)
{
GENERATE_DESC
_test_double_less_than(NULL, 0, desc, val, max_expected);
}
void
_test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected)
{
_test_print(file, line, desc, (val <= max_expected), "%f", val, "<=%f", max_expected);
}
void
test_double_less_than_or_equal_format(double val, double max_expected, const char *format, ...)
{
GENERATE_DESC
_test_double_less_than_or_equal(NULL, 0, desc, val, max_expected);
}
void
_test_double_equal(const char* file, long line, const char* desc, double val, double expected)
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
_test_print(file, line, desc, (val == expected), "%f", val, "%f", expected);
#pragma clang diagnostic pop
}
void
test_double_equal_format(double val, double expected, const char *format, ...)
{
GENERATE_DESC
_test_double_equal(NULL, 0, desc, val, expected);
}
void
_test_errno(const char* file, long line, const char* desc, int actual, int expected)
{
char* actual_str;
char* expected_str;
(void)asprintf(&actual_str, "%d\t%s", actual, actual ? strerror(actual) : "");
(void)asprintf(&expected_str, "%d\t%s", expected, expected ? strerror(expected) : "");
_test_print(file, line, desc,
(actual == expected), "%s", actual_str, "%s", expected_str);
free(actual_str);
free(expected_str);
}
void
test_errno_format(int actual, int expected, const char *format, ...)
{
GENERATE_DESC
_test_errno(NULL, 0, desc, actual, expected);
}
#ifdef __APPLE__
void
_test_mach_error(const char* file, long line, const char* desc,
mach_error_t actual, mach_error_t expected)
{
char* actual_str;
char* expected_str;
(void)asprintf(&actual_str, "%d %s", actual, actual ? mach_error_string(actual) : "");
(void)asprintf(&expected_str, "%d %s", expected, expected ? mach_error_string(expected) : "");
_test_print(file, line, desc,
(actual == expected), "%s", actual_str, "%s", expected_str);
free(actual_str);
free(expected_str);
}
void
test_mach_error_format(mach_error_t actual, mach_error_t expected, const char *format, ...)
{
GENERATE_DESC
_test_mach_error(NULL, 0, desc, actual, expected);
}
#endif
void
_test_skip(const char* file, long line, const char* desc)
{
if (file != NULL && file[0] != '\0') {
fprintf(stdout, "[SKIP] %s (%s:%ld)\n", desc, file, line);
} else {
fprintf(stdout, "[SKIP] %s\n", desc);
}
fflush(stdout);
}
void
test_skip_format(const char *format, ...)
{
GENERATE_DESC
fprintf(stdout, "[SKIP] %s\n", desc);
}
#if USE_COREFOUNDATION
void
test_cferror(const char *desc, CFErrorRef actual, CFIndex expectedCode)
{
if (actual != NULL) {
CFStringRef errDesc = CFErrorCopyDescription(actual);
CFIndex code = CFErrorGetCode(actual);
char* actual_str;
char* expected_str;
if (code != expectedCode) {
char buffer[BUFSIZ];
CFStringGetCString(errDesc, buffer, sizeof(buffer), kCFStringEncodingUTF8);
(void)asprintf(&actual_str, "%ld\t%s", code, buffer);
} else {
(void)asprintf(&actual_str, "%ld", code);
}
(void)asprintf(&expected_str, "%ld", expectedCode);
_test_print("", (long) 0, desc,
(code == expectedCode), "%s", actual_str, "%s", expected_str);
free(actual_str);
free(expected_str);
CFRelease(errDesc);
} else {
_test_print("", (long) 0, desc, (0 == expectedCode), "%ld", (long) 0, "%ld", (long) expectedCode);
}
}
void
test_cferror_format(CFErrorRef actual, CFIndex expectedCode, const char *format, ...)
{
GENERATE_DESC
test_cferror(desc, actual, expectedCode);
}
#endif
void
test_start(const char* desc)
{
if (desc) {
fprintf(stdout, "\n==================================================\n");
fprintf(stdout, "[TEST] %s\n", desc);
fprintf(stdout, "[PID] %d\n", getpid());
fprintf(stdout, "==================================================\n\n");
fflush(stdout);
}
_test_exit_code = EXIT_SUCCESS;
usleep(100000); // give 'gdb --waitfor=' a chance to find this proc
}
#if defined(__APPLE__) && defined(__MACH__)
static char** get_environment(void)
{
return (* _NSGetEnviron());
}
void
test_leaks_pid(const char *name, pid_t pid)
{
int res;
char pidstr[10];
if (getenv("NOLEAKS")) {
return;
}
if (!name) {
name = "Leaks";
}
/* leaks doesn't work against debug variant malloc */
const char *dyld_image_suffix = getenv("DYLD_IMAGE_SUFFIX");
if (dyld_image_suffix && strstr(dyld_image_suffix, "_debug")) {
return;
}
char *inserted_libs = getenv("DYLD_INSERT_LIBRARIES");
if (inserted_libs && strstr(inserted_libs, "/usr/lib/libgmalloc.dylib")) {
return;
}
unsetenv("DYLD_IMAGE_SUFFIX");
unsetenv("DYLD_INSERT_LIBRARIES");
unsetenv("DYLD_LIBRARY_PATH");
unsetenv("MallocStackLogging");
unsetenv("MallocStackLoggingNoCompact");
snprintf(pidstr, sizeof(pidstr), "%d", pid);
char* args[] = { "./leaks-wrapper", pidstr, NULL };
res = posix_spawnp(&pid, args[0], NULL, NULL, args, get_environment());
if (res == 0 && pid > 0) {
int status;
waitpid(pid, &status, 0);
test_long(name, status, 0);
} else {
perror(args[0]);
}
}
void
test_leaks(const char *name)
{
test_leaks_pid(name, getpid());
}
#endif
void
test_stop_after_delay(void *delay)
{
if (delay != NULL) {
sleep((unsigned int)(intptr_t)delay);
}
#if defined(__APPLE__) && defined(__MACH__)
test_leaks(NULL);
#endif
fflush(stdout);
_exit(_test_exit_code);
}
void
test_stop(void)
{
test_stop_after_delay((void *)(intptr_t)0);
}

190
tests/bsdtests.h Normal file
View File

@ -0,0 +1,190 @@
/*
* 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@
*/
#ifndef __BSD_TEST_H__
#define __BSD_TEST_H__
#if defined(HAVE_CONFIG_H)
#if __has_include(<config/config_ac.h>)
#include <config/config_ac.h>
#else
#include <config/config.h>
#endif
#endif
#if !HAVE_PRINTFLIKE
#ifndef __printflike
#if __has_attribute(format)
#define __printflike(a,b) __attribute__((format(printf, a, b)))
#else
#define __printflike(a,b)
#endif // __has_attribute(format)
#endif // !defined(__printflike)
#endif // !HAVE_PRINTFLIKE
#include <errno.h>
#ifdef __APPLE__
#include <mach/error.h>
#endif
#if defined(__APPLE__) || defined(HAVE_COREFOUNDATION)
#include <CoreFoundation/CoreFoundation.h>
#endif
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <string.h>
#include <stdint.h>
static inline const char*
__BASENAME__(const char *_str_)
{
const char *_s_ = strrchr(_str_, '/');
return (_s_ ? _s_ : _str_ - 1) + 1;
}
#define __SOURCE_FILE__ __BASENAME__(__FILE__)
#if defined(__cplusplus)
extern "C" {
#endif
/**
* test_start() provides the TEST token. Use this once per test "tool"
*/
void test_start(const char* desc);
#if defined(__APPLE__) && defined(__MACH__)
/**
* Explicitly runs the 'leaks' test without stopping the process.
*/
void test_leaks_pid(const char *name, pid_t pid);
void test_leaks(const char *name);
#endif
/**
* test_stop() checks for leaks during the tests using leaks-wrapper. Use this at the end of each "tool"
*/
void test_stop(void) __attribute__((__noreturn__));
void test_stop_after_delay(void *delay) __attribute__((__noreturn__));
/**
* Each test "tool" can used one or more of these functions to perform actual
* testing and provide a PASS/FAIL token. All APIs take a descriptive string
* that is printed after the token.
*/
void _test_ptr_null(const char* file, long line, const char* desc, const void* ptr);
#define test_ptr_null(a,b) _test_ptr_null(__SOURCE_FILE__, __LINE__, a, b)
void test_ptr_null_format(void *ptr, const char *format, ...);
void _test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr);
#define test_ptr_notnull(a,b) _test_ptr_notnull(__SOURCE_FILE__, __LINE__, a, b)
void test_ptr_notnull_format(const void *ptr, const char *format, ...) __printflike(2, 3);
void _test_ptr_not(const char* file, long line, const char* desc, const void* actual, const void* expected);
#define test_ptr_not(a, b, c) _test_ptr_not(__SOURCE_FILE__, __LINE__, a, b, c)
void test_ptr_not_format(const void* actual, const void* expected, const char *format, ...);
void _test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected);
#define test_ptr(a,b,c) _test_ptr(__SOURCE_FILE__, __LINE__, a, b, c)
void test_ptr_format(const void* actual, const void* expected, const char *format, ...) __printflike(3,4);
void _test_uint32(const char* file, long line, const char* desc, uint32_t actual, uint32_t expected);
#define test_uint32(a,b,c) _test_uint32(__SOURCE_FILE__, __LINE__, a, b, c)
void test_uint32_format(uint32_t actual, uint32_t expected, const char *format, ...) __printflike(3,4);
void _test_int32(const char* file, long line, const char* desc, int32_t actual, int32_t expected);
#define test_int32(a,b,c) _test_int32(__SOURCE_FILE__, __LINE__, a, b, c)
void test_int32_format(int32_t actual, int32_t expected, const char* format, ...) __printflike(3,4);
void _test_long(const char* file, long line, const char* desc, long actual, long expected);
#define test_long(a,b,c) _test_long(__SOURCE_FILE__, __LINE__, a, b, c)
void test_long_format(long actual, long expected, const char *format, ...) __printflike(3,4);
void _test_sizet(const char* file, long line, const char* desc, size_t actual, size_t expected);
#define test_sizet(a,b,c) _test_sizet(__SOURCE_FILE__, __LINE__, a, b, c)
void test_sizet_format(size_t actual, size_t expected, const char *format, ...) __printflike(3,4);
void _test_uint64(const char* file, long line, const char* desc, uint64_t actual, uint64_t expected);
#define test_uint64(a,b,c) _test_uint64(__SOURCE_FILE__, __LINE__, a, b, c)
void test_uint64_format(uint64_t actual, uint64_t expected, const char* desc, ...);
void _test_int64(const char* file, long line, const char* desc, int64_t actual, int64_t expected);
#define test_int64(a,b,c) _test_uint64(__SOURCE_FILE__, __LINE__, a, b, c)
void test_int64_format(int64_t actual, int64_t expected, const char* desc, ...);
void _test_long_less_than(const char* file, long line, const char* desc, long actual, long max_expected);
#define test_long_less_than(a,b,c) _test_long_less_than(__SOURCE_FILE__, __LINE__, a, b, c)
void test_long_less_than_format(long actual, long max_expected, const char *format, ...) __printflike(3,4);
void _test_long_less_than_or_equal(const char* file, long line, const char* desc, long actual, long max_expected);
#define test_long_less_than_or_equal(a,b,c) _test_long_less_than_or_equal(__SOURCE_FILE__, __LINE__, a, b, c)
void test_long_less_than_or_equal_format(long actual, long max_expected, const char *format, ...) __printflike(3,4);
void _test_long_greater_than_or_equal(const char* file, long line, const char* desc, long actual, long expected_min);
#define test_long_greater_than_or_equal(a,b,c) _test_long_greater_than_or_equal(__SOURCE_FILE__, __LINE__, a, b, c)
void test_long_greater_than_or_equal_format(long actual, long expected_min, const char *format, ...) __printflike(3,4);
void _test_sizet_less_than(const char* file, long line, const char* desc, size_t actual, size_t max_expected);
#define test_sizet_less_than(a,b,c) _test_sizet_less_than(__SOURCE_FILE__, __LINE__, a, b, c)
void test_sizet_less_than_format(size_t actual, size_t max_expected, const char *format, ...) __printflike(3,4);
void _test_sizet_less_than_or_equal(const char* file, long line, const char* desc, size_t actual, size_t max_expected);
#define test_sizet_less_than_or_equal(a,b,c) _test_sizet_less_than_or_equal(__SOURCE_FILE__, __LINE__, a, b, c)
void test_sizet_less_than_or_equal_format(size_t actual, size_t max_expected, const char *format, ...) __printflike(3,4);
void _test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected);
#define test_double_less_than_or_equal(d, v, m) _test_double_less_than_or_equal(__SOURCE_FILE__, __LINE__, d, v, m)
void test_double_less_than_or_equal_format(double val, double max_expected, const char *format, ...) __printflike(3,4);
void _test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected);
#define test_double_less_than(d, v, m) _test_double_less_than(__SOURCE_FILE__, __LINE__, d, v, m)
void test_double_less_than_format(double val, double max_expected, const char *format, ...) __printflike(3,4);
void _test_double_equal(const char* file, long line, const char* desc, double val, double expected);
#define test_double_equal(d, v, m) _test_double_equal(__SOURCE_FILE__, __LINE__, d, v, m)
void test_double_equal_format(double val, double expected, const char *format, ...) __printflike(3,4);
void _test_errno(const char* file, long line, const char* desc, int actual, int expected);
#define test_errno(a,b,c) _test_errno(__SOURCE_FILE__, __LINE__, a, b, c)
void test_errno_format(int actual, int expected, const char *format, ...) __printflike(3,4);
#if defined(__APPLE__)
void _test_mach_error(const char* file, long line, const char* desc, mach_error_t actual, mach_error_t expected);
#define test_mach_error(a,b,c) _test_mach_error(__SOURCE_FILE__, __LINE__, a, b, c)
void test_mach_error_format(mach_error_t actual, mach_error_t expected, const char *format, ...) __printflike(3,4);
#endif
#if defined(__APPLE__) || defined(HAVE_COREFOUNDATION)
void test_cferror(const char* desc, CFErrorRef actual, CFIndex expectedCode);
void test_cferror_format(CFErrorRef actual, CFIndex expectedCode, const char *format, ...) __printflike(3,4);
#endif
void _test_skip(const char* file, long line, const char* desc);
#define test_skip(m) _test_skip(__SOURCE_FILE__, __LINE__, m)
#define test_skip2(m) _test_skip("", 0, m)
void test_skip_format(const char *format, ...) __printflike(1,2);
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif /* __BSD_TEST_H__ */

122
tests/bsdtestsummarize.c Normal file
View File

@ -0,0 +1,122 @@
/*
* 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
int
has_prefix(const char* str, const char* prefix)
{
return (strncmp(str, prefix, strlen(prefix)) == 0);
}
int
print_summary(FILE* f, long total, long pass, long fail, long skip)
{
double dpass = 100.0 ,dfail = 0.0, dskip = 0.0;
fprintf(f, "Total: %ld\n", total);
if (total) {
dpass = ((double)pass / (double)(total - skip)) * 10000.0;
dpass = floor(dpass) / 100.0;
dfail = ((double)fail / (double)(total - skip)) * 10000.0;
dfail = ceil(dfail) / 100.0;
dskip = ((double)skip / (double)total) * 10000.0;
dskip = ceil(dskip) / 100.0;
}
fprintf(f, "Passed: %ld (%0.2lf%%)\nFailed: %ld (%0.2lf%%)\nSkipped: %ld (%0.2lf%%)\n\n", pass, dpass, fail, dfail, skip, dskip);
return 0;
}
int
main(int argc, char* argv[])
{
if (argc > 1) {
fprintf(stderr, "%s: usage: summarize\n", argv[0]);
exit(1);
}
/*
FILE* f = fopen(argv[1], "w");
if (f == NULL) {
perror(argv[1]);
exit(1);
}
*/
FILE* f = stdout;
fprintf(f, "\n==================================================\n");
fprintf(f, "[SUMMARY] Test Summary\n");
fprintf(f, "==================================================\n\n");
size_t len;
char* ln, lastln[1024];
int first_test = 1;
long total = 0;
long pass = 0;
long fail = 0;
long skip = 0;
long total_total = 0;
long total_pass = 0;
long total_fail = 0;
long total_skip = 0;
for(;;) {
ln = fgetln(stdin, &len);
//if (ln) fprintf(stdout, "%.*s", (int)len, ln);
if (ln == NULL || (has_prefix(ln, "[TEST]") &&
strncmp(ln, lastln, MIN(len,1024)))) {
if (total || !first_test) {
print_summary(f, total, pass, fail, skip);
first_test = 0;
}
total_total += total;
total_pass += pass;
total_fail += fail;
total_skip += skip;
total = 0;
pass = 0;
fail = 0;
skip = 0;
if (ln) {
fprintf(f, "%.*s", (int)len, ln);
strncpy(lastln, ln, MIN(len,1024));
} else {
fprintf(f, "[TOTAL]\n");
print_summary(f, total_total, total_pass, total_fail, total_skip);
break;
}
} else if (has_prefix(ln, "[PASS]")) {
++total;
++pass;
} else if (has_prefix(ln, "[FAIL]")) {
++total;
++fail;
} else if (has_prefix(ln, "[SKIP]")) {
++total;
++skip;
}
}
return (total_fail ? EXIT_FAILURE : EXIT_SUCCESS);
}

175
tests/cffd.c Normal file
View File

@ -0,0 +1,175 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <string.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <sys/queue.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <CoreFoundation/CoreFoundation.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static int long kevent_data = 0;
#if 0
int debug = 0;
#define DEBUG(...) do { \
if (debug) fprintf(stderr, __VA_ARGS__); \
} while(0);
#endif
#define assert_errno(str, expr) do { \
if (!(expr)) { \
fprintf(stderr, "%s: %s\n", (str), strerror(errno)); \
exit(1); \
} } while(0);
int
init_kqueue(void)
{
int kq;
int res;
struct kevent ke;
static struct timespec t0;
kq = kqueue();
assert_errno("kqueue", kq >= 0);
EV_SET(&ke, 1, EVFILT_TIMER, EV_ADD, NOTE_USECONDS, USEC_PER_SEC/10, 0);
res = kevent(kq, &ke, 1, NULL, 0, &t0);
assert_errno("kevent", res == 0);
return kq;
}
int
read_kevent(int kq)
{
int res;
struct kevent ke;
//static struct timespec t0;
res = kevent(kq, NULL, 0, &ke, 1, NULL);
assert_errno("kevent", res >= 0);
kevent_data += ke.data;
fprintf(stdout, "kevent.data = %ld\n", ke.data);
return (res < 0);
}
static void
cffd_callback(CFFileDescriptorRef cffd,
CFOptionFlags callBackTypes __attribute__((unused)),
void *info __attribute__((unused)))
{
int kq;
kq = CFFileDescriptorGetNativeDescriptor(cffd);
if (read_kevent(kq) == 0) {
// ...
}
CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
}
#if 0
void
timer()
{
dispatch_source_t ds;
ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
assert(ds);
dispatch_source_set_timer(ds, dispatch_time(0, 1*NSEC_PER_SEC), NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(ds, ^{
printf("ping\n");
});
dispatch_resume(ds);
}
void
hangup()
{
dispatch_source_t ds;
ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, dispatch_get_main_queue());
assert(ds);
dispatch_source_set_event_handler(ds, ^{
printf("hangup\n");
});
dispatch_resume(ds);
}
#endif
int
main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
{
int kq;
CFFileDescriptorRef cffd;
CFRunLoopSourceRef rls;
CFFileDescriptorContext ctx;
dispatch_test_start("CFFileDescriptor");
#if 0
signal(SIGHUP, SIG_IGN);
#endif
kq = init_kqueue();
memset(&ctx, 0, sizeof(CFFileDescriptorContext));
cffd = CFFileDescriptorCreate(NULL, kq, 1, cffd_callback, &ctx);
assert(cffd);
rls = CFFileDescriptorCreateRunLoopSource(NULL, cffd, 0);
assert(rls);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
#if 0
timer();
hangup();
#endif
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.05, false);
// Should fire at least 10 times ...
test_long_greater_than_or_equal("kevent data", kevent_data, 10);
test_stop();
return 0;
}

10
tests/darling/run-all.sh.in Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
# FIXME: see run-single.sh
TEST_DIR="/usr/libexec/test/dispatch"
run-test() {
"${TEST_DIR}/run-single.sh" "$1"
}
@TEST_COMMANDS@

14
tests/darling/run-single.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# FIXME: this path should probably not be hardcoded, but determining the script path on old Bash like the one macOS ships is weird/difficult
TEST_DIR="/usr/libexec/test/dispatch"
if [ $# -ne 1 ]; then
echo "Usage: $0 <test-name>"
exit 1
fi
# Darling doesn't have the `leaks` command yet
export NOLEAKS=1
"${TEST_DIR}/dispatch_bsdtestharness" "${TEST_DIR}/dispatch_test_$1"

92
tests/dispatch_after.c Normal file
View File

@ -0,0 +1,92 @@
/*
* 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 <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <stdlib.h>
#include <assert.h>
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#endif
#include <bsdtests.h>
#include <Block.h>
#include "dispatch_test.h"
static void
done(void *arg /*__unused */)
{
(void)arg;
sleep(1);
test_stop();
}
static void
test_after(void)
{
__block dispatch_time_t time_a_min, time_a, time_a_max;
__block dispatch_time_t time_b_min, time_b, time_b_max;
__block dispatch_time_t time_c_min, time_c, time_c_max;
dispatch_test_start("Dispatch After");
dispatch_async(dispatch_get_main_queue(), ^{
time_a_min = dispatch_time(0, (int64_t)(5.5*NSEC_PER_SEC));
time_a = dispatch_time(0, (int64_t)(6*NSEC_PER_SEC));
time_a_max = dispatch_time(0, (int64_t)(6.5*NSEC_PER_SEC));
dispatch_after(time_a, dispatch_get_main_queue(), ^{
dispatch_time_t now_a = dispatch_time(0, 0);
test_long_less_than("can't finish faster than 5.5s", 0, (long)(now_a - time_a_min));
test_long_less_than("must finish faster than 6.5s", 0, (long)(time_a_max - now_a));
time_b_min = dispatch_time(0, (int64_t)(1.5*NSEC_PER_SEC));
time_b = dispatch_time(0, (int64_t)(2*NSEC_PER_SEC));
time_b_max = dispatch_time(0, (int64_t)(2.5*NSEC_PER_SEC));
dispatch_after(time_b, dispatch_get_main_queue(), ^{
dispatch_time_t now_b = dispatch_time(0, 0);
test_long_less_than("can't finish faster than 1.5s", 0, (long)(now_b - time_b_min));
test_long_less_than("must finish faster than 2.5s", 0, (long)(time_b_max - now_b));
time_c_min = dispatch_time(0, 0*NSEC_PER_SEC);
time_c = dispatch_time(0, 0*NSEC_PER_SEC);
time_c_max = dispatch_time(0, (int64_t)(.5*NSEC_PER_SEC));
dispatch_after(time_c, dispatch_get_main_queue(), ^{
dispatch_time_t now_c = dispatch_time(0, 0);
test_long_less_than("can't finish faster than 0s", 0, (long)(now_c - time_c_min));
test_long_less_than("must finish faster than .5s", 0, (long)(time_c_max - now_c));
dispatch_async_f(dispatch_get_main_queue(), NULL, done);
});
});
});
});
}
int
main(void)
{
test_after();
dispatch_main();
return 1;
}

44
tests/dispatch_api.c Normal file
View File

@ -0,0 +1,44 @@
/*
* 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 <stdlib.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static void
work(void *context __attribute__((unused)))
{
test_stop();
exit(0);
}
int
main(void)
{
dispatch_queue_t q;
dispatch_test_start("Dispatch (Public) API");
q = dispatch_get_main_queue();
test_ptr_notnull("dispatch_get_main_queue", q);
dispatch_async_f(dispatch_get_main_queue(), NULL, work);
dispatch_main();
return 0;
}

157
tests/dispatch_apply.c Normal file
View File

@ -0,0 +1,157 @@
/*
* 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 <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#ifdef __ANDROID__
#include <linux/sysctl.h>
#else
#if !defined(__linux__)
#include <sys/sysctl.h>
#endif
#endif /* __ANDROID__ */
#endif
#include <stdlib.h>
#include <assert.h>
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#endif
#include <sys/types.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static volatile int32_t busy_threads_started, busy_threads_finished;
/*
* Keep a thread busy, spinning on the CPU.
*/
static volatile int all_done = 0;
/* Fiddling with j in the middle and hitting this global will hopefully keep
* the optimizer from cutting the whole thing out as dead code.
*/
static volatile unsigned int busythread_useless;
static void busythread(void *ignored)
{
(void)ignored;
/* prevent i and j been optimized out */
volatile uint64_t i = 0, j = 0;
OSAtomicIncrement32(&busy_threads_started);
while(!all_done)
{
if(i == 500000) { j -= busythread_useless; }
j += i;
i += 1;
}
OSAtomicIncrement32(&busy_threads_finished);
}
/*
* Test that dispatch_apply can make progress and finish, even if there are
* so many other running and unblocked workqueue threads that the apply's
* helper threads never get a chance to come up.
*
* <rdar://problem/10718199> dispatch_apply should not block waiting on other
* threads while calling thread is available
*/
static void test_apply_contended(dispatch_queue_t dq)
{
uint32_t activecpu;
#if defined(__linux__) || defined(__OpenBSD__)
activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
#elif defined(_WIN32)
SYSTEM_INFO si;
GetSystemInfo(&si);
activecpu = si.dwNumberOfProcessors;
#else
size_t s = sizeof(activecpu);
sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0);
#endif
int tIndex, n_threads = (int)activecpu;
dispatch_group_t grp = dispatch_group_create();
for(tIndex = 0; tIndex < n_threads; tIndex++) {
dispatch_group_async_f(grp, dq, NULL, busythread);
}
// Spin until all the threads have actually started
while(busy_threads_started < n_threads) {
usleep(1);
}
volatile __block int32_t count = 0;
const int32_t final = 32;
int32_t before = busy_threads_started;
dispatch_apply(final, dq, ^(size_t i __attribute__((unused))) {
OSAtomicIncrement32(&count);
});
int32_t after = busy_threads_finished;
test_long("contended: threads started before apply", before, n_threads);
test_long("contended: count", count, final);
test_long("contended: threads finished before apply", after, 0);
/* Release busy threads by setting all_done to 1 */
all_done = 1;
dispatch_group_wait(grp, DISPATCH_TIME_FOREVER);
dispatch_release(grp);
}
int
main(void)
{
dispatch_test_start("Dispatch Apply");
volatile __block int32_t count = 0;
const int32_t final = 32;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
test_ptr_notnull("dispatch_get_global_queue", queue);
dispatch_apply(final, queue, ^(size_t i __attribute__((unused))) {
OSAtomicIncrement32(&count);
});
test_long("count", count, final);
count = 0; // rdar://problem/9294578
dispatch_apply(final, queue, ^(size_t i __attribute__((unused))) {
dispatch_apply(final, queue, ^(size_t ii __attribute__((unused))) {
dispatch_apply(final, queue, ^(size_t iii __attribute__((unused))) {
OSAtomicIncrement32(&count);
});
});
});
test_long("nested count", count, final * final * final);
test_apply_contended(queue);
test_stop();
return 0;
}

44
tests/dispatch_c99.c Normal file
View File

@ -0,0 +1,44 @@
/*
* 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 <stdlib.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static void
work(void *context __attribute__((unused)))
{
test_stop();
exit(0);
}
int
main(void)
{
dispatch_test_start("Dispatch C99");
dispatch_queue_t q = dispatch_get_main_queue();
test_ptr_notnull("dispatch_get_main_queue", q);
dispatch_async_f(dispatch_get_main_queue(), NULL, work);
dispatch_main();
return 0;
}

136
tests/dispatch_cascade.c Normal file
View File

@ -0,0 +1,136 @@
/*
* 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>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <stdlib.h>
#include <bsdtests.h>
#include "dispatch_test.h"
int done = 0;
#define QUEUES 80
dispatch_queue_t queues[QUEUES];
#define BLOCKS 10000
union {
size_t index;
char padding[64];
} indices[BLOCKS];
size_t iterations = (QUEUES * BLOCKS) / 4;
static void
noop(void *ctxt __attribute__((unused)))
{
return;
}
static void
cleanup(void *ctxt __attribute__((unused)))
{
size_t q;
for (q = 0; q < QUEUES; ++q) {
dispatch_sync_f(queues[q], NULL, noop);
dispatch_release(queues[q]);
}
test_stop();
exit(0);
}
static void
histogram(void)
{
size_t counts[QUEUES] = {};
size_t maxcount = 0;
size_t q;
for (q = 0; q < QUEUES; ++q) {
size_t i;
for (i = 0; i < BLOCKS; ++i) {
if (indices[i].index == q) {
++counts[q];
}
}
}
for (q = 0; q < QUEUES; ++q) {
if (counts[q] > maxcount) {
maxcount = counts[q];
}
}
printf("maxcount = %zd\n", maxcount);
size_t x,y;
for (y = 20; y > 0; --y) {
for (x = 0; x < QUEUES; ++x) {
double fraction = (double)counts[x] / (double)maxcount;
double value = fraction * (double)20;
printf("%s", (value > y) ? "*" : " ");
}
printf("\n");
}
}
static void
cascade(void* context)
{
size_t idx, *idxptr = context;
if (done) return;
idx = *idxptr + 1;
if (idx < QUEUES) {
*idxptr = idx;
dispatch_async_f(queues[idx], context, cascade);
}
if (__sync_sub_and_fetch(&iterations, 1) == 0) {
done = 1;
histogram();
dispatch_async_f(dispatch_get_main_queue(), NULL, cleanup);
}
}
int
main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
{
int i;
dispatch_test_start("Dispatch Cascade");
for (i = 0; i < QUEUES; ++i) {
queues[i] = dispatch_queue_create(NULL, NULL);
}
for (i = 0; i < BLOCKS; ++i) {
cascade(&indices[i].index);
}
dispatch_main();
return 0;
}

61
tests/dispatch_cf_main.c Normal file
View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2010-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 <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
const int32_t final = 10;
static volatile int32_t count;
static void
work(void* ctxt __attribute__((unused)))
{
int32_t c = OSAtomicIncrement32(&count);
if (c < final-1) {
dispatch_async_f(dispatch_get_main_queue(), NULL, work);
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^{
fprintf(stderr, "CFRunLoopPerformBlock %d\n", c);
test_long_less_than("CFRunLoopPerformBlock", count, final);
});
}
}
int
main(void)
{
dispatch_test_start("Dispatch CF main queue"); // <rdar://problem/7760523>
dispatch_async_f(dispatch_get_main_queue(), NULL, work);
dispatch_async_f(dispatch_get_main_queue(), NULL, work);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
test_long("done", count, final);
test_stop();
});
CFRunLoopRun();
return 0;
}

295
tests/dispatch_concur.c Normal file
View File

@ -0,0 +1,295 @@
/*
* Copyright (c) 2010-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 <dispatch/private.h>
#include <stdlib.h>
#include <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/types.h>
#include <unistd.h>
#ifdef __ANDROID__
#include <linux/sysctl.h>
#else
#if !defined(__linux__)
#include <sys/sysctl.h>
#endif
#endif /* __ANDROID__ */
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
static volatile size_t done, concur;
static int use_group_async;
static uint32_t activecpu;
static uint32_t min_acceptable_concurrency;
static dispatch_queue_t q;
static dispatch_group_t g, gw;
const size_t workers = 4;
static void
nop(void* ctxt __attribute__((unused)))
{
return;
}
static void
work(void* ctxt __attribute__((unused)))
{
usleep(1000);
__sync_add_and_fetch(&done, 1);
if (!use_group_async) dispatch_group_leave(gw);
}
static void
submit_work(void* ctxt)
{
size_t c = __sync_add_and_fetch(&concur, 1), *m = (size_t *)ctxt, i;
if (c > *m) *m = c;
for (i = 0; i < workers; ++i) {
if (use_group_async) {
dispatch_group_async_f(gw, q, NULL, work);
} else {
dispatch_group_enter(gw);
dispatch_async_f(q, NULL, work);
}
}
usleep(10000);
__sync_sub_and_fetch(&concur, 1);
if (!use_group_async) dispatch_group_leave(g);
}
static void
test_concur_async(size_t n, size_t qw)
{
size_t i, max_concur = 0, *mcs = calloc(n, sizeof(size_t)), *mc;
done = concur = 0;
dispatch_suspend(q);
for (i = 0, mc = mcs; i < n; i++, mc++) {
if (use_group_async) {
dispatch_group_async_f(g, q, mc, submit_work);
} else {
dispatch_group_enter(g);
dispatch_async_f(q, mc, submit_work);
}
}
dispatch_resume(q);
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
if (qw > 1) {
size_t concurrency = MIN(n * workers, qw);
if (done > min_acceptable_concurrency) {
test_sizet_less_than_or_equal("concurrently completed workers", done, concurrency);
} else {
test_sizet("concurrently completed workers", done, concurrency);
}
} else {
test_sizet_less_than_or_equal("concurrently completed workers", done, 1);
}
for (i = 0, mc = mcs; i < n; i++, mc++) {
if (*mc > max_concur) max_concur = *mc;
}
free(mcs);
size_t expect = MIN(n, qw);
if (max_concur > min_acceptable_concurrency) {
test_sizet_less_than_or_equal("max submission concurrency", max_concur, expect);
} else {
test_sizet("max submission concurrency", max_concur, expect);
}
dispatch_group_wait(gw, DISPATCH_TIME_FOREVER);
usleep(1000);
}
static void
sync_work(void* ctxt)
{
size_t c = __sync_add_and_fetch(&concur, 1), *m = (size_t *)ctxt;
if (c > *m) *m = c;
usleep(10000);
__sync_sub_and_fetch(&concur, 1);
}
static void
test_concur_sync(size_t n, size_t qw)
{
size_t i, max_concur = 0, *mcs = calloc(n, sizeof(size_t)), *mc;
concur = 0;
for (i = 0, mc = mcs; i < n; i++, mc++) {
dispatch_group_async(g,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,
DISPATCH_QUEUE_OVERCOMMIT), ^{
usleep(100000);
dispatch_sync_f(q, mc, sync_work);
});
}
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
for (i = 0, mc = mcs; i < n; i++, mc++) {
if (*mc > max_concur) max_concur = *mc;
}
free(mcs);
size_t expect = qw == 1 ? 1 : n;
if (max_concur > min_acceptable_concurrency) {
test_sizet_less_than_or_equal("max sync concurrency", max_concur, expect);
} else {
test_sizet("max sync concurrency", max_concur, expect);
}
}
static void
apply_work(void* ctxt, size_t i)
{
size_t c = __sync_add_and_fetch(&concur, 1), *m = ((size_t *)ctxt) + i;
if (c > *m) *m = c;
usleep(100000);
__sync_sub_and_fetch(&concur, 1);
}
static void
test_concur_apply(size_t n, size_t qw)
{
size_t i, max_concur = 0, *mcs = calloc(n, sizeof(size_t)), *mc;
concur = 0;
dispatch_apply_f(n, q, mcs, apply_work);
for (i = 0, mc = mcs; i < n; i++, mc++) {
if (*mc > max_concur) max_concur = *mc;
}
free(mcs);
size_t expect = MIN(n, qw);
if (max_concur > min_acceptable_concurrency) {
test_sizet_less_than_or_equal("max apply concurrency", max_concur, expect);
} else {
test_sizet("max apply concurrency", max_concur, expect);
}
}
static dispatch_queue_t
create_queue(long width, dispatch_queue_t tq, long *qw, const char **ql)
{
if (!width) {
*qw = LONG_MAX;
*ql = "global";
return dispatch_get_global_queue(0, 0);
};
dispatch_queue_t queue;
dispatch_queue_attr_t qattr = NULL;
*qw = width;
*ql = width < LONG_MAX ? ( width == 1 ? "serial": "wide" ) : "concurrent";
#if DISPATCH_API_VERSION >= 20100518 // <rdar://problem/7790099>
qattr = width < LONG_MAX ? NULL : DISPATCH_QUEUE_CONCURRENT;
#endif
queue = dispatch_queue_create(*ql, qattr);
if (!qattr) {
dispatch_queue_set_width(queue, width);
}
if (tq) {
dispatch_set_target_queue(queue, tq);
}
if (!qattr || tq) {
dispatch_barrier_sync_f(queue, NULL, nop); // wait for changes to take effect
}
return queue;
}
int
main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
{
dispatch_test_start("Dispatch Private Concurrent/Wide Queue"); // <rdar://problem/8049506&8169448&8186485>
#ifdef __linux__
activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
#elif defined(_WIN32)
SYSTEM_INFO si;
GetSystemInfo(&si);
activecpu = si.dwNumberOfProcessors;
#else
size_t s = sizeof(activecpu);
sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0);
#endif
size_t n = activecpu / 2 > 1 ? activecpu / 2 : 1, w = activecpu * 2;
min_acceptable_concurrency = (uint32_t)n;
dispatch_queue_t tq, ttq;
long qw, tqw, ttqw;
const char *ql, *tql, *ttql;
size_t qi, tqi, ttqi;
long qws[] = {
0, LONG_MAX, (long)w, 1, // 0 <=> global queue
};
g = dispatch_group_create();
gw = dispatch_group_create();
for (ttqi = 0; ttqi < sizeof(qws)/sizeof(*qws); ttqi++) {
ttq = create_queue(qws[ttqi], NULL, &ttqw, &ttql);
for (tqi = 0; tqi < sizeof(qws)/sizeof(*qws); tqi++) {
if (!qws[tqi] && qws[ttqi]) continue;
tq = create_queue(qws[tqi], ttq, &tqw, &tql);
for (qi = 0; qi < sizeof(qws)/sizeof(*qws); qi++) {
if (!qws[qi] && qws[tqi]) continue;
q = create_queue(qws[qi], tq, &qw, &ql);
for (use_group_async = 0; use_group_async < 2; use_group_async++) {
fprintf(stdout, "Testing dispatch%s_async on "
"queue hierarchy: %s -> %s -> %s\n",
use_group_async ? "_group" : "", ql, tql, ttql);
fflush(stdout);
test_concur_async(n, (size_t)MIN(qw, MIN(tqw, ttqw)));
}
fprintf(stdout, "Testing dispatch_sync on "
"queue hierarchy: %s -> %s -> %s\n", ql, tql, ttql);
fflush(stdout);
test_concur_sync(w, (size_t)MIN(qw, MIN(tqw, ttqw)));
fprintf(stdout, "Testing dispatch_apply on "
"queue hierarchy: %s -> %s -> %s\n", ql, tql, ttql);
fflush(stdout);
test_concur_apply(activecpu, (size_t)MIN(qw, MIN(tqw, ttqw)));
dispatch_release(q);
}
dispatch_release(tq);
}
dispatch_release(ttq);
}
dispatch_release(g);
dispatch_release(gw);
test_stop();
return 0;
}

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2010-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 <stdlib.h>
#include <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <assert.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#if DISPATCH_API_VERSION >= 20100825 && DISPATCH_API_VERSION != 20101110
static const char *ctxts[] = {"ctxt for app", "ctxt for key 1",
"ctxt for key 2", "ctxt for key 1 bis", "ctxt for key 4"};
volatile long ctxts_destroyed;
static dispatch_group_t g;
static void
destructor(void *ctxt)
{
fprintf(stderr, "destructor of %s\n", (char*)ctxt);
(void)__sync_add_and_fetch(&ctxts_destroyed, 1);
dispatch_group_leave(g);
}
static void
test_context_for_key(void)
{
g = dispatch_group_create();
dispatch_queue_t q = dispatch_queue_create("q", NULL);
#if DISPATCH_API_VERSION >= 20100518
dispatch_queue_t tq = dispatch_queue_create("tq", DISPATCH_QUEUE_CONCURRENT);
#else
dispatch_queue_t tq = dispatch_queue_create("tq", NULL);
dispatch_queue_set_width(tq, LONG_MAX);
#endif
dispatch_queue_t ttq = dispatch_get_global_queue(0, 0);
dispatch_group_enter(g);
#if DISPATCH_API_VERSION >= 20101011
dispatch_queue_set_specific(tq, &ctxts[4], (char *)ctxts[4], destructor);
#else
dispatch_set_context_for_key(tq, &ctxts[4], ctxts[4], ttq, destructor);
#endif
dispatch_set_target_queue(tq, ttq);
dispatch_group_enter(g);
dispatch_set_context(q, (char *)ctxts[0]);
dispatch_set_target_queue(q, tq);
dispatch_set_finalizer_f(q, destructor);
dispatch_async(q, ^{
dispatch_group_enter(g);
#if DISPATCH_API_VERSION >= 20101011
dispatch_queue_set_specific(q, &ctxts[1], (char *)ctxts[1], destructor);
#else
dispatch_set_context_for_key(q, &ctxts[1], ctxts[1], ttq, destructor);
#endif
});
dispatch_retain(q);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_group_enter(g);
#if DISPATCH_API_VERSION >= 20101011
dispatch_queue_set_specific(q, &ctxts[2], (char *)ctxts[2], destructor);
#else
dispatch_set_context_for_key(q, &ctxts[2], ctxts[2], ttq, destructor);
#endif
dispatch_async(dispatch_get_global_queue(0, 0), ^{
void *ctxt;
#if DISPATCH_API_VERSION >= 20101011
ctxt = dispatch_queue_get_specific(q, &ctxts[2]);
#else
ctxt = dispatch_get_context_for_key(q, &ctxts[2]);
#endif
test_ptr("get context for key 2", ctxt, ctxts[2]);
dispatch_release(q);
});
});
dispatch_async(q, ^{
void *ctxt;
#if DISPATCH_API_VERSION >= 20101011
ctxt = dispatch_get_specific(&ctxts[1]);
test_ptr("get current context for key 1", ctxt, ctxts[1]);
ctxt = dispatch_get_specific(&ctxts[4]);
test_ptr("get current context for key 4 (on target queue)", ctxt, ctxts[4]);
ctxt = dispatch_queue_get_specific(q, &ctxts[1]);
#else
ctxt = dispatch_get_context_for_key(tq, &ctxts[4]);
test_ptr("get context for key 4 (on target queue)", ctxt, ctxts[4]);
ctxt = dispatch_get_context_for_key(q, &ctxts[1]);
#endif
test_ptr("get context for key 1", ctxt, ctxts[1]);
});
dispatch_async(q, ^{
dispatch_group_enter(g);
void *ctxt;
#if DISPATCH_API_VERSION >= 20101011
dispatch_queue_set_specific(q, &ctxts[1], (char *)ctxts[3], destructor);
ctxt = dispatch_queue_get_specific(q, &ctxts[1]);
#else
dispatch_set_context_for_key(q, &ctxts[1], ctxts[3], ttq, destructor);
ctxt = dispatch_get_context_for_key(q, &ctxts[1]);
#endif
test_ptr("get context for key 1", ctxt, ctxts[3]);
});
dispatch_async(q, ^{
void *ctxt;
#if DISPATCH_API_VERSION >= 20101011
dispatch_queue_set_specific(q, &ctxts[1], NULL, destructor);
ctxt = dispatch_queue_get_specific(q, &ctxts[1]);
#else
dispatch_set_context_for_key(q, &ctxts[1], NULL, ttq, destructor);
ctxt = dispatch_get_context_for_key(q, &ctxts[1]);
#endif
test_ptr("get context for key 1", ctxt, NULL);
});
void *ctxt = dispatch_get_context(q);
test_ptr("get context for app", ctxt, ctxts[0]);
dispatch_release(tq);
dispatch_release(q);
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
test_long("contexts destroyed", ctxts_destroyed, 5);
dispatch_release(g);
}
#endif
int
main(void)
{
dispatch_test_start("Dispatch Queue Specific"); // rdar://problem/8429188
dispatch_async(dispatch_get_main_queue(), ^{
#if DISPATCH_API_VERSION >= 20100825 && DISPATCH_API_VERSION != 20101110
test_context_for_key();
#endif
test_stop();
});
dispatch_main();
}

120
tests/dispatch_data.c Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2009-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 <stdlib.h>
#include <dispatch/dispatch.h>
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
#ifndef DISPATCHTEST_DATA
#if DISPATCH_API_VERSION >= 20100226 && DISPATCH_API_VERSION != 20101110
#define DISPATCHTEST_DATA 1
#endif
#endif
dispatch_group_t g;
#if DISPATCHTEST_DATA
static void
test_concat(void)
{
dispatch_group_enter(g);
dispatch_async(dispatch_get_main_queue(), ^{
const char* buffer1 = "This is buffer1 ";
size_t size1 = 17;
const char* buffer2 = "This is buffer2 ";
size_t size2 = 17;
__block bool buffer2_destroyed = false;
dispatch_data_t data1 = dispatch_data_create(buffer1, size1, NULL, NULL);
dispatch_data_t data2 = dispatch_data_create(buffer2, size2,
dispatch_get_main_queue(), ^{
buffer2_destroyed = true;
});
dispatch_data_t concat = dispatch_data_create_concat(data1, data2);
dispatch_release(data1);
dispatch_release(data2);
test_long("Data size of concatenated dispatch data",
(long)dispatch_data_get_size(concat), 34);
const void* contig;
size_t contig_size;
dispatch_data_t contig_data =
dispatch_data_create_map(concat, &contig, &contig_size);
dispatch_release(concat);
dispatch_release(contig_data);
test_long("Contiguous memory size", (long)contig_size, 34);
dispatch_async(dispatch_get_main_queue(), ^{
test_long("buffer2 destroyed", buffer2_destroyed, true);
dispatch_group_leave(g);
});
});
}
static void
test_cleanup(void) // <rdar://problem/9843440>
{
static char buffer4[16];
dispatch_group_enter(g);
dispatch_async(dispatch_get_main_queue(), ^{
void *buffer3 = malloc(1024);
dispatch_data_t data3 = dispatch_data_create(buffer3, 0,
dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_FREE);
__block bool buffer4_destroyed = false;
dispatch_data_t data4 = dispatch_data_create(buffer4, 16,
dispatch_get_main_queue(), ^{
buffer4_destroyed = true;
});
dispatch_release(data3);
dispatch_release(data4);
dispatch_async(dispatch_get_main_queue(), ^{
test_long("buffer4 destroyed", buffer4_destroyed, true);
dispatch_group_leave(g);
});
});
}
#endif
int
main(void)
{
dispatch_test_start("Dispatch Data");
g = dispatch_group_create();
#if DISPATCHTEST_DATA
test_concat();
test_cleanup();
#endif
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
dispatch_release(g);
test_stop();
});
dispatch_main();
}

466
tests/dispatch_deadname.c Normal file
View File

@ -0,0 +1,466 @@
/*
* 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 <dispatch/private.h>
#ifdef __APPLE__
#include <mach/mach.h>
#endif
#include <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#if TEST_MACHPORT_DEBUG
#define test_mach_assume_zero(x) ({kern_return_t _kr = (x); \
if (_kr) fprintf(stderr, "mach error 0x%x \"%s\": %s\n", \
_kr, mach_error_string(_kr), #x); (void)kr; })
void
test_mach_debug_port(mach_port_t name, const char *str, unsigned int line)
{
mach_port_type_t type;
mach_msg_bits_t ns = 0, nr = 0, nso = 0, nd = 0;
unsigned int dnreqs = 0, dnrsiz;
kern_return_t kr = mach_port_type(mach_task_self(), name, &type);
if (kr) {
fprintf(stderr, "machport[0x%08x] = { error(0x%x) \"%s\" }: %s %u\n",
name, kr, mach_error_string(kr), str, line);
return;
}
if (type & MACH_PORT_TYPE_SEND) {
test_mach_assume_zero(mach_port_get_refs(mach_task_self(), name,
MACH_PORT_RIGHT_SEND, &ns));
}
if (type & MACH_PORT_TYPE_SEND_ONCE) {
test_mach_assume_zero(mach_port_get_refs(mach_task_self(), name,
MACH_PORT_RIGHT_SEND_ONCE, &nso));
}
if (type & MACH_PORT_TYPE_DEAD_NAME) {
test_mach_assume_zero(mach_port_get_refs(mach_task_self(), name,
MACH_PORT_RIGHT_DEAD_NAME, &nd));
}
if (type & (MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_SEND)) {
test_mach_assume_zero(mach_port_dnrequest_info(mach_task_self(), name,
&dnrsiz, &dnreqs));
}
if (type & MACH_PORT_TYPE_RECEIVE) {
mach_port_status_t status = { .mps_pset = 0, };
mach_msg_type_number_t cnt = MACH_PORT_RECEIVE_STATUS_COUNT;
test_mach_assume_zero(mach_port_get_refs(mach_task_self(), name,
MACH_PORT_RIGHT_RECEIVE, &nr));
test_mach_assume_zero(mach_port_get_attributes(mach_task_self(), name,
MACH_PORT_RECEIVE_STATUS, (void*)&status, &cnt));
fprintf(stderr, "machport[0x%08x] = { R(%03u) S(%03u) SO(%03u) D(%03u) "
"dnreqs(%03u) spreq(%s) nsreq(%s) pdreq(%s) srights(%s) "
"sorights(%03u) qlim(%03u) msgcount(%03u) mkscount(%03u) "
"seqno(%03u) }: %s %u\n", name, nr, ns, nso, nd, dnreqs,
type & MACH_PORT_TYPE_SPREQUEST ? "Y":"N",
status.mps_nsrequest ? "Y":"N", status.mps_pdrequest ? "Y":"N",
status.mps_srights ? "Y":"N", status.mps_sorights,
status.mps_qlimit, status.mps_msgcount, status.mps_mscount,
status.mps_seqno, str, line);
} else if (type & (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE|
MACH_PORT_TYPE_DEAD_NAME)) {
fprintf(stderr, "machport[0x%08x] = { R(%03u) S(%03u) SO(%03u) D(%03u) "
"dnreqs(%03u) spreq(%s) }: %s %u\n", name, nr, ns, nso, nd,
dnreqs, type & MACH_PORT_TYPE_SPREQUEST ? "Y":"N", str, line);
} else {
fprintf(stderr, "machport[0x%08x] = { type(0x%08x) }: %s %u\n", name,
type, str, line);
}
fflush(stderr);
}
#define test_mach_debug_port(x) test_mach_debug_port(x, __func__, __LINE__)
#else
#define test_mach_debug_port(x) (void)(x)
#endif
static dispatch_group_t g;
static volatile long sent, received;
void
test_dead_name(void)
{
dispatch_group_enter(g);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_source_t ds0;
kern_return_t kr;
mach_port_t mp = pthread_mach_thread_np(pthread_self());
assert(mp);
kr = mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
test_mach_error("mach_port_mod_refs", kr, KERN_SUCCESS);
ds0 = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, mp,
DISPATCH_MACH_SEND_DEAD, dispatch_get_main_queue());
test_ptr_notnull("DISPATCH_SOURCE_TYPE_MACH_SEND", ds0);
dispatch_source_set_event_handler(ds0, ^{
test_long("DISPATCH_MACH_SEND_DEAD",
dispatch_source_get_handle(ds0), mp);
dispatch_source_cancel(ds0);
});
dispatch_source_set_cancel_handler(ds0, ^{
kern_return_t kr = mach_port_deallocate(mach_task_self(), mp);
test_mach_error("mach_port_deallocate", kr, KERN_SUCCESS);
dispatch_release(ds0);
dispatch_group_leave(g);
});
dispatch_resume(ds0);
// give the mgr queue time to start, otherwise the mgr queue will run
// on this thread, thus defeating the test which assumes that this
// thread will die.
usleep(100000);
});
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
}
void
test_register_already_dead_name(void)
{
dispatch_source_t ds0;
kern_return_t kr;
mach_port_t mp;
dispatch_group_enter(g);
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
test_mach_error("mach_port_allocate", kr, KERN_SUCCESS);
kr = mach_port_insert_right(mach_task_self(), mp, mp,
MACH_MSG_TYPE_MAKE_SEND);
test_mach_error("mach_port_insert_right", kr, KERN_SUCCESS);
kr = mach_port_mod_refs(mach_task_self(), mp,
MACH_PORT_RIGHT_RECEIVE, -1); // turn send right into dead name
test_mach_error("mach_port_mod_refs", kr, KERN_SUCCESS);
ds0 = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, mp,
DISPATCH_MACH_SEND_DEAD, dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(ds0, ^{
test_long("DISPATCH_MACH_SEND_DEAD",
dispatch_source_get_handle(ds0), mp);
dispatch_source_cancel(ds0);
dispatch_release(ds0);
});
dispatch_source_set_cancel_handler(ds0, ^{
kern_return_t kr = mach_port_deallocate(mach_task_self(), mp);
test_mach_error("mach_port_deallocate", kr, KERN_SUCCESS);
dispatch_group_leave(g);
});
dispatch_resume(ds0);
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
}
void
test_receive_and_dead_name(void)
{
dispatch_source_t ds0, ds;
kern_return_t kr;
mach_port_t mp;
received = 0;
dispatch_group_enter(g);
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
test_mach_error("mach_port_allocate", kr, KERN_SUCCESS);
kr = mach_port_insert_right(mach_task_self(), mp, mp,
MACH_MSG_TYPE_MAKE_SEND);
test_mach_error("mach_port_insert_right", kr, KERN_SUCCESS);
ds0 = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, mp,
DISPATCH_MACH_SEND_DEAD, dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(ds0, ^{
test_long("DISPATCH_MACH_SEND_DEAD",
dispatch_source_get_handle(ds0), mp);
dispatch_source_cancel(ds0);
dispatch_release(ds0);
});
dispatch_source_set_cancel_handler(ds0, ^{
kern_return_t kr = mach_port_deallocate(mach_task_self(), mp);
test_mach_error("mach_port_deallocate", kr, KERN_SUCCESS);
dispatch_group_leave(g);
});
dispatch_resume(ds0);
ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0,
dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(ds, ^{
__sync_add_and_fetch(&received, 1);
usleep(100000); // rdar://problem/7676437 race with send source re-arm
mach_msg_empty_rcv_t msg = { .header = {
.msgh_size = sizeof(mach_msg_empty_rcv_t),
.msgh_local_port = mp,
}};
kern_return_t kr = mach_msg_receive(&msg.header);
test_mach_error("mach_msg_receive", kr, KERN_SUCCESS);
});
dispatch_source_set_cancel_handler(ds, ^{
kern_return_t kr = mach_port_mod_refs(mach_task_self(), mp,
MACH_PORT_RIGHT_RECEIVE, -1); // turns send right into dead name
test_mach_error("mach_port_mod_refs", kr, KERN_SUCCESS);
});
dispatch_resume(ds);
mach_msg_empty_send_t msg = { .header = {
.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND),
.msgh_size = sizeof(mach_msg_empty_send_t),
.msgh_remote_port = mp,
}};
kr = mach_msg_send(&msg.header);
test_mach_error("mach_msg_send", kr, KERN_SUCCESS);
usleep(200000);
dispatch_source_cancel(ds);
dispatch_release(ds);
test_long("DISPATCH_SOURCE_TYPE_MACH_RECV", received, 1);
if (received > 1 ) {
test_stop();
}
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
}
#if DISPATCH_API_VERSION >= 20110201 && defined(MACH_NOTIFY_SEND_POSSIBLE) && \
defined(MACH_SEND_NOTIFY)
#define TEST_SP_MSGCOUNT 11 // (2*qlim)+1
static bool
send_until_timeout(mach_port_t mp)
{
kern_return_t kr;
do {
test_mach_debug_port(mp);
mach_msg_empty_send_t msg = { .header = {
.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND),
.msgh_size = sizeof(mach_msg_empty_send_t),
.msgh_remote_port = mp,
}};
kr = mach_msg(&msg.header,
MACH_SEND_MSG|MACH_SEND_NOTIFY|MACH_SEND_TIMEOUT,
msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (kr == MACH_SEND_TIMED_OUT) {
mach_msg_destroy(&msg.header);
test_mach_error("mach_msg(MACH_SEND_MSG) timed out", kr,
MACH_SEND_TIMED_OUT);
} else {
test_mach_error("mach_msg(MACH_SEND_MSG)", kr, KERN_SUCCESS);
if (kr) test_stop();
}
} while (!kr && __sync_add_and_fetch(&sent, 1) < TEST_SP_MSGCOUNT);
test_mach_debug_port(mp);
return kr;
}
void
test_send_possible(void) // rdar://problem/8758200
{
dispatch_source_t ds0, ds, dsp;
kern_return_t kr;
mach_port_t mp;
sent = 0;
received = 0;
dispatch_group_enter(g);
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
test_mach_error("mach_port_allocate", kr, KERN_SUCCESS);
test_mach_debug_port(mp);
kr = mach_port_insert_right(mach_task_self(), mp, mp,
MACH_MSG_TYPE_MAKE_SEND);
test_mach_error("mach_port_insert_right", kr, KERN_SUCCESS);
test_mach_debug_port(mp);
ds0 = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, mp,
DISPATCH_MACH_SEND_DEAD, dispatch_get_global_queue(0, 0));
dispatch_source_set_registration_handler(ds0, ^{
test_long("DISPATCH_MACH_SEND_DEAD registered",
dispatch_source_get_handle(ds0), mp);
test_mach_debug_port(mp);
});
dispatch_source_set_event_handler(ds0, ^{
test_long("DISPATCH_MACH_SEND_DEAD delivered",
dispatch_source_get_handle(ds0), mp);
test_mach_debug_port(mp);
dispatch_source_cancel(ds0);
dispatch_release(ds0);
});
dispatch_source_set_cancel_handler(ds0, ^{
test_long("DISPATCH_MACH_SEND_DEAD canceled",
dispatch_source_get_handle(ds0), mp);
test_mach_debug_port(mp);
kern_return_t kr = mach_port_deallocate(mach_task_self(), mp);
test_mach_error("mach_port_deallocate", kr, KERN_SUCCESS);
test_mach_debug_port(mp);
dispatch_group_leave(g);
});
dispatch_resume(ds0);
ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0,
dispatch_get_global_queue(0, 0));
dispatch_source_set_registration_handler(ds, ^{
test_long("DISPATCH_SOURCE_TYPE_MACH_RECV registered",
dispatch_source_get_handle(ds), mp);
test_mach_debug_port(mp);
});
dispatch_source_set_event_handler(ds, ^{
test_long("DISPATCH_SOURCE_TYPE_MACH_RECV delivered",
dispatch_source_get_handle(ds), mp);
kern_return_t kr;
do {
test_mach_debug_port(mp);
usleep(10000); // simulate slow receiver
mach_msg_empty_rcv_t msg = { .header = {
.msgh_size = sizeof(mach_msg_empty_rcv_t),
.msgh_local_port = mp,
}};
kr = mach_msg(&msg.header,
MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, msg.header.msgh_size,
msg.header.msgh_local_port, MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (kr == MACH_RCV_TIMED_OUT) {
test_mach_error("mach_msg(MACH_RCV_MSG) timed out", kr,
MACH_RCV_TIMED_OUT);
} else {
test_mach_error("mach_msg(MACH_RCV_MSG)", kr, KERN_SUCCESS);
if (kr) test_stop();
}
} while (!kr && __sync_add_and_fetch(&received, 1));
test_mach_debug_port(mp);
});
dispatch_source_set_cancel_handler(ds, ^{
test_long("DISPATCH_SOURCE_TYPE_MACH_RECV canceled",
dispatch_source_get_handle(ds), mp);
test_mach_debug_port(mp);
kern_return_t kr = mach_port_mod_refs(mach_task_self(), mp,
MACH_PORT_RIGHT_RECEIVE, -1); // trigger dead name notification
test_mach_error("mach_port_mod_refs", kr, KERN_SUCCESS);
test_mach_debug_port(mp);
});
dispatch_resume(ds);
dsp = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, mp,
DISPATCH_MACH_SEND_POSSIBLE, dispatch_get_global_queue(0, 0));
dispatch_source_set_registration_handler(dsp, ^{
test_long("DISPATCH_MACH_SEND_POSSIBLE registered",
dispatch_source_get_handle(dsp), mp);
if (!send_until_timeout(mp)) {
dispatch_source_cancel(dsp); // stop sending
dispatch_release(dsp);
}
});
dispatch_source_set_event_handler(dsp, ^{
test_long("DISPATCH_MACH_SEND_POSSIBLE delivered",
dispatch_source_get_handle(dsp), mp);
if (!send_until_timeout(mp)) {
dispatch_source_cancel(dsp); // stop sending
dispatch_release(dsp);
}
});
dispatch_source_set_cancel_handler(dsp, ^{
test_long("DISPATCH_MACH_SEND_POSSIBLE canceled",
dispatch_source_get_handle(dsp), mp);
test_mach_debug_port(mp);
dispatch_source_cancel(ds); // stop receving
dispatch_release(ds);
});
dispatch_resume(dsp);
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
test_long("DISPATCH_SOURCE_TYPE_MACH_SEND", sent, TEST_SP_MSGCOUNT);
test_long("DISPATCH_SOURCE_TYPE_MACH_RECV", received, TEST_SP_MSGCOUNT);
}
#else
#define test_send_possible()
#endif
static boolean_t
test_mig_callback(mach_msg_header_t *message __attribute__((unused)),
mach_msg_header_t *reply)
{
__sync_add_and_fetch(&received, 1);
reply->msgh_remote_port = 0;
return false;
}
void
test_mig_server_large_msg(void) // rdar://problem/8422992
{
dispatch_source_t ds;
kern_return_t kr;
mach_port_t mp;
received = 0;
dispatch_group_enter(g);
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
test_mach_error("mach_port_allocate", kr, KERN_SUCCESS);
kr = mach_port_insert_right(mach_task_self(), mp, mp,
MACH_MSG_TYPE_MAKE_SEND);
test_mach_error("mach_port_insert_right", kr, KERN_SUCCESS);
ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0,
dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(ds, ^{
mach_msg_return_t r = dispatch_mig_server(ds, sizeof(mach_msg_header_t),
test_mig_callback);
test_mach_error("dispatch_mig_server", r, MACH_RCV_TOO_LARGE);
dispatch_group_leave(g);
});
dispatch_source_set_cancel_handler(ds, ^{
kern_return_t kr = mach_port_mod_refs(mach_task_self(), mp,
MACH_PORT_RIGHT_RECEIVE, -1);
test_mach_error("mach_port_mod_refs", kr, KERN_SUCCESS);
kr = mach_port_deallocate(mach_task_self(), mp);
test_mach_error("mach_port_deallocate", kr, KERN_SUCCESS);
dispatch_group_leave(g);
});
dispatch_resume(ds);
struct { mach_msg_header_t header; char payload[4096]; } msg = {
.header = {
.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND),
.msgh_size = sizeof(mach_msg_header_t) + 4096,
.msgh_remote_port = mp,
.msgh_id = 0xfeedface,
}
};
kr = mach_msg_send(&msg.header);
test_mach_error("mach_msg_send", kr, KERN_SUCCESS);
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
dispatch_group_enter(g);
dispatch_source_cancel(ds);
dispatch_release(ds);
test_long("DISPATCH_SOURCE_TYPE_MACH_RECV", received, 0);
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
}
int
main(void)
{
dispatch_test_start("Dispatch dead-name and send-possible notifications");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
g = dispatch_group_create();
test_dead_name();
test_register_already_dead_name();
test_receive_and_dead_name();
test_send_possible();
test_mig_server_large_msg();
test_stop();
});
dispatch_main();
return 0;
}

55
tests/dispatch_debug.c Normal file
View File

@ -0,0 +1,55 @@
/*
* 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 <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
int
main(void)
{
#if defined(_WIN32)
_putenv_s("LIBDISPATCH_LOG", "stderr");
#else
setenv("LIBDISPATCH_LOG", "stderr", 1); // rdar://problem/8493990
#endif
dispatch_test_start("Dispatch Debug");
dispatch_queue_t main_q = dispatch_get_main_queue();
dispatch_debug(main_q, "dispatch_queue_t");
dispatch_queue_t default_q = dispatch_get_global_queue(0, 0);
dispatch_debug(default_q, "dispatch_queue_t");
dispatch_source_t s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
dispatch_source_set_timer(s, DISPATCH_TIME_FOREVER, 1000000000ull, 100);
dispatch_debug(s, "dispatch_source_t");
dispatch_group_t g = dispatch_group_create();
dispatch_debug(g, "dispatch_group_t");
return 0;
}

102
tests/dispatch_drift.c Normal file
View File

@ -0,0 +1,102 @@
/*
* 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@
*/
#ifdef __APPLE__
#include <mach/mach_time.h>
#endif
#include <dispatch/dispatch.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/time.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
#if LENIENT_DEADLINES
#define ACCEPTABLE_DRIFT 0.1
#else
#define ACCEPTABLE_DRIFT 0.001
#endif
int
main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
{
__block uint32_t count = 0;
__block double last_jitter = 0;
__block double drift_sum = 0;
#if defined(_WIN32)
// 25 times a second (Windows timer resolution is poor)
uint64_t interval = 1000000000 / 25;
#else
// 100 times a second
uint64_t interval = 1000000000 / 100;
#endif
double interval_d = interval / 1000000000.0;
// for 25 seconds
unsigned int target = (unsigned int)(25.0 / interval_d);
dispatch_test_start("Dispatch Timer Drift");
dispatch_source_t t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
test_ptr_notnull("dispatch_source_create", t);
dispatch_source_set_timer(t, dispatch_time(DISPATCH_TIME_NOW, (int64_t)interval), interval, 0);
dispatch_source_set_event_handler(t, ^{
struct timeval now_tv;
static double first = 0;
gettimeofday(&now_tv, NULL);
double now = now_tv.tv_sec + now_tv.tv_usec / 1000000.0;
if (count == 0) {
// Because this is taken at 1st timer fire,
// later jitter values may be negitave.
// This doesn't effect the drift calculation.
first = now;
}
double goal = first + interval_d * count;
double jitter = goal - now;
double drift = jitter - last_jitter;
drift_sum += drift;
printf("%4d: jitter %f, drift %f\n", count, jitter, drift);
if (target <= ++count) {
drift_sum /= count - 1;
if (drift_sum < 0) {
drift_sum = -drift_sum;
}
double acceptable_drift = ACCEPTABLE_DRIFT;
test_double_less_than("drift", drift_sum, acceptable_drift);
test_stop();
}
last_jitter = jitter;
});
dispatch_resume(t);
dispatch_main();
return 0;
}

204
tests/dispatch_group.c Normal file
View File

@ -0,0 +1,204 @@
/*
* 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>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#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.
#if HAVE_ARC4RANDOM
const int n = 1 + arc4random() % 8;
#else
const int n = 1 + random() % 8;
#endif
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;
}

849
tests/dispatch_io.c Normal file
View File

@ -0,0 +1,849 @@
/*
* Copyright (c) 2009-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 <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <fcntl.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <fts.h>
#include <sys/param.h>
#include <unistd.h>
#endif
#include <errno.h>
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <libkern/OSAtomic.h>
#include <TargetConditionals.h>
#endif
#ifdef __linux__
#include <sys/resource.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#ifndef DISPATCHTEST_IO
#if DISPATCH_API_VERSION >= 20100226 && DISPATCH_API_VERSION != 20101110
#define DISPATCHTEST_IO 1
#if DISPATCH_API_VERSION >= 20100723
#define DISPATCHTEST_IO_PATH 1 // rdar://problem/7738093
#endif
#endif
#endif
static void
test_fin(void *cxt)
{
test_ptr("test_fin run", cxt, cxt);
test_stop();
}
#if DISPATCHTEST_IO
#if TARGET_OS_EMBEDDED
#define LARGE_FILE "/System/Library/Fonts/Cache/STHeiti-Light.ttc" // 29MB file
#define maxopenfiles 768
#else
#define LARGE_FILE "/System/Library/Speech/Voices/Alex.SpeechVoice/Contents/Resources/PCMWave" // 417MB file
#define maxopenfiles 4096
#endif
static void
test_io_close(int with_timer, bool from_path)
{
#define chunks 4
#define READSIZE (512*1024)
unsigned int i;
const char *path = LARGE_FILE;
dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
if (fd == -1) {
if (errno == ENOENT) {
test_skip("Large file not found");
return;
}
test_errno("open", errno, 0);
test_stop();
}
#ifdef F_GLOBAL_NOCACHE
if (fcntl(fd, F_GLOBAL_NOCACHE, 1) == -1) {
test_errno("fcntl F_GLOBAL_NOCACHE", errno, 0);
test_stop();
}
#endif
const size_t size = (size_t)dispatch_test_fd_lseek(fd, 0, SEEK_END) / chunks;
dispatch_test_fd_lseek(fd, 0, SEEK_SET);
const int expected_error = with_timer? ECANCELED : 0;
dispatch_source_t t = NULL;
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
void (^cleanup_handler)(int error) = ^(int error) {
test_errno("create error", error, 0);
dispatch_group_leave(g);
dispatch_test_fd_close(fd);
};
dispatch_io_t io;
if (!from_path) {
io = dispatch_io_create(DISPATCH_IO_RANDOM, fd,
dispatch_get_global_queue(0, 0), cleanup_handler);
} else {
#if DISPATCHTEST_IO_PATH
io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path, O_RDONLY, 0,
dispatch_get_global_queue(0, 0), cleanup_handler);
#endif
}
dispatch_io_set_high_water(io, READSIZE);
if (with_timer == 1) {
dispatch_io_set_low_water(io, READSIZE);
dispatch_io_set_interval(io, 2 * NSEC_PER_SEC,
DISPATCH_IO_STRICT_INTERVAL);
} else if (with_timer == 2) {
t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(0,0));
dispatch_retain(io);
dispatch_source_set_event_handler(t, ^{
dispatch_io_close(io, DISPATCH_IO_STOP);
dispatch_source_cancel(t);
});
dispatch_source_set_cancel_handler(t, ^{
dispatch_release(io);
});
dispatch_source_set_timer(t, dispatch_time(DISPATCH_TIME_NOW,
2 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
dispatch_resume(t);
}
size_t chunk_sizes[chunks] = {}, *chunk_size = chunk_sizes, total = 0;
dispatch_data_t data_chunks[chunks], *data = data_chunks;
for (i = 0; i < chunks; i++) {
data[i] = dispatch_data_empty;
dispatch_group_enter(g);
dispatch_io_read(io, (off_t)(i * size), size, dispatch_get_global_queue(0,0),
^(bool done, dispatch_data_t d, int error) {
if (d) {
chunk_size[i] += dispatch_data_get_size(d);
dispatch_data_t concat = dispatch_data_create_concat(data[i], d);
dispatch_release(data[i]);
data[i] = concat;
if ((dispatch_data_get_size(d) < READSIZE && !error && !done)) {
// The timer must have fired
dispatch_io_close(io, DISPATCH_IO_STOP);
return;
}
}
if (done) {
test_errno("read error", error,
error == expected_error ? expected_error : 0);
dispatch_group_leave(g);
dispatch_release(data[i]);
}
});
}
dispatch_io_close(io, 0);
dispatch_io_close(io, 0);
dispatch_io_read(io, 0, 1, dispatch_get_global_queue(0,0),
^(bool done, dispatch_data_t d, int error) {
test_long("closed done", done, true);
test_errno("closed error", error, ECANCELED);
test_ptr_null("closed data", d);
});
dispatch_release(io);
test_group_wait(g);
dispatch_release(g);
if (t) {
dispatch_source_cancel(t);
dispatch_release(t);
}
for (i = 0; i < chunks; i++) {
if (with_timer) {
test_sizet_less_than("chunk size", chunk_size[i], size + 1);
} else {
test_sizet("chunk size", chunk_size[i], size);
}
total += chunk_size[i];
}
if (with_timer) {
test_sizet_less_than("total size", total, chunks * size + 1);
} else {
test_sizet("total size", total, chunks * size);
}
}
static void
test_io_stop(void) // rdar://problem/8250057
{
dispatch_fd_t fds[2], *fd = fds;
#if defined(_WIN32)
if (!CreatePipe((PHANDLE)&fds[0], (PHANDLE)&fds[1], NULL, 0)) {
test_long("CreatePipe", GetLastError(), ERROR_SUCCESS);
test_stop();
}
#else
if(pipe(fd) == -1) {
test_errno("pipe", errno, 0);
test_stop();
}
#endif
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
dispatch_io_t io = dispatch_io_create(DISPATCH_IO_STREAM, *fd,
dispatch_get_global_queue(0, 0), ^(int error) {
test_errno("create error", error, 0);
dispatch_test_fd_close(*fd);
dispatch_test_fd_close(*(fd+1));
dispatch_group_leave(g);
});
dispatch_group_enter(g);
dispatch_io_read(io, 0, 1, dispatch_get_global_queue(0, 0),
^(bool done, dispatch_data_t d __attribute__((unused)), int error) {
if (done) {
test_errno("read error", error, ECANCELED);
dispatch_group_leave(g);
}
});
dispatch_source_t t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0,
0, dispatch_get_global_queue(0,0));
dispatch_retain(io);
dispatch_source_set_event_handler(t, ^{
dispatch_io_close(io, DISPATCH_IO_STOP);
dispatch_release(io);
});
dispatch_source_set_timer(t, dispatch_time(DISPATCH_TIME_NOW,
2 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
dispatch_resume(t);
dispatch_release(io);
test_group_wait(g);
dispatch_release(g);
dispatch_source_cancel(t);
dispatch_release(t);
}
static void
test_io_read_write(void)
{
char *path_in = dispatch_test_get_large_file();
#if defined(_WIN32)
char *temp_dir = getenv("TMP");
if (!temp_dir) {
temp_dir = getenv("TEMP");
}
if (!temp_dir) {
test_ptr_notnull("temporary directory", temp_dir);
test_stop();
}
char *path_out = NULL;
asprintf(&path_out, "%s\\dispatchtest_io.XXXXXX", temp_dir);
#else
char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
#endif
dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
dispatch_test_release_large_file(path_in);
free(path_in);
const size_t siz_in =
MIN(1024 * 1024, (size_t)dispatch_test_fd_lseek(in, 0, SEEK_END));
dispatch_test_fd_lseek(in, 0, SEEK_SET);
dispatch_fd_t out = mkstemp(path_out);
if (out == -1) {
test_errno("mkstemp", errno, 0);
test_stop();
}
if (unlink(path_out) == -1) {
test_errno("unlink", errno, 0);
test_stop();
}
#if defined(_WIN32)
free(path_out);
#endif
dispatch_queue_t q = dispatch_get_global_queue(0,0);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
dispatch_io_t io_in = dispatch_io_create(DISPATCH_IO_STREAM, in,
q, ^(int error) {
test_errno("dispatch_io_create", error, 0);
dispatch_test_fd_close(in);
dispatch_group_leave(g);
});
dispatch_io_set_high_water(io_in, siz_in/4);
dispatch_group_enter(g);
dispatch_io_t io_out = dispatch_io_create(DISPATCH_IO_STREAM, out,
q, ^(int error) {
test_errno("dispatch_io_create", error, 0);
dispatch_group_leave(g);
});
dispatch_io_set_high_water(io_out, siz_in/16);
__block dispatch_data_t data = dispatch_data_empty;
dispatch_group_enter(g);
dispatch_io_read(io_in, 0, siz_in, q,
^(bool done_in, dispatch_data_t data_in, int err_in) {
test_sizet_less_than("read size", dispatch_data_get_size(data_in),
siz_in);
if (data_in) {
dispatch_group_enter(g);
dispatch_io_write(io_out, 0, data_in, q,
^(bool done_out, dispatch_data_t data_out, int err_out) {
if (done_out) {
test_errno("dispatch_io_write", err_out, 0);
test_sizet("remaining write size",
data_out ? dispatch_data_get_size(data_out) : 0, 0);
dispatch_group_leave(g);
} else {
test_sizet_less_than("remaining write size",
dispatch_data_get_size(data_out), siz_in);
}
});
dispatch_data_t concat = dispatch_data_create_concat(data, data_in);
dispatch_release(data);
data = concat;
}
if (done_in) {
test_errno("dispatch_io_read", err_in, 0);
dispatch_release(io_out);
dispatch_group_leave(g);
}
});
dispatch_release(io_in);
test_group_wait(g);
dispatch_test_fd_lseek(out, 0, SEEK_SET);
dispatch_group_enter(g);
dispatch_read(out, siz_in, q,
^(dispatch_data_t cmp, int err_cmp) {
if (err_cmp) {
test_errno("dispatch_read", err_cmp, 0);
test_stop();
}
dispatch_test_fd_close(out);
size_t siz_cmp = dispatch_data_get_size(cmp);
test_sizet("readback size", siz_cmp, siz_in);
const void *data_buf, *cmp_buf;
dispatch_data_t data_map, cmp_map;
data_map = dispatch_data_create_map(data, &data_buf, NULL);
cmp_map = dispatch_data_create_map(cmp, &cmp_buf, NULL);
test_long("readback memcmp",
memcmp(data_buf, cmp_buf, MIN(siz_in, siz_cmp)), 0);
dispatch_release(cmp_map);
dispatch_release(data_map);
dispatch_release(data);
dispatch_group_leave(g);
});
test_group_wait(g);
dispatch_release(g);
}
enum {
DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE = 0,
DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE,
DISPATCH_READ_ON_CONCURRENT_QUEUE,
DISPATCH_IO_READ_ON_CONCURRENT_QUEUE,
DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE,
};
static void
test_async_read(char *path, size_t size, int option, dispatch_queue_t queue,
void (^process_data)(size_t))
{
dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
if (fd == -1) {
// Don't stop for access permission issues
if (errno == EACCES) {
process_data(size);
return;
}
test_errno("Failed to open file", errno, 0);
test_stop();
}
#ifdef F_GLOBAL_NOCACHE
// disable caching also for extra fd opened by dispatch_io_create_with_path
if (fcntl(fd, F_GLOBAL_NOCACHE, 1) == -1) {
test_errno("fcntl F_GLOBAL_NOCACHE", errno, 0);
test_stop();
}
#endif
switch (option) {
case DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE:
case DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE:
dispatch_async(queue, ^{
char* buffer = NULL;
#if defined(_WIN32)
SYSTEM_INFO si;
GetSystemInfo(&si);
buffer = _aligned_malloc(size, si.dwPageSize);
#else
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
posix_memalign((void **)&buffer, pagesize, size);
#endif
ssize_t r = dispatch_test_fd_read(fd, buffer, size);
if (r == -1) {
test_errno("async read error", errno, 0);
test_stop();
}
#if defined(_WIN32)
_aligned_free(buffer);
#else
free(buffer);
#endif
dispatch_test_fd_close(fd);
process_data((size_t)r);
});
break;
case DISPATCH_READ_ON_CONCURRENT_QUEUE:
dispatch_read(fd, size, queue, ^(dispatch_data_t d, int error) {
if (error) {
test_errno("dispatch_read error", error, 0);
test_stop();
}
dispatch_test_fd_close(fd);
process_data(dispatch_data_get_size(d));
});
break;
case DISPATCH_IO_READ_ON_CONCURRENT_QUEUE:
case DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE: {
__block dispatch_data_t d = dispatch_data_empty;
void (^cleanup_handler)(int error) = ^(int error) {
if (error) {
test_errno("dispatch_io_create error", error, 0);
test_stop();
}
dispatch_test_fd_close(fd);
process_data(dispatch_data_get_size(d));
dispatch_release(d);
};
dispatch_io_t io = NULL;
if (option == DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE) {
#if DISPATCHTEST_IO_PATH
io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path,
O_RDONLY, 0, queue, cleanup_handler);
#endif
} else {
io = dispatch_io_create(DISPATCH_IO_RANDOM, fd, queue,
cleanup_handler);
}
if (!io) {
test_ptr_notnull("dispatch_io_create", io);
test_stop();
}
// Timeout after 20 secs
dispatch_io_set_interval(io, 20 * NSEC_PER_SEC,
DISPATCH_IO_STRICT_INTERVAL);
dispatch_io_read(io, 0, size, queue,
^(bool done, dispatch_data_t data, int error) {
if (!done && !error && !dispatch_data_get_size(data)) {
// Timer fired, and no progress from last delivery
dispatch_io_close(io, DISPATCH_IO_STOP);
}
if (data) {
dispatch_data_t c = dispatch_data_create_concat(d, data);
dispatch_release(d);
d = c;
}
if (error) {
test_errno("dispatch_io_read error", error, 0);
if (error != ECANCELED) {
test_stop();
}
}
});
dispatch_release(io);
break;
}
}
}
static void
test_enumerate_dir_trees(char **paths,
void (^process_file)(char *path, size_t size))
{
#if defined(_WIN32)
for (size_t i = 0; paths[i]; i++) {
char *search_path = NULL;
asprintf(&search_path, "%s\\*", paths[i]);
WIN32_FIND_DATAA node;
HANDLE find = FindFirstFileA(search_path, &node);
free(search_path);
if (find == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_ACCESS_DENIED) {
return;
}
test_ptr_not("FindFirstFile", find, INVALID_HANDLE_VALUE);
test_stop();
}
do {
if (strcmp(node.cFileName, ".") == 0 ||
strcmp(node.cFileName, "..") == 0) {
continue;
}
char *node_path = NULL;
asprintf(&node_path, "%s\\%s", paths[i], node.cFileName);
if (node.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
char *subdir_paths[] = {node_path, NULL};
test_enumerate_dir_trees(subdir_paths, process_file);
} else {
size_t size = (size_t)(((uint64_t)node.nFileSizeHigh << 32) |
node.nFileSizeLow);
process_file(node_path, size);
}
free(node_path);
} while (FindNextFileA(find, &node));
FindClose(find);
}
#else
FTS *tree = fts_open(paths, FTS_PHYSICAL|FTS_XDEV, NULL);
if (!tree) {
test_ptr_notnull("fts_open failed", tree);
test_stop();
}
FTSENT *node;
while ((node = fts_read(tree)) &&
!(node->fts_info == FTS_ERR || node->fts_info == FTS_NS)) {
if (node->fts_level > 0 && node->fts_name[0] == '.') {
fts_set(tree, node, FTS_SKIP);
} else if (node->fts_info == FTS_F) {
size_t size = (size_t)node->fts_statp->st_size;
process_file(node->fts_path, size);
}
}
if ((!node && errno) || (node && (node->fts_info == FTS_ERR ||
node->fts_info == FTS_NS))) {
test_errno("fts_read failed", !node ? errno : node->fts_errno, 0);
test_stop();
}
if (fts_close(tree)) {
test_errno("fts_close failed", errno, 0);
test_stop();
}
#endif
}
static int
test_read_dirs(char **paths, dispatch_queue_t queue, dispatch_group_t g,
dispatch_semaphore_t s, _Atomic size_t *bytes, int option)
{
__block int files_opened = 0;
__block size_t total_size = 0;
test_enumerate_dir_trees(paths, ^(char *path, size_t size){
dispatch_group_enter(g);
dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);
total_size += size;
files_opened++;
test_async_read(path, size, option, queue, ^(size_t len){
atomic_fetch_add_explicit(bytes, len, memory_order_relaxed);
dispatch_semaphore_signal(s);
dispatch_group_leave(g);
});
});
test_group_wait(g);
test_sizet("total size", *bytes, total_size);
return files_opened;
}
#if defined(_WIN32)
extern __declspec(dllimport)
#else
extern __attribute__((weak_import))
#endif
void
_dispatch_iocntl(uint32_t param, uint64_t value);
enum {
DISPATCH_IOCNTL_CHUNK_PAGES = 1,
DISPATCH_IOCNTL_LOW_WATER_CHUNKS,
DISPATCH_IOCNTL_INITIAL_DELIVERY,
};
static void
test_read_many_files(void)
{
#if defined(_WIN32)
char *paths[] = {NULL, NULL};
char *system_root = getenv("SystemRoot");
if (!system_root) {
test_ptr_notnull("SystemRoot", system_root);
test_stop();
}
asprintf(&paths[0], "%s\\System32", system_root);
#else
char *paths[] = {"/usr/lib", NULL};
#endif
dispatch_group_t g = dispatch_group_create();
dispatch_semaphore_t s = dispatch_semaphore_create(maxopenfiles);
uint64_t start;
_Atomic size_t bytes;
int files_read, i;
const dispatch_queue_t queues[] = {
[DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE] =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
[DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE] =
dispatch_queue_create("read", NULL),
[DISPATCH_READ_ON_CONCURRENT_QUEUE] =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
[DISPATCH_IO_READ_ON_CONCURRENT_QUEUE] =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
#if DISPATCHTEST_IO_PATH
[DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE] =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
#endif
};
dispatch_set_target_queue(queues[DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE],
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
static const char *names[] = {
[DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE] =
"dispatch_async(^{read();}) on concurrent queue",
[DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE] =
"dispatch_async(^{read();}) on serial queue",
[DISPATCH_READ_ON_CONCURRENT_QUEUE] =
"dispatch_read() on concurrent queue",
[DISPATCH_IO_READ_ON_CONCURRENT_QUEUE] =
"dispatch_io_read() on concurrent queue",
[DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE] =
"dispatch_io_read() from path on concurrent queue",
};
if (&_dispatch_iocntl) {
const size_t chunk_pages = 3072;
_dispatch_iocntl(DISPATCH_IOCNTL_CHUNK_PAGES, (uint64_t)chunk_pages);
}
#if !defined(_WIN32)
struct rlimit l;
if (!getrlimit(RLIMIT_NOFILE, &l) && l.rlim_cur < 2 * maxopenfiles + 256) {
l.rlim_cur = 2 * maxopenfiles + 256;
setrlimit(RLIMIT_NOFILE, &l);
}
#endif
for (i = 0; i < (int)(sizeof(queues)/sizeof(dispatch_queue_t)); ++i) {
fprintf(stdout, "%s:\n", names[i]);
bytes = 0;
start = mach_absolute_time();
files_read = test_read_dirs(paths, queues[i], g, s, &bytes, i);
double elapsed = (double)(mach_absolute_time() - start) / NSEC_PER_SEC;
double throughput = ((double)bytes / elapsed)/(1024 * 1024);
fprintf(stdout, "Total Files read: %u, Total MBytes %g, "
"Total time: %g s, Throughput: %g MB/s\n", files_read,
(double)bytes / (1024 * 1024), elapsed, throughput);
}
dispatch_release(queues[DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE]);
dispatch_release(s);
dispatch_release(g);
#if defined(_WIN32)
free(paths[0]);
#endif
}
static void
test_io_from_io(void) // rdar://problem/8388909
{
#if DISPATCH_API_VERSION >= 20101012
const size_t siz_in = 10240;
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_group_t g = dispatch_group_create();
dispatch_io_t io = NULL;
// Windows does not easily support immutable directories
#if !defined(_WIN32)
char path[] = "/tmp/dispatchtest_io.XXXXXX/file.name";
char *tmp = strrchr(path, '/');
*tmp = '\0';
if (!mkdtemp(path)) {
test_ptr_notnull("mkdtemp failed", path);
test_stop();
}
#ifdef UF_IMMUTABLE
// Make the directory immutable
if (chflags(path, UF_IMMUTABLE) == -1) {
test_errno("chflags", errno, 0);
test_stop();
}
#else
// Make the directory non-read/writeable
if (chmod(path, 0) == -1) {
test_errno("chmod", errno, 0);
test_stop();
}
#endif
*tmp = '/';
io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path,
O_CREAT|O_RDWR, 0600, q, ^(int error) {
if (error) {
test_errno("channel cleanup called with error", error, 0);
test_stop();
}
test_errno("channel cleanup called", error, 0);
});
char *foo = malloc(256);
dispatch_data_t tdata;
tdata = dispatch_data_create(foo, 256, NULL, DISPATCH_DATA_DESTRUCTOR_FREE);
dispatch_group_enter(g);
dispatch_io_write(io, 0, tdata, q, ^(bool done, dispatch_data_t data_out,
int err_out) {
#ifdef UF_IMMUTABLE
test_errno("error from write to immutable directory", err_out, EPERM);
#else
test_errno("error from write to write protected directory", err_out, EACCES);
#endif
test_sizet("unwritten data", dispatch_data_get_size(data_out), 256);
if (!err_out && done) {
test_stop();
}
if (done) {
dispatch_group_leave(g);
}
});
dispatch_release(tdata);
dispatch_release(io);
test_group_wait(g);
*tmp = '\0';
#ifdef UF_IMMUTABLE
// Change the directory to mutable
if (chflags(path, 0) == -1) {
test_errno("chflags", errno, 0);
test_stop();
}
#else
// Change the directory to user read/write/execute
if (chmod(path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) {
test_errno("chmod", errno, 0);
test_stop();
}
#endif
*tmp = '/';
#endif // !defined(_WIN32)
#if defined(_WIN32)
char *path = dispatch_test_get_large_file();
char *path_in = dispatch_test_get_large_file();
dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
dispatch_test_release_large_file(path_in);
free(path_in);
#else
const char *path_in = "/dev/urandom";
dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
#endif
dispatch_group_enter(g);
io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path,
O_CREAT|O_RDWR, 0600, q, ^(int error) {
if (error) {
test_errno("channel cleanup called with error", error, 0);
test_stop();
}
test_errno("channel cleanup called", error, 0);
});
dispatch_read(in, siz_in, q, ^(dispatch_data_t data_in, int err_in ) {
if (err_in) {
test_errno("dispatch_read", err_in, 0);
test_stop();
}
dispatch_io_write(io, 0, data_in, q,
^(bool done, dispatch_data_t data_out, int err_out) {
if (done) {
test_errno("dispatch_io_write", err_out, 0);
test_sizet("remaining write size",
data_out ? dispatch_data_get_size(data_out) : 0, 0);
dispatch_group_leave(g);
} else {
test_sizet_less_than("remaining write size",
dispatch_data_get_size(data_out), siz_in);
}
});
});
test_group_wait(g);
dispatch_io_t io2 = dispatch_io_create_with_io(DISPATCH_IO_STREAM, io, q,
^(int error) {
if (error) {
test_errno("dispatch_io_create_with_io", error, 0);
test_stop();
}
});
dispatch_release(io);
dispatch_group_enter(g);
__block dispatch_data_t data_out = dispatch_data_empty;
dispatch_io_read(io2, 0, siz_in, q,
^(bool done, dispatch_data_t d, int error) {
if (d) {
dispatch_data_t concat = dispatch_data_create_concat(data_out, d);
dispatch_release(data_out);
data_out = concat;
}
if (done) {
test_errno("read error from channel created_with_io", error, 0);
dispatch_group_leave(g);
}
});
dispatch_release(io2);
test_group_wait(g);
dispatch_release(g);
test_sizet("readback size", dispatch_data_get_size(data_out), siz_in);
dispatch_release(data_out);
#if defined(_WIN32)
dispatch_test_release_large_file(path);
free(path);
#endif
#endif
}
#endif // DISPATCHTEST_IO
int
main(void)
{
dispatch_test_start("Dispatch IO");
dispatch_async(dispatch_get_main_queue(), ^{
#if DISPATCHTEST_IO
int i; bool from_path = false;
do {
for (i = 0; i < 3; i++) {
test_io_close(i, from_path);
}
#if DISPATCHTEST_IO_PATH
from_path = !from_path;
#endif
} while (from_path);
test_io_stop();
test_io_from_io();
test_io_read_write();
test_read_many_files();
#endif
test_fin(NULL);
});
dispatch_main();
}

280
tests/dispatch_io_muxed.c Normal file
View File

@ -0,0 +1,280 @@
/*
* Copyright (c) 2019 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 <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <Windows.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#if !defined(_WIN32)
#define closesocket(x) close(x)
#endif
static void
test_file_muxed(void)
{
printf("\nFile Muxed\n");
#if defined(_WIN32)
const char *temp_dir = getenv("TMP");
if (!temp_dir) {
temp_dir = getenv("TEMP");
}
if (!temp_dir) {
test_ptr_notnull("temporary directory", temp_dir);
test_stop();
}
const char *path_separator = "\\";
#else
const char *temp_dir = getenv("TMPDIR");
if (!temp_dir) {
temp_dir = "/tmp";
}
const char *path_separator = "/";
#endif
char *path = NULL;
(void)asprintf(&path, "%s%sdispatchtest_io.XXXXXX", temp_dir, path_separator);
dispatch_fd_t fd = mkstemp(path);
if (fd == -1) {
test_errno("mkstemp", errno, 0);
test_stop();
}
if (unlink(path) == -1) {
test_errno("unlink", errno, 0);
test_stop();
}
#if defined(_WIN32)
free(path);
#endif
dispatch_test_fd_write(fd, "test", 4);
dispatch_test_fd_lseek(fd, 0, SEEK_SET);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
dispatch_group_enter(g);
dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
(uintptr_t)fd, 0, dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", reader);
assert(reader);
dispatch_source_set_event_handler(reader, ^{
dispatch_source_cancel(reader);
});
dispatch_source_set_cancel_handler(reader, ^{
dispatch_release(reader);
dispatch_group_leave(g);
});
dispatch_source_t writer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_WRITE, (uintptr_t)fd, 0,
dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", writer);
assert(writer);
dispatch_source_set_event_handler(writer, ^{
dispatch_source_cancel(writer);
});
dispatch_source_set_cancel_handler(writer, ^{
dispatch_release(writer);
dispatch_group_leave(g);
});
dispatch_resume(reader);
dispatch_resume(writer);
test_group_wait(g);
dispatch_release(g);
dispatch_test_fd_close(fd);
}
static void
test_stream_muxed(dispatch_fd_t serverfd, dispatch_fd_t clientfd)
{
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
dispatch_group_enter(g);
dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
(uintptr_t)serverfd, 0, dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", reader);
assert(reader);
dispatch_source_set_event_handler(reader, ^{
dispatch_source_cancel(reader);
});
dispatch_source_set_cancel_handler(reader, ^{
dispatch_release(reader);
dispatch_group_leave(g);
});
dispatch_source_t writer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_WRITE, (uintptr_t)serverfd, 0,
dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", writer);
assert(writer);
dispatch_source_set_event_handler(writer, ^{
dispatch_source_cancel(writer);
});
dispatch_source_set_cancel_handler(writer, ^{
dispatch_release(writer);
dispatch_group_leave(g);
});
dispatch_resume(reader);
dispatch_resume(writer);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
dispatch_get_global_queue(0, 0), ^{
dispatch_group_enter(g);
char buf[512] = {0};
ssize_t n = dispatch_test_fd_write(clientfd, buf, sizeof(buf));
if (n < 0) {
test_errno("write error", errno, 0);
test_stop();
}
test_sizet("num written", (size_t)n, sizeof(buf));
dispatch_group_leave(g);
});
test_group_wait(g);
dispatch_release(g);
}
static void
test_socket_muxed(void)
{
printf("\nSocket Muxed\n");
int listenfd = -1, serverfd = -1, clientfd = -1;
struct sockaddr_in addr;
socklen_t addrlen;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1) {
test_errno("socket()", errno, 0);
test_stop();
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = 0;
addrlen = sizeof(addr);
if (bind(listenfd, (struct sockaddr *)&addr, addrlen) == -1) {
test_errno("bind()", errno, 0);
test_stop();
}
if (listen(listenfd, 3) == -1) {
test_errno("listen()", errno, 0);
test_stop();
}
if (getsockname(listenfd, (struct sockaddr *)&addr, &addrlen) == -1) {
test_errno("getsockname()", errno, 0);
test_stop();
}
clientfd = socket(AF_INET, SOCK_STREAM, 0);
if (clientfd == -1) {
test_errno("socket()", errno, 0);
test_stop();
}
if (connect(clientfd, (struct sockaddr *)&addr, addrlen)) {
test_errno("connect()", errno, 0);
test_stop();
}
serverfd = accept(listenfd, (struct sockaddr *)&addr, &addrlen);
if (serverfd == -1) {
test_errno("accept()", errno, 0);
test_stop();
}
test_stream_muxed((dispatch_fd_t)serverfd, (dispatch_fd_t)clientfd);
closesocket(clientfd);
closesocket(serverfd);
closesocket(listenfd);
}
#if defined(_WIN32)
static void
test_pipe_muxed(void)
{
printf("\nDuplex Pipe Muxed\n");
wchar_t name[64];
swprintf(name, sizeof(name), L"\\\\.\\pipe\\dispatch_io_muxed_%lu",
GetCurrentProcessId());
HANDLE server = CreateNamedPipeW(name,
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE,
/* nMaxInstances */ 1, /* nOutBufferSize */ 0x1000,
/* nInBufferSize */ 0x1000, /* nDefaultTimeOut */ 0,
/* lpSecurityAttributes */ NULL);
if (server == INVALID_HANDLE_VALUE) {
test_ptr_not("CreateNamedPipe", server, INVALID_HANDLE_VALUE);
test_stop();
}
HANDLE client = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
/* dwShareMode */ 0, /* lpSecurityAttributes */ NULL, OPEN_EXISTING,
/* dwFlagsAndAttributes */ 0, /* hTemplateFile */ NULL);
if (client == INVALID_HANDLE_VALUE) {
test_ptr_not("CreateFile", client, INVALID_HANDLE_VALUE);
test_stop();
}
test_stream_muxed((dispatch_fd_t)server, (dispatch_fd_t)client);
CloseHandle(client);
CloseHandle(server);
}
#endif
int
main(void)
{
dispatch_test_start("Dispatch IO Muxed");
#if defined(_WIN32)
WSADATA wsa;
int err = WSAStartup(MAKEWORD(2, 2), &wsa);
if (err != 0) {
fprintf(stderr, "WSAStartup failed with %d\n", err);
test_stop();
}
#endif
dispatch_async(dispatch_get_main_queue(), ^{
test_file_muxed();
test_socket_muxed();
#if defined(_WIN32)
test_pipe_muxed();
#endif
test_stop();
});
dispatch_main();
}

379
tests/dispatch_io_net.c Normal file
View File

@ -0,0 +1,379 @@
/*
* Copyright (c) 2010-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 <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <netdb.h>
#include <netinet/in.h>
#include <spawn.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <Windows.h>
#endif
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef __APPLE__
#include <crt_externs.h>
#include <mach-o/dyld.h>
#endif
#include <Block.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#include <dispatch/dispatch.h>
#if !defined(_WIN32)
extern char **environ;
#endif
#ifndef DISPATCHTEST_IO
#if DISPATCH_API_VERSION >= 20100226 && DISPATCH_API_VERSION != 20101110
#define DISPATCHTEST_IO 1
#endif
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32) || defined(__OpenBSD__)
#define _NSGetExecutablePath(ef,bs) (*(bs)=(size_t)snprintf(ef,*(bs),"%s",argv[0]),0)
#endif
#if defined(_WIN32)
typedef USHORT in_port_t;
#endif
#if !defined(_WIN32)
#define closesocket(x) close(x)
#endif
#if DISPATCHTEST_IO
int
main(int argc, char** argv)
{
struct hostent *he;
int sockfd = -1, clientfd = -1;
dispatch_fd_t read_fd = -1, fd = -1;
struct sockaddr_in addr1, addr2, server;
socklen_t addr2len;
socklen_t addr1len;
pid_t clientid;
#if defined(_WIN32)
WSADATA wsa;
int err = WSAStartup(MAKEWORD(2, 2), &wsa);
if (err != 0) {
fprintf(stderr, "WSAStartup failed with %d\n", err);
test_stop();
}
#endif
if (argc == 3) {
// Client
dispatch_test_start(NULL);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
test_errno("Client-socket()", errno, 0);
test_stop();
}
if ((he = gethostbyname("localhost")) == NULL) {
fprintf(stderr, "Client-gethostbyname() failed\n");
test_stop();
}
memcpy(&server.sin_addr, he->h_addr_list[0], (size_t)he->h_length);
server.sin_family = AF_INET;
server.sin_port = (in_port_t)atoi(argv[1]);
fprintf(stderr, "Client-connecting on port ... %d\n", server.sin_port);
if (connect(sockfd, (struct sockaddr *)&server, sizeof(server))) {
test_errno("client-connect()", errno, 0);
test_stop();
}
// Read from the socket and compare the contents are what we expect
const char *path = argv[2];
fd = dispatch_test_fd_open(path, O_RDONLY);
if (fd == -1) {
test_errno("client-open", errno, 0);
test_stop();
}
// The reference file path given to us by the server was produced by
// dispatch_test_get_large_file(). It may point to a temporary file, and
// we are responsible for cleaning it up because we are the last to
// access it.
dispatch_test_release_large_file(path);
#ifdef F_NOCACHE
if (fcntl(fd, F_NOCACHE, 1)) {
test_errno("client-fcntl F_NOCACHE", errno, 0);
test_stop();
}
#else
// investigate what the impact of lack of file cache disabling has
// for this test
#endif
size_t size = (size_t)dispatch_test_fd_lseek(fd, 0, SEEK_END);
dispatch_test_fd_lseek(fd, 0, SEEK_SET);
__block dispatch_data_t g_d1 = dispatch_data_empty;
__block dispatch_data_t g_d2 = dispatch_data_empty;
__block int g_error = 0;
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
dispatch_read(fd, size, dispatch_get_global_queue(0, 0),
^(dispatch_data_t d1, int error) {
test_errno("Client-dict-read error", error, 0);
test_long("Client-dict-dispatch data size",
(long)dispatch_data_get_size(d1), (long)size);
dispatch_retain(d1);
g_d1 = d1;
dispatch_group_leave(g);
});
__block void (^b)(dispatch_data_t, int);
b = Block_copy(^(dispatch_data_t d2, int error) {
dispatch_data_t concat = dispatch_data_create_concat(g_d2, d2);
dispatch_release(g_d2);
g_d2 = concat;
if (!error && dispatch_data_get_size(d2)) {
dispatch_read(sockfd, SIZE_MAX,
dispatch_get_global_queue(0, 0), b);
} else {
g_error = error;
dispatch_group_leave(g);
}
});
dispatch_group_enter(g);
dispatch_read(sockfd, SIZE_MAX, dispatch_get_global_queue(0, 0), b);
test_group_wait(g);
test_errno("Client-read error", g_error, 0);
test_long("Client-dispatch data size", (long)dispatch_data_get_size(g_d2),
(long)size);
size_t dict_contig_size, socket_contig_size;
const void *dict_contig_buf, *socket_contig_buf;
dispatch_data_t dict_data = dispatch_data_create_map(g_d1,
&dict_contig_buf, &dict_contig_size);
dispatch_data_t socket_data = dispatch_data_create_map(g_d2,
&socket_contig_buf, &socket_contig_size);
test_long("Client-dispatch data contents",
memcmp(dict_contig_buf, socket_contig_buf,
MIN(dict_contig_size, socket_contig_size)), 0);
dispatch_test_fd_close(fd);
closesocket(sockfd);
dispatch_release(g_d1);
dispatch_release(g_d2);
dispatch_release(dict_data);
dispatch_release(socket_data);
dispatch_release(g);
test_stop();
} else {
// Server
dispatch_test_start("Dispatch IO Network test");
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
test_errno("Server-socket()", errno, 0);
test_stop();
}
addr1.sin_family = AF_INET;
addr1.sin_addr.s_addr = INADDR_ANY;
addr1.sin_port = 0;
if (bind(sockfd, (struct sockaddr *)&addr1, sizeof(struct sockaddr)) ==
-1) {
test_errno("Server-bind()", errno, 0);
test_stop();
}
addr1len = sizeof(struct sockaddr);
if (getsockname(sockfd, (struct sockaddr*)&addr1, &addr1len) == -1) {
test_errno("Server-getsockname()", errno, 0);
test_stop();
}
if(listen(sockfd, 3) == -1) {
test_errno("Server-listen()", errno, 0);
test_stop();
}
fprintf(stderr, "Server started and listening on port %d\n",
addr1.sin_port);
char exec_filename [256] = {};
size_t bufsize = 256;
if (_NSGetExecutablePath(exec_filename, &bufsize) == -1) {
fprintf(stderr, "Failed to get path name for running executable\n");
test_stop();
}
char port_str[10] = {};
snprintf(port_str, 10, " %d", addr1.sin_port);
// The client must read from the same test file as the server. It will
// unlink the file as soon as it can, so the server must open it before
// starting the client process.
char *path = dispatch_test_get_large_file();
read_fd = dispatch_test_fd_open(path, O_RDONLY);
if (read_fd == -1) {
test_errno("open", errno, 0);
goto stop_test;
}
char *arguments [4] = {};
arguments[0] = exec_filename;
arguments[1] = port_str;
arguments[2] = path;
arguments[3] = NULL;
#ifdef HAVE_POSIX_SPAWNP
int error;
if ((error = posix_spawnp(&clientid, exec_filename, NULL, NULL,
arguments, environ)) != 0) {
test_errno("Server-posix_spawnp()", error, 0);
goto stop_test;
}
#elif defined(_WIN32)
WCHAR *cmdline = argv_to_command_line(arguments);
if (!cmdline) {
fprintf(stderr, "argv_to_command_line() failed\n");
test_stop();
}
STARTUPINFOW si = {.cb = sizeof(si)};
PROCESS_INFORMATION pi;
BOOL created = CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL,
NULL, &si, &pi);
DWORD error = GetLastError();
free(cmdline);
if (!created) {
print_winapi_error("CreateProcessW", error);
test_stop();
}
clientid = (pid_t)pi.dwProcessId;
#elif defined(__unix__)
clientid = fork();
if (clientid == -1) {
test_errno("Server-fork()", errno, 0);
test_stop();
} else if (clientid == 0) {
// Child process
if (execve(exec_filename, arguments, environ) == -1) {
perror(exec_filename);
_Exit(EXIT_FAILURE);
}
}
#else
#error "dispatch_io_net not implemented on this platform"
#endif
addr2len = sizeof(struct sockaddr_in);
clientfd = accept(sockfd, (struct sockaddr *)&addr2, &addr2len);
if(clientfd == -1) {
test_errno("Server-accept()", errno, 0);
goto stop_test;
}
fprintf(stderr, "Server accepted connection. Server now writing\n");
#ifdef F_NOCACHE
if (fcntl(read_fd, F_NOCACHE, 1)) {
test_errno("fcntl F_NOCACHE", errno, 0);
goto stop_test;
}
#else
// investigate what the impact of lack of file cache disabling has
// for this test
#endif
size_t size = (size_t)dispatch_test_fd_lseek(read_fd, 0, SEEK_END);
dispatch_test_fd_lseek(read_fd, 0, SEEK_SET);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
dispatch_read(read_fd, size, dispatch_get_global_queue(0, 0),
^(dispatch_data_t d, int r_err){
fprintf(stderr, "Server-dispatch_read()\n");
test_errno("Server-read error", r_err, 0);
test_long("Server-dispatch data size", (long)dispatch_data_get_size(d),
(long)size);
// convenience method handlers should only be called once
if (dispatch_data_get_size(d)!= size) {
fprintf(stderr, "Reading of data didn't complete\n");
dispatch_test_fd_close(read_fd);
closesocket(clientfd);
closesocket(sockfd);
test_stop();
}
dispatch_group_enter(g);
dispatch_write(clientfd, d, dispatch_get_global_queue(0, 0),
^(dispatch_data_t remaining, int w_err) {
test_errno("Server-write error", w_err, 0);
test_ptr_null("Server-dispatch write remaining data",remaining);
// convenience method handlers should only be called once
if (remaining) {
fprintf(stderr, "Server-dispatch_write() incomplete .. "
"%zu bytes\n", dispatch_data_get_size(remaining));
dispatch_test_fd_close(read_fd);
closesocket(clientfd);
closesocket(sockfd);
test_stop();
}
closesocket(clientfd); // Sending the client EOF
dispatch_group_leave(g);
});
dispatch_test_fd_close(read_fd);
dispatch_group_leave(g);
});
test_group_wait(g);
dispatch_release(g);
fprintf(stderr, "Shutting down server\n");
closesocket(sockfd);
free(path);
test_stop();
stop_test:
if (path != NULL) {
dispatch_test_release_large_file(path);
free(path);
}
dispatch_test_fd_close(read_fd);
closesocket(clientfd);
closesocket(sockfd);
test_stop();
}
}
#else
int
main()
{
dispatch_test_start("Dispatch IO Network test - No Dispatch IO");
test_stop();
}
#endif

488
tests/dispatch_io_pipe.c Normal file
View File

@ -0,0 +1,488 @@
/*
* Copyright (c) 2019 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 <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
enum {
DISPATCH_PIPE_KIND_ANONYMOUS,
#if defined(_WIN32)
DISPATCH_PIPE_KIND_NAMED_INBOUND,
DISPATCH_PIPE_KIND_NAMED_OUTBOUND,
DISPATCH_PIPE_KIND_NAMED_INBOUND_OVERLAPPED,
DISPATCH_PIPE_KIND_NAMED_OUTBOUND_OVERLAPPED,
#endif
DISPATCH_PIPE_KIND_COUNT,
};
enum {
DISPATCH_TEST_IMMEDIATE,
DISPATCH_TEST_DELAYED,
};
static const char *const pipe_names[] = {
[DISPATCH_PIPE_KIND_ANONYMOUS] = "anonymous",
#if defined(_WIN32)
[DISPATCH_PIPE_KIND_NAMED_INBOUND] = "named, inbound",
[DISPATCH_PIPE_KIND_NAMED_OUTBOUND] = "named, outbound",
[DISPATCH_PIPE_KIND_NAMED_INBOUND_OVERLAPPED] = "named, inbound, overlapped",
[DISPATCH_PIPE_KIND_NAMED_OUTBOUND_OVERLAPPED] = "named, outbound, overlapped",
#endif
};
static const char *const delay_names[] = {
[DISPATCH_TEST_IMMEDIATE] = "Immediate",
[DISPATCH_TEST_DELAYED] = "Delayed",
};
#if defined(_WIN32)
enum {
NAMED_PIPE_BUFFER_SIZE = 0x1000,
};
#endif
static size_t
test_get_pipe_buffer_size(int kind)
{
#if defined(_WIN32)
if (kind != DISPATCH_PIPE_KIND_ANONYMOUS) {
return NAMED_PIPE_BUFFER_SIZE;
}
static dispatch_once_t once;
static DWORD size;
dispatch_once(&once, ^{
HANDLE read_handle, write_handle;
if (!CreatePipe(&read_handle, &write_handle, NULL, 0)) {
test_long("CreatePipe", GetLastError(), ERROR_SUCCESS);
test_stop();
}
GetNamedPipeInfo(write_handle, NULL, &size, NULL, NULL);
CloseHandle(read_handle);
CloseHandle(write_handle);
});
return size;
#else
(void)kind;
static dispatch_once_t once;
static size_t size;
dispatch_once(&once, ^{
int fds[2];
if (pipe(fds) < 0) {
test_errno("pipe", errno, 0);
test_stop();
}
fcntl(fds[1], F_SETFL, O_NONBLOCK);
for (size = 0; write(fds[1], "", 1) > 0; size++) {}
close(fds[0]);
close(fds[1]);
});
return size;
#endif
}
#if defined(_WIN32)
static void
test_make_named_pipe(DWORD flags, dispatch_fd_t *readfd, dispatch_fd_t *writefd)
{
wchar_t name[64];
static int counter = 0;
swprintf(name, sizeof(name), L"\\\\.\\pipe\\dispatch_io_pipe_%lu_%d",
GetCurrentProcessId(), counter++);
HANDLE server = CreateNamedPipeW(name,
flags | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE,
/* nMaxInstances */ 1, NAMED_PIPE_BUFFER_SIZE,
NAMED_PIPE_BUFFER_SIZE, /* nDefaultTimeOut */ 0,
/* lpSecurityAttributes */ NULL);
if (server == INVALID_HANDLE_VALUE) {
test_ptr_not("CreateNamedPipe", server, INVALID_HANDLE_VALUE);
test_stop();
}
HANDLE client = CreateFileW(name,
(flags & PIPE_ACCESS_INBOUND) ? GENERIC_WRITE : GENERIC_READ,
/* dwShareMode */ 0, /* lpSecurityAttributes */ NULL, OPEN_EXISTING,
flags & FILE_FLAG_OVERLAPPED, /* hTemplateFile */ NULL);
if (client == INVALID_HANDLE_VALUE) {
test_ptr_not("CreateFile", client, INVALID_HANDLE_VALUE);
test_stop();
}
if (flags & PIPE_ACCESS_INBOUND) {
*readfd = (dispatch_fd_t)server;
*writefd = (dispatch_fd_t)client;
} else {
*readfd = (dispatch_fd_t)client;
*writefd = (dispatch_fd_t)server;
}
}
#endif
static void
test_make_pipe(int kind, dispatch_fd_t *readfd, dispatch_fd_t *writefd)
{
#if defined(_WIN32)
switch (kind) {
case DISPATCH_PIPE_KIND_ANONYMOUS:
if (!CreatePipe((PHANDLE)readfd, (PHANDLE)writefd, NULL, 0)) {
test_long("CreatePipe", GetLastError(), ERROR_SUCCESS);
test_stop();
}
break;
case DISPATCH_PIPE_KIND_NAMED_INBOUND:
test_make_named_pipe(PIPE_ACCESS_INBOUND, readfd, writefd);
break;
case DISPATCH_PIPE_KIND_NAMED_OUTBOUND:
test_make_named_pipe(PIPE_ACCESS_OUTBOUND, readfd, writefd);
break;
case DISPATCH_PIPE_KIND_NAMED_INBOUND_OVERLAPPED:
test_make_named_pipe(PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, readfd,
writefd);
break;
case DISPATCH_PIPE_KIND_NAMED_OUTBOUND_OVERLAPPED:
test_make_named_pipe(PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
readfd, writefd);
break;
}
#else
(void)kind;
int fds[2];
if (pipe(fds) < 0) {
test_errno("pipe", errno, 0);
test_stop();
}
*readfd = fds[0];
*writefd = fds[1];
#endif
}
static void
test_source_read(int kind, int delay)
{
printf("\nSource Read %s: %s\n", delay_names[delay], pipe_names[kind]);
dispatch_fd_t readfd, writefd;
test_make_pipe(kind, &readfd, &writefd);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
void (^write_block)(void) = ^{
dispatch_group_enter(g);
char buf[512] = {0};
ssize_t n = dispatch_test_fd_write(writefd, buf, sizeof(buf));
if (n < 0) {
test_errno("write error", errno, 0);
test_stop();
}
test_sizet("num written", (size_t)n, sizeof(buf));
dispatch_group_leave(g);
};
if (delay == DISPATCH_TEST_IMMEDIATE) {
write_block();
}
dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
(uintptr_t)readfd, 0, dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", reader);
assert(reader);
dispatch_source_set_event_handler(reader, ^{
dispatch_group_enter(g);
char buf[512];
size_t available = dispatch_source_get_data(reader);
test_sizet("num available", available, sizeof(buf));
ssize_t n = dispatch_test_fd_read(readfd, buf, sizeof(buf));
if (n >= 0) {
test_sizet("num read", (size_t)n, sizeof(buf));
} else {
test_errno("read error", errno, 0);
}
dispatch_source_cancel(reader);
dispatch_group_leave(g);
});
dispatch_source_set_cancel_handler(reader, ^{
dispatch_release(reader);
dispatch_group_leave(g);
});
dispatch_resume(reader);
dispatch_source_t t = NULL;
if (delay == DISPATCH_TEST_DELAYED) {
t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(t, write_block);
dispatch_source_set_timer(t,
dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(t);
}
test_group_wait(g);
dispatch_release(g);
if (t) {
dispatch_source_cancel(t);
dispatch_release(t);
}
dispatch_test_fd_close(readfd);
dispatch_test_fd_close(writefd);
}
static void
test_source_write(int kind, int delay)
{
printf("\nSource Write %s: %s\n", delay_names[delay], pipe_names[kind]);
dispatch_fd_t readfd, writefd;
test_make_pipe(kind, &readfd, &writefd);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
const size_t bufsize = test_get_pipe_buffer_size(kind);
void (^write_block)(void) = ^{
char *buf = calloc(bufsize, 1);
assert(buf);
ssize_t nw = dispatch_test_fd_write(writefd, buf, bufsize);
free(buf);
if (nw < 0) {
test_errno("write error", errno, 0);
test_stop();
}
test_sizet("num written", (size_t)nw, bufsize);
};
write_block();
void (^read_block)(void) = ^{
dispatch_group_enter(g);
char *buf = calloc(bufsize, 1);
assert(buf);
ssize_t nr = dispatch_test_fd_read(readfd, buf, bufsize);
free(buf);
if (nr < 0) {
test_errno("read error", errno, 0);
test_stop();
}
test_sizet("num read", (size_t)nr, bufsize);
dispatch_group_leave(g);
};
if (delay == DISPATCH_TEST_IMMEDIATE) {
read_block();
}
dispatch_source_t writer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_WRITE, (uintptr_t)writefd, 0,
dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", writer);
assert(writer);
dispatch_source_set_event_handler(writer, ^{
dispatch_group_enter(g);
size_t available = dispatch_source_get_data(writer);
test_sizet_less_than("num available", 0, available);
write_block();
read_block();
dispatch_source_cancel(writer);
dispatch_group_leave(g);
});
dispatch_source_set_cancel_handler(writer, ^{
dispatch_release(writer);
dispatch_group_leave(g);
});
dispatch_resume(writer);
dispatch_source_t t = NULL;
if (delay == DISPATCH_TEST_DELAYED) {
t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(t, read_block);
dispatch_source_set_timer(t,
dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(t);
}
test_group_wait(g);
dispatch_release(g);
if (t) {
dispatch_source_cancel(t);
dispatch_release(t);
}
dispatch_test_fd_close(readfd);
dispatch_test_fd_close(writefd);
}
static void
test_dispatch_read(int kind, int delay)
{
printf("\nDispatch Read %s: %s\n", delay_names[delay], pipe_names[kind]);
dispatch_fd_t readfd, writefd;
test_make_pipe(kind, &readfd, &writefd);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
char writebuf[512] = {0};
char *writebufp = writebuf;
void (^write_block)(void) = ^{
dispatch_group_enter(g);
ssize_t n =
dispatch_test_fd_write(writefd, writebufp, sizeof(writebuf));
if (n < 0) {
test_errno("write error", errno, 0);
test_stop();
}
test_sizet("num written", (size_t)n, sizeof(writebuf));
dispatch_group_leave(g);
};
if (delay == DISPATCH_TEST_IMMEDIATE) {
write_block();
}
dispatch_read(readfd, sizeof(writebuf), dispatch_get_global_queue(0, 0),
^(dispatch_data_t data, int err) {
test_errno("read error", err, 0);
test_sizet("num read", dispatch_data_get_size(data), sizeof(writebuf));
dispatch_group_leave(g);
});
dispatch_source_t t = NULL;
if (delay == DISPATCH_TEST_DELAYED) {
t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(t, write_block);
dispatch_source_set_timer(t,
dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(t);
}
test_group_wait(g);
dispatch_release(g);
if (t) {
dispatch_source_cancel(t);
dispatch_release(t);
}
dispatch_test_fd_close(readfd);
dispatch_test_fd_close(writefd);
}
static void
test_dispatch_write(int kind, int delay)
{
printf("\nDispatch Write %s: %s\n", delay_names[delay], pipe_names[kind]);
dispatch_fd_t readfd, writefd;
test_make_pipe(kind, &readfd, &writefd);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
const size_t bufsize = test_get_pipe_buffer_size(kind);
char *buf = calloc(bufsize, 1);
assert(buf);
ssize_t nw = dispatch_test_fd_write(writefd, buf, bufsize);
free(buf);
if (nw < 0) {
test_errno("write error", errno, 0);
test_stop();
}
test_sizet("num written", (size_t)nw, bufsize);
void (^read_block)(void) = ^{
dispatch_group_enter(g);
char *readbuf = calloc(bufsize, 1);
assert(readbuf);
ssize_t nr = dispatch_test_fd_read(readfd, readbuf, bufsize);
free(readbuf);
if (nr < 0) {
test_errno("read error", errno, 0);
test_stop();
}
test_sizet("num read", (size_t)nr, bufsize);
dispatch_group_leave(g);
};
if (delay == DISPATCH_TEST_IMMEDIATE) {
read_block();
}
buf = calloc(bufsize, 1);
assert(buf);
dispatch_data_t wd = dispatch_data_create(buf, bufsize,
dispatch_get_global_queue(0, 0), DISPATCH_DATA_DESTRUCTOR_FREE);
dispatch_write(writefd, wd, dispatch_get_global_queue(0, 0),
^(dispatch_data_t data, int err) {
test_errno("write error", err, 0);
test_ptr_null("data written", data);
read_block();
dispatch_group_leave(g);
});
dispatch_source_t t = NULL;
if (delay == DISPATCH_TEST_DELAYED) {
t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(t, read_block);
dispatch_source_set_timer(t,
dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(t);
}
test_group_wait(g);
dispatch_release(g);
dispatch_release(wd);
if (t) {
dispatch_source_cancel(t);
dispatch_release(t);
}
dispatch_test_fd_close(readfd);
dispatch_test_fd_close(writefd);
}
int
main(void)
{
dispatch_test_start("Dispatch IO Pipe");
dispatch_async(dispatch_get_main_queue(), ^{
for (int kind = 0; kind < DISPATCH_PIPE_KIND_COUNT; kind++) {
test_source_read(kind, DISPATCH_TEST_IMMEDIATE);
test_source_read(kind, DISPATCH_TEST_DELAYED);
test_source_write(kind, DISPATCH_TEST_IMMEDIATE);
test_source_write(kind, DISPATCH_TEST_DELAYED);
test_dispatch_read(kind, DISPATCH_TEST_IMMEDIATE);
test_dispatch_read(kind, DISPATCH_TEST_DELAYED);
test_dispatch_write(kind, DISPATCH_TEST_IMMEDIATE);
test_dispatch_write(kind, DISPATCH_TEST_DELAYED);
}
test_stop();
});
dispatch_main();
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2019 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 <stdlib.h>
#include <limits.h>
#include <errno.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#elif defined(_WIN32)
#include <Windows.h>
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
#include <dispatch/dispatch.h>
int
main() {
dispatch_test_start(NULL);
#if defined(_WIN32)
dispatch_fd_t readFD, writeFD;
if (!CreatePipe((PHANDLE)&readFD, (PHANDLE)&writeFD, NULL, 0)) {
test_long("CreatePipe", GetLastError(), ERROR_SUCCESS);
test_stop();
_Exit(EXIT_FAILURE);
}
#else
int pipe_fds[2] = { -1, -1 };
int pipe_err = pipe(pipe_fds);
int readFD = pipe_fds[0];
int writeFD = pipe_fds[1];
if (pipe_err) {
test_errno("pipe", errno, 0);
test_stop();
_Exit(EXIT_FAILURE);
}
#endif
printf("readFD=%lld, writeFD=%lld\n", (long long)readFD, (long long)writeFD);
dispatch_queue_t q = dispatch_queue_create("q", NULL);
dispatch_io_t io = dispatch_io_create(DISPATCH_IO_STREAM, readFD, q, ^(int err) {
printf("cleanup, err=%d\n", err);
dispatch_test_fd_close(readFD);
printf("all done\n");
test_stop();
_Exit(EXIT_SUCCESS);
});
dispatch_io_set_low_water(io, 0);
dispatch_io_read(io, 0, UINT_MAX, q, ^(bool done, dispatch_data_t data, int err) {
printf("read: \%d, %zu, %d\n", done, data == NULL ? 0 : dispatch_data_get_size(data), err);
if (data != NULL && dispatch_data_get_size(data) > 0) {
// will only happen once
printf("closing writeFD\n");
dispatch_test_fd_close(writeFD);
dispatch_after(DISPATCH_TIME_NOW + 1, q, ^{
dispatch_io_close(io, 0);
});
}
});
dispatch_resume(io);
printf("writing\n");
dispatch_test_fd_write(writeFD, "x", 1);
printf("wrtten\n");
dispatch_main();
}

View File

@ -0,0 +1,72 @@
/*
* 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@
*/
#ifdef __linux__
// for asprintf
#define _GNU_SOURCE 1
#endif
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
#include <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <stdlib.h>
#include <assert.h>
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
int32_t count = 0;
const int32_t final = 32;
int
main(void)
{
dispatch_test_start("Dispatch Overcommit");
int i;
for (i = 0; i < final; ++i) {
char* name;
(void)asprintf(&name, "test.overcommit.%d", i);
dispatch_queue_t queue = dispatch_queue_create(name, NULL);
test_ptr_notnull("dispatch_queue_create", queue);
free(name);
dispatch_set_target_queue(queue, dispatch_get_global_queue(0, DISPATCH_QUEUE_OVERCOMMIT));
dispatch_async(queue, ^{
OSAtomicIncrement32(&count);
if (count == final) {
test_long("count", count, final);
test_stop();
} else {
while (1); // spin
}
});
}
dispatch_main();
return 0;
}

66
tests/dispatch_pingpong.c Normal file
View File

@ -0,0 +1,66 @@
/*
* 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 <stdio.h>
#include <bsdtests.h>
#include "dispatch_test.h"
uint32_t count = 0;
const uint32_t final = 1000000; // 10M
static void
pingpongloop(dispatch_group_t group, dispatch_queue_t ping, dispatch_queue_t pong, size_t counter)
{
//printf("[%p] %s: %lu\n", (void*)(uintptr_t)pthread_self(), dispatch_queue_get_label(dispatch_get_current_queue()), counter);
if (counter < final) {
dispatch_group_async(group, pong, ^{ pingpongloop(group, pong, ping, counter+1); });
} else {
count = (uint32_t)counter;
}
}
int
main(void)
{
dispatch_test_start("Dispatch Ping Pong");
dispatch_queue_t ping = dispatch_queue_create("ping", NULL);
test_ptr_notnull("dispatch_queue_create(ping)", ping);
dispatch_queue_t pong = dispatch_queue_create("pong", NULL);
test_ptr_notnull("dispatch_queue_create(pong)", pong);
dispatch_group_t group = dispatch_group_create();
test_ptr_notnull("dispatch_group_create", group);
pingpongloop(group, ping, pong, 0);
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
test_long("count", count, final);
dispatch_release(ping);
dispatch_release(pong);
dispatch_release(group);
test_stop();
return 0;
}

View File

@ -0,0 +1,41 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include <bsdtests.h>
#include "dispatch_test.h"
int
main(void)
{
dispatch_test_start("Dispatch C++");
dispatch_queue_t q = dispatch_get_main_queue();
test_ptr_notnull("dispatch_get_main_queue", q);
dispatch_async(dispatch_get_main_queue(), ^{
test_stop();
exit(0);
});
dispatch_main();
return 0;
}

222
tests/dispatch_priority.c Normal file
View File

@ -0,0 +1,222 @@
/*
* 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>
#ifdef __ANDROID__
#include <linux/sysctl.h>
#else
#if !defined(__linux__)
#include <sys/sysctl.h>
#endif
#endif /* __ANDROID__ */
#endif
#include <stdlib.h>
#include <assert.h>
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#include <sys/types.h>
#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);
#elif defined(_WIN32)
SYSTEM_INFO si;
GetSystemInfo(&si);
n = (int)si.dwNumberOfProcessors;
#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;
}

131
tests/dispatch_proc.c Normal file
View File

@ -0,0 +1,131 @@
/*
* 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 <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <spawn.h>
#include <signal.h>
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
#define PID_CNT 5
static long event_cnt;
void
test_proc(pid_t bad_pid)
{
dispatch_source_t proc_s[PID_CNT], proc;
int res;
pid_t pid, monitor_pid;
event_cnt = 0;
// Creates a process and register multiple observers. Send a signal,
// exit the process, etc., and verify all observers were notified.
posix_spawnattr_t attr;
res = posix_spawnattr_init(&attr);
assert(res == 0);
res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
assert(res == 0);
char* args[] = {
"/bin/sleep", "2", NULL
};
res = posix_spawnp(&pid, args[0], NULL, &attr, args, NULL);
if (res < 0) {
perror(args[0]);
exit(127);
}
res = posix_spawnattr_destroy(&attr);
assert(res == 0);
dispatch_group_t group = dispatch_group_create();
assert(pid > 0);
monitor_pid = bad_pid ? bad_pid : pid; // rdar://problem/8090801
int i;
for (i = 0; i < PID_CNT; ++i) {
dispatch_group_enter(group);
proc = proc_s[i] = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,
monitor_pid, DISPATCH_PROC_EXIT, dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_proc_create", proc);
dispatch_source_set_event_handler(proc, ^{
long flags = dispatch_source_get_data(proc);
test_long("DISPATCH_PROC_EXIT", flags, DISPATCH_PROC_EXIT);
event_cnt++;
dispatch_source_cancel(proc);
});
dispatch_source_set_cancel_handler(proc, ^{
dispatch_group_leave(group);
});
dispatch_resume(proc);
}
kill(pid, SIGCONT);
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC))) {
for (i = 0; i < PID_CNT; ++i) {
dispatch_source_cancel(proc_s[i]);
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
for (i = 0; i < PID_CNT; ++i) {
dispatch_release(proc_s[i]);
}
dispatch_release(group);
// delay 5 seconds to give a chance for any bugs that
// result in too many events to be noticed
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
int status;
int res2 = waitpid(pid, &status, 0);
assert(res2 != -1);
//int passed = (WIFEXITED(status) && WEXITSTATUS(status) == 0);
test_long("Sub-process exited", WEXITSTATUS(status) | WTERMSIG(status), 0);
test_long("Event count", event_cnt, PID_CNT);
if (bad_pid) {
test_stop();
} else {
dispatch_async(dispatch_get_main_queue(), ^{
test_proc(pid);
});
}
});
}
int
main(void)
{
dispatch_test_start("Dispatch Proc");
test_proc(0);
dispatch_main();
return 0;
}

View File

@ -0,0 +1,95 @@
/*
* 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>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <bsdtests.h>
#include "dispatch_test.h"
void *ctxt_magic = NULL;
static void
finalize(void *ctxt)
{
test_ptr_null("finalizer ran", NULL);
test_ptr("correct context", ctxt, ctxt_magic);
test_stop();
}
static void
never_call(void *ctxt)
{
test_ptr_notnull("never_call should not run", NULL);
test_ptr("correct context", ctxt, NULL);
}
int
main(void)
{
dispatch_test_start("Dispatch Queue Finalizer");
#if HAS_ARC4RANDOM
#if defined(__LP64__) || defined(_WIN64)
ctxt_magic = (void*)((uintptr_t)arc4random() << 32 | arc4random());
#else
ctxt_magic = (void*)arc4random();
#endif
#else // that is, if !HAS_ARC4RANDOM
ctxt_magic = (void *)random();
#endif
// we need a non-NULL value for the tests to work properly
if (ctxt_magic == NULL) {
ctxt_magic = &ctxt_magic;
}
dispatch_queue_t q = dispatch_queue_create("com.apple.testing.finalizer", NULL);
test_ptr_notnull("dispatch_queue_new", q);
dispatch_set_finalizer_f(q, finalize);
dispatch_queue_t q_null_context = dispatch_queue_create("com.apple.testing.finalizer.context_null", NULL);
dispatch_set_context(q_null_context, NULL);
dispatch_set_finalizer_f(q_null_context, never_call);
dispatch_release(q_null_context);
// Don't test k
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^{
// Usign async to set the context helps test that blocks are
// run before the release as opposed to just thrown away.
dispatch_async(q, ^{
dispatch_set_context(q, ctxt_magic);
});
dispatch_release(q);
});
dispatch_main();
return 0;
}

114
tests/dispatch_read.c Normal file
View File

@ -0,0 +1,114 @@
/*
* 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 <sys/types.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <errno.h>
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static size_t bytes_total;
static size_t bytes_read;
static void
test_fin(void *cxt)
{
test_ptr("test_fin run", cxt, cxt);
test_stop();
}
int
main(void)
{
char *path = dispatch_test_get_large_file();
dispatch_test_start("Dispatch Source Read");
dispatch_fd_t infd = dispatch_test_fd_open(path, O_RDONLY);
if (infd == -1) {
perror(path);
exit(EXIT_FAILURE);
}
dispatch_test_release_large_file(path);
free(path);
bytes_total = (size_t)dispatch_test_fd_lseek(infd, 0, SEEK_END);
dispatch_test_fd_lseek(infd, 0, SEEK_SET);
#if !defined(_WIN32)
if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) {
perror(path);
exit(EXIT_FAILURE);
}
#endif
if (!dispatch_test_check_evfilt_read_for_fd(infd)) {
test_skip("EVFILT_READ kevent not firing for test file");
test_fin(NULL);
}
dispatch_queue_t main_q = dispatch_get_main_queue();
test_ptr_notnull("dispatch_get_main_queue", main_q);
dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)infd, 0, main_q);
test_ptr_notnull("dispatch_source_create", reader);
assert(reader);
dispatch_source_set_event_handler(reader, ^{
size_t estimated = dispatch_source_get_data(reader);
fprintf(stderr, "bytes available: %zu\n", estimated);
test_double_less_than_or_equal("estimated", estimated, bytes_total - bytes_read);
const ssize_t bufsiz = 1024*500; // 500 KB buffer
static char buffer[1024*500]; // 500 KB buffer
ssize_t actual = dispatch_test_fd_read(infd, buffer, sizeof(buffer));
bytes_read += (size_t)actual;
printf("bytes read: %zd\n", actual);
if (actual < bufsiz) {
actual = dispatch_test_fd_read(infd, buffer, sizeof(buffer));
bytes_read += (size_t)actual;
// confirm EOF condition
test_long("EOF", actual, 0);
dispatch_source_cancel(reader);
}
});
dispatch_source_set_cancel_handler(reader, ^{
test_sizet("Bytes read", bytes_read, bytes_total);
int res = dispatch_test_fd_close(infd);
test_errno("close", res == -1 ? errno : 0, 0);
dispatch_release(reader);
});
dispatch_set_context(reader, reader);
dispatch_set_finalizer_f(reader, test_fin);
dispatch_resume(reader);
dispatch_main();
}

464
tests/dispatch_read2.c Normal file
View File

@ -0,0 +1,464 @@
/*
* Copyright (c) 2010-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 <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <fts.h>
#include <sys/param.h>
#include <unistd.h>
#endif
#include <errno.h>
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <libkern/OSAtomic.h>
#include <TargetConditionals.h>
#endif
#include <Block.h>
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#ifndef DISPATCHTEST_IO
#if DISPATCH_API_VERSION >= 20100226 && DISPATCH_API_VERSION != 20101110
#define DISPATCHTEST_IO 1
#endif
#endif
static void
test_fin(void *cxt)
{
test_ptr("test_fin run", cxt, cxt);
test_stop();
}
#if DISPATCHTEST_IO
/*
Basic way of implementing dispatch_io's dispatch_read without
using dispatch channel api's
*/
static void
dispatch_read2(dispatch_fd_t fd,
size_t length,
dispatch_queue_t queue,
void (^handler)(dispatch_data_t d, int error))
{
#if !defined(_WIN32)
if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
test_errno("fcntl O_NONBLOCK", errno, 0);
test_stop();
}
#endif
dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
(uintptr_t)fd, 0, queue);
test_ptr_notnull("reader", reader);
__block size_t bytes_read = 0;
__block dispatch_data_t data = dispatch_data_empty;
__block int err = 0;
dispatch_source_set_event_handler(reader, ^{
const ssize_t bufsiz = 1024*512; // 512KB buffer
char *buffer = NULL;
#if defined(_WIN32)
SYSTEM_INFO si;
GetSystemInfo(&si);
size_t pagesize = (size_t)si.dwPageSize;
buffer = _aligned_malloc(bufsiz, pagesize);
#else
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
posix_memalign((void **)&buffer, pagesize, bufsiz);
#endif
ssize_t actual = dispatch_test_fd_read(fd, buffer, bufsiz);
if (actual == -1) {
err = errno;
}
if (actual > 0) {
bytes_read += (size_t)actual;
#if defined(_WIN32)
dispatch_data_t tmp_data = dispatch_data_create(buffer, (size_t)actual,
NULL, ^{ _aligned_free(buffer); });
#else
dispatch_data_t tmp_data = dispatch_data_create(buffer, (size_t)actual,
NULL, DISPATCH_DATA_DESTRUCTOR_FREE);
#endif
dispatch_data_t concat = dispatch_data_create_concat(data,tmp_data);
dispatch_release(tmp_data);
dispatch_release(data);
data = concat;
}
// If we reached EOF or we read as much we were asked to.
if (actual < bufsiz || bytes_read >= length) {
char foo[2];
actual = dispatch_test_fd_read(fd, foo, 2);
bytes_read += (size_t)actual;
// confirm EOF condition
test_long("EOF", actual, 0);
dispatch_source_cancel(reader);
}
});
dispatch_source_set_cancel_handler(reader, ^{
dispatch_data_t d = dispatch_data_create_subrange(data, 0, length);
dispatch_release(data);
handler(d, err);
dispatch_release(d);
dispatch_release(reader);
});
dispatch_resume(reader);
}
static void
test_read(void)
{
char *path = dispatch_test_get_large_file();
dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
if (fd == -1) {
test_errno("open", errno, 0);
test_stop();
}
dispatch_test_release_large_file(path);
free(path);
#ifdef F_NOCACHE
if (fcntl(fd, F_NOCACHE, 1)) {
test_errno("fcntl F_NOCACHE", errno, 0);
test_stop();
}
#else
// investigate what the impact of lack of file cache disabling has
// for this test
#endif
size_t size = (size_t)dispatch_test_fd_lseek(fd, 0, SEEK_END);
dispatch_test_fd_lseek(fd, 0, SEEK_SET);
dispatch_group_t g = dispatch_group_create();
void (^b)(dispatch_data_t, int) = ^(dispatch_data_t d, int error) {
test_errno("read error", error, 0);
test_sizet("dispatch data size", d ? dispatch_data_get_size(d) : 0, size);
if (d) {
const void *contig_buf;
size_t contig_size;
dispatch_data_t tmp = dispatch_data_create_map(d, &contig_buf,
&contig_size);
test_sizet("dispatch data contig size", contig_size, size);
if (contig_size) {
// Validate the copied buffer is similar to what we expect
char *buf = (char*)malloc(size);
dispatch_test_fd_pread(fd, buf, size, 0);
test_long("dispatch data contents", memcmp(buf, contig_buf,
size), 0);
free(buf);
}
dispatch_release(tmp);
}
dispatch_group_leave(g);
};
dispatch_group_enter(g);
dispatch_read(fd, SIZE_MAX, dispatch_get_global_queue(0, 0), b); // rdar://problem/7795794
test_group_wait(g);
dispatch_test_fd_lseek(fd, 0, SEEK_SET);
if (dispatch_test_check_evfilt_read_for_fd(fd)) {
dispatch_group_enter(g);
dispatch_read2(fd, size, dispatch_get_global_queue(0,0), b);
test_group_wait(g);
} else {
test_skip("EVFILT_READ kevent not firing for test file");
}
dispatch_release(g);
dispatch_test_fd_close(fd);
}
static void
test_read_write(void)
{
#if defined(_WIN32)
char *path_in = dispatch_test_get_large_file();
char path_out[] = "dispatchtest_io.XXXXXX";
dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
dispatch_test_release_large_file(path_in);
free(path_in);
size_t siz_in = (size_t)dispatch_test_fd_lseek(in, 0, SEEK_END);
dispatch_test_fd_lseek(in, 0, SEEK_SET);
#else
const char *path_in = "/dev/urandom";
char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
const size_t siz_in = 10240;
dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
#endif
dispatch_fd_t out = mkstemp(path_out);
if (out == -1) {
test_errno("mkstemp", errno, 0);
test_stop();
}
if (unlink(path_out) == -1) {
test_errno("unlink", errno, 0);
test_stop();
}
dispatch_queue_t q = dispatch_get_global_queue(0,0);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
__block dispatch_data_t data;
dispatch_read(in, siz_in, q, ^(dispatch_data_t data_in, int err_in) {
if (err_in) {
test_errno("dispatch_read", err_in, 0);
test_stop();
}
dispatch_test_fd_close(in);
size_t siz_out = dispatch_data_get_size(data_in);
test_sizet("read size", siz_out, siz_in);
dispatch_retain(data_in);
data = data_in;
dispatch_write(out, data, q, ^(dispatch_data_t data_out, int err_out) {
if (err_out || data_out) {
test_errno("dispatch_write", err_out, 0);
test_stop();
}
dispatch_test_fd_lseek(out, 0, SEEK_SET);
dispatch_read(out, siz_out, q,
^(dispatch_data_t cmp, int err_cmp) {
if (err_cmp) {
test_errno("dispatch_read", err_cmp, 0);
test_stop();
}
dispatch_test_fd_close(out);
size_t siz_cmp = dispatch_data_get_size(cmp);
test_sizet("readback size", siz_cmp, siz_out);
const void *data_buf, *cmp_buf;
dispatch_data_t data_map, cmp_map;
data_map = dispatch_data_create_map(data, &data_buf, NULL);
cmp_map = dispatch_data_create_map(cmp, &cmp_buf, NULL);
test_long("readback memcmp",
memcmp(data_buf, cmp_buf, MIN(siz_out, siz_cmp)), 0);
dispatch_release(cmp_map);
dispatch_release(data_map);
dispatch_release(data);
dispatch_group_leave(g);
});
});
});
test_group_wait(g);
dispatch_release(g);
}
static void
test_read_writes(void) // <rdar://problem/7785143>
{
const size_t chunks_out = 320;
const size_t siz_chunk = 32, siz_in = siz_chunk * chunks_out;
#if defined(_WIN32)
char *path_in = dispatch_test_get_large_file();
char path_out[] = "dispatchtest_io.XXXXXX";
dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
dispatch_test_release_large_file(path_in);
free(path_in);
#else
const char *path_in = "/dev/urandom";
char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
#endif
dispatch_fd_t out = mkstemp(path_out);
if (out == -1) {
test_errno("mkstemp", errno, 0);
test_stop();
}
if (unlink(path_out) == -1) {
test_errno("unlink", errno, 0);
test_stop();
}
dispatch_queue_t q = dispatch_get_global_queue(0,0);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
__block dispatch_data_t data;
__block size_t siz_out;
dispatch_read(in, siz_in, q, ^(dispatch_data_t data_in, int err_in) {
if (err_in) {
test_errno("dispatch_read", err_in, 0);
test_stop();
}
dispatch_test_fd_close(in);
siz_out = dispatch_data_get_size(data_in);
test_sizet("read size", siz_out, siz_in);
dispatch_retain(data_in);
data = data_in;
dispatch_data_t data_chunks[chunks_out];
size_t i;
for (i = 0; i < chunks_out; i++) {
data_chunks[i] = dispatch_data_create_subrange(data_in,
i * siz_chunk, siz_chunk);
}
for (i = 0; i < chunks_out; i++) {
dispatch_data_t d = data_chunks[i];
dispatch_group_enter(g);
dispatch_write(out, d, q, ^(dispatch_data_t data_out,
int err_out) {
if (err_out || data_out) {
test_errno("dispatch_write", err_out, 0);
test_stop();
}
dispatch_group_leave(g);
});
}
for (i = 0; i < chunks_out; i++) {
dispatch_release(data_chunks[i]);
}
dispatch_group_leave(g);
});
test_group_wait(g);
dispatch_group_enter(g);
dispatch_test_fd_lseek(out, 0, SEEK_SET);
dispatch_read(out, siz_in, q,
^(dispatch_data_t cmp, int err_cmp) {
if (err_cmp) {
test_errno("dispatch_read", err_cmp, 0);
test_stop();
}
dispatch_test_fd_close(out);
size_t siz_cmp = dispatch_data_get_size(cmp);
test_sizet("readback size", siz_cmp, siz_out);
const void *data_buf, *cmp_buf;
dispatch_data_t data_map, cmp_map;
data_map = dispatch_data_create_map(data, &data_buf, NULL);
cmp_map = dispatch_data_create_map(cmp, &cmp_buf, NULL);
test_long("readback memcmp",
memcmp(data_buf, cmp_buf, MIN(siz_out, siz_cmp)), 0);
dispatch_release(cmp_map);
dispatch_release(data_map);
dispatch_release(data);
dispatch_group_leave(g);
});
test_group_wait(g);
dispatch_release(g);
}
#if !defined(_WIN32)
static void
test_writes_reads_eagain(void) // rdar://problem/8333366
{
int in = open("/dev/urandom", O_RDONLY);
if (in == -1) {
test_errno("open", errno, 0);
test_stop();
}
int fds[2], *fd = fds;
if(pipe(fd) == -1) {
test_errno("pipe", errno, 0);
test_stop();
}
const size_t chunks = 320;
const size_t siz_chunk = 32, siz = siz_chunk * chunks;
dispatch_queue_t q = dispatch_get_global_queue(0,0);
dispatch_group_t g = dispatch_group_create();
__block size_t siz_acc = 0, deliveries = 0;
__block void (^b)(dispatch_data_t, int);
b = Block_copy(^(dispatch_data_t data, int err) {
if (err) {
test_errno("dispatch_read", err, 0);
test_stop();
}
deliveries++;
siz_acc += dispatch_data_get_size(data);
if (siz_acc < siz) {
dispatch_group_enter(g);
dispatch_read(*fd, siz, q, b);
}
dispatch_group_leave(g);
});
dispatch_group_enter(g);
dispatch_read(*fd, siz, q, b);
char *buf[siz_chunk];
size_t i;
for (i = 0; i < chunks; i++) {
ssize_t s = read(in, buf, siz_chunk);
if (s < (ssize_t)siz_chunk) {
test_errno("read", errno, 0);
test_stop();
}
s = write(*(fd+1), buf, siz_chunk);
if (s < (ssize_t)siz_chunk) {
test_errno("write", errno, 0);
test_stop();
}
usleep(10000);
}
close(in);
close(*(fd+1));
test_group_wait(g);
test_sizet("dispatch_read deliveries", deliveries, chunks);
test_sizet("dispatch_read data size", siz_acc, siz);
close(*fd);
Block_release(b);
dispatch_release(g);
}
#endif
#endif // DISPATCHTEST_IO
int
main(void)
{
dispatch_test_start("Dispatch IO Convenience Read/Write");
dispatch_async(dispatch_get_main_queue(), ^{
#if DISPATCHTEST_IO
test_read();
test_read_write();
test_read_writes();
#if !defined(_WIN32)
test_writes_reads_eagain();
#endif
#endif
test_fin(NULL);
});
dispatch_main();
}

189
tests/dispatch_readsync.c Normal file
View File

@ -0,0 +1,189 @@
/*
* 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 <dispatch/private.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#ifdef __ANDROID__
#include <linux/sysctl.h>
#else
#if !defined(__linux__)
#include <sys/sysctl.h>
#endif
#endif /* __ANDROID__ */
#endif
#include <assert.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#define LAPS 10000
#define INTERVAL 100
#if TARGET_OS_EMBEDDED
#define BUSY 10000
#define NTHREADS 16
#else
#define BUSY 1000000
#define NTHREADS 64
#endif
static dispatch_group_t g;
static volatile size_t r_count, w_count, workers, readers, writers, crw, count, drain;
static void
writer(void *ctxt)
{
size_t w = __sync_add_and_fetch(&writers, 1), *m = (size_t *)ctxt;
if (w > *m) *m = w;
usleep(10000);
size_t busy = BUSY;
while (busy--) if (readers) __sync_add_and_fetch(&crw, 1);
if (__sync_sub_and_fetch(&w_count, 1) == 0) {
if (r_count == 0) {
dispatch_async(dispatch_get_main_queue(), ^{test_stop();});
}
}
__sync_sub_and_fetch(&writers, 1);
dispatch_group_leave(g);
}
static void
reader(void *ctxt)
{
size_t r = __sync_add_and_fetch(&readers, 1), *m = (size_t *)ctxt;
if (r > *m) *m = r;
usleep(10000);
size_t busy = BUSY;
while (busy--) if (writers) __sync_add_and_fetch(&crw, 1);
if (__sync_sub_and_fetch(&r_count, 1) == 0) {
if (r_count == 0) {
dispatch_async(dispatch_get_main_queue(), ^{test_stop();});
}
}
__sync_sub_and_fetch(&readers, 1);
}
static void
test_readsync(dispatch_queue_t rq, dispatch_queue_t wq, size_t n)
{
size_t i, max_readers = 0, max_writers = 0;
size_t *mrs = calloc(n, sizeof(size_t)), *mr, *mw = &max_writers;
r_count = LAPS * 2;
w_count = LAPS / INTERVAL;
workers = readers = writers = crw = count = 0;
for (i = 0, mr = mrs; i < n; i++, mr++) {
dispatch_group_async(g,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,
DISPATCH_QUEUE_OVERCOMMIT), ^{
__sync_add_and_fetch(&workers, 1);
do {
usleep(100000);
} while (workers < n);
for (;;) {
size_t idx = __sync_add_and_fetch(&count, 1);
if (idx > LAPS) break;
dispatch_sync_f(rq, mr, reader);
if (!(idx % INTERVAL)) {
dispatch_group_enter(g);
dispatch_barrier_async_f(wq, mw, writer);
}
dispatch_sync_f(rq, mr, reader);
if (!(idx % (INTERVAL*10))) {
// Let the queue drain
__sync_add_and_fetch(&drain, 1);
usleep(10000);
dispatch_barrier_sync(wq, ^{});
__sync_sub_and_fetch(&drain, 1);
} else while (drain) usleep(1000);
}
});
}
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
for (i = 0, mr = mrs; i < n; i++, mr++) {
if (*mr > max_readers) max_readers = *mr;
}
free(mrs);
test_sizet("max readers", max_readers, n);
test_sizet("max writers", max_writers, 1);
test_sizet("concurrent readers & writers", crw, 0);
}
int
main(void)
{
dispatch_test_start("Dispatch Reader/Writer Queues");
uint32_t activecpu, wq_max_threads;
#ifdef __linux__
activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
// don't want to parse /proc/sys/kernel/threads-max
wq_max_threads = activecpu * NTHREADS + 2;
#elif defined(_WIN32)
SYSTEM_INFO si;
GetSystemInfo(&si);
activecpu = si.dwNumberOfProcessors;
wq_max_threads = activecpu * NTHREADS + 2;
#else
size_t s = sizeof(uint32_t);
sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0);
s = sizeof(uint32_t);
sysctlbyname("kern.wq_max_threads", &wq_max_threads, &s, NULL, 0);
#endif
// cap at wq_max_threads - one wq thread for dq - one wq thread for manager
size_t n = MIN(activecpu * NTHREADS, wq_max_threads - 2);
g = dispatch_group_create();
dispatch_queue_attr_t qattr = NULL;
#if DISPATCH_API_VERSION >= 20100518 // rdar://problem/7790099
qattr = DISPATCH_QUEUE_CONCURRENT;
#endif
dispatch_queue_t dq = dispatch_queue_create("readsync", qattr);
assert(dq);
if (!qattr) {
dispatch_queue_set_width(dq, LONG_MAX); // rdar://problem/7919264
dispatch_barrier_sync(dq, ^{}); // wait for changes to take effect
}
test_readsync(dq, dq, n);
dispatch_queue_t tq = dispatch_queue_create("writebarrierasync", qattr);
assert(tq);
if (!qattr) {
dispatch_queue_set_width(tq, LONG_MAX);
}
dispatch_set_target_queue(dq, tq);
dispatch_barrier_sync(tq, ^{}); // wait for changes to take effect
test_readsync(dq, tq, n); // rdar://problem/8186485
dispatch_release(tq);
dispatch_release(dq);
dispatch_release(g);
dispatch_main();
}

174
tests/dispatch_select.c Normal file
View File

@ -0,0 +1,174 @@
/*
* Copyright (c) 2010-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 <fcntl.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <dispatch/dispatch.h>
#include "dispatch_test.h"
#include <bsdtests.h>
static ssize_t actual;
void stage1(int stage);
void stage2(void);
void finish(void* cxt);
void
stage1(int stage)
{
#if defined(_WIN32)
char *path = dispatch_test_get_large_file();
dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
if (fd == -1)
{
perror(path);
exit(EXIT_FAILURE);
}
dispatch_test_release_large_file(path);
free(path);
#else
const char *path = "/dev/random";
int fd = open(path, O_RDONLY);
if (fd == -1)
{
perror(path);
exit(EXIT_FAILURE);
}
#endif
dispatch_queue_t main_q = dispatch_get_main_queue();
test_ptr_notnull("main_q", main_q);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, main_q);
test_ptr_notnull("select source", source);
dispatch_source_set_event_handler(source, ^{
size_t buffer_size = 500*1024;
char buffer[500*1024];
ssize_t sz = dispatch_test_fd_read(fd, buffer, buffer_size);
test_sizet_less_than_or_equal("kevent read 1", sz, buffer_size+1);
dispatch_source_cancel(source);
});
dispatch_source_set_cancel_handler(source, ^{
int res = dispatch_test_fd_close(fd);
test_errno("close", res == -1 ? errno : 0, 0);
dispatch_release(source);
if (stage == 1)
{
dispatch_async(dispatch_get_main_queue(), ^{
stage2();
});
}
});
if (stage == 3)
{
dispatch_set_context(source, source);
dispatch_set_finalizer_f(source, finish);
}
dispatch_resume(source);
}
void
stage2(void)
{
char *path = dispatch_test_get_large_file();
dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
if (fd == -1)
{
perror(path);
exit(EXIT_FAILURE);
}
dispatch_test_release_large_file(path);
free(path);
if (!dispatch_test_check_evfilt_read_for_fd(fd)) {
test_skip("EVFILT_READ kevent not firing for test file");
dispatch_test_fd_close(fd);
dispatch_async(dispatch_get_main_queue(), ^{
stage1(3);
});
return;
}
ssize_t expected = dispatch_test_fd_lseek(fd, 0, SEEK_END);
dispatch_test_fd_lseek(fd, 0, SEEK_SET);
actual = 0;
dispatch_queue_t main_q = dispatch_get_main_queue();
test_ptr_notnull("main_q", main_q);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, main_q);
test_ptr_notnull("kevent source", source);
dispatch_source_set_event_handler(source, ^{
size_t est = dispatch_source_get_data(source);
test_sizet_less_than_or_equal("estimated", est, expected - actual);
char buffer[500*1024];
ssize_t sz = dispatch_test_fd_read(fd, buffer, sizeof(buffer));
actual += sz;
if (sz < (ssize_t)sizeof(buffer))
{
sz = dispatch_test_fd_read(fd, buffer, sizeof(buffer));
actual += sz;
test_long("EOF", sz, 0);
dispatch_source_cancel(source);
}
});
dispatch_source_set_cancel_handler(source, ^{
test_long("bytes read", actual, expected);
int res = dispatch_test_fd_close(fd);
test_errno("close", res == -1 ? errno : 0, 0);
dispatch_release(source);
dispatch_async(dispatch_get_main_queue(), ^{
stage1(3);
});
});
dispatch_resume(source);
}
void
finish(void* cxt)
{
test_ptr("finish", cxt, cxt);
test_stop();
}
int
main(void)
{
dispatch_test_start("Dispatch select workaround test"); // <rdar://problem/7678012>
dispatch_async(dispatch_get_main_queue(), ^{
stage1(1);
});
dispatch_main();
}

56
tests/dispatch_sema.c Normal file
View File

@ -0,0 +1,56 @@
/*
* 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>
#if !USE_WIN32_SEM
#include <pthread.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#define LAPS 10000
int
main(void)
{
static long total;
dispatch_semaphore_t dsema;
dispatch_test_start("Dispatch Semaphore");
dsema = dispatch_semaphore_create(1);
assert(dsema);
dispatch_apply(LAPS, dispatch_get_global_queue(0, 0), ^(size_t idx __attribute__((unused))) {
dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
total++;
dispatch_semaphore_signal(dsema);
});
dispatch_release(dsema);
test_long("count", total, LAPS);
test_stop();
return 0;
}

172
tests/dispatch_starfish.c Normal file
View File

@ -0,0 +1,172 @@
/*
* 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@
*/
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#include <dispatch/dispatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#include <bsdtests.h>
#include "dispatch_test.h"
#define COUNT 1000ul
#define LAPS 10ul
#if LENIENT_DEADLINES
#define ACCEPTABLE_LATENCY 10000
#elif TARGET_OS_EMBEDDED
#define ACCEPTABLE_LATENCY 3000
#else
#define ACCEPTABLE_LATENCY 1000
#endif
static dispatch_queue_t queues[COUNT];
static size_t lap_count_down = LAPS;
static size_t count_down;
static uint64_t start;
static mach_timebase_info_data_t tbi;
static void do_test(void);
static void
collect(void *context __attribute__((unused)))
{
uint64_t delta;
long double math;
size_t i;
if (--count_down) {
return;
}
delta = mach_absolute_time() - start;
delta *= tbi.numer;
delta /= tbi.denom;
math = delta;
math /= COUNT * COUNT * 2ul + COUNT * 2ul;
printf("lap: %zd\n", lap_count_down);
printf("count: %lu\n", COUNT);
printf("delta: %lu ns\n", (unsigned long)delta);
printf("math: %Lf ns / lap\n", math);
for (i = 0; i < COUNT; i++) {
dispatch_release(queues[i]);
}
// our malloc could be a lot better,
// this result is really a malloc torture test
test_long_less_than("Latency" , (long)math, ACCEPTABLE_LATENCY);
if (--lap_count_down) {
return do_test();
}
// give the threads some time to settle before test_stop() runs "leaks"
// ...also note, this is a total cheat. dispatch_after lets this
// thread go idle, so dispatch cleans up the continuations cache.
// Doign the "old style" sleep left that stuff around and leaks
// took a LONG TIME to complete. Long enough that the test harness
// decided to kill us.
dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), NULL, test_stop_after_delay);
}
static void
pong(void *context)
{
dispatch_queue_t this_q = context;
size_t replies = (size_t)dispatch_get_context(this_q);
dispatch_set_context(this_q, (void *)--replies);
if (!replies) {
//printf("collect from: %s\n", dispatch_queue_get_label(dispatch_get_current_queue()));
dispatch_async_f(dispatch_get_main_queue(), NULL, collect);
}
}
static void
ping(void *context)
{
dispatch_queue_t reply_q = context;
dispatch_async_f(reply_q, reply_q, pong);
}
static void
start_node(void *context)
{
dispatch_queue_t this_q = context;
size_t i;
dispatch_set_context(this_q, (void *)COUNT);
for (i = 0; i < COUNT; i++) {
dispatch_async_f(queues[i], this_q, ping);
}
}
void
do_test(void)
{
dispatch_queue_t soup = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
kern_return_t kr;
char buf[1000];
size_t i;
count_down = COUNT;
kr = mach_timebase_info(&tbi);
assert(kr == 0);
start = mach_absolute_time();
for (i = 0; i < COUNT; i++) {
snprintf(buf, sizeof(buf), "com.example.starfish-node#%zd", i);
queues[i] = dispatch_queue_create(buf, NULL);
dispatch_suspend(queues[i]);
dispatch_set_target_queue(queues[i], soup);
}
for (i = 0; i < COUNT; i++) {
dispatch_async_f(queues[i], queues[i], start_node);
}
for (i = 0; i < COUNT; i++) {
dispatch_resume(queues[i]);
}
}
int
main(void)
{
dispatch_test_start("Dispatch Starfish");
do_test();
dispatch_main();
}

View File

@ -0,0 +1,115 @@
/*
* 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 <assert.h>
#include <stdio.h>
#include <string.h>
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
dispatch_source_t tweedledee;
dispatch_source_t tweedledum;
static void
fini(void *cxt)
{
test_ptr_notnull("finalizer ran", cxt);
if (cxt == tweedledum) {
test_stop();
}
}
static void
test_timer(void)
{
dispatch_test_start("Dispatch Suspend Timer");
dispatch_queue_t main_q = dispatch_get_main_queue();
//test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
__block int i = 0, i_prime = 0;
__block int j = 0;
tweedledee = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
test_ptr_notnull("dispatch_source_timer_create", tweedledee);
dispatch_source_set_timer(tweedledee, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), NSEC_PER_SEC, 0);
dispatch_source_set_cancel_handler(tweedledee, ^{
dispatch_release(tweedledee);
});
dispatch_source_set_event_handler(tweedledee, ^{
i_prime += dispatch_source_get_data(tweedledee);
fprintf(stderr, "tweedledee %d (%d)\n", ++i, i_prime);
if (i == 10) {
dispatch_source_cancel(tweedledee);
}
});
dispatch_set_context(tweedledee, tweedledee);
dispatch_set_finalizer_f(tweedledee, fini);
dispatch_resume(tweedledee);
tweedledum = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
test_ptr_notnull("dispatch_source_timer_create", tweedledum);
dispatch_source_set_timer(tweedledum, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC + NSEC_PER_SEC / 2), 3 * NSEC_PER_SEC, 0);
dispatch_source_set_cancel_handler(tweedledum, ^{
dispatch_release(tweedledum);
});
dispatch_source_set_event_handler(tweedledum, ^{
switch(++j) {
case 1:
fprintf(stderr, "suspending timer for 3 seconds\n");
dispatch_suspend(tweedledee);
break;
case 2:
fprintf(stderr, "resuming timer\n");
test_long("tweedledee tick count", i, 3);
test_long("tweedledee virtual tick count", i_prime, 3);
dispatch_resume(tweedledee);
break;
default:
test_long("tweedledee tick count", i, 7);
test_long("tweedledee virtual tick count", i_prime, 9);
dispatch_source_cancel(tweedledum);
break;
}
});
dispatch_set_context(tweedledum, tweedledum);
dispatch_set_finalizer_f(tweedledum, fini);
dispatch_resume(tweedledum);
}
int
main(void)
{
test_timer();
dispatch_main();
return 0;
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2009-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 <dispatch/private.h>
#include <stdlib.h>
#include <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <CoreFoundation/CoreFoundation.h>
#include <bsdtests.h>
#include "dispatch_test.h"
const int32_t final = 10;
int global_count = 0;
static void
work(void* ctxt __attribute__((unused)))
{
if (global_count == INT_MAX) {
test_stop();
}
printf("Firing timer on main %d\n", ++global_count);
dispatch_after_f(dispatch_time(0, 100000*NSEC_PER_USEC),
dispatch_get_main_queue(), NULL, work);
}
int
main(void)
{
dispatch_test_start("Dispatch Sync on main"); // <rdar://problem/7181849>
dispatch_queue_t dq = dispatch_queue_create("foo.bar", NULL);
dispatch_async(dq, ^{
dispatch_async_f(dispatch_get_main_queue(), NULL, work);
int i;
for (i=0; i<final; ++i) {
dispatch_sync(dispatch_get_main_queue(), ^{
printf("Calling sync %d\n", i);
test_long("sync on main", pthread_main_np(), 1);
if (i == final-1) {
global_count = INT_MAX;
}
});
const struct timespec t = {.tv_nsec = 50000*NSEC_PER_USEC};
nanosleep(&t, NULL);
}
});
dispatch_release(dq);
CFRunLoopRun();
return 0;
}

396
tests/dispatch_test.c Normal file
View File

@ -0,0 +1,396 @@
/*
* 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_test.h"
#include "bsdtests.h"
#ifdef __OBJC_GC__
#include <objc/objc-auto.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#if __has_include(<sys/event.h>)
#define HAS_SYS_EVENT_H 1
#include <sys/event.h>
#else
#include <sys/poll.h>
#endif
#elif defined(_WIN32)
#include <Windows.h>
#include <bcrypt.h>
#endif
#include <assert.h>
#include <dispatch/dispatch.h>
void test_start(const char* desc);
void
dispatch_test_start(const char* desc)
{
#if defined(__OBJC_GC__) && MAC_OS_X_VERSION_MIN_REQUIRED < 1070
objc_startCollectorThread();
#endif
test_start(desc);
}
bool
dispatch_test_check_evfilt_read_for_fd(dispatch_fd_t fd)
{
#if HAS_SYS_EVENT_H
int kq = kqueue();
assert(kq != -1);
struct kevent ke = {
.ident = (uintptr_t)fd,
.filter = EVFILT_READ,
.flags = EV_ADD|EV_ENABLE,
};
struct timespec t = {
.tv_sec = 1,
};
int r = kevent(kq, &ke, 1, &ke, 1, &t);
close(kq);
return r > 0;
#elif defined(_WIN32)
HANDLE handle = (HANDLE)fd;
// A zero-distance move retrieves the file pointer
LARGE_INTEGER currentPosition;
LARGE_INTEGER distance = {.QuadPart = 0};
if (!SetFilePointerEx(handle, distance, &currentPosition, FILE_CURRENT)) {
return false;
}
// If we are not at the end, assume the file is readable
LARGE_INTEGER fileSize;
if (GetFileSizeEx(handle, &fileSize) == 0) {
return false;
}
return currentPosition.QuadPart < fileSize.QuadPart;
#else
struct pollfd pfd = {
.fd = fd,
.events = POLLIN,
};
int rc;
do {
rc = poll(&pfd, 1, 0);
} while (rc == -1 && errno == EINTR);
assert(rc != -1);
return rc == 1;
#endif
}
char *
dispatch_test_get_large_file(void)
{
#if defined(__APPLE__)
return strdup("/usr/bin/vi");
#elif defined(__unix__) || defined(_WIN32)
// Depending on /usr/bin/vi being present is unreliable (especially on
// Android), so fill up a large-enough temp file with random bytes
#if defined(_WIN32)
char temp_dir_buf[MAX_PATH];
const char *temp_dir = getenv("TEMP") ?: getenv("TMP");
if (!temp_dir) {
DWORD len = GetTempPathA(sizeof(temp_dir_buf), temp_dir_buf);
if (len > 0 && len < sizeof(temp_dir_buf)) {
temp_dir = temp_dir_buf;
} else {
temp_dir = ".";
}
}
#else
const char *temp_dir = getenv("TMPDIR");
if (temp_dir == NULL || temp_dir[0] == '\0') {
temp_dir = "/tmp";
}
#endif
const char *const suffix = "/dispatch_test.XXXXXX";
size_t temp_dir_len = strlen(temp_dir);
size_t suffix_len = strlen(suffix);
char *path = malloc(temp_dir_len + suffix_len + 1);
assert(path != NULL);
memcpy(path, temp_dir, temp_dir_len);
memcpy(&path[temp_dir_len], suffix, suffix_len + 1);
dispatch_fd_t temp_fd = mkstemp(path);
if (temp_fd == -1) {
perror("mkstemp");
exit(EXIT_FAILURE);
}
const size_t file_size = 2 * 1024 * 1024;
char *file_buf = malloc(file_size);
assert(file_buf != NULL);
ssize_t num;
#if defined(_WIN32)
NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)file_buf, file_size,
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (status < 0) {
fprintf(stderr, "BCryptGenRandom failed with %ld\n", status);
dispatch_test_release_large_file(path);
exit(EXIT_FAILURE);
}
#else
int urandom_fd = open("/dev/urandom", O_RDONLY);
if (urandom_fd == -1) {
perror("/dev/urandom");
dispatch_test_release_large_file(path);
exit(EXIT_FAILURE);
}
size_t pos = 0;
while (pos < file_size) {
num = read(urandom_fd, &file_buf[pos], file_size - pos);
if (num > 0) {
pos += (size_t)num;
} else if (num == -1 && errno != EINTR) {
perror("read");
dispatch_test_release_large_file(path);
exit(EXIT_FAILURE);
}
}
close(urandom_fd);
#endif
do {
num = dispatch_test_fd_write(temp_fd, file_buf, file_size);
} while (num == -1 && errno == EINTR);
if (num == -1) {
perror("write");
dispatch_test_release_large_file(path);
exit(EXIT_FAILURE);
}
assert(num == file_size);
dispatch_test_fd_close(temp_fd);
free(file_buf);
return path;
#else
#error "dispatch_test_get_large_file not implemented on this platform"
#endif
}
void
dispatch_test_release_large_file(const char *path)
{
#if defined(__APPLE__)
// The path is fixed to a system file - do nothing
(void)path;
#elif defined(__unix__) || defined(_WIN32)
if (unlink(path) < 0) {
perror("unlink");
}
#else
#error "dispatch_test_release_large_file not implemented on this platform"
#endif
}
void
_dispatch_test_current(const char* file, long line, const char* desc, dispatch_queue_t expected)
{
dispatch_queue_t actual = dispatch_get_current_queue();
_test_ptr(file, line, desc, actual, expected);
}
dispatch_fd_t
dispatch_test_fd_open(const char *path, int flags)
{
#if defined(_WIN32)
DWORD desired_access = 0;
DWORD creation_disposition = OPEN_EXISTING;
switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) {
case O_RDONLY:
desired_access = GENERIC_READ;
break;
case O_WRONLY:
desired_access = GENERIC_WRITE;
break;
case O_RDWR:
desired_access = GENERIC_READ | GENERIC_WRITE;
break;
}
if (flags & O_CREAT) {
creation_disposition = OPEN_ALWAYS;
if (flags & O_EXCL) {
creation_disposition = CREATE_NEW;
}
}
// FILE_SHARE_DELETE is important here because tests must be able to delete
// temporary files after opening them
HANDLE handle = CreateFileA(path, desired_access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
/* lpSecurityAttributes */ NULL, creation_disposition,
/* dwFlagsAndAttributes */ 0, /* hTemplateFile */ NULL);
if (handle == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
switch (error) {
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_FILE_EXISTS:
errno = EEXIST;
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
errno = ENOENT;
break;
default:
print_winapi_error("CreateFileA", GetLastError());
errno = EIO;
break;
}
return -1;
}
return (dispatch_fd_t)handle;
#else
return open(path, flags);
#endif
}
int
dispatch_test_fd_close(dispatch_fd_t fd)
{
#if defined(_WIN32)
if (!CloseHandle((HANDLE)fd)) {
errno = EBADF;
return -1;
}
return 0;
#else
return close(fd);
#endif
}
off_t
dispatch_test_fd_lseek(dispatch_fd_t fd, off_t offset, int whence)
{
#if defined(_WIN32)
DWORD method;
switch (whence) {
case SEEK_CUR:
method = FILE_CURRENT;
break;
case SEEK_END:
method = FILE_END;
break;
case SEEK_SET:
default:
method = FILE_BEGIN;
break;
}
LARGE_INTEGER distance = {.QuadPart = offset};
LARGE_INTEGER new_pos;
if (!SetFilePointerEx((HANDLE)fd, distance, &new_pos, method)) {
print_winapi_error("SetFilePointerEx", GetLastError());
errno = EINVAL;
return -1;
}
return (off_t)new_pos.QuadPart;
#else
return lseek(fd, offset, whence);
#endif
}
ssize_t
dispatch_test_fd_pread(dispatch_fd_t fd, void *buf, size_t count, off_t offset)
{
#if defined(_WIN32)
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
LARGE_INTEGER lioffset = {.QuadPart = offset};
overlapped.Offset = lioffset.LowPart;
overlapped.OffsetHigh = lioffset.HighPart;
DWORD num_read;
if (!ReadFile((HANDLE)fd, buf, count, &num_read, &overlapped)) {
print_winapi_error("ReadFile", GetLastError());
errno = EIO;
return -1;
}
return (ssize_t)num_read;
#else
return pread(fd, buf, count, offset);
#endif
}
ssize_t
dispatch_test_fd_read(dispatch_fd_t fd, void *buf, size_t count)
{
#if defined(_WIN32)
if (GetFileType((HANDLE)fd) == FILE_TYPE_PIPE) {
OVERLAPPED ov = {0};
DWORD num_read;
BOOL success = ReadFile((HANDLE)fd, buf, count, &num_read, &ov);
if (!success && GetLastError() == ERROR_IO_PENDING) {
success = GetOverlappedResult((HANDLE)fd, &ov, &num_read,
/* bWait */ TRUE);
}
if (!success) {
print_winapi_error("ReadFile", GetLastError());
errno = EIO;
return -1;
}
return (ssize_t)num_read;
}
DWORD num_read;
if (!ReadFile((HANDLE)fd, buf, count, &num_read, NULL)) {
print_winapi_error("ReadFile", GetLastError());
errno = EIO;
return -1;
}
return (ssize_t)num_read;
#else
return read(fd, buf, count);
#endif
}
ssize_t
dispatch_test_fd_write(dispatch_fd_t fd, const void *buf, size_t count)
{
#if defined(_WIN32)
if (GetFileType((HANDLE)fd) == FILE_TYPE_PIPE) {
OVERLAPPED ov = {0};
DWORD num_written;
BOOL success = WriteFile((HANDLE)fd, buf, count, &num_written, &ov);
if (!success && GetLastError() == ERROR_IO_PENDING) {
success = GetOverlappedResult((HANDLE)fd, &ov, &num_written,
/* bWait */ TRUE);
}
if (!success) {
print_winapi_error("WriteFile", GetLastError());
errno = EIO;
return -1;
}
return (ssize_t)num_written;
}
DWORD num_written;
if (!WriteFile((HANDLE)fd, buf, count, &num_written, NULL)) {
print_winapi_error("WriteFile", GetLastError());
errno = EIO;
return -1;
}
return (ssize_t)num_written;
#else
return write(fd, buf, count);
#endif
}

65
tests/dispatch_test.h Normal file
View File

@ -0,0 +1,65 @@
/*
* 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 <stdbool.h>
#include <dispatch/dispatch.h>
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <generic_unix_port.h>
#elif defined(_WIN32)
#include <generic_win_port.h>
#endif
#define test_group_wait(g) do { \
if (dispatch_group_wait(g, dispatch_time(DISPATCH_TIME_NOW, \
25ull * NSEC_PER_SEC))) { \
test_long("group wait timed out", 1, 0); \
test_stop(); \
} } while (0)
#if defined(__cplusplus)
extern "C" {
#endif
void dispatch_test_start(const char* desc);
bool dispatch_test_check_evfilt_read_for_fd(dispatch_fd_t fd);
char *dispatch_test_get_large_file(void);
void dispatch_test_release_large_file(const char *path);
void _dispatch_test_current(const char* file, long line, const char* desc, dispatch_queue_t expected);
#define dispatch_test_current(a,b) _dispatch_test_current(__SOURCE_FILE__, __LINE__, a, b)
#if __APPLE__ && !defined(DARLING)
int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp,
size_t *newpl);
#endif
dispatch_fd_t dispatch_test_fd_open(const char *path, int flags);
int dispatch_test_fd_close(dispatch_fd_t fd);
off_t dispatch_test_fd_lseek(dispatch_fd_t fd, off_t offset, int whence);
ssize_t dispatch_test_fd_pread(dispatch_fd_t fd, void *buf, size_t count, off_t offset);
ssize_t dispatch_test_fd_read(dispatch_fd_t fd, void *buf, size_t count);
ssize_t dispatch_test_fd_write(dispatch_fd_t fd, const void *buf, size_t count);
#if defined(__cplusplus)
} /* extern "C" */
#endif

109
tests/dispatch_timer.c Normal file
View File

@ -0,0 +1,109 @@
/*
* 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 <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static bool finalized = false;
static void
test_fin(void *cxt)
{
test_ptr("finalizer ran", cxt, cxt);
finalized = true;
test_stop();
}
static void
test_timer(void)
{
dispatch_test_start("Dispatch Source Timer");
const int stop_at = 3;
dispatch_queue_t main_q = dispatch_get_main_queue();
//test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
int64_t j;
// create timers in two classes:
// * ones that should trigger before the test ends
// * ones that shouldn't trigger before the test ends
for (j = 1; j <= 5; ++j)
{
dispatch_source_t s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", s);
int64_t delta = (int64_t)((uint64_t)j * NSEC_PER_SEC + NSEC_PER_SEC / 10);
dispatch_source_set_timer(s, dispatch_time(DISPATCH_TIME_NOW, delta), DISPATCH_TIME_FOREVER, 0);
dispatch_source_set_event_handler(s, ^{
if (!finalized) {
test_long_less_than("timer number", (long)j, stop_at);
fprintf(stderr, "timer[%lu]\n", (unsigned long)j);
}
dispatch_release(s);
});
dispatch_resume(s);
}
__block int i = 0;
dispatch_source_t s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
test_ptr_notnull("dispatch_source_create", s);
dispatch_source_set_timer(s, dispatch_time(DISPATCH_TIME_NOW, 0), NSEC_PER_SEC, 0);
dispatch_source_set_cancel_handler(s, ^{
test_ptr_notnull("cancel handler run", s);
dispatch_release(s);
});
dispatch_source_set_event_handler(s, ^{
fprintf(stderr, "%d\n", ++i);
if (i >= stop_at) {
test_long("i", i, stop_at);
dispatch_source_set_timer(s, dispatch_time(DISPATCH_TIME_NOW, 0),
DISPATCH_TIME_FOREVER, 0);
dispatch_source_cancel(s);
}
});
dispatch_set_context(s, s);
dispatch_set_finalizer_f(s, test_fin);
dispatch_resume(s);
}
int
main(void)
{
test_timer();
dispatch_main();
return 0;
}

View File

@ -0,0 +1,74 @@
/*
* 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 <assert.h>
#include <stdio.h>
#include <string.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/time.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static void
test_timer(void)
{
dispatch_test_start("Dispatch Source Timer, bit 31");
dispatch_queue_t main_q = dispatch_get_main_queue();
//test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
struct timeval start_time;
static dispatch_source_t s;
s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
test_ptr_notnull("dispatch_source_create", s);
dispatch_source_set_timer(s, dispatch_time(DISPATCH_TIME_NOW, 0x80000000ull), 0x80000000ull, 0);
gettimeofday(&start_time, NULL);
dispatch_source_set_event_handler(s, ^{
dispatch_source_cancel(s);
});
dispatch_source_set_cancel_handler(s, ^{
struct timeval end_time;
gettimeofday(&end_time, NULL);
// check, s/b 2.0799... seconds, which is <4 seconds
// when it could end on a bad boundry.
test_long_less_than("needs to finish faster than 4 seconds", end_time.tv_sec - start_time.tv_sec, 4);
// And it has to take at least two seconds...
test_long_less_than("can't finish faster than 2 seconds", 1, end_time.tv_sec - start_time.tv_sec);
test_stop();
});
dispatch_resume(s);
}
int
main(void)
{
test_timer();
dispatch_main();
return 0;
}

View File

@ -0,0 +1,71 @@
/*
* 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 <assert.h>
#include <stdio.h>
#include <string.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/time.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static void
test_timer(void)
{
dispatch_test_start("Dispatch Source Timer, bit 63");
//uint64_t interval = 0xffffffffffffffffull;
uint64_t interval = 0x8000000000000001ull;
dispatch_queue_t mainq = dispatch_get_main_queue();
__block int i = 0;
struct timeval start_time;
gettimeofday(&start_time, NULL);
static dispatch_source_t ds;
ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, mainq);
assert(ds);
dispatch_source_set_event_handler(ds, ^{
assert(i < 1);
printf("%d\n", i++);
});
dispatch_source_set_timer(ds, DISPATCH_TIME_NOW, interval, 0);
dispatch_resume(ds);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
test_stop();
});
}
int
main(void)
{
test_timer();
dispatch_main();
return 0;
}

View File

@ -0,0 +1,82 @@
/*
* 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 <assert.h>
#include <stdio.h>
#include <string.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/time.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
static void
test_timer(void)
{
dispatch_test_start("Dispatch Update Timer");
dispatch_queue_t main_q = dispatch_get_main_queue();
//test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
__block int i = 0;
struct timeval start_time;
gettimeofday(&start_time, NULL);
dispatch_source_t s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
test_ptr_notnull("dispatch_source_create", s);
dispatch_source_set_timer(s, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), NSEC_PER_SEC, 0);
dispatch_source_set_cancel_handler(s, ^{
struct timeval end_time;
gettimeofday(&end_time, NULL);
// Make sure we actually managed to adjust the interval
// duration. Fifteen one second ticks would blow past
// this.
test_long_less_than("total duration", end_time.tv_sec - start_time.tv_sec, 10);
test_stop();
dispatch_release(s);
});
dispatch_source_set_event_handler(s, ^{
fprintf(stderr, "%d\n", ++i);
if (i >= 15) {
dispatch_source_cancel(s);
} else if (i == 1) {
dispatch_source_set_timer(s, dispatch_time(DISPATCH_TIME_NOW, 0), NSEC_PER_SEC / 10, 0);
}
});
test_ptr_notnull("dispatch_source_timer_create", s);
dispatch_resume(s);
}
int
main(void) {
test_timer();
dispatch_main();
return 0;
}

View File

@ -0,0 +1,135 @@
/*
* Copyright (c) 2010-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 <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __APPLE__
#include <mach/mach_time.h>
#include <libkern/OSAtomic.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#define delay (1ull * NSEC_PER_SEC)
#define interval (5ull * NSEC_PER_USEC)
#define N 25000
static dispatch_source_t t[N];
static dispatch_queue_t q;
static dispatch_group_t g;
static volatile int32_t count;
static mach_timebase_info_data_t tbi;
static uint64_t start, last;
#define elapsed_ms(x) (((now-(x))*tbi.numer/tbi.denom)/(1000ull*NSEC_PER_USEC))
static
void
test_fin(void *cxt)
{
uint32_t finalCount = (uint32_t)count;
fprintf(stderr, "Called back every %llu us on average\n",
(delay/finalCount)/NSEC_PER_USEC);
test_long_less_than("Frequency", 1,
(long)ceil((double)delay/(double)(finalCount*interval)));
int i;
for (i = 0; i < N; i++) {
dispatch_source_cancel(t[i]);
dispatch_release(t[i]);
}
dispatch_resume(q);
dispatch_release(q);
dispatch_release(g);
test_ptr("finalizer ran", cxt, cxt);
test_stop();
}
static
void
test_short_timer(void)
{
// Add a large number of timers with suspended target queue in front of
// the timer being measured <rdar://problem/7401353>
g = dispatch_group_create();
q = dispatch_queue_create("q", NULL);
int i;
for (i = 0; i < N; i++) {
t[i] = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, q);
dispatch_source_set_timer(t[i], DISPATCH_TIME_NOW, interval, 0);
dispatch_group_enter(g);
dispatch_source_set_registration_handler(t[i], ^{
dispatch_suspend(t[i]);
dispatch_group_leave(g);
});
dispatch_resume(t[i]);
}
// Wait for registration & configuration of all timers
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
dispatch_suspend(q);
for (i = 0; i < N; i++) {
dispatch_resume(t[i]);
}
dispatch_source_t s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0, 0, dispatch_get_global_queue(0, 0));
test_ptr_notnull("dispatch_source_create", s);
dispatch_source_set_timer(s, DISPATCH_TIME_NOW, interval, 0);
dispatch_source_set_event_handler(s, ^{
uint64_t now = mach_absolute_time();
if (!count) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay),
dispatch_get_global_queue(0, 0), ^{
dispatch_source_cancel(s);
dispatch_release(s);
});
fprintf(stderr, "First timer callback (after %4llu ms)\n",
elapsed_ms(start));
}
OSAtomicIncrement32(&count);
if (elapsed_ms(last) >= 100) {
fprintf(stderr, "%5d timer callbacks (after %4llu ms)\n", count,
elapsed_ms(start));
last = now;
}
});
dispatch_set_context(s, s);
dispatch_set_finalizer_f(s, test_fin);
fprintf(stderr, "Scheduling %llu us timer\n", interval/NSEC_PER_USEC);
start = last = mach_absolute_time();
dispatch_resume(s);
}
int
main(void)
{
dispatch_test_start("Dispatch Short Timer"); // <rdar://problem/7765184>
mach_timebase_info(&tbi);
test_short_timer();
dispatch_main();
return 0;
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2010-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 <assert.h>
#include <stdio.h>
#include <string.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/time.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
int
main(void)
{
dispatch_test_start("Dispatch Source Timeout"); // <rdar://problem/8015967>
uint64_t mini_interval = 100ull; // 100 ns
uint64_t long_interval = 2000000000ull; // 2 secs
dispatch_queue_t timer_queue = dispatch_queue_create("timer_queue", NULL);
__block int value_to_be_changed = 5;
__block int fired = 0;
dispatch_source_t mini_timer, long_timer;
mini_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer_queue);
dispatch_source_set_event_handler(mini_timer, ^{
printf("Firing mini-timer %d\n", ++fired);
printf("Suspending mini-timer queue\n");
dispatch_suspend(timer_queue);
});
dispatch_source_set_timer(mini_timer, DISPATCH_TIME_NOW, mini_interval, 0);
long_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(long_timer, ^{
printf("Firing long timer\n");
value_to_be_changed = 10;
dispatch_source_cancel(long_timer);
});
dispatch_source_set_timer(long_timer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC) ,
long_interval, 0);
dispatch_resume(mini_timer);
dispatch_resume(long_timer);
sleep(6);
test_long("Checking final value", value_to_be_changed, 10);
test_long("Mini-timer fired", fired, 1);
dispatch_source_cancel(mini_timer);
dispatch_release(mini_timer);
dispatch_resume(timer_queue);
dispatch_release(timer_queue);
dispatch_release(long_timer);
test_stop();
return 0;
}

875
tests/dispatch_transform.c Normal file
View File

@ -0,0 +1,875 @@
/*
* Copyright (c) 2011-2012 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 <bsdtests.h>
#if DISPATCH_API_VERSION >= 20111008 && !TARGET_OS_EMBEDDED
#include <Security/Security.h>
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
#include <fcntl.h>
#define printf_data(p, s) ({ \
__typeof__(s) _i; \
for (_i=0; _i<s; _i++) { \
printf("%c", ((uint8_t *)p)[_i]); \
} \
printf("\n"); \
})
#define test_data_equal(a, b, c) ({ \
const void * ptr, * ptr2; \
size_t size, size2; \
dispatch_data_t map = dispatch_data_create_map(b, &ptr, &size); \
assert(map); \
dispatch_data_t map2 = dispatch_data_create_map(c, &ptr2, &size2); \
assert(map); \
test_long(a ": length", size, size2); \
test_long(a ": memcmp", memcmp(ptr, ptr2, size), 0); \
if (size != size2 || (memcmp(ptr, ptr2, size) != 0)) { \
printf_data(ptr, size); \
printf_data(ptr2, size2); \
} \
dispatch_release(map); \
dispatch_release(map2); \
})
static bool
dispatch_data_equal(dispatch_data_t a, dispatch_data_t b)
{
const void * ptr, * ptr2;
size_t size, size2;
bool equal = true;
dispatch_data_t map = dispatch_data_create_map(a, &ptr, &size); \
assert(map);
dispatch_data_t map2 = dispatch_data_create_map(b, &ptr2, &size2); \
assert(map2);
if (size == size2) {
if (memcmp(ptr, ptr2, size) != 0) {
equal = false;
}
} else {
equal = false;
}
dispatch_release(map);
dispatch_release(map2);
return equal;
}
static dispatch_data_t
execute_sectransform(SecTransformRef transformRef, dispatch_data_t data)
{
const void * bytes;
size_t size;
dispatch_data_t map = dispatch_data_create_map(data, &bytes, &size);
assert(map);
CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, bytes, size);
assert(dataRef);
dispatch_release(map);
SecTransformSetAttribute(transformRef, kSecTransformInputAttributeName, dataRef, NULL);
CFDataRef transformedDataRef = SecTransformExecute(transformRef, NULL);
assert(transformedDataRef);
CFRelease(dataRef);
dispatch_data_t output = dispatch_data_create(CFDataGetBytePtr(transformedDataRef), CFDataGetLength(transformedDataRef), dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_DEFAULT);
CFRelease(transformedDataRef);
return output;
}
#pragma mark - UTF tests
static uint8_t utf8[] = {
0x53, 0x6f, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x6b, 0x73, 0x20, 0x66, 0x6f,
0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x73, 0x68, 0x2e, 0x20, 0xeb, 0x84, 0x88, 0xeb, 0xac,
0xb4, 0x20, 0xec, 0x98, 0xa4, 0xeb, 0x9e, 0x98, 0x20, 0xea, 0xb7, 0xb8, 0xeb, 0xa6, 0xac, 0xea, 0xb3, 0xa0, 0x20, 0xea, 0xb7,
0xb8, 0x20, 0xeb, 0x8f, 0x99, 0xec, 0x95, 0x88, 0x20, 0xeb, 0xa7, 0x9b, 0xec, 0x9e, 0x88, 0xeb, 0x8a, 0x94, 0x20, 0xec, 0x83,
0x9d, 0xec, 0x84, 0xa0, 0xec, 0x9d, 0x80, 0x20, 0xea, 0xb3, 0xa0, 0xeb, 0xa7, 0x88, 0xec, 0x9b, 0xa0, 0xec, 0x96, 0xb4, 0x2e,
0x20, 0xf0, 0x9f, 0x98, 0x84, 0xf0, 0x9f, 0x98, 0x8a, 0xf0, 0x9f, 0x98, 0x83, 0xe2, 0x98, 0xba, 0xf0, 0x9f, 0x98, 0x89, 0xf0,
0x9f, 0x98, 0x8d, 0xf0, 0x9f, 0x92, 0xa8, 0xf0, 0x9f, 0x92, 0xa9, 0xf0, 0x9f, 0x91, 0x8e, 0x2e,
};
static uint16_t utf16[] = {
0xfeff, 0x53, 0x6f, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x6b, 0x73, 0x20, 0x66,
0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x73, 0x68, 0x2e, 0x20, 0xb108, 0xbb34, 0x20,
0xc624, 0xb798, 0x20, 0xadf8, 0xb9ac, 0xace0, 0x20, 0xadf8, 0x20, 0xb3d9, 0xc548, 0x20, 0xb9db, 0xc788, 0xb294, 0x20, 0xc0dd,
0xc120, 0xc740, 0x20, 0xace0, 0xb9c8, 0xc6e0, 0xc5b4, 0x2e, 0x20, 0xd83d, 0xde04, 0xd83d, 0xde0a, 0xd83d, 0xde03, 0x263a, 0xd83d,
0xde09, 0xd83d, 0xde0d, 0xd83d, 0xdca8, 0xd83d, 0xdca9, 0xd83d, 0xdc4e, 0x2e,
};
static uint16_t utf16be[] = {
0xfffe, 0x5300, 0x6f00, 0x2000, 0x6c00, 0x6f00, 0x6e00, 0x6700, 0x2000, 0x6100, 0x6e00, 0x6400, 0x2000, 0x7400, 0x6800, 0x6100,
0x6e00, 0x6b00, 0x7300, 0x2000, 0x6600, 0x6f00, 0x7200, 0x2000, 0x6100, 0x6c00, 0x6c00, 0x2000, 0x7400, 0x6800, 0x6500, 0x2000,
0x6600, 0x6900, 0x7300, 0x6800, 0x2e00, 0x2000, 0x8b1, 0x34bb, 0x2000, 0x24c6, 0x98b7, 0x2000, 0xf8ad, 0xacb9, 0xe0ac, 0x2000,
0xf8ad, 0x2000, 0xd9b3, 0x48c5, 0x2000, 0xdbb9, 0x88c7, 0x94b2, 0x2000, 0xddc0, 0x20c1, 0x40c7, 0x2000, 0xe0ac, 0xc8b9, 0xe0c6,
0xb4c5, 0x2e00, 0x2000, 0x3dd8, 0x4de, 0x3dd8, 0xade, 0x3dd8, 0x3de, 0x3a26, 0x3dd8, 0x9de, 0x3dd8, 0xdde, 0x3dd8, 0xa8dc,
0x3dd8, 0xa9dc, 0x3dd8, 0x4edc, 0x2e00,
};
// Invalid due to half missing surrogate
static uint16_t utf16le_invalid[] = {
0xfeff, 0x53, 0x6f, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x6b, 0x73, 0x20, 0x66,
0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x73, 0x68, 0x2e, 0x20, 0xb108, 0xbb34, 0x20,
0xc624, 0xb798, 0x20, 0xadf8, 0xb9ac, 0xace0, 0x20, 0xadf8, 0x20, 0xb3d9, 0xc548, 0x20, 0xb9db, 0xc788, 0xb294, 0x20, 0xc0dd,
0xc120, 0xc740, 0x20, 0xace0, 0xb9c8, 0xc6e0, 0xc5b4, 0x2e, 0x20, 0xd83d, 0xde04, 0xd83d, 0xde0a, 0xd83d, 0xde03, 0x263a, 0xd83d,
0xde09, 0xd83d, 0xde0d, 0xd83d, 0xdca8, 0xd83d, 0xd83d, 0xdc4e, 0x2e,
};
void
invalid_utf8_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8 + sizeof(utf8) - 8, 8, NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf8_data, DISPATCH_DATA_FORMAT_TYPE_UTF8, DISPATCH_DATA_FORMAT_TYPE_UTF16LE);
test_ptr_null("dispatch_data_create_with_transform (UTF8 (invalid start) -> UTF16LE)", transformed);
dispatch_release(utf8_data);
(void)context;
}
void
truncated_utf8_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8) - 3, NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf8_data, DISPATCH_DATA_FORMAT_TYPE_UTF8, DISPATCH_DATA_FORMAT_TYPE_UTF16LE);
test_ptr_null("dispatch_data_create_with_transform (UTF8 (truncated) -> UTF16LE)", transformed);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, invalid_utf8_test);
}
void
invalid_utf16le_surrogate_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16le_invalid, sizeof(utf16le_invalid), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16_data, DISPATCH_DATA_FORMAT_TYPE_UTF16LE, DISPATCH_DATA_FORMAT_TYPE_UTF8);
test_ptr_null("dispatch_data_create_with_transform (UTF16LE (missing surrogate) -> UTF8)", transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, truncated_utf8_test);
}
void
invalid_utf16le_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16, (sizeof(utf16) % 2) + 1, NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16_data, DISPATCH_DATA_FORMAT_TYPE_UTF16LE, DISPATCH_DATA_FORMAT_TYPE_UTF8);
test_ptr_null("dispatch_data_create_with_transform (UTF16LE (invalid) -> UTF8)", transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, invalid_utf16le_surrogate_test);
}
void
utf16le_bytes_to_utf8_test(void * context)
{
dispatch_data_t utf16_data = dispatch_data_empty;
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
size_t i;
for (i=0; i<sizeof(utf16); i++) {
dispatch_data_t new = dispatch_data_create((char*)utf16 + i, 1, NULL, ^{});
dispatch_data_t concat = dispatch_data_create_concat(utf16_data, new);
dispatch_release(new);
dispatch_release(utf16_data);
utf16_data = concat;
}
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16_data, DISPATCH_DATA_FORMAT_TYPE_UTF_ANY, DISPATCH_DATA_FORMAT_TYPE_UTF8);
test_ptr_notnull("dispatch_data_create_with_transform (UTF16LE (any, single bytes) -> UTF8)", transformed);
test_data_equal("utf16le_bytes_to_utf8_test", transformed, utf8_data);
dispatch_release(transformed);
dispatch_release(utf8_data);
dispatch_release(utf16_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, invalid_utf16le_test);
}
void
utf8_bytes_to_utf16le_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_empty;
dispatch_data_t utf16_data = dispatch_data_create(utf16, sizeof(utf16), NULL, ^{});
size_t i;
for (i=0; i<sizeof(utf8); i++) {
dispatch_data_t new = dispatch_data_create(utf8 + i, 1, NULL, ^{});
dispatch_data_t concat = dispatch_data_create_concat(utf8_data, new);
dispatch_release(new);
dispatch_release(utf8_data);
utf8_data = concat;
}
dispatch_data_t transformed = dispatch_data_create_with_transform(utf8_data, DISPATCH_DATA_FORMAT_TYPE_UTF_ANY, DISPATCH_DATA_FORMAT_TYPE_UTF16LE);
test_ptr_notnull("dispatch_data_create_with_transform (UTF8 (any, single bytes) -> UTF16LE)", transformed);
test_data_equal("utf8_bytes_to_utf16le_test", transformed, utf16_data);
dispatch_release(transformed);
dispatch_release(utf8_data);
dispatch_release(utf16_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf16le_bytes_to_utf8_test);
}
void
utf16be_detect_to_utf16le_test(void * context)
{
dispatch_data_t utf16be_data = dispatch_data_create(utf16be, sizeof(utf16be), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16, sizeof(utf16), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16be_data, DISPATCH_DATA_FORMAT_TYPE_UTF_ANY, DISPATCH_DATA_FORMAT_TYPE_UTF16LE);
test_ptr_notnull("dispatch_data_create_with_transform (UTF16BE (any) -> UTF16LE)", transformed);
test_data_equal("utf16be_detect_to_utf16le_test", transformed, utf16_data);
dispatch_release(transformed);
dispatch_release(utf16be_data);
dispatch_release(utf16_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf8_bytes_to_utf16le_test);
}
void
utf16be_detect_to_utf8_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16be, sizeof(utf16be), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16_data, DISPATCH_DATA_FORMAT_TYPE_UTF_ANY, DISPATCH_DATA_FORMAT_TYPE_UTF8);
test_ptr_notnull("dispatch_data_create_with_transform (UTF16BE (any) -> UTF8)", transformed);
test_data_equal("utf16be_detect_to_utf8_test", transformed, utf8_data);
dispatch_release(transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf16be_detect_to_utf16le_test);
}
void
utf16le_detect_to_utf8_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16, sizeof(utf16), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16_data, DISPATCH_DATA_FORMAT_TYPE_UTF_ANY, DISPATCH_DATA_FORMAT_TYPE_UTF8);
test_ptr_notnull("dispatch_data_create_with_transform (UTF16LE (any) -> UTF8)", transformed);
test_data_equal("utf16le_detect_to_utf8_test", transformed, utf8_data);
dispatch_release(transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf16be_detect_to_utf8_test);
}
void
utf16be_to_utf8_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16be, sizeof(utf16be), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16_data, DISPATCH_DATA_FORMAT_TYPE_UTF16BE, DISPATCH_DATA_FORMAT_TYPE_UTF8);
test_ptr_notnull("dispatch_data_create_with_transform (UTF16BE -> UTF8)", transformed);
test_data_equal("utf16be_to_utf8_test", transformed, utf8_data);
dispatch_release(transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf16le_detect_to_utf8_test);
}
void
utf16le_to_utf8_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16, sizeof(utf16), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf16_data, DISPATCH_DATA_FORMAT_TYPE_UTF16LE, DISPATCH_DATA_FORMAT_TYPE_UTF8);
test_ptr_notnull("dispatch_data_create_with_transform (UTF16LE -> UTF8)", transformed);
test_data_equal("utf16le_to_utf8_test", transformed, utf8_data);
dispatch_release(transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf16be_to_utf8_test);
}
void
utf8_to_utf16be_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16be, sizeof(utf16be), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf8_data, DISPATCH_DATA_FORMAT_TYPE_UTF8, DISPATCH_DATA_FORMAT_TYPE_UTF16BE);
test_ptr_notnull("dispatch_data_create_with_transform (UTF8 -> UTF16BE)", transformed);
test_data_equal("utf8_to_utf16be_test", transformed, utf16_data);
dispatch_release(transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf16le_to_utf8_test);
}
void
utf8_to_utf16le_test(void * context)
{
dispatch_data_t utf8_data = dispatch_data_create(utf8, sizeof(utf8), NULL, ^{});
dispatch_data_t utf16_data = dispatch_data_create(utf16, sizeof(utf16), NULL, ^{});
dispatch_data_t transformed = dispatch_data_create_with_transform(utf8_data, DISPATCH_DATA_FORMAT_TYPE_UTF8, DISPATCH_DATA_FORMAT_TYPE_UTF16LE);
test_ptr_notnull("dispatch_data_create_with_transform (UTF8 -> UTF16LE)", transformed);
test_data_equal("utf8_to_utf16le_test", transformed, utf16_data);
dispatch_release(transformed);
dispatch_release(utf16_data);
dispatch_release(utf8_data);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf8_to_utf16be_test);
}
#pragma mark - base32 tests
void
decode32_corrupt_test(void * context)
{
dispatch_group_enter((dispatch_group_t)context);
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_read(fd, 4096, dispatch_get_main_queue(), ^(dispatch_data_t data, int error) {
assert(error == 0);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
void * corrupt_buffer = malloc(dispatch_data_get_size(sectransform_data));
const void * source;
size_t size;
dispatch_data_t map = dispatch_data_create_map(sectransform_data, &source, &size);
memcpy(corrupt_buffer, source, size);
size_t i;
for (i=0; i<size; i += (arc4random() % (int)(size * 0.05))) {
char x = arc4random() & 0xff;
while ((x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || (x >= '0' && x <= '9') || x == '/' || x == '+' || x == '=') {
x = arc4random() & 0xff;
}
((char*)corrupt_buffer)[i] = x;
}
dispatch_release(map);
dispatch_release(sectransform_data);
dispatch_data_t corrupt_data = dispatch_data_create(corrupt_buffer, size, dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_FREE);
dispatch_data_t transform_data = dispatch_data_create_with_transform(corrupt_data, DISPATCH_DATA_FORMAT_TYPE_BASE32, DISPATCH_DATA_FORMAT_TYPE_NONE);
test_ptr_null("decode32_corrupt_test: dispatch_data_create_with_transform", transform_data);
dispatch_release(corrupt_data);
close(fd);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, utf8_to_utf16le_test);
dispatch_group_leave((dispatch_group_t)context);
});
}
void
chunking_decode32_test(void * context)
{
(void)context;
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_data_t __block data = dispatch_data_empty;
int i;
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("read", 0);
for (i=0; i<4096; i++) {
dispatch_group_enter(group);
dispatch_read(fd, 1, queue, ^(dispatch_data_t d, int error) {
assert(error == 0);
dispatch_data_t concat = dispatch_data_create_concat(data, d);
dispatch_release(data);
data = concat;
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(queue);
dispatch_release(group);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transformed_data = dispatch_data_create_with_transform(sectransform_data, DISPATCH_DATA_FORMAT_TYPE_BASE32, DISPATCH_DATA_FORMAT_TYPE_NONE);
test_ptr_notnull("chunking_decode32_test: dispatch_data_create_with_transform", transformed_data);
test_data_equal("chunking_decode32_test", transformed_data, data);
dispatch_release(sectransform_data);
dispatch_release(transformed_data);
dispatch_release(data);
close(fd);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, decode32_corrupt_test);
}
void
chunking_encode32_test(void * context)
{
(void)context;
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_data_t __block data = dispatch_data_empty;
int i;
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("read", 0);
for (i=0; i<4096; i++) {
dispatch_group_enter(group);
dispatch_read(fd, 1, queue, ^(dispatch_data_t d, int error) {
assert(error == 0);
dispatch_data_t concat = dispatch_data_create_concat(data, d);
dispatch_release(data);
data = concat;
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(queue);
dispatch_release(group);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transformed_data = dispatch_data_create_with_transform(data, DISPATCH_DATA_FORMAT_TYPE_NONE, DISPATCH_DATA_FORMAT_TYPE_BASE32);
test_ptr_notnull("chunking_encode32_test: dispatch_data_create_with_transform", transformed_data);
test_data_equal("chunking_encode32_test", transformed_data, sectransform_data);
dispatch_release(sectransform_data);
dispatch_release(transformed_data);
dispatch_release(data);
close(fd);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, chunking_decode32_test);
}
void
decode32_test(void * context)
{
dispatch_group_enter((dispatch_group_t)context);
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_read(fd, 4096, dispatch_get_main_queue(), ^(dispatch_data_t data, int error) {
assert(error == 0);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transform_data = dispatch_data_create_with_transform(sectransform_data, DISPATCH_DATA_FORMAT_TYPE_BASE32, DISPATCH_DATA_FORMAT_TYPE_NONE);
test_ptr_notnull("decode32_test: dispatch_data_create_with_transform", transform_data);
test_data_equal("decode32_test", transform_data, data);
dispatch_release(sectransform_data);
dispatch_release(transform_data);
close(fd);
dispatch_group_async_f((dispatch_group_t)context, dispatch_get_main_queue(), context, chunking_encode32_test);
dispatch_group_leave((dispatch_group_t)context);
});
}
void
encode32_test(void * context)
{
dispatch_group_enter((dispatch_group_t)context);
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_read(fd, 4096, dispatch_get_main_queue(), ^(dispatch_data_t data, int error) {
assert(error == 0);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transformed_data = dispatch_data_create_with_transform(data, DISPATCH_DATA_FORMAT_TYPE_NONE, DISPATCH_DATA_FORMAT_TYPE_BASE32);
test_ptr_notnull("encode32_test: dispatch_data_create_with_transform", transformed_data);
test_data_equal("encode32_test", transformed_data, sectransform_data);
dispatch_release(sectransform_data);
dispatch_release(transformed_data);
close(fd);
dispatch_group_async_f((dispatch_group_t)context, dispatch_get_main_queue(), context, decode32_test);
dispatch_group_leave((dispatch_group_t)context);
});
}
#pragma mark - base64 tests
void
decode64_loop_test(void * context)
{
if (getenv("LOOP_SKIP") == NULL)
{
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
size_t i, __block tests = 0;
for (i=1; i<4097; i++) {
dispatch_read(fd, i, dispatch_get_global_queue(0, 0), ^(dispatch_data_t data, int error) {
assert(error == 0);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transform_data = dispatch_data_create_with_transform(sectransform_data, DISPATCH_DATA_FORMAT_TYPE_BASE64, DISPATCH_DATA_FORMAT_TYPE_NONE);
if (dispatch_data_equal(transform_data, data)) {
tests++;
}
dispatch_release(sectransform_data);
dispatch_release(transform_data);
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
dispatch_release(sema);
close(fd);
test_long("decode64_loop_test", tests, 4096);
}
dispatch_group_async_f((dispatch_group_t)context, dispatch_get_main_queue(), context, encode32_test);
}
void
decode64_corrupt_test(void * context)
{
dispatch_group_enter((dispatch_group_t)context);
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_read(fd, 4096, dispatch_get_main_queue(), ^(dispatch_data_t data, int error) {
assert(error == 0);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
void * corrupt_buffer = malloc(dispatch_data_get_size(sectransform_data));
const void * source;
size_t size;
dispatch_data_t map = dispatch_data_create_map(sectransform_data, &source, &size);
memcpy(corrupt_buffer, source, size);
size_t i;
for (i=0; i<size; i += (arc4random() % (int)(size * 0.05))) {
char x = arc4random() & 0xff;
while ((x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || (x >= '0' && x <= '9') || x == '/' || x == '+' || x == '=') {
x = arc4random() & 0xff;
}
((char*)corrupt_buffer)[i] = x;
}
dispatch_release(map);
dispatch_release(sectransform_data);
dispatch_data_t corrupt_data = dispatch_data_create(corrupt_buffer, size, dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_FREE);
dispatch_data_t transform_data = dispatch_data_create_with_transform(corrupt_data, DISPATCH_DATA_FORMAT_TYPE_BASE64, DISPATCH_DATA_FORMAT_TYPE_NONE);
test_ptr_null("decode64_corrupt_test: dispatch_data_create_with_transform", transform_data);
dispatch_release(corrupt_data);
close(fd);
dispatch_group_async_f((dispatch_group_t)context, dispatch_get_main_queue(), context, decode64_loop_test);
dispatch_group_leave((dispatch_group_t)context);
});
}
void
chunking_decode64_test(void * context)
{
(void)context;
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_data_t __block data = dispatch_data_empty;
int i;
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("read", 0);
for (i=0; i<4096; i++) {
dispatch_group_enter(group);
dispatch_read(fd, 1, queue, ^(dispatch_data_t d, int error) {
assert(error == 0);
dispatch_data_t concat = dispatch_data_create_concat(data, d);
dispatch_release(data);
data = concat;
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(queue);
dispatch_release(group);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transformed_data = dispatch_data_create_with_transform(sectransform_data, DISPATCH_DATA_FORMAT_TYPE_BASE64, DISPATCH_DATA_FORMAT_TYPE_NONE);
test_ptr_notnull("chunking_decode64_test: dispatch_data_create_with_transform", transformed_data);
test_data_equal("chunking_decode64_test", transformed_data, data);
dispatch_release(sectransform_data);
dispatch_release(transformed_data);
dispatch_release(data);
close(fd);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, decode64_corrupt_test);
}
void
chunking_encode64_test(void * context)
{
(void)context;
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_data_t __block data = dispatch_data_empty;
int i;
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("read", 0);
for (i=0; i<4097; i++) {
dispatch_group_enter(group);
dispatch_read(fd, 1, queue, ^(dispatch_data_t d, int error) {
assert(error == 0);
dispatch_data_t concat = dispatch_data_create_concat(data, d);
dispatch_release(data);
data = concat;
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(queue);
dispatch_release(group);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transformed_data = dispatch_data_create_with_transform(data, DISPATCH_DATA_FORMAT_TYPE_NONE, DISPATCH_DATA_FORMAT_TYPE_BASE64);
test_ptr_notnull("chunking_encode64_test: dispatch_data_create_with_transform", transformed_data);
test_data_equal("chunking_encode64_test", transformed_data, sectransform_data);
dispatch_release(sectransform_data);
dispatch_release(transformed_data);
dispatch_release(data);
close(fd);
dispatch_group_async_f(context, dispatch_get_main_queue(), context, chunking_decode64_test);
}
void
decode64_test(void * context)
{
dispatch_group_enter((dispatch_group_t)context);
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_read(fd, 4096, dispatch_get_main_queue(), ^(dispatch_data_t data, int error) {
assert(error == 0);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transform_data = dispatch_data_create_with_transform(sectransform_data, DISPATCH_DATA_FORMAT_TYPE_BASE64, DISPATCH_DATA_FORMAT_TYPE_NONE);
test_ptr_notnull("decode64_test: dispatch_data_create_with_transform", transform_data);
test_data_equal("decode64_test", transform_data, data);
dispatch_release(sectransform_data);
dispatch_release(transform_data);
close(fd);
dispatch_group_async_f((dispatch_group_t)context, dispatch_get_main_queue(), context, chunking_encode64_test);
dispatch_group_leave((dispatch_group_t)context);
});
}
void
encode64_test(void * context)
{
dispatch_group_enter((dispatch_group_t)context);
int fd = open("/dev/random", O_RDONLY);
assert(fd >= 0);
dispatch_read(fd, 4096, dispatch_get_main_queue(), ^(dispatch_data_t data, int error) {
assert(error == 0);
SecTransformRef transformRef = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
assert(transformRef);
dispatch_data_t sectransform_data = execute_sectransform(transformRef, data);
assert(sectransform_data);
CFRelease(transformRef);
dispatch_data_t transformed_data = dispatch_data_create_with_transform(data, DISPATCH_DATA_FORMAT_TYPE_NONE, DISPATCH_DATA_FORMAT_TYPE_BASE64);
test_ptr_notnull("encode64_test: dispatch_data_create_with_transform", transformed_data);
test_data_equal("encode64_test", transformed_data, sectransform_data);
dispatch_release(sectransform_data);
dispatch_release(transformed_data);
close(fd);
dispatch_group_async_f((dispatch_group_t)context, dispatch_get_main_queue(), context, decode64_test);
dispatch_group_leave((dispatch_group_t)context);
});
}
#pragma mark - main
int
main(void)
{
test_start("Dispatch data transforms test");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async_f(group, dispatch_get_main_queue(), group, encode64_test);
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
dispatch_release(group);
test_stop();
exit(0);
});
dispatch_main();
return 0;
}
#else
int
main(void)
{
test_skip("Dispatch data transforms test");
return 0;
}
#endif

203
tests/dispatch_vm.c Normal file
View File

@ -0,0 +1,203 @@
/*
* Copyright (c) 2010-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 <sys/event.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#endif
#include <assert.h>
#ifdef __ANDROID__
#include <linux/sysctl.h>
#else
#if !defined(__linux__)
#include <sys/sysctl.h>
#endif
#endif /* __ANDROID__ */
#include <stdarg.h>
#include <time.h>
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#if defined(DISPATCH_SOURCE_TYPE_VM) && defined(NOTE_VM_PRESSURE)
#if TARGET_OS_EMBEDDED
#define ALLOC_SIZE ((size_t)(1024*1024*1ul)) // 1MB
#define NOTIFICATIONS 1
#else
#define ALLOC_SIZE ((size_t)(1024*1024*20ul)) // 20MB
#define NOTIFICATIONS 2
#endif
#define pg2mb(p) ((p) * ALLOC_SIZE/(1024*1024))
#ifdef __LP64__
#define MAXMEM ((size_t)SIZE_MAX)
#else
#define MAXMEM ((size_t)(3200ul*1024*1024)) // 3200MB
#endif
static char **pages;
static volatile int32_t handler_call_count;
static volatile int32_t page_count;
static int32_t max_page_count;
static dispatch_source_t vm_source;
static dispatch_queue_t vm_queue;
static time_t initial;
static int interval = 16;
#define log_msg(msg, ...) \
do { \
fprintf(stderr, "[%2ds] " msg, (int)(time(NULL) - initial), ##__VA_ARGS__);\
} while (0)
static bool
dispatch_test_check_evfilt_vm(void)
{
int kq = kqueue();
assert(kq != -1);
struct kevent ke = {
.filter = EVFILT_VM,
.flags = EV_ADD|EV_ENABLE|EV_RECEIPT,
.fflags = NOTE_VM_PRESSURE,
};
int r = kevent(kq, &ke, 1, &ke, 1, NULL);
close(kq);
return !(r > 0 && ke.flags & EV_ERROR && ke.data == ENOTSUP);
}
static void
cleanup(void)
{
dispatch_source_cancel(vm_source);
dispatch_release(vm_source);
dispatch_release(vm_queue);
int32_t pc = 0, i;
for (i = 0; i < max_page_count; ++i) {
if (pages[i]) {
pc++;
free(pages[i]);
}
}
if (pc) {
log_msg("Freed %ldMB\n", pg2mb(pc));
}
free(pages);
test_stop();
}
int
main(void)
{
dispatch_test_start("Dispatch VM Pressure test"); // rdar://problem/7000945
if (!dispatch_test_check_evfilt_vm()) {
test_skip("EVFILT_VM not supported");
test_stop();
return 0;
}
initial = time(NULL);
uint64_t memsize;
#ifdef __linux__
memsize = sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES);
#else
size_t s = sizeof(memsize);
int rc = sysctlbyname("hw.memsize", &memsize, &s, NULL, 0);
assert(rc == 0);
#endif
max_page_count = MIN(memsize, MAXMEM) / ALLOC_SIZE;
pages = calloc(max_page_count, sizeof(char*));
vm_queue = dispatch_queue_create("VM Pressure", NULL);
vm_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VM, 0,
DISPATCH_VM_PRESSURE, vm_queue);
dispatch_source_set_event_handler(vm_source, ^{
if (!page_count) {
// Too much memory pressure already to start the test
test_skip("Memory pressure at start of test");
cleanup();
}
if (OSAtomicIncrement32Barrier(&handler_call_count) != NOTIFICATIONS) {
log_msg("Ignoring vm pressure notification\n");
interval = 1;
return;
}
test_long("dispatch_source_get_data()",
dispatch_source_get_data(vm_source), NOTE_VM_PRESSURE);
int32_t i, pc = page_count + 1;
for (i = 0; i < pc && pages[i]; ++i) {
free(pages[i]);
pages[i] = NULL;
}
log_msg("Freed %ldMB\n", pg2mb(i));
});
dispatch_resume(vm_source);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (handler_call_count < NOTIFICATIONS &&
page_count < max_page_count) {
void *p = valloc(ALLOC_SIZE);
if (!p) {
break;
}
bzero(p, ALLOC_SIZE);
pages[page_count] = p;
if (!(OSAtomicIncrement32Barrier(&page_count) % interval)) {
log_msg("Allocated %ldMB\n", pg2mb(page_count));
usleep(200000);
}
}
if (page_count % interval) {
log_msg("Allocated %ldMB\n", pg2mb(page_count));
}
if (handler_call_count < NOTIFICATIONS) {
// Cannot allocate enough memory for test (e.g. on 32 bit)
test_skip("Cannot allocate enough memory for test");
dispatch_async(vm_queue, ^{cleanup();});
return;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),
vm_queue, ^{
test_long_greater_than_or_equal("VM Pressure fired",
handler_call_count, NOTIFICATIONS);
test_long_less_than("VM Pressure stopped firing",
handler_call_count, 4);
cleanup();
});
});
dispatch_main();
return 0;
}
#else //DISPATCH_SOURCE_TYPE_VM
int
main(void)
{
dispatch_test_start("Dispatch VM Pressure test"
" - No DISPATCH_SOURCE_TYPE_VM");
test_stop();
return 0;
}
#endif //DISPATCH_SOURCE_TYPE_VM

113
tests/dispatch_vnode.c Normal file
View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2010-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 <fcntl.h>
#include <stdlib.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include <dispatch/dispatch.h>
#include <bsdtests.h>
#include "dispatch_test.h"
#define ITERATIONS 1000
long iterations, notifications;
int
main(void)
{
dispatch_test_start("Dispatch VNODE RENAME");
char path1[] = "/tmp/dispatchtest_vnode.XXXXXX";
char path2[] = "/tmp/dispatchtest_vnode.XXXXXX";
int fd1 = mkstemp(path1);
if (fd1 == -1) {
test_errno("mkstemp", errno, 0);
test_stop();
}
close(fd1);
if (!mktemp(path2)) {
test_errno("mktemp", errno, 0);
test_stop();
}
char *currentName = path1;
char *renameDest = path2;
dispatch_semaphore_t renamedSemaphore = dispatch_semaphore_create(0);
dispatch_group_t g = dispatch_group_create();
while (iterations++ < ITERATIONS) {
int fd = open(currentName, O_EVTONLY);
if (fd == -1) {
test_errno("open", errno, 0);
test_stop();
}
dispatch_source_t monitorSource = dispatch_source_create(
DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_RENAME,
dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(monitorSource, ^{
dispatch_semaphore_signal(renamedSemaphore);
});
dispatch_source_set_cancel_handler(monitorSource, ^{
close(fd);
});
#if DISPATCH_API_VERSION >= 20100818 // <rdar://problem/7731284>
dispatch_group_enter(g);
dispatch_source_set_registration_handler(monitorSource, ^{
dispatch_group_leave(g);
});
#endif
dispatch_resume(monitorSource);
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
if(rename(currentName, renameDest)) {
test_errno("rename", errno, 0);
test_stop();
}
char *tmp = currentName;
currentName = renameDest;
renameDest = tmp;
if(!dispatch_semaphore_wait(renamedSemaphore,
dispatch_time(DISPATCH_TIME_NOW, 10 * 1000 * NSEC_PER_USEC))) {
fprintf(stderr, ".");
notifications++;
dispatch_source_cancel(monitorSource);
} else {
fprintf(stderr, "!");
dispatch_source_cancel(monitorSource);
dispatch_semaphore_signal(renamedSemaphore);
}
if (!(iterations % 80)) {
fprintf(stderr, "\n");
}
dispatch_release(monitorSource);
}
fprintf(stderr, "\n");
unlink(currentName);
dispatch_release(g);
dispatch_release(renamedSemaphore);
test_long("VNODE RENAME notifications", notifications, ITERATIONS);
test_stop();
return 0;
}

33
tests/func.c Normal file
View File

@ -0,0 +1,33 @@
/*
* 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@
*/
#ifdef __cplusplus
extern "C" {
#endif
void
func(void)
{
}
#ifdef __BLOCKS__
void (^block)(void) = ^{ };
#endif
#ifdef __cplusplus
};
#endif

54
tests/generic_unix_port.h Normal file
View File

@ -0,0 +1,54 @@
#include <limits.h>
#include <sys/param.h>
static inline int32_t
OSAtomicIncrement32(volatile int32_t *var)
{
return __c11_atomic_fetch_add((_Atomic(int)*)var, 1, __ATOMIC_RELAXED)+1;
}
static inline int32_t
OSAtomicIncrement32Barrier(volatile int32_t *var)
{
return __c11_atomic_fetch_add((_Atomic(int)*)var, 1, __ATOMIC_SEQ_CST)+1;
}
static inline int32_t
OSAtomicAdd32(int32_t val, volatile int32_t *var)
{
return __c11_atomic_fetch_add((_Atomic(int)*)var, val, __ATOMIC_RELAXED)+val;
}
// Simulation of mach_absolute_time related infrastructure
// For now, use gettimeofday.
// Consider using clockgettime(CLOCK_MONOTONIC) instead.
#include <sys/time.h>
struct mach_timebase_info {
uint32_t numer;
uint32_t denom;
};
typedef struct mach_timebase_info *mach_timebase_info_t;
typedef struct mach_timebase_info mach_timebase_info_data_t;
typedef int kern_return_t;
static inline
uint64_t
mach_absolute_time()
{
struct timeval tv;
gettimeofday(&tv,NULL);
return (1000ull)*((unsigned long long)tv.tv_sec*(1000000ull) + (unsigned long long)tv.tv_usec);
}
static inline
int
mach_timebase_info(mach_timebase_info_t tbi)
{
tbi->numer = 1;
tbi->denom = 1;
return 0;
}

297
tests/generic_win_port.c Normal file
View File

@ -0,0 +1,297 @@
#define _CRT_RAND_S
#include <generic_win_port.h>
#include <dispatch_test.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <wchar.h>
#include <Windows.h>
static bool
expand_wstr(WCHAR **str, size_t *capacity, size_t needed)
{
if (*capacity >= needed) {
return true;
}
if (needed > UNICODE_STRING_MAX_CHARS) {
return false;
}
size_t new_capacity = *capacity ?: needed;
while (new_capacity < needed) {
new_capacity *= 2;
}
WCHAR *new_str = realloc(*str, new_capacity * sizeof(WCHAR));
if (!new_str) {
return false;
}
*str = new_str;
*capacity = new_capacity;
return true;
}
static bool
append_wstr(WCHAR **str, size_t *capacity, size_t *len, WCHAR *suffix)
{
size_t suffix_len = wcslen(suffix);
if (!expand_wstr(str, capacity, *len + suffix_len)) {
return false;
}
memcpy(*str + *len, suffix, suffix_len * sizeof(WCHAR));
*len += suffix_len;
return true;
}
WCHAR *
argv_to_command_line(char **argv)
{
// This is basically the reverse of CommandLineToArgvW(). We want to convert
// an argv array into a command-line compatible with CreateProcessW().
//
// See also:
// <https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw>
// <https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/>
size_t len = 0, capacity = 0;
WCHAR *cmdline = NULL;
if (!expand_wstr(&cmdline, &capacity, 256)) {
goto error;
}
for (size_t i = 0; argv[i]; i++) {
// Separate arguments with spaces.
if (i > 0 && !append_wstr(&cmdline, &capacity, &len, L" ")) {
goto error;
}
// Surround the argument with quotes if it's empty or contains special
// characters.
char *cur = argv[i];
bool quoted = (*cur == '\0' || cur[strcspn(cur, " \t\n\v\"")] != '\0');
if (quoted && !append_wstr(&cmdline, &capacity, &len, L"\"")) {
goto error;
}
while (*cur != '\0') {
if (*cur == '"') {
// Quotes must be escaped with a backslash.
if (!append_wstr(&cmdline, &capacity, &len, L"\\\"")) {
goto error;
}
cur++;
} else if (*cur == '\\') {
// Windows treats backslashes differently depending on whether
// they're followed by a quote. If the backslashes aren't
// followed by a quote, then all slashes are copied into the
// argument string. Otherwise, only n/2 slashes are included.
// Count the number of slashes and double them if they're
// followed by a quote.
size_t backslashes = strspn(cur, "\\");
cur += backslashes;
// If the argument needs to be surrounded with quotes, we must
// also check if the backslashes are at the end of the argument
// because the added quote will follow them.
if (*cur == '"' || (quoted && *cur == '\0')) {
backslashes *= 2;
}
if (!expand_wstr(&cmdline, &capacity, len + backslashes)) {
goto error;
}
wmemset(&cmdline[len], L'\\', backslashes);
len += backslashes;
} else {
// Widen as many characters as possible.
size_t mb_len = strcspn(cur, "\"\\");
int wide_len = MultiByteToWideChar(CP_UTF8, 0, cur, mb_len,
NULL, 0);
if (wide_len == 0) {
goto error;
}
if (!expand_wstr(&cmdline, &capacity, len + wide_len)) {
goto error;
}
wide_len = MultiByteToWideChar(CP_UTF8, 0, cur, mb_len,
&cmdline[len], wide_len);
if (wide_len == 0) {
goto error;
}
cur += mb_len;
len += wide_len;
}
}
if (quoted && !append_wstr(&cmdline, &capacity, &len, L"\"")) {
goto error;
}
}
if (!expand_wstr(&cmdline, &capacity, len + 1)) {
goto error;
}
cmdline[len] = L'\0';
return cmdline;
error:
free(cmdline);
return NULL;
}
int
asprintf(char **strp, const char *format, ...)
{
va_list arg1;
va_start(arg1, format);
int len = vsnprintf(NULL, 0, format, arg1);
va_end(arg1);
if (len >= 0) {
size_t size = (size_t)len + 1;
*strp = malloc(size);
if (!*strp) {
return -1;
}
va_list arg2;
va_start(arg2, format);
len = vsnprintf(*strp, size, format, arg2);
va_end(arg2);
}
return len;
}
void
filetime_to_timeval(struct timeval *tp, const FILETIME *ft)
{
int64_t ticks = ft->dwLowDateTime | (((int64_t)ft->dwHighDateTime) << 32);
static const int64_t ticks_per_sec = 10LL * 1000LL * 1000LL;
static const int64_t ticks_per_usec = 10LL;
if (ticks >= 0) {
tp->tv_sec = (long)(ticks / ticks_per_sec);
tp->tv_usec = (long)((ticks % ticks_per_sec) / ticks_per_usec);
} else {
tp->tv_sec = (long)((ticks + 1) / ticks_per_sec - 1);
tp->tv_usec = (long)((ticks_per_sec - 1 + (ticks + 1) % ticks_per_sec) / ticks_per_usec);
}
}
pid_t
getpid(void)
{
return (pid_t)GetCurrentProcessId();
}
int
gettimeofday(struct timeval *tp, void *tzp)
{
(void)tzp;
FILETIME ft;
GetSystemTimePreciseAsFileTime(&ft);
int64_t ticks = ft.dwLowDateTime | (((int64_t)ft.dwHighDateTime) << 32);
ticks -= 116444736000000000LL; // Convert to Unix time
FILETIME unix_ft = {.dwLowDateTime = (DWORD)ticks, .dwHighDateTime = ticks >> 32};
filetime_to_timeval(tp, &unix_ft);
return 0;
}
typedef void (WINAPI *QueryUnbiasedInterruptTimePreciseT)(PULONGLONG);
static QueryUnbiasedInterruptTimePreciseT QueryUnbiasedInterruptTimePrecisePtr;
static BOOL
mach_absolute_time_init(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
{
// QueryUnbiasedInterruptTimePrecise() is declared in the Windows headers
// but it isn't available in any import libraries. We must manually load it
// from KernelBase.dll.
HMODULE kernelbase = LoadLibraryW(L"KernelBase.dll");
if (!kernelbase) {
print_winapi_error("LoadLibraryW", GetLastError());
abort();
}
QueryUnbiasedInterruptTimePrecisePtr =
(QueryUnbiasedInterruptTimePreciseT)GetProcAddress(kernelbase,
"QueryUnbiasedInterruptTimePrecise");
if (!QueryUnbiasedInterruptTimePrecisePtr) {
fprintf(stderr, "QueryUnbiasedInterruptTimePrecise is not available\n");
abort();
}
return TRUE;
}
uint64_t
mach_absolute_time(void)
{
static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
if (!InitOnceExecuteOnce(&init_once, mach_absolute_time_init, NULL, NULL)) {
print_winapi_error("InitOnceExecuteOnce", GetLastError());
abort();
}
ULONGLONG result = 0;
QueryUnbiasedInterruptTimePrecisePtr(&result);
return result * 100; // Convert from 100ns units
}
static void
randomize_name(char *out)
{
static const char chars[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-";
const size_t num_chars = sizeof(chars) - 1;
unsigned int lo, hi;
rand_s(&lo);
rand_s(&hi);
uint64_t val = ((uint64_t)hi << 32) | lo;
for (int j = 0; j < 6; j++) {
out[j] = chars[val % num_chars];
val /= num_chars;
}
}
dispatch_fd_t
mkstemp(char *tmpl)
{
size_t len = strlen(tmpl);
if (len < 6) {
errno = EINVAL;
return -1;
}
char *replace = &tmpl[len - 6];
for (int i = 0; i < 100; i++) {
randomize_name(replace);
dispatch_fd_t fd = dispatch_test_fd_open(tmpl, O_RDWR | O_CREAT | O_EXCL);
if (fd != -1) {
return fd;
}
}
errno = EEXIST;
return -1;
}
void
print_winapi_error(const char *function_name, DWORD error)
{
char *message = NULL;
DWORD len = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
error, 0, (LPSTR)&message, 0, NULL);
if (len > 0) {
// Note: FormatMessage includes a newline at the end of the message
fprintf(stderr, "%s: %s", function_name, message);
LocalFree(message);
} else {
fprintf(stderr, "%s: error %lu\n", function_name, error);
}
}
intptr_t
random(void)
{
unsigned int x;
rand_s(&x);
return x & INT_MAX;
}
unsigned int
sleep(unsigned int seconds)
{
Sleep(seconds * 1000);
return 0;
}
int
usleep(unsigned int usec)
{
Sleep((usec + 999) / 1000);
return 0;
}

84
tests/generic_win_port.h Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include <dispatch/dispatch.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/types.h>
#include <Windows.h>
typedef int kern_return_t;
typedef int pid_t;
#if defined(_WIN64)
typedef long long ssize_t;
#else
typedef long ssize_t;
#endif
struct mach_timebase_info {
uint32_t numer;
uint32_t denom;
};
typedef struct mach_timebase_info *mach_timebase_info_t;
typedef struct mach_timebase_info mach_timebase_info_data_t;
static inline int32_t
OSAtomicIncrement32(volatile int32_t *var)
{
return __c11_atomic_fetch_add((_Atomic(int)*)var, 1, __ATOMIC_RELAXED)+1;
}
static inline int32_t
OSAtomicIncrement32Barrier(volatile int32_t *var)
{
return __c11_atomic_fetch_add((_Atomic(int)*)var, 1, __ATOMIC_SEQ_CST)+1;
}
static inline int32_t
OSAtomicAdd32(int32_t val, volatile int32_t *var)
{
return __c11_atomic_fetch_add((_Atomic(int)*)var, val, __ATOMIC_RELAXED)+val;
}
WCHAR *
argv_to_command_line(char **argv);
int
asprintf(char **strp, const char *format, ...);
void
filetime_to_timeval(struct timeval *tp, const FILETIME *ft);
pid_t
getpid(void);
int
gettimeofday(struct timeval *tp, void *tzp);
uint64_t
mach_absolute_time(void);
static inline
int
mach_timebase_info(mach_timebase_info_t tbi)
{
tbi->numer = 1;
tbi->denom = 1;
return 0;
}
dispatch_fd_t
mkstemp(char *tmpl);
void
print_winapi_error(const char *function_name, DWORD error);
intptr_t
random(void);
unsigned int
sleep(unsigned int seconds);
int
usleep(unsigned int usec);

10
tests/leaks-wrapper.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
/usr/bin/leaks "$@" 2>&1 | tee "${TMPDIR}$*.leakslog" | grep -q " 0 leaks for 0 total leaked bytes"
if [ $? -eq 0 ]; then
rm "${TMPDIR}$*.leakslog"
exit 0
else
exit $?
fi