| /* |
| * Copyright (c) 2008-2016 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 "internal.h" |
| |
| DISPATCH_NOINLINE |
| static dispatch_unote_t |
| _dispatch_unote_create(dispatch_source_type_t dst, |
| uintptr_t handle, unsigned long mask) |
| { |
| dispatch_unote_linkage_t dul; |
| dispatch_unote_class_t du; |
| |
| if (mask & ~dst->dst_mask) { |
| return DISPATCH_UNOTE_NULL; |
| } |
| |
| if (dst->dst_filter != DISPATCH_EVFILT_TIMER) { |
| if (dst->dst_mask && !mask) { |
| return DISPATCH_UNOTE_NULL; |
| } |
| } |
| |
| if ((dst->dst_flags & EV_UDATA_SPECIFIC) || |
| (dst->dst_filter == DISPATCH_EVFILT_TIMER)) { |
| du = _dispatch_calloc(1u, dst->dst_size); |
| } else { |
| dul = _dispatch_calloc(1u, sizeof(*dul) + dst->dst_size); |
| du = _dispatch_unote_linkage_get_unote(dul)._du; |
| } |
| du->du_type = dst; |
| du->du_can_be_wlh = dst->dst_per_trigger_qos; |
| du->du_ident = (uint32_t)handle; |
| du->du_filter = dst->dst_filter; |
| du->du_fflags = (typeof(du->du_fflags))mask; |
| if (dst->dst_flags & EV_UDATA_SPECIFIC) { |
| du->du_is_direct = true; |
| } |
| du->du_data_action = DISPATCH_UNOTE_ACTION_DATA_OR; |
| return (dispatch_unote_t){ ._du = du }; |
| } |
| |
| DISPATCH_NOINLINE |
| dispatch_unote_t |
| _dispatch_unote_create_with_handle(dispatch_source_type_t dst, |
| uintptr_t handle, unsigned long mask) |
| { |
| if (!handle) { |
| return DISPATCH_UNOTE_NULL; |
| } |
| return _dispatch_unote_create(dst, handle, mask); |
| } |
| |
| DISPATCH_NOINLINE |
| dispatch_unote_t |
| _dispatch_unote_create_with_fd(dispatch_source_type_t dst, |
| uintptr_t handle, unsigned long mask) |
| { |
| #if !TARGET_OS_MAC // <rdar://problem/27756657> |
| if (handle > INT_MAX) { |
| return DISPATCH_UNOTE_NULL; |
| } |
| #endif |
| dispatch_unote_t du = _dispatch_unote_create(dst, handle, mask); |
| if (du._du) { |
| int16_t filter = dst->dst_filter; |
| du._du->du_data_action = (filter == EVFILT_READ||filter == EVFILT_WRITE) |
| ? DISPATCH_UNOTE_ACTION_DATA_SET : DISPATCH_UNOTE_ACTION_DATA_OR; |
| } |
| return du; |
| } |
| |
| DISPATCH_NOINLINE |
| dispatch_unote_t |
| _dispatch_unote_create_without_handle(dispatch_source_type_t dst, |
| uintptr_t handle, unsigned long mask) |
| { |
| if (handle) { |
| return DISPATCH_UNOTE_NULL; |
| } |
| return _dispatch_unote_create(dst, handle, mask); |
| } |
| |
| DISPATCH_NOINLINE |
| void |
| _dispatch_unote_dispose(dispatch_unote_t du) |
| { |
| void *ptr = du._du; |
| #if HAVE_MACH |
| if (du._du->dmrr_handler_is_block) { |
| Block_release(du._dmrr->dmrr_handler_ctxt); |
| } |
| #endif |
| if (du._du->du_is_timer) { |
| if (unlikely(du._dt->dt_heap_entry[DTH_TARGET_ID] != DTH_INVALID_ID || |
| du._dt->dt_heap_entry[DTH_DEADLINE_ID] != DTH_INVALID_ID)) { |
| DISPATCH_INTERNAL_CRASH(0, "Disposing of timer still in its heap"); |
| } |
| if (unlikely(du._dt->dt_pending_config)) { |
| free(du._dt->dt_pending_config); |
| du._dt->dt_pending_config = NULL; |
| } |
| } else if (!du._du->du_is_direct) { |
| ptr = _dispatch_unote_get_linkage(du); |
| } |
| free(ptr); |
| } |
| |
| #pragma mark data or / add |
| |
| static dispatch_unote_t |
| _dispatch_source_data_create(dispatch_source_type_t dst, uintptr_t handle, |
| unsigned long mask) |
| { |
| if (handle || mask) { |
| return DISPATCH_UNOTE_NULL; |
| } |
| |
| // bypass _dispatch_unote_create() because this is always "direct" |
| // even when EV_UDATA_SPECIFIC is 0 |
| dispatch_unote_class_t du = _dispatch_calloc(1u, dst->dst_size); |
| du->du_type = dst; |
| du->du_filter = dst->dst_filter; |
| du->du_is_direct = true; |
| return (dispatch_unote_t){ ._du = du }; |
| } |
| |
| const dispatch_source_type_s _dispatch_source_type_data_add = { |
| .dst_kind = "data-add", |
| .dst_filter = DISPATCH_EVFILT_CUSTOM_ADD, |
| .dst_flags = EV_UDATA_SPECIFIC|EV_CLEAR, |
| .dst_size = sizeof(struct dispatch_source_refs_s), |
| |
| .dst_create = _dispatch_source_data_create, |
| .dst_merge_evt = NULL, |
| }; |
| |
| const dispatch_source_type_s _dispatch_source_type_data_or = { |
| .dst_kind = "data-or", |
| .dst_filter = DISPATCH_EVFILT_CUSTOM_OR, |
| .dst_flags = EV_UDATA_SPECIFIC|EV_CLEAR, |
| .dst_size = sizeof(struct dispatch_source_refs_s), |
| |
| .dst_create = _dispatch_source_data_create, |
| .dst_merge_evt = NULL, |
| }; |
| |
| const dispatch_source_type_s _dispatch_source_type_data_replace = { |
| .dst_kind = "data-replace", |
| .dst_filter = DISPATCH_EVFILT_CUSTOM_REPLACE, |
| .dst_flags = EV_UDATA_SPECIFIC|EV_CLEAR, |
| .dst_size = sizeof(struct dispatch_source_refs_s), |
| |
| .dst_create = _dispatch_source_data_create, |
| .dst_merge_evt = NULL, |
| }; |
| |
| #pragma mark file descriptors |
| |
| const dispatch_source_type_s _dispatch_source_type_read = { |
| .dst_kind = "read", |
| .dst_filter = EVFILT_READ, |
| .dst_flags = EV_UDATA_SPECIFIC|EV_DISPATCH|EV_VANISHED, |
| #if DISPATCH_EVENT_BACKEND_KEVENT |
| #if HAVE_DECL_NOTE_LOWAT |
| .dst_fflags = NOTE_LOWAT, |
| #endif |
| .dst_data = 1, |
| #endif // DISPATCH_EVENT_BACKEND_KEVENT |
| .dst_size = sizeof(struct dispatch_source_refs_s), |
| |
| .dst_create = _dispatch_unote_create_with_fd, |
| .dst_merge_evt = _dispatch_source_merge_evt, |
| }; |
| |
| const dispatch_source_type_s _dispatch_source_type_write = { |
| .dst_kind = "write", |
| .dst_filter = EVFILT_WRITE, |
| .dst_flags = EV_UDATA_SPECIFIC|EV_DISPATCH|EV_VANISHED, |
| #if DISPATCH_EVENT_BACKEND_KEVENT |
| #if HAVE_DECL_NOTE_LOWAT |
| .dst_fflags = NOTE_LOWAT, |
| #endif |
| .dst_data = 1, |
| #endif // DISPATCH_EVENT_BACKEND_KEVENT |
| .dst_size = sizeof(struct dispatch_source_refs_s), |
| |
| .dst_create = _dispatch_unote_create_with_fd, |
| .dst_merge_evt = _dispatch_source_merge_evt, |
| }; |
| |
| #pragma mark signals |
| |
| static dispatch_unote_t |
| _dispatch_source_signal_create(dispatch_source_type_t dst, uintptr_t handle, |
| unsigned long mask) |
| { |
| if (handle >= NSIG) { |
| return DISPATCH_UNOTE_NULL; |
| } |
| dispatch_unote_t du = _dispatch_unote_create_with_handle(dst, handle, mask); |
| if (du._du) { |
| du._du->du_data_action = DISPATCH_UNOTE_ACTION_DATA_ADD; |
| } |
| return du; |
| } |
| |
| const dispatch_source_type_s _dispatch_source_type_signal = { |
| .dst_kind = "signal", |
| .dst_filter = EVFILT_SIGNAL, |
| .dst_flags = DISPATCH_EV_DIRECT|EV_CLEAR, |
| .dst_size = sizeof(struct dispatch_source_refs_s), |
| |
| .dst_create = _dispatch_source_signal_create, |
| .dst_merge_evt = _dispatch_source_merge_evt, |
| }; |
| |
| #pragma mark timers |
| |
| bool _dispatch_timers_reconfigure, _dispatch_timers_expired; |
| uint32_t _dispatch_timers_processing_mask; |
| #if DISPATCH_USE_DTRACE |
| uint32_t _dispatch_timers_will_wake; |
| #endif |
| #define DISPATCH_TIMER_HEAP_INITIALIZER(tidx) \ |
| [tidx] = { \ |
| .dth_target = UINT64_MAX, \ |
| .dth_deadline = UINT64_MAX, \ |
| } |
| #define DISPATCH_TIMER_HEAP_INIT(kind, qos) \ |
| DISPATCH_TIMER_HEAP_INITIALIZER(DISPATCH_TIMER_INDEX( \ |
| DISPATCH_CLOCK_##kind, DISPATCH_TIMER_QOS_##qos)) |
| |
| struct dispatch_timer_heap_s _dispatch_timers_heap[] = { |
| DISPATCH_TIMER_HEAP_INIT(WALL, NORMAL), |
| DISPATCH_TIMER_HEAP_INIT(MACH, NORMAL), |
| #if DISPATCH_HAVE_TIMER_QOS |
| DISPATCH_TIMER_HEAP_INIT(WALL, CRITICAL), |
| DISPATCH_TIMER_HEAP_INIT(MACH, CRITICAL), |
| DISPATCH_TIMER_HEAP_INIT(WALL, BACKGROUND), |
| DISPATCH_TIMER_HEAP_INIT(MACH, BACKGROUND), |
| #endif |
| }; |
| |
| static dispatch_unote_t |
| _dispatch_source_timer_create(dispatch_source_type_t dst, |
| uintptr_t handle, unsigned long mask) |
| { |
| uint32_t fflags = dst->dst_fflags; |
| dispatch_unote_t du; |
| |
| // normalize flags |
| if (mask & DISPATCH_TIMER_STRICT) { |
| mask &= ~(unsigned long)DISPATCH_TIMER_BACKGROUND; |
| } |
| |
| if (fflags & DISPATCH_TIMER_INTERVAL) { |
| if (!handle) return DISPATCH_UNOTE_NULL; |
| du = _dispatch_unote_create_without_handle(dst, 0, mask); |
| } else { |
| du = _dispatch_unote_create_without_handle(dst, handle, mask); |
| } |
| |
| if (du._dt) { |
| du._dt->du_is_timer = true; |
| du._dt->du_data_action = DISPATCH_UNOTE_ACTION_DATA_ADD; |
| du._dt->du_fflags |= fflags; |
| du._dt->du_ident = _dispatch_source_timer_idx(du); |
| du._dt->dt_timer.target = UINT64_MAX; |
| du._dt->dt_timer.deadline = UINT64_MAX; |
| du._dt->dt_timer.interval = UINT64_MAX; |
| du._dt->dt_heap_entry[DTH_TARGET_ID] = DTH_INVALID_ID; |
| du._dt->dt_heap_entry[DTH_DEADLINE_ID] = DTH_INVALID_ID; |
| } |
| return du; |
| } |
| |
| const dispatch_source_type_s _dispatch_source_type_timer = { |
| .dst_kind = "timer", |
| .dst_filter = DISPATCH_EVFILT_TIMER, |
| .dst_flags = EV_DISPATCH, |
| .dst_mask = DISPATCH_TIMER_STRICT|DISPATCH_TIMER_BACKGROUND, |
| .dst_fflags = 0, |
| .dst_size = sizeof(struct dispatch_timer_source_refs_s), |
| |
| .dst_create = _dispatch_source_timer_create, |
| }; |
| |
| const dispatch_source_type_s _dispatch_source_type_after = { |
| .dst_kind = "timer (after)", |
| .dst_filter = DISPATCH_EVFILT_TIMER, |
| .dst_flags = EV_DISPATCH, |
| .dst_mask = 0, |
| .dst_fflags = DISPATCH_TIMER_AFTER, |
| .dst_size = sizeof(struct dispatch_timer_source_refs_s), |
| |
| .dst_create = _dispatch_source_timer_create, |
| }; |
| |
| const dispatch_source_type_s _dispatch_source_type_interval = { |
| .dst_kind = "timer (interval)", |
| .dst_filter = DISPATCH_EVFILT_TIMER, |
| .dst_flags = EV_DISPATCH, |
| .dst_mask = DISPATCH_TIMER_STRICT|DISPATCH_TIMER_BACKGROUND |
| |DISPATCH_INTERVAL_UI_ANIMATION, |
| .dst_fflags = DISPATCH_TIMER_INTERVAL|DISPATCH_TIMER_CLOCK_MACH, |
| .dst_size = sizeof(struct dispatch_timer_source_refs_s), |
| |
| .dst_create = _dispatch_source_timer_create, |
| }; |