source_internal.h   [plain text]


/*
 * Copyright (c) 2008-2013 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@
 */

/*
 * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch
 * which are subject to change in future releases of Mac OS X. Any applications
 * relying on these interfaces WILL break.
 */

#ifndef __DISPATCH_SOURCE_INTERNAL__
#define __DISPATCH_SOURCE_INTERNAL__

#ifndef __DISPATCH_INDIRECT__
#error "Please #include <dispatch/dispatch.h> instead of this file directly."
#include <dispatch/base.h> // for HeaderDoc
#endif

#define DISPATCH_EVFILT_TIMER		(-EVFILT_SYSCOUNT - 1)
#define DISPATCH_EVFILT_CUSTOM_ADD	(-EVFILT_SYSCOUNT - 2)
#define DISPATCH_EVFILT_CUSTOM_OR	(-EVFILT_SYSCOUNT - 3)
#define DISPATCH_EVFILT_MACH_NOTIFICATION	(-EVFILT_SYSCOUNT - 4)
#define DISPATCH_EVFILT_SYSCOUNT	( EVFILT_SYSCOUNT + 4)

#if HAVE_MACH
// NOTE: dispatch_source_mach_send_flags_t and dispatch_source_mach_recv_flags_t
//       bit values must not overlap as they share the same kevent fflags !

/*!
 * @enum dispatch_source_mach_send_flags_t
 *
 * @constant DISPATCH_MACH_SEND_DELETED
 * Port-deleted notification. Disabled for source registration.
 */
enum {
	DISPATCH_MACH_SEND_DELETED = 0x4,
};
/*!
 * @enum dispatch_source_mach_recv_flags_t
 *
 * @constant DISPATCH_MACH_RECV_MESSAGE
 * Receive right has pending messages
 *
 * @constant DISPATCH_MACH_RECV_MESSAGE_DIRECT
 * Receive messages from receive right directly via kevent64()
 *
 * @constant DISPATCH_MACH_RECV_NO_SENDERS
 * Receive right has no more senders. TODO <rdar://problem/8132399>
 */
enum {
	DISPATCH_MACH_RECV_MESSAGE = 0x2,
	DISPATCH_MACH_RECV_MESSAGE_DIRECT = 0x10,
	DISPATCH_MACH_RECV_MESSAGE_DIRECT_ONCE = 0x20,
	DISPATCH_MACH_RECV_NO_SENDERS = 0x40,
};
#endif // HAVE_MACH

enum {
	/* DISPATCH_TIMER_STRICT 0x1 */
	/* DISPATCH_TIMER_BACKGROUND = 0x2, */
	DISPATCH_TIMER_WALL_CLOCK = 0x4,
	DISPATCH_TIMER_INTERVAL = 0x8,
	DISPATCH_TIMER_WITH_AGGREGATE = 0x10,
	/* DISPATCH_INTERVAL_UI_ANIMATION = 0x20 */
	DISPATCH_TIMER_AFTER = 0x40,
};

#define DISPATCH_TIMER_QOS_NORMAL 0u
#define DISPATCH_TIMER_QOS_CRITICAL 1u
#define DISPATCH_TIMER_QOS_BACKGROUND 2u
#define DISPATCH_TIMER_QOS_COUNT (DISPATCH_TIMER_QOS_BACKGROUND + 1)
#define DISPATCH_TIMER_QOS(tidx) (((uintptr_t)(tidx) >> 1) & 0x3ul)

#define DISPATCH_TIMER_KIND_WALL 0u
#define DISPATCH_TIMER_KIND_MACH 1u
#define DISPATCH_TIMER_KIND_COUNT (DISPATCH_TIMER_KIND_MACH + 1)
#define DISPATCH_TIMER_KIND(tidx) ((uintptr_t)(tidx) & 0x1ul)

#define DISPATCH_TIMER_INDEX(kind, qos) ((qos) << 1 | (kind))
#define DISPATCH_TIMER_INDEX_DISARM \
		DISPATCH_TIMER_INDEX(0, DISPATCH_TIMER_QOS_COUNT)
