mirror of
https://github.com/darlinghq/darling-libdispatch.git
synced 2024-11-23 03:59:40 +00:00
Add back test cases
This commit is contained in:
parent
5490af73c1
commit
4ae5219973
44
tests/.gitignore
vendored
Normal file
44
tests/.gitignore
vendored
Normal 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
199
tests/CMakeLists.apple.txt
Normal 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
115
tests/CMakeLists.txt
Normal 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
728
tests/Foundation/bench.mm
Normal 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();
|
||||
}
|
63
tests/Foundation/dispatch_apply_gc.m
Normal file
63
tests/Foundation/dispatch_apply_gc.m
Normal 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;
|
||||
}
|
67
tests/Foundation/dispatch_sync_gc.m
Normal file
67
tests/Foundation/dispatch_sync_gc.m
Normal 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;
|
||||
}
|
73
tests/Foundation/nsoperation.m
Normal file
73
tests/Foundation/nsoperation.m
Normal 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
279
tests/bsdtestharness.c
Normal 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
536
tests/bsdtests.c
Normal 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
190
tests/bsdtests.h
Normal 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
122
tests/bsdtestsummarize.c
Normal 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
175
tests/cffd.c
Normal 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
10
tests/darling/run-all.sh.in
Executable 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
14
tests/darling/run-single.sh
Executable 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
92
tests/dispatch_after.c
Normal 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
44
tests/dispatch_api.c
Normal 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
157
tests/dispatch_apply.c
Normal 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
44
tests/dispatch_c99.c
Normal 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
136
tests/dispatch_cascade.c
Normal 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
61
tests/dispatch_cf_main.c
Normal 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
295
tests/dispatch_concur.c
Normal 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;
|
||||
}
|
158
tests/dispatch_context_for_key.c
Normal file
158
tests/dispatch_context_for_key.c
Normal 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
120
tests/dispatch_data.c
Normal 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
466
tests/dispatch_deadname.c
Normal 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
55
tests/dispatch_debug.c
Normal 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
102
tests/dispatch_drift.c
Normal 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
204
tests/dispatch_group.c
Normal 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
849
tests/dispatch_io.c
Normal 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
280
tests/dispatch_io_muxed.c
Normal 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
379
tests/dispatch_io_net.c
Normal 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
488
tests/dispatch_io_pipe.c
Normal 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();
|
||||
}
|
84
tests/dispatch_io_pipe_close.c
Normal file
84
tests/dispatch_io_pipe_close.c
Normal 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();
|
||||
}
|
72
tests/dispatch_overcommit.c
Normal file
72
tests/dispatch_overcommit.c
Normal 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
66
tests/dispatch_pingpong.c
Normal 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;
|
||||
}
|
41
tests/dispatch_plusplus.cpp
Normal file
41
tests/dispatch_plusplus.cpp
Normal 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
222
tests/dispatch_priority.c
Normal 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
131
tests/dispatch_proc.c
Normal 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;
|
||||
}
|
95
tests/dispatch_queue_finalizer.c
Normal file
95
tests/dispatch_queue_finalizer.c
Normal 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
114
tests/dispatch_read.c
Normal 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
464
tests/dispatch_read2.c
Normal 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
189
tests/dispatch_readsync.c
Normal 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
174
tests/dispatch_select.c
Normal 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
56
tests/dispatch_sema.c
Normal 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
172
tests/dispatch_starfish.c
Normal 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();
|
||||
}
|
115
tests/dispatch_suspend_timer.c
Normal file
115
tests/dispatch_suspend_timer.c
Normal 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;
|
||||
}
|
72
tests/dispatch_sync_on_main.c
Normal file
72
tests/dispatch_sync_on_main.c
Normal 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
396
tests/dispatch_test.c
Normal 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, ¤tPosition, 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
65
tests/dispatch_test.h
Normal 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
109
tests/dispatch_timer.c
Normal 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;
|
||||
}
|
74
tests/dispatch_timer_bit31.c
Normal file
74
tests/dispatch_timer_bit31.c
Normal 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;
|
||||
}
|
71
tests/dispatch_timer_bit63.c
Normal file
71
tests/dispatch_timer_bit63.c
Normal 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;
|
||||
}
|
82
tests/dispatch_timer_set_time.c
Normal file
82
tests/dispatch_timer_set_time.c
Normal 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;
|
||||
}
|
135
tests/dispatch_timer_short.c
Normal file
135
tests/dispatch_timer_short.c
Normal 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;
|
||||
}
|
77
tests/dispatch_timer_timeout.c
Normal file
77
tests/dispatch_timer_timeout.c
Normal 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
875
tests/dispatch_transform.c
Normal 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
203
tests/dispatch_vm.c
Normal 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
113
tests/dispatch_vnode.c
Normal 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
33
tests/func.c
Normal 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
54
tests/generic_unix_port.h
Normal 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
297
tests/generic_win_port.c
Normal 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
84
tests/generic_win_port.h
Normal 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
10
tests/leaks-wrapper.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user