16 #include <mach/mach.h>
17 #include <mach-o/dyld.h>
18 #include <mach/mach_vm.h>
19 #include <sys/fsgetpath.h>
20 #include <mach-o/getsect.h>
21 #include <mach/vm_region.h>
22 #include <dispatch/private.h>
23 #include <dispatch/dispatch.h>
31 #include "execserverServer.h"
33 union catch_mach_exc_request_reply
{
34 union __RequestUnion__catch_mach_exc_subsystem request
;
35 union __ReplyUnion__catch_mach_exc_subsystem reply
;
39 #include "test_support.h"
41 extern const int NXArgc
;
42 extern const char** NXArgv
;
43 extern const char** environ
;
44 extern char* __progname
;
46 static const cpu_type_t currentArch
= CPU_TYPE_X86_64
;
48 static const cpu_type_t currentArch
= CPU_TYPE_I386
;
50 static const cpu_type_t currentArch
= CPU_TYPE_ARM64
;
52 static const cpu_type_t currentArch
= CPU_TYPE_ARM
;
57 ScopedLock() : _lock(OS_UNFAIR_LOCK_INIT
) {}
60 os_unfair_lock_lock(&_lock
);
62 os_unfair_lock_unlock(&_lock
);
68 template <typename T
, int QUANT
=4, int INIT
=1>
73 T
& operator[](size_t idx
) { assert(idx
< _usedCount
); return _elements
[idx
]; }
74 const T
& operator[](size_t idx
) const { assert(idx
< _usedCount
); return _elements
[idx
]; }
75 T
& back() { assert(_usedCount
> 0); return _elements
[_usedCount
-1]; }
76 uintptr_t count() const { return _usedCount
; }
77 uintptr_t maxCount() const { return _allocCount
; }
78 bool empty() const { return (_usedCount
== 0); }
79 uintptr_t index(const T
& element
) { return &element
- _elements
; }
80 void push_back(const T
& t
) { verifySpace(1); _elements
[_usedCount
++] = t
; }
81 void pop_back() { assert(_usedCount
> 0); _usedCount
--; }
82 T
* begin() { return &_elements
[0]; }
83 T
* end() { return &_elements
[_usedCount
]; }
84 const T
* begin() const { return &_elements
[0]; }
85 const T
* end() const { return &_elements
[_usedCount
]; }
86 bool contains(const T
& targ
) const { for (const T
& a
: *this) { if ( a
== targ
) return true; } return false; }
90 void growTo(uintptr_t n
);
91 void verifySpace(uintptr_t n
) { if (this->_usedCount
+n
> this->_allocCount
) growTo(this->_usedCount
+ n
); }
94 T
* _elements
= _initialAlloc
;
95 uintptr_t _allocCount
= INIT
;
96 uintptr_t _usedCount
= 0;
97 T _initialAlloc
[INIT
] = { };
101 template <typename T
, int QUANT
, int INIT
>
102 inline void GrowableArray
<T
,QUANT
,INIT
>::growTo(uintptr_t n
)
104 uintptr_t newCount
= (n
+ QUANT
- 1) & (-QUANT
);
105 T
* newArray
= (T
*)::malloc(sizeof(T
)*newCount
);
106 T
* oldArray
= this->_elements
;
107 if ( this->_usedCount
!= 0 )
108 ::memcpy(newArray
, oldArray
, sizeof(T
)*this->_usedCount
);
109 this->_elements
= newArray
;
110 this->_allocCount
= newCount
;
111 if ( oldArray
!= this->_initialAlloc
)
115 template <typename T
, int QUANT
, int INIT
>
116 inline void GrowableArray
<T
,QUANT
,INIT
>::erase(T
& targ
)
118 intptr_t index
= &targ
- _elements
;
120 assert(index
< (intptr_t)_usedCount
);
121 intptr_t moveCount
= _usedCount
-index
-1;
123 ::memcpy(&_elements
[index
], &_elements
[index
+1], moveCount
*sizeof(T
));
129 static TestState
* getState();
130 void _PASSV(const char* file
, unsigned line
, const char* format
, va_list args
) __attribute__ ((noreturn
));
131 void _FAILV(const char* file
, unsigned line
, const char* format
, va_list args
) __attribute__ ((noreturn
));
132 void _LOGV(const char* file
, unsigned line
, const char* format
, va_list args
);
133 GrowableArray
<std::pair
<mach_port_t
, _dyld_test_crash_handler_t
>>& getCrashHandlers();
144 void getLogsString(char** buffer
);
145 static uint8_t hexCharToUInt(const char hexByte
, uint8_t* value
);
146 static uint64_t hexToUInt64(const char* startHexByte
, const char** endHexByte
);
149 GrowableArray
<const char *> logs
;
150 const char *testName
;
155 GrowableArray
<std::pair
<mach_port_t
, _dyld_test_crash_handler_t
>> crashHandlers
;
158 // Okay, this is tricky. We need something with roughly he semantics of a weak def, but without using weak defs as their presence
159 // may impact certain tests. Instead we do the following:
161 // 1. Embed a stuct containing a lock and a pointer to our global state object in each binary
162 // 2. Once per binary we walk the entire image list looking for the first entry that also has state data
163 // 3. If it has state we lock its initializaion lock, and if it is not initialized we initialize it
164 // 4. We then copy the initalized pointer into our own state, and unlock the initializer lock
166 // This should work because the image list forms a stable ordering. The one loose end is if an executable is running where logging
167 // is only used in dylibs that are all being dlopned() and dlclosed. Since many dylibs cannot be dlclosed that should be a non-issue
171 __attribute__((section("__DATA,__dyld_test")))
172 static std::atomic
<TestState
*> sState
;
175 catch_mach_exception_raise(mach_port_t exception_port
,
178 exception_type_t exception
,
179 mach_exception_data_t code
,
180 mach_msg_type_number_t codeCnt
)
182 _dyld_test_crash_handler_t crashHandler
= NULL
;
183 for (const auto& handler
: TestState::getState()->getCrashHandlers()) {
184 if (handler
.first
== exception_port
) {
185 crashHandler
= handler
.second
;
189 if (exception
== EXC_CORPSE_NOTIFY
) {
199 catch_mach_exception_raise_state(mach_port_t exception_port
,
200 exception_type_t exception
,
201 const mach_exception_data_t code
,
202 mach_msg_type_number_t codeCnt
,
204 const thread_state_t old_state
,
205 mach_msg_type_number_t old_stateCnt
,
206 thread_state_t new_state
,
207 mach_msg_type_number_t
* new_stateCnt
)
209 return KERN_NOT_SUPPORTED
;
213 catch_mach_exception_raise_state_identity(mach_port_t exception_port
,
216 exception_type_t exception
,
217 mach_exception_data_t code
,
218 mach_msg_type_number_t codeCnt
,
220 thread_state_t old_state
,
221 mach_msg_type_number_t old_stateCnt
,
222 thread_state_t new_state
,
223 mach_msg_type_number_t
* new_stateCnt
)
225 return KERN_NOT_SUPPORTED
;
228 _process::_process() : executablePath(nullptr), args(nullptr), env(nullptr), stdoutHandler(nullptr), stderrHandler(nullptr),
229 crashHandler(nullptr), exitHandler(nullptr), pid(0), arch(currentArch
), suspended(false) {}
230 _process::~_process() {
231 if (stdoutHandler
) { Block_release(stdoutHandler
);}
232 if (stderrHandler
) { Block_release(stderrHandler
);}
233 if (crashHandler
) { Block_release(crashHandler
);}
234 if (exitHandler
) { Block_release(exitHandler
);}
237 void _process::set_executable_path(const char* EP
) { executablePath
= EP
; }
238 void _process::set_args(const char** A
) { args
= A
; }
239 void _process::set_env(const char** E
) { env
= E
; }
240 void _process::set_stdout_handler(_dyld_test_reader_t SOH
) { stdoutHandler
= Block_copy(SOH
); };
241 void _process::set_stderr_handler(_dyld_test_reader_t SEH
) { stderrHandler
= Block_copy(SEH
); }
242 void _process::set_exit_handler(_dyld_test_exit_handler_t EH
) { exitHandler
= Block_copy(EH
); }
243 void _process::set_crash_handler(_dyld_test_crash_handler_t CH
) { crashHandler
= Block_copy(CH
); }
244 void _process::set_launch_suspended(bool S
) { suspended
= S
; }
245 void _process::set_launch_arch(cpu_type_t A
) { arch
= A
; }
247 pid_t
_process::launch() {
248 dispatch_queue_t queue
= dispatch_queue_create("com.apple.dyld.test.launch", NULL
);
249 posix_spawn_file_actions_t fileActions
= NULL
;
250 posix_spawnattr_t attr
= NULL
;
251 dispatch_source_t stdoutSource
= NULL
;
252 dispatch_source_t stderrSource
= NULL
;
256 if (posix_spawn_file_actions_init(&fileActions
) != 0) {
257 FAIL("Setting up spawn filea actions");
259 if (posix_spawnattr_init(&attr
) != 0) { FAIL("Setting up spawn attr"); }
260 if (posix_spawnattr_setflags(&attr
, POSIX_SPAWN_START_SUSPENDED
) != 0) {
261 FAIL("Setting up spawn attr: POSIX_SPAWN_START_SUSPENDED");
264 if (pipe(stdoutPipe
) != 0) { FAIL("Setting up pipe"); }
265 if (posix_spawn_file_actions_addclose(&fileActions
, stdoutPipe
[0]) != 0) { FAIL("Setting up pipe"); }
266 if (posix_spawn_file_actions_adddup2(&fileActions
, stdoutPipe
[1], STDOUT_FILENO
) != 0) { FAIL("Setting up pipe"); }
267 if (posix_spawn_file_actions_addclose(&fileActions
, stdoutPipe
[1]) != 0) { FAIL("Setting up pipe"); }
268 fcntl((int)stdoutPipe
[0], F_SETFL
, O_NONBLOCK
);
269 stdoutSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
, (uintptr_t)stdoutPipe
[0], 0, queue
);
270 dispatch_source_set_event_handler(stdoutSource
, ^{
271 int fd
= (int)dispatch_source_get_handle(stdoutSource
);
278 size
= read(fd
, &buffer
[0], 16384);
282 dispatch_source_set_cancel_handler(stdoutSource
, ^{
283 dispatch_release(stdoutSource
);
285 dispatch_resume(stdoutSource
);
287 if (pipe(stderrPipe
) != 0) { FAIL("Setting up pipe"); }
288 if (posix_spawn_file_actions_addclose(&fileActions
, stderrPipe
[0]) != 0) { FAIL("Setting up pipe"); }
289 if (posix_spawn_file_actions_adddup2(&fileActions
, stderrPipe
[1], STDERR_FILENO
) != 0) { FAIL("Setting up pipe"); }
290 if (posix_spawn_file_actions_addclose(&fileActions
, stderrPipe
[1]) != 0) { FAIL("Setting up pipe"); }
291 fcntl((int)stderrPipe
[0], F_SETFL
, O_NONBLOCK
);
292 stderrSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
, (uintptr_t)stderrPipe
[0], 0, queue
);
293 dispatch_source_set_event_handler(stderrSource
, ^{
294 int fd
= (int)dispatch_source_get_handle(stderrSource
);
301 size
= read(fd
, &buffer
[0], 16384);
305 dispatch_source_set_cancel_handler(stderrSource
, ^{
306 dispatch_release(stderrSource
);
308 dispatch_resume(stderrSource
);
311 auto& crashHandlers
= TestState::getState()->getCrashHandlers();
312 mach_port_t exceptionPort
= MACH_PORT_NULL
;
313 mach_port_options_t options
= { .flags
= MPO_CONTEXT_AS_GUARD
| MPO_STRICT
| MPO_INSERT_SEND_RIGHT
, .mpl
= { 1 }};
314 if ( mach_port_construct(mach_task_self(), &options
, (mach_port_context_t
)exceptionPort
, &exceptionPort
) != KERN_SUCCESS
) {
315 FAIL("Could not construct port");
317 if (posix_spawnattr_setexceptionports_np(&attr
, EXC_MASK_CRASH
| EXC_MASK_CORPSE_NOTIFY
, exceptionPort
,
318 EXCEPTION_DEFAULT
| MACH_EXCEPTION_CODES
, 0) != 0) {
319 FAIL("posix_spawnattr_setexceptionports_np failed");
321 crashHandlers
.push_back(std::make_pair(exceptionPort
, crashHandler
));
322 dispatch_source_t crashSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, exceptionPort
, 0, queue
);
323 dispatch_source_set_event_handler(crashSource
, ^{
324 dispatch_mig_server(crashSource
, sizeof(union catch_mach_exc_request_reply
), ::mach_exc_server
);
326 dispatch_source_set_cancel_handler(crashSource
, ^{
327 mach_port_destruct(mach_task_self(), exceptionPort
, 0, (mach_port_context_t
)exceptionPort
);
329 dispatch_resume(crashSource
);
335 for (argc
= 0; args
[argc
] != NULL
; ++argc
) {}
338 const char *argv
[argc
+1];
339 argv
[0] = executablePath
;
340 for (uint32_t i
= 1; i
< argc
; ++i
) {
345 int result
= posix_spawn(&pid
, executablePath
, &fileActions
, &attr
, (char **)argv
, (char **)env
);
347 FAIL("posix_spawn(%s) failed, err=%d", executablePath
, result
);
349 dispatch_source_t exitSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC
, (pid_t
)pid
,
350 DISPATCH_PROC_EXIT
, queue
);
351 dispatch_source_set_event_handler(exitSource
, ^{
353 exitHandler((pid_t
)dispatch_source_get_handle(exitSource
));
355 dispatch_source_cancel(exitSource
);
357 dispatch_source_cancel(stdoutSource
);
360 dispatch_source_cancel(stderrSource
);
362 dispatch_source_cancel(exitSource
);
364 dispatch_resume(exitSource
);
367 close(stdoutPipe
[1]);
370 close(stderrPipe
[1]);
373 posix_spawn_file_actions_destroy(&fileActions
);
375 posix_spawnattr_destroy(&attr
);
379 dispatch_release(queue
);
383 void *_process::operator new(size_t size
) {
387 void _process::operator delete(void *ptr
) {
391 // MARK: Private implementation details
395 void forEachEnvVar(const char* envp
[], F
&& f
) {
396 for (uint32_t i
= 0; envp
[i
] != nullptr; ++i
) {
397 const char* envBegin
= envp
[i
];
398 const char* envEnd
= strchr(envp
[i
], '=');
399 if (!envEnd
) { continue; }
400 size_t envSize
= (envEnd
-envBegin
)+1;
401 const char* valBegin
= envEnd
+1;
402 const char* valEnd
= strchr(envp
[i
], '\0');
403 if (!valEnd
) { continue; }
404 size_t valSize
= (valEnd
-valBegin
)+1;
407 strlcpy(&env
[0], envBegin
, envSize
);
408 strlcpy(&val
[0], valBegin
, valSize
);
413 uint8_t TestState::hexCharToUInt(const char hexByte
, uint8_t* value
) {
414 if (hexByte
>= '0' && hexByte
<= '9') {
415 *value
= hexByte
- '0';
417 } else if (hexByte
>= 'A' && hexByte
<= 'F') {
418 *value
= hexByte
- 'A' + 10;
420 } else if (hexByte
>= 'a' && hexByte
<= 'f') {
421 *value
= hexByte
- 'a' + 10;
428 uint64_t TestState::hexToUInt64(const char* startHexByte
, const char** endHexByte
) {
430 if (endHexByte
== NULL
) {
431 endHexByte
= &scratch
;
433 if (startHexByte
== NULL
)
436 if (startHexByte
[0] == '0' && startHexByte
[1] == 'x') {
439 *endHexByte
= startHexByte
+ 16;
442 for (uint32_t i
= 0; i
< 16; ++i
) {
444 if (!hexCharToUInt(startHexByte
[i
], &value
)) {
445 *endHexByte
= &startHexByte
[i
];
448 retval
= (retval
<< 4) + value
;
453 void TestState::getLogsString(char** buffer
)
456 if ( logs
.count() ) {
459 for (const auto& log
: logs
) {
460 size_t logSize
= strlen(log
);
461 bufSize
+= logSize
+ 2; // \t and \n
462 logBuf
= (char*)realloc(logBuf
, bufSize
);
463 strncpy(logBuf
+idx
, "\t", 1);
465 strncpy(logBuf
+idx
, log
, logSize
);
467 strncpy(logBuf
+idx
, "\n", 1);
470 logBuf
= (char*)realloc(logBuf
, bufSize
+ 1);
471 logBuf
[bufSize
] = '\0';
476 TestState::TestState() : testName(__progname
), logImmediate(false), logOnSuccess(false), checkForLeaks(false), output(Console
) {
477 forEachEnvVar(environ
, [this](const char* env
, const char* val
) {
478 if (strcmp(env
, "TEST_LOG_IMMEDIATE") == 0) {
481 if (strcmp(env
, "TEST_LOG_ON_SUCCESS") == 0) {
484 if (strcmp(env
, "MallocStackLogging") == 0) {
485 checkForLeaks
= true;
487 if (strcmp(env
, "TEST_OUTPUT") == 0) {
488 if (strcmp(val
, "BATS") == 0) {
490 } else if (strcmp(val
, "XCTest") == 0) {
497 void TestState::emitBegin() {
498 if (output
== BATS
) {
501 printf(" MallocStackLogging=1 MallocDebugReport=none");
503 forEachEnvVar(environ
, [this](const char* env
, const char* val
) {
504 if ((strncmp(env
, "DYLD_", 5) == 0) || (strncmp(env
, "TEST_", 5) == 0)) {
505 printf(" %s=%s", env
, val
);
508 printf(" %s", testName
);
509 for (uint32_t i
= 1; i
< NXArgc
; ++i
) {
510 printf(" %s", NXArgv
[i
]);
516 GrowableArray
<std::pair
<mach_port_t
, _dyld_test_crash_handler_t
>>& TestState::getCrashHandlers() {
517 return crashHandlers
;
520 TestState
* TestState::getState() {
522 uint32_t imageCnt
= _dyld_image_count();
523 for (uint32_t i
= 0; i
< imageCnt
; ++i
) {
525 const struct mach_header_64
* mh
= (const struct mach_header_64
*)_dyld_get_image_header(i
);
527 const struct mach_header
* mh
= _dyld_get_image_header(i
);
529 if (mh
->filetype
!= MH_EXECUTE
) {
533 auto state
= (std::atomic
<TestState
*>*)getsectiondata(mh
, "__DATA", "__dyld_test", &size
);
534 // fprintf(stderr, "__dyld_test -> 0x%llx\n", state);
536 fprintf(stderr
, "Could not find test state in main executable TestState\n");
539 if (*state
== nullptr) {
540 void *temp
= malloc(sizeof(TestState
));
541 auto newState
= new (temp
) TestState();
542 TestState
* expected
= nullptr;
543 if(!state
->compare_exchange_strong(expected
, newState
)) {
544 newState
->~TestState();
547 newState
->emitBegin();
550 sState
.store(*state
);
553 assert(sState
!= nullptr);
557 __attribute__((noreturn
))
558 void TestState::runLeaks(void) {
559 auto testState
= TestState::getState();
560 pid_t pid
= getpid();
562 sprintf(&pidString
[0], "%d", pid
);
564 printf("Insufficient priviledges, skipping Leak check: %s\n", testState
->testName
);
567 const char *args
[] = { pidString
, NULL
};
568 // We do this instead of using a dispatch_semaphore to prevent priority inversions
569 __block dispatch_data_t leaksOutput
= NULL
;
571 process
.set_executable_path("/usr/bin/leaks");
572 process
.set_args(args
);
573 process
.set_stdout_handler(^(int fd
) {
577 size
= read(fd
, &buffer
[0], 16384);
578 if (size
== -1) { break; }
579 dispatch_data_t data
= dispatch_data_create(&buffer
[0], size
, NULL
, DISPATCH_DATA_DESTRUCTOR_DEFAULT
);
583 leaksOutput
= dispatch_data_create_concat(leaksOutput
, data
);
587 process
.set_exit_handler(^(pid_t pid
) {
589 (void)waitpid(pid
, &status
, 0);
591 int exitStatus
= WEXITSTATUS(status
);
592 if (exitStatus
== 0) {
598 __unused dispatch_data_t map
= dispatch_data_create_map(leaksOutput
, &buffer
, &size
);
599 FAIL("Found Leaks:\n\n%s", buffer
);
604 testState
->checkForLeaks
= false;
605 (void)process
.launch();
609 void TestState::_PASSV(const char* file
, unsigned line
, const char* format
, va_list args
) {
610 if (output
== None
) {
616 _IOlock
.withLock([this,&format
,&args
,&file
,&line
](){
617 if (output
== Console
) {
618 printf("[\033[0;32mPASS\033[0m] %s: ", testName
);
619 vprintf(format
, args
);
621 if (logOnSuccess
&& logs
.count()) {
622 printf("[\033[0;33mLOG\033[0m]\n");
623 for (const auto& log
: logs
) {
624 printf("\t%s\n", log
);
627 } else if (output
== BATS
) {
628 printf("[PASS] %s: ", testName
);
629 vprintf(format
, args
);
631 if (logOnSuccess
&& logs
.count()) {
633 for (const auto& log
: logs
) {
634 printf("\t%s\n", log
);
637 } else if (output
== XCTest
) {
638 printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
639 printf("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
640 printf("<plist version=\"1.0\">");
642 printf("<key>PASS</key><true />");
644 char *logBuffer
= NULL
;
645 getLogsString(&logBuffer
);
646 if ( logBuffer
!= NULL
) {
647 printf("<key>LOGS</key><string>%s</string>", logBuffer
);
657 __builtin_unreachable();
660 void _PASS(const char* file
, unsigned line
, const char* format
, ...) {
662 va_start (args
, format
);
663 TestState::getState()->_PASSV(file
, line
, format
, args
);
667 void TestState::_FAILV(const char* file
, unsigned line
, const char* format
, va_list args
) {
668 if (output
== None
) {
671 _IOlock
.withLock([this,&format
,&args
,&file
,&line
](){
672 if (output
== Console
) {
673 printf("[\033[0;31mFAIL\033[0m] %s: ", testName
);
674 vprintf(format
, args
);
676 printf("[\033[0;33mLOG\033[0m]\n");
678 for (const auto& log
: logs
) {
679 printf("\t%s\n", log
);
682 } else if (output
== BATS
) {
683 printf("[FAIL] %s: ", testName
);
684 vprintf(format
, args
);
688 for (const auto& log
: logs
) {
689 printf("\t%s\n", log
);
692 } else if (output
== XCTest
) {
693 printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
694 printf("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
695 printf("<plist version=\"1.0\">");
697 printf("<key>PASS</key><false />");
698 printf("<key>FILE</key><string>%s</string>", file
);
699 printf("<key>LINE</key><integer>%u</integer>", line
);
701 vasprintf(&buffer
, format
, args
);
702 printf("<key>INFO</key><string>%s</string>", buffer
);
704 char *logBuffer
= NULL
;
705 getLogsString(&logBuffer
);
706 if ( logBuffer
!= NULL
) {
707 printf("<key>LOGS</key><string>%s</string>", logBuffer
);
715 __builtin_unreachable();
718 void _FAIL(const char* file
, unsigned line
, const char* format
, ...) {
720 va_start (args
, format
);
721 TestState::getState()->_FAILV(file
, line
, format
, args
);
725 void TestState::_LOGV(const char* file
, unsigned line
, const char* format
, va_list args
) {
726 _IOlock
.withLock([this,&format
,&args
](){
728 vprintf(format
, args
);
732 vasprintf(&str
, format
, args
);
738 void _LOG(const char* file
, unsigned line
, const char* format
, ...) {
740 va_start (args
, format
);
741 TestState::getState()->_LOGV(file
, line
, format
, args
);
745 void _TIMEOUT(const char* file
, unsigned line
, uint64_t seconds
) {
746 _LOG(file
, line
, "Registering %llu second test timeout", seconds
);
747 dispatch_source_t source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, DISPATCH_TARGET_QUEUE_DEFAULT
);
748 dispatch_time_t milestone
= dispatch_time(DISPATCH_WALLTIME_NOW
, seconds
* NSEC_PER_SEC
);
749 dispatch_source_set_timer(source
, milestone
, 0, 0);
750 dispatch_source_set_event_handler(source
, ^{
751 FAIL("Test timed out");
753 dispatch_resume(source
);