#define DISPATCH_TIMER_INDEX_COUNT (DISPATCH_TIMER_INDEX_DISARM + 1)
#define DISPATCH_TIMER_IDENT(flags) ({ unsigned long f = (flags); \
		DISPATCH_TIMER_INDEX(f & DISPATCH_TIMER_WALL_CLOCK ? \
		DISPATCH_TIMER_KIND_WALL : DISPATCH_TIMER_KIND_MACH, \
		f & DISPATCH_TIMER_STRICT ? DISPATCH_TIMER_QOS_CRITICAL : \
		f & DISPATCH_TIMER_BACKGROUND ? DISPATCH_TIMER_QOS_BACKGROUND : \
		DISPATCH_TIMER_QOS_NORMAL); })

struct dispatch_kevent_s {
	TAILQ_ENTRY(dispatch_kevent_s) dk_list;
	TAILQ_HEAD(, dispatch_source_refs_s) dk_sources;
	_dispatch_kevent_qos_s dk_kevent;
};

typedef struct dispatch_kevent_s *dispatch_kevent_t;

typedef typeof(((dispatch_kevent_t)NULL)->dk_kevent.udata) _dispatch_kevent_qos_udata_t;

#define DISPATCH_KEV_CUSTOM_ADD ((dispatch_kevent_t)DISPATCH_EVFILT_CUSTOM_ADD)
#define DISPATCH_KEV_CUSTOM_OR  ((dispatch_kevent_t)DISPATCH_EVFILT_CUSTOM_OR)

struct dispatch_source_type_s {
	_dispatch_kevent_qos_s ke;
	uint64_t mask;
	void (*init)(dispatch_source_t ds, dispatch_source_type_t type,
			uintptr_t handle, unsigned long mask, dispatch_queue_t q);
};

struct dispatch_timer_source_s {
	uint64_t target;
	uint64_t deadline;
	uint64_t last_fire;
	uint64_t interval;
	uint64_t leeway;
	unsigned long flags; // dispatch_timer_flags_t
	unsigned long missed;
};

enum {
	DS_EVENT_HANDLER = 0,
	DS_CANCEL_HANDLER,
	DS_REGISTN_HANDLER,
};

// Source state which may contain references to the source object
// Separately allocated so that 'leaks' can see sources <rdar://problem/9050566>
typedef struct dispatch_source_refs_s {
	TAILQ_ENTRY(dispatch_source_refs_s) dr_list;
	uintptr_t dr_source_wref; // "weak" backref to dispatch_source_t
	dispatch_continuation_t volatile ds_handler[3];
} *dispatch_source_refs_t;

typedef struct dispatch_timer_source_refs_s {
	struct dispatch_source_refs_s _ds_refs;
	struct dispatch_timer_source_s _ds_timer;
	TAILQ_ENTRY(dispatch_timer_source_refs_s) dt_list;
} *dispatch_timer_source_refs_t;

typedef struct dispatch_timer_source_aggregate_refs_s {
	struct dispatch_timer_source_refs_s _dsa_refs;
	TAILQ_ENTRY(dispatch_timer_source_aggregate_refs_s) dra_list;
	TAILQ_ENTRY(dispatch_timer_source_aggregate_refs_s) dta_list;
} *dispatch_timer_source_aggregate_refs_t;

#define _dispatch_ptr2wref(ptr) (~(uintptr_t)(ptr))
#define _dispatch_wref2ptr(ref) ((void*)~(ref))
#define _dispatch_source_from_refs(dr) \
		((dispatch_source_t)_dispatch_wref2ptr((dr)->dr_source_wref))
#define ds_timer(dr) \
		(((dispatch_timer_source_refs_t)(dr))->_ds_timer)
#define ds_timer_aggregate(ds) \
		((dispatch_timer_aggregate_t)((ds)->dq_specific_q))

DISPATCH_ALWAYS_INLINE
static inline unsigned int
_dispatch_source_timer_idx(dispatch_source_refs_t dr)
{
	return DISPATCH_TIMER_IDENT(ds_timer(dr).flags);
}

