X-Git-Url: https://git.saurik.com/apple/libdispatch.git/blobdiff_plain/00abc8588ccfc6d910a4ae103bbb45112e917d4c..e85f44377864e428703fb21503e29f422c11288f:/src/init.c?ds=sidebyside diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..d72219c --- /dev/null +++ b/src/init.c @@ -0,0 +1,622 @@ +/* + * 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@ + */ + +// Contains exported global data and initialization & other routines that must +// only exist once in the shared library even when resolvers are used. + +#include "internal.h" + +#if HAVE_MACH +#include "protocolServer.h" +#endif + +#pragma mark - +#pragma mark dispatch_init + +#if USE_LIBDISPATCH_INIT_CONSTRUCTOR +DISPATCH_NOTHROW __attribute__((constructor)) +void +_libdispatch_init(void); + +DISPATCH_EXPORT DISPATCH_NOTHROW +void +_libdispatch_init(void) +{ + libdispatch_init(); +} +#endif + +DISPATCH_EXPORT DISPATCH_NOTHROW +void +dispatch_atfork_prepare(void) +{ +} + +DISPATCH_EXPORT DISPATCH_NOTHROW +void +dispatch_atfork_parent(void) +{ +} + +void +dummy_function(void) +{ +} + +long +dummy_function_r0(void) +{ + return 0; +} + +#pragma mark - +#pragma mark dispatch_globals + +#if DISPATCH_COCOA_COMPAT +// dispatch_begin_thread_4GC having non-default value triggers GC-only slow +// paths and is checked frequently, testing against NULL is faster than +// comparing for equality with "dummy_function" +void (*dispatch_begin_thread_4GC)(void) = NULL; +void (*dispatch_end_thread_4GC)(void) = dummy_function; +void (*dispatch_no_worker_threads_4GC)(void) = NULL; +void *(*_dispatch_begin_NSAutoReleasePool)(void) = (void *)dummy_function; +void (*_dispatch_end_NSAutoReleasePool)(void *) = (void *)dummy_function; +#endif + +struct _dispatch_hw_config_s _dispatch_hw_config; +bool _dispatch_safe_fork = true; + +const struct dispatch_queue_offsets_s dispatch_queue_offsets = { + .dqo_version = 3, + .dqo_label = offsetof(struct dispatch_queue_s, dq_label), + .dqo_label_size = sizeof(((dispatch_queue_t)NULL)->dq_label), + .dqo_flags = 0, + .dqo_flags_size = 0, + .dqo_width = offsetof(struct dispatch_queue_s, dq_width), + .dqo_width_size = sizeof(((dispatch_queue_t)NULL)->dq_width), + .dqo_serialnum = offsetof(struct dispatch_queue_s, dq_serialnum), + .dqo_serialnum_size = sizeof(((dispatch_queue_t)NULL)->dq_serialnum), + .dqo_running = offsetof(struct dispatch_queue_s, dq_running), + .dqo_running_size = sizeof(((dispatch_queue_t)NULL)->dq_running), +}; + +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +DISPATCH_CACHELINE_ALIGN +struct dispatch_queue_s _dispatch_main_q = { +#if !DISPATCH_USE_RESOLVERS + .do_vtable = &_dispatch_queue_vtable, + .do_targetq = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], +#endif + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .dq_label = "com.apple.main-thread", + .dq_running = 1, + .dq_width = 1, + .dq_serialnum = 1, +}; + +const struct dispatch_queue_attr_vtable_s dispatch_queue_attr_vtable = { + .do_type = DISPATCH_QUEUE_ATTR_TYPE, + .do_kind = "queue-attr", +}; + +struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent = { + .do_vtable = &dispatch_queue_attr_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_next = DISPATCH_OBJECT_LISTLESS, +}; + +struct dispatch_data_s _dispatch_data_empty = { +#if !DISPATCH_USE_RESOLVERS + .do_vtable = &_dispatch_data_vtable, +#endif + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_next = DISPATCH_OBJECT_LISTLESS, +}; + +const dispatch_block_t _dispatch_data_destructor_free = ^{ + DISPATCH_CRASH("free destructor called"); +}; + +#pragma mark - +#pragma mark dispatch_log + +static char _dispatch_build[16]; + +static void +_dispatch_bug_init(void *context DISPATCH_UNUSED) +{ +#ifdef __APPLE__ + int mib[] = { CTL_KERN, KERN_OSVERSION }; + size_t bufsz = sizeof(_dispatch_build); + + sysctl(mib, 2, _dispatch_build, &bufsz, NULL, 0); +#else + /* + * XXXRW: What to do here for !Mac OS X? + */ + memset(_dispatch_build, 0, sizeof(_dispatch_build)); +#endif +} + +void +_dispatch_bug(size_t line, long val) +{ + static dispatch_once_t pred; + static void *last_seen; + void *ra = __builtin_return_address(0); + + dispatch_once_f(&pred, NULL, _dispatch_bug_init); + if (last_seen != ra) { + last_seen = ra; + _dispatch_log("BUG in libdispatch: %s - %lu - 0x%lx", + _dispatch_build, (unsigned long)line, val); + } +} + +void +_dispatch_bug_mach_client(const char* msg, mach_msg_return_t kr) +{ + static void *last_seen; + void *ra = __builtin_return_address(0); + if (last_seen != ra) { + last_seen = ra; + _dispatch_log("BUG in libdispatch client: %s %s - 0x%x", msg, + mach_error_string(kr), kr); + } +} + +void +_dispatch_abort(size_t line, long val) +{ + _dispatch_bug(line, val); + abort(); +} + +void +_dispatch_log(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + _dispatch_logv(msg, ap); + va_end(ap); +} + +static FILE *dispatch_logfile; +static bool dispatch_log_disabled; + +static void +_dispatch_logv_init(void *context DISPATCH_UNUSED) +{ +#if DISPATCH_DEBUG + bool log_to_file = true; +#else + bool log_to_file = false; +#endif + char *e = getenv("LIBDISPATCH_LOG"); + if (e) { + if (strcmp(e, "YES") == 0) { + // default + } else if (strcmp(e, "NO") == 0) { + dispatch_log_disabled = true; + } else if (strcmp(e, "syslog") == 0) { + log_to_file = false; + } else if (strcmp(e, "file") == 0) { + log_to_file = true; + } else if (strcmp(e, "stderr") == 0) { + log_to_file = true; + dispatch_logfile = stderr; + } + } + if (!dispatch_log_disabled) { + if (log_to_file && !dispatch_logfile) { + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/var/tmp/libdispatch.%d.log", + getpid()); + dispatch_logfile = fopen(path, "a"); + } + if (dispatch_logfile) { + struct timeval tv; + gettimeofday(&tv, NULL); + fprintf(dispatch_logfile, "=== log file opened for %s[%u] at " + "%ld.%06u ===\n", getprogname() ?: "", getpid(), + tv.tv_sec, tv.tv_usec); + fflush(dispatch_logfile); + } + } +} + +void +_dispatch_logv(const char *msg, va_list ap) +{ + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_logv_init); + + if (slowpath(dispatch_log_disabled)) { + return; + } + if (slowpath(dispatch_logfile)) { + vfprintf(dispatch_logfile, msg, ap); + // TODO: May cause interleaving with another thread's log + fputc('\n', dispatch_logfile); + fflush(dispatch_logfile); + return; + } + vsyslog(LOG_NOTICE, msg, ap); +} + +#pragma mark - +#pragma mark dispatch_debug + +void +dispatch_debug(dispatch_object_t dou, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + dispatch_debugv(dou._do, msg, ap); + va_end(ap); +} + +void +dispatch_debugv(dispatch_object_t dou, const char *msg, va_list ap) +{ + char buf[4096]; + size_t offs; + + if (dou._do && dou._do->do_vtable->do_debug) { + offs = dx_debug(dou._do, buf, sizeof(buf)); + } else { + offs = snprintf(buf, sizeof(buf), "NULL vtable slot"); + } + + snprintf(buf + offs, sizeof(buf) - offs, ": %s", msg); + _dispatch_logv(buf, ap); +} + +#pragma mark - +#pragma mark dispatch_block_t + +#ifdef __BLOCKS__ + +#undef _dispatch_Block_copy +dispatch_block_t +_dispatch_Block_copy(dispatch_block_t db) +{ + dispatch_block_t rval; + + while (!(rval = Block_copy(db))) { + sleep(1); + } + return rval; +} + +void +_dispatch_call_block_and_release(void *block) +{ + void (^b)(void) = block; + b(); + Block_release(b); +} + +#endif // __BLOCKS__ + +#pragma mark - +#pragma mark dispatch_client_callout + +#if DISPATCH_USE_CLIENT_CALLOUT + +#undef _dispatch_client_callout +#undef _dispatch_client_callout2 + +DISPATCH_NOINLINE +void +_dispatch_client_callout(void *ctxt, dispatch_function_t f) +{ + return f(ctxt); +} + +DISPATCH_NOINLINE +void +_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) +{ + return f(ctxt, i); +} + +#endif + +#pragma mark - +#pragma mark dispatch_source_types + +static void +dispatch_source_type_timer_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask, + dispatch_queue_t q DISPATCH_UNUSED) +{ + ds->ds_refs = calloc(1ul, sizeof(struct dispatch_timer_source_refs_s)); + if (slowpath(!ds->ds_refs)) return; + ds->ds_needs_rearm = true; + ds->ds_is_timer = true; + ds_timer(ds->ds_refs).flags = mask; +} + +const struct dispatch_source_type_s _dispatch_source_type_timer = { + .ke = { + .filter = DISPATCH_EVFILT_TIMER, + }, + .mask = DISPATCH_TIMER_WALL_CLOCK, + .init = dispatch_source_type_timer_init, +}; + +const struct dispatch_source_type_s _dispatch_source_type_read = { + .ke = { + .filter = EVFILT_READ, + .flags = EV_DISPATCH, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_write = { + .ke = { + .filter = EVFILT_WRITE, + .flags = EV_DISPATCH, + }, +}; + +#if DISPATCH_USE_VM_PRESSURE +#if TARGET_IPHONE_SIMULATOR // rdar://problem/9219483 +static int _dispatch_ios_simulator_memory_warnings_fd = -1; +static void +_dispatch_ios_simulator_vm_source_init(void *context DISPATCH_UNUSED) +{ + char *e = getenv("IPHONE_SIMULATOR_MEMORY_WARNINGS"); + if (!e) return; + _dispatch_ios_simulator_memory_warnings_fd = open(e, O_EVTONLY); + if (_dispatch_ios_simulator_memory_warnings_fd == -1) { + (void)dispatch_assume_zero(errno); + } +} +static void +dispatch_source_type_vm_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask, + dispatch_queue_t q DISPATCH_UNUSED) +{ + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_ios_simulator_vm_source_init); + ds->ds_dkev->dk_kevent.ident = (mask & DISPATCH_VM_PRESSURE ? + _dispatch_ios_simulator_memory_warnings_fd : -1); +} + +const struct dispatch_source_type_s _dispatch_source_type_vm = { + .ke = { + .filter = EVFILT_VNODE, + .flags = EV_CLEAR, + }, + .mask = NOTE_ATTRIB, + .init = dispatch_source_type_vm_init, +}; +#else +static void +dispatch_source_type_vm_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask DISPATCH_UNUSED, + dispatch_queue_t q DISPATCH_UNUSED) +{ + ds->ds_is_level = false; +} + +const struct dispatch_source_type_s _dispatch_source_type_vm = { + .ke = { + .filter = EVFILT_VM, + .flags = EV_DISPATCH, + }, + .mask = NOTE_VM_PRESSURE, + .init = dispatch_source_type_vm_init, +}; +#endif +#endif + +const struct dispatch_source_type_s _dispatch_source_type_proc = { + .ke = { + .filter = EVFILT_PROC, + .flags = EV_CLEAR, + }, + .mask = NOTE_EXIT|NOTE_FORK|NOTE_EXEC +#if HAVE_DECL_NOTE_SIGNAL + |NOTE_SIGNAL +#endif +#if HAVE_DECL_NOTE_REAP + |NOTE_REAP +#endif + , +}; + +const struct dispatch_source_type_s _dispatch_source_type_signal = { + .ke = { + .filter = EVFILT_SIGNAL, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_vnode = { + .ke = { + .filter = EVFILT_VNODE, + .flags = EV_CLEAR, + }, + .mask = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK| + NOTE_RENAME|NOTE_REVOKE +#if HAVE_DECL_NOTE_NONE + |NOTE_NONE +#endif + , +}; + +const struct dispatch_source_type_s _dispatch_source_type_vfs = { + .ke = { + .filter = EVFILT_FS, + .flags = EV_CLEAR, + }, + .mask = VQ_NOTRESP|VQ_NEEDAUTH|VQ_LOWDISK|VQ_MOUNT|VQ_UNMOUNT|VQ_DEAD| + VQ_ASSIST|VQ_NOTRESPLOCK +#if HAVE_DECL_VQ_UPDATE + |VQ_UPDATE +#endif +#if HAVE_DECL_VQ_VERYLOWDISK + |VQ_VERYLOWDISK +#endif + , +}; + +const struct dispatch_source_type_s _dispatch_source_type_data_add = { + .ke = { + .filter = DISPATCH_EVFILT_CUSTOM_ADD, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_data_or = { + .ke = { + .filter = DISPATCH_EVFILT_CUSTOM_OR, + .flags = EV_CLEAR, + .fflags = ~0, + }, +}; + +#if HAVE_MACH + +static void +dispatch_source_type_mach_send_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, unsigned long mask, + dispatch_queue_t q DISPATCH_UNUSED) +{ + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_mach_notify_source_init); + if (!mask) { + // Preserve legacy behavior that (mask == 0) => DISPATCH_MACH_SEND_DEAD + ds->ds_dkev->dk_kevent.fflags = DISPATCH_MACH_SEND_DEAD; + ds->ds_pending_data_mask = DISPATCH_MACH_SEND_DEAD; + } +} + +const struct dispatch_source_type_s _dispatch_source_type_mach_send = { + .ke = { + .filter = EVFILT_MACHPORT, + .flags = EV_CLEAR, + }, + .mask = DISPATCH_MACH_SEND_DEAD|DISPATCH_MACH_SEND_POSSIBLE, + .init = dispatch_source_type_mach_send_init, +}; + +static void +dispatch_source_type_mach_recv_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask DISPATCH_UNUSED, + dispatch_queue_t q DISPATCH_UNUSED) +{ + ds->ds_is_level = false; +} + +const struct dispatch_source_type_s _dispatch_source_type_mach_recv = { + .ke = { + .filter = EVFILT_MACHPORT, + .flags = EV_DISPATCH, + .fflags = DISPATCH_MACH_RECV_MESSAGE, + }, + .init = dispatch_source_type_mach_recv_init, +}; + +#pragma mark - +#pragma mark dispatch_mig + +void * +dispatch_mach_msg_get_context(mach_msg_header_t *msg) +{ + mach_msg_context_trailer_t *tp; + void *context = NULL; + + tp = (mach_msg_context_trailer_t *)((uint8_t *)msg + + round_msg(msg->msgh_size)); + if (tp->msgh_trailer_size >= + (mach_msg_size_t)sizeof(mach_msg_context_trailer_t)) { + context = (void *)(uintptr_t)tp->msgh_context; + } + return context; +} + +kern_return_t +_dispatch_wakeup_main_thread(mach_port_t mp DISPATCH_UNUSED) +{ + // dummy function just to pop out the main thread out of mach_msg() + return 0; +} + +kern_return_t +_dispatch_consume_send_once_right(mach_port_t mp DISPATCH_UNUSED) +{ + // dummy function to consume a send-once right + return 0; +} + +kern_return_t +_dispatch_mach_notify_port_destroyed(mach_port_t notify DISPATCH_UNUSED, + mach_port_t name) +{ + kern_return_t kr; + // this function should never be called + (void)dispatch_assume_zero(name); + kr = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE,-1); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_no_senders(mach_port_t notify, + mach_port_mscount_t mscnt DISPATCH_UNUSED) +{ + // this function should never be called + (void)dispatch_assume_zero(notify); + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_send_once(mach_port_t notify DISPATCH_UNUSED) +{ + // we only register for dead-name notifications + // some code deallocated our send-once right without consuming it +#if DISPATCH_DEBUG + _dispatch_log("Corruption: An app/library deleted a libdispatch " + "dead-name notification"); +#endif + return KERN_SUCCESS; +} + + +#endif // HAVE_MACH