blob: 34abbf0418945e969fb85922f03a8dc2220412ae [file] [log] [blame]
/*
* 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,
};