#define _DISPATCH_SOURCE_HEADER(refs) \
	DISPATCH_QUEUE_HEADER(refs); \
	/* LP64: fills 32bit hole in QUEUE_HEADER */ \
	unsigned int \
		ds_is_level:1, \
		ds_is_adder:1, \
		ds_is_installed:1, \
		ds_is_direct_kevent:1, \
		ds_is_custom_source:1, \
		ds_needs_rearm:1, \
		ds_is_timer:1, \
		ds_vmpressure_override:1, \
		ds_memorypressure_override:1, \
		dm_handler_is_block:1, \
		dm_connect_handler_called:1, \
		dm_cancel_handler_called:1; \
	dispatch_kevent_t ds_dkev; \
	dispatch_##refs##_refs_t ds_refs; \
	unsigned long ds_pending_data_mask;

#define DISPATCH_SOURCE_HEADER(refs) \
	struct dispatch_source_s _as_ds[0]; \
	_DISPATCH_SOURCE_HEADER(refs)

DISPATCH_CLASS_DECL_BARE(source);
_OS_OBJECT_CLASS_IMPLEMENTS_PROTOCOL(dispatch_source, dispatch_object);

#if DISPATCH_PURE_C
struct dispatch_source_s {
	_DISPATCH_SOURCE_HEADER(source);
	unsigned long ds_ident_hack;
	unsigned long ds_data;
	unsigned long ds_pending_data;
} DISPATCH_QUEUE_ALIGN;
#endif

#if HAVE_MACH
// Mach channel state which may contain references to the channel object
// layout must match dispatch_source_refs_s
struct dispatch_mach_refs_s {
	TAILQ_ENTRY(dispatch_mach_refs_s) dr_list;
	uintptr_t dr_source_wref; // "weak" backref to dispatch_mach_t
	dispatch_mach_handler_function_t dm_handler_func;
	void *dm_handler_ctxt;
};
typedef struct dispatch_mach_refs_s *dispatch_mach_refs_t;

struct dispatch_mach_reply_refs_s {
	TAILQ_ENTRY(dispatch_mach_reply_refs_s) dr_list;
	uintptr_t dr_source_wref; // "weak" backref to dispatch_mach_t
	dispatch_kevent_t dmr_dkev;
	void *dmr_ctxt;
	mach_port_t dmr_reply;
	dispatch_priority_t dmr_priority;
	voucher_t dmr_voucher;
	TAILQ_ENTRY(dispatch_mach_reply_refs_s) dmr_list;
};
typedef struct dispatch_mach_reply_refs_s *dispatch_mach_reply_refs_t;

#define _DISPATCH_MACH_STATE_UNUSED_MASK_2       0xff00000000000000ull
#define DISPATCH_MACH_STATE_OVERRIDE_MASK        0x00ffff0000000000ull
#define _DISPATCH_MACH_STATE_UNUSED_MASK_1       0x000000f000000000ull
#define DISPATCH_MACH_STATE_DIRTY                0x0000000800000000ull
#define DISPATCH_MACH_STATE_RECEIVED_OVERRIDE    0x0000000400000000ull
#define _DISPATCH_MACH_STATE_UNUSED_MASK_0       0x0000000200000000ull
#define DISPATCH_MACH_STATE_PENDING_BARRIER      0x0000000100000000ull
#define DISPATCH_MACH_STATE_UNLOCK_MASK          0x00000000ffffffffull

struct dispatch_mach_send_refs_s {
	TAILQ_ENTRY(dispatch_mach_send_refs_s) dr_list;
	uintptr_t dr_source_wref; // "weak" backref to dispatch_mach_t
	dispatch_mach_msg_t dm_checkin;
	TAILQ_HEAD(, dispatch_mach_reply_refs_s) dm_replies;
	dispatch_unfair_lock_s dm_replies_lock;
#define DISPATCH_MACH_DISCONNECT_MAGIC_BASE (0x80000000)
#define DISPATCH_MACH_NEVER_INSTALLED (DISPATCH_MACH_DISCONNECT_MAGIC_BASE + 0)
#define DISPATCH_MACH_NEVER_CONNECTED (DISPATCH_MACH_DISCONNECT_MAGIC_BASE + 1)
	uint32_t volatile dm_disconnect_cnt;
	union {
		uint64_t volatile dm_state;
		DISPATCH_STRUCT_LITTLE_ENDIAN_2(
			dispatch_unfair_lock_s dm_state_lock,
			uint32_t dm_state_bits
		);
	};
	unsigned int dm_needs_mgr:1;
	struct dispatch_object_s *volatile dm_tail;
	struct dispatch_object_s *volatile dm_head;
	mach_port_t dm_send, dm_checkin_port;
};
typedef struct dispatch_mach_send_refs_s *dispatch_mach_send_refs_t;

DISPATCH_CLASS_DECL(mach);
#if DISPATCH_PURE_C
struct dispatch_mach_s {
	DISPATCH_SOURCE_HEADER(mach);
	dispatch_kevent_t dm_dkev;
	dispatch_mach_send_refs_t dm_refs;
} DISPATCH_QUEUE_ALIGN;
#endif

DISPATCH_CLASS_DECL(mach_msg);
struct dispatch_mach_msg_s {
	DISPATCH_OBJECT_HEADER(mach_msg);
	union {
		mach_msg_option_t dmsg_options;
		mach_error_t dmsg_error;
	};
	mach_port_t dmsg_reply;
	pthread_priority_t dmsg_priority;
	voucher_t dmsg_voucher;
	dispatch_mach_msg_destructor_t dmsg_destructor;
	size_t dmsg_size;
	union {
		mach_msg_header_t *dmsg_msg;
		char dmsg_buf[0];
	};
};
#endif // HAVE_MACH

extern const struct dispatch_source_type_s _dispatch_source_type_after;

#if TARGET_OS_EMBEDDED
#define DSL_HASH_SIZE  64u // must be a power of two
#else
#define DSL_HASH_SIZE 256u // must be a power of two
#endif

dispatch_source_t
_dispatch_source_create_mach_msg_direct_recv(mach_port_t recvp,
		const struct dispatch_continuation_s *dc);
void _dispatch_source_xref_dispose(dispatch_source_t ds);
void _dispatch_source_dispose(dispatch_source_t ds);
void _dispatch_source_finalize_activation(dispatch_source_t ds);
void _dispatch_source_invoke(dispatch_source_t ds, dispatch_invoke_flags_t flags);
void _dispatch_source_wakeup(dispatch_source_t ds, pthread_priority_t pp,
		dispatch_wakeup_flags_t flags);
size_t _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz);
void _dispatch_source_set_interval(dispatch_source_t ds, uint64_t interval);
void _dispatch_source_set_event_handler_continuation(dispatch_source_t ds,
		dispatch_continuation_t dc);
DISPATCH_EXPORT // for firehose server
void _dispatch_source_merge_data(dispatch_source_t ds, pthread_priority_t pp,
		unsigned long val);

#if HAVE_MACH
void _dispatch_mach_dispose(dispatch_mach_t dm);
void _dispatch_mach_finalize_activation(dispatch_mach_t dm);
void _dispatch_mach_invoke(dispatch_mach_t dm, dispatch_invoke_flags_t flags);
void _dispatch_mach_wakeup(dispatch_mach_t dm, pthread_priority_t pp,
		dispatch_wakeup_flags_t flags);
size_t _dispatch_mach_debug(dispatch_mach_t dm, char* buf, size_t bufsiz);

void _dispatch_mach_msg_dispose(dispatch_mach_msg_t dmsg);
void _dispatch_mach_msg_invoke(dispatch_mach_msg_t dmsg,
		dispatch_invoke_flags_t flags);
size_t _dispatch_mach_msg_debug(dispatch_mach_msg_t dmsg, char* buf,
		size_t bufsiz);

void _dispatch_mach_send_barrier_drain_invoke(dispatch_continuation_t dc,
		dispatch_invoke_flags_t flags);
void _dispatch_mach_barrier_invoke(dispatch_continuation_t dc,
		dispatch_invoke_flags_t flags);
#endif // HAVE_MACH

void _dispatch_mgr_queue_wakeup(dispatch_queue_t dq, pthread_priority_t pp,
		dispatch_wakeup_flags_t flags);
void _dispatch_mgr_thread(dispatch_queue_t dq, dispatch_invoke_flags_t flags);
#if DISPATCH_USE_KEVENT_WORKQUEUE
void _dispatch_kevent_worker_thread(_dispatch_kevent_qos_s **events,
		int *nevents);
#endif

#endif /* __DISPATCH_SOURCE_INTERNAL__ */