diff --git a/.gitignore b/.gitignore index 20254db67bd11..1bf54ca6957c7 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,4 @@ config.status config configure libtool - +.dirstamp diff --git a/INSTALL b/INSTALL index 5e4be9ac1f502..a02f6306a314d 100644 --- a/INSTALL +++ b/INSTALL @@ -109,7 +109,7 @@ Prepare your system 2. Install dtrace (to generate provider.h) sudo apt-get install systemtap-sdt-dev 3. Install libdispatch pre-reqs - sudo apt-get install libblocksruntime-dev libkqueue-dev libpthread-workqueue-dev + sudo apt-get install libblocksruntime-dev libkqueue-dev libpthread-workqueue-dev libbsd-dev Build: sh autogen.sh diff --git a/configure.ac b/configure.ac index 3669c1d9e621c..b5d6b2afed9d2 100644 --- a/configure.ac +++ b/configure.ac @@ -140,6 +140,13 @@ AC_CHECK_HEADER(sys/event.h, [], [PKG_CHECK_MODULES(KQUEUE, libkqueue)] ) +AC_CHECK_FUNCS([strlcpy getprogname], [], + [PKG_CHECK_MODULES(BSD_OVERLAY, libbsd-overlay,[ + AC_DEFINE(HAVE_STRLCPY, 1, []) + AC_DEFINE(HAVE_GETPROGNAME, 1, []) + ])], [#include ] +) + # # Checks for header files. # @@ -241,7 +248,7 @@ AC_CHECK_DECLS([FD_COPY], [], [], [[#include ]]) AC_CHECK_DECLS([SIGEMT], [], [], [[#include ]]) AC_CHECK_DECLS([VQ_UPDATE, VQ_VERYLOWDISK], [], [], [[#include ]]) AC_CHECK_DECLS([program_invocation_short_name], [], [], [[#include ]]) -AC_CHECK_FUNCS([pthread_key_init_np pthread_main_np mach_absolute_time malloc_create_zone sysconf getprogname]) +AC_CHECK_FUNCS([pthread_key_init_np pthread_main_np mach_absolute_time malloc_create_zone sysconf]) AC_CHECK_DECLS([POSIX_SPAWN_START_SUSPENDED], [have_posix_spawn_start_suspended=true], [have_posix_spawn_start_suspended=false], diff --git a/os/linux_base.h b/os/linux_base.h index f68ffc3ff4111..46f92791cbce5 100644 --- a/os/linux_base.h +++ b/os/linux_base.h @@ -70,8 +70,6 @@ typedef void (*dispatch_mach_handler_function_t)(void*, dispatch_mach_reason_t, typedef void (*dispatch_mach_msg_destructor_t)(void*); -typedef uint32_t voucher_activity_mode_t; - struct voucher_offsets_s { uint32_t vo_version; }; diff --git a/private/voucher_private.h b/private/voucher_private.h index 1d2ee914f97d0..058289496adcf 100644 --- a/private/voucher_private.h +++ b/private/voucher_private.h @@ -404,7 +404,6 @@ dispatch_queue_create_with_accounting_override_voucher(const char *label, #ifdef __APPLE__ #include -#endif /*! * @function voucher_create_with_mach_msg @@ -428,6 +427,7 @@ OS_VOUCHER_EXPORT OS_OBJECT_RETURNS_RETAINED OS_WARN_RESULT OS_NOTHROW voucher_t voucher_create_with_mach_msg(mach_msg_header_t *msg); +#endif __END_DECLS #endif // __OS_VOUCHER_PRIVATE__ diff --git a/src/Makefile.am b/src/Makefile.am index bf3b8418128b0..cd57e85673582 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,19 +50,19 @@ AM_CPPFLAGS=-I$(top_builddir) -I$(top_srcdir) \ -I$(top_srcdir)/private -I$(top_srcdir)/os DISPATCH_CFLAGS=-Wall $(VISIBILITY_FLAGS) $(OMIT_LEAF_FP_FLAGS) \ - $(MARCH_FLAGS) $(KQUEUE_CFLAGS) + $(MARCH_FLAGS) $(KQUEUE_CFLAGS) $(BSD_OVERLAY_CFLAGS) AM_CFLAGS=$(DISPATCH_CFLAGS) $(CBLOCKS_FLAGS) AM_OBJCFLAGS=$(DISPATCH_CFLAGS) $(CBLOCKS_FLAGS) AM_CXXFLAGS=$(DISPATCH_CFLAGS) $(CXXBLOCKS_FLAGS) AM_OBJCXXFLAGS=$(DISPATCH_CFLAGS) $(CXXBLOCKS_FLAGS) -libdispatch_la_LDFLAGS=-avoid-version -libdispatch_la_LIBADD=$(KQUEUE_LIBS) $(PTHREAD_WORKQUEUE_LIBS) - if HAVE_PTHREAD_WORKQUEUES PTHREAD_WORKQUEUE_LIBS=-lpthread_workqueue endif +libdispatch_la_LDFLAGS=-avoid-version +libdispatch_la_LIBADD=$(KQUEUE_LIBS) $(PTHREAD_WORKQUEUE_LIBS) $(BSD_OVERLAY_LIBS) + if HAVE_DARWIN_LD libdispatch_la_LDFLAGS+=-Wl,-compatibility_version,1 \ -Wl,-current_version,$(VERSION) -Wl,-dead_strip \ diff --git a/src/allocator.c b/src/allocator.c index 926f6dbbdfe5e..147c5a901d1e0 100644 --- a/src/allocator.c +++ b/src/allocator.c @@ -676,7 +676,7 @@ _dispatch_malloc_init(void) malloc_set_zone_name(_dispatch_ccache_zone, "DispatchContinuations"); } #else -static inline void _dispatch_malloc_init(void) {} +#define _dispatch_malloc_init() ((void)0) #endif // DISPATCH_USE_MALLOCZONE static dispatch_continuation_t diff --git a/src/init.c b/src/init.c index 92619e71b28f2..0c034436cdf99 100644 --- a/src/init.c +++ b/src/init.c @@ -335,6 +335,7 @@ DISPATCH_VTABLE_INSTANCE(source, .do_debug = _dispatch_source_debug, ); +#if HAVE_MACH DISPATCH_VTABLE_INSTANCE(mach, .do_type = DISPATCH_MACH_CHANNEL_TYPE, .do_kind = "mach-channel", @@ -351,6 +352,7 @@ DISPATCH_VTABLE_INSTANCE(mach_msg, .do_invoke = _dispatch_mach_msg_invoke, .do_debug = _dispatch_mach_msg_debug, ); +#endif #if !USE_OBJC DISPATCH_VTABLE_INSTANCE(data, @@ -793,6 +795,7 @@ _dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) _dispatch_set_unwind_tsd(u); } +#if HAVE_MACH #undef _dispatch_client_callout4 void _dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason, @@ -807,6 +810,7 @@ _dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason, _dispatch_free_unwind_tsd(); _dispatch_set_unwind_tsd(u); } +#endif #endif // DISPATCH_USE_CLIENT_CALLOUT diff --git a/src/inline_internal.h b/src/inline_internal.h index 0c885d6245ce8..cc29b88b20b09 100644 --- a/src/inline_internal.h +++ b/src/inline_internal.h @@ -38,10 +38,12 @@ DISPATCH_NOTHROW void _dispatch_client_callout(void *ctxt, dispatch_function_t f); DISPATCH_NOTHROW void _dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)); +#if HAVE_MACH DISPATCH_NOTHROW void _dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason, dispatch_mach_msg_t dmsg, mach_error_t error, dispatch_mach_handler_function_t f); +#endif #else // !DISPATCH_USE_CLIENT_CALLOUT @@ -59,6 +61,7 @@ _dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) return f(ctxt, i); } +#if HAVE_MACH DISPATCH_ALWAYS_INLINE static inline void _dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason, @@ -67,6 +70,7 @@ _dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason, { return f(ctxt, reason, dmsg, error); } +#endif #endif // !DISPATCH_USE_CLIENT_CALLOUT diff --git a/src/internal.h b/src/internal.h index 0106b8263a3ec..3ffc2ab9dc355 100644 --- a/src/internal.h +++ b/src/internal.h @@ -208,11 +208,7 @@ DISPATCH_EXPORT DISPATCH_NOTHROW void dispatch_atfork_child(void); #if !TARGET_OS_WIN32 #include #include -#ifdef __linux__ -#include -#else #include -#endif #include #include #include @@ -328,10 +324,10 @@ DISPATCH_NOINLINE void _dispatch_bug_client(const char* msg); DISPATCH_NOINLINE void _dispatch_bug_mach_client(const char *msg, mach_msg_return_t kr); +#endif DISPATCH_NOINLINE void _dispatch_bug_kevent_client(const char* msg, const char* filter, const char *operation, int err); -#endif DISPATCH_NOINLINE DISPATCH_NORETURN void _dispatch_abort(size_t line, long val); diff --git a/src/object.m b/src/object.m index 1a98d7e0e6232..a8bcc4fcc29fd 100644 --- a/src/object.m +++ b/src/object.m @@ -524,6 +524,7 @@ - (NSString *)debugDescription { } } +#if HAVE_MACH #undef _dispatch_client_callout4 void _dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason, @@ -537,6 +538,7 @@ - (NSString *)debugDescription { objc_terminate(); } } +#endif #endif // DISPATCH_USE_CLIENT_CALLOUT diff --git a/src/queue.c b/src/queue.c index 182b6c191e7c1..ac33c8d7ba40d 100644 --- a/src/queue.c +++ b/src/queue.c @@ -858,6 +858,7 @@ _dispatch_root_queue_init_pthread_pool(dispatch_root_queue_context_t qc, thread_pool_size = pool_size; } qc->dgq_thread_pool_size = thread_pool_size; +#if HAVE_PTHREAD_WORKQUEUES if (qc->dgq_qos) { (void)dispatch_assume_zero(pthread_attr_init(&pqc->dpq_thread_attr)); (void)dispatch_assume_zero(pthread_attr_setdetachstate( @@ -867,6 +868,7 @@ _dispatch_root_queue_init_pthread_pool(dispatch_root_queue_context_t qc, &pqc->dpq_thread_attr, qc->dgq_qos, 0)); #endif } +#endif #if USE_MACH_SEM // override the default FIFO behavior for the pool semaphores kern_return_t kr = semaphore_create(mach_task_self(), @@ -1464,6 +1466,7 @@ static dispatch_once_t _dispatch_mgr_sched_pred; // TODO: switch to "event-reflector thread" property +#if HAVE_PTHREAD_WORKQUEUE_QOS // Must be kept in sync with list of qos classes in sys/qos.h static const int _dispatch_mgr_sched_qos2prio[] = { [_DISPATCH_QOS_CLASS_MAINTENANCE] = 4, @@ -1473,6 +1476,7 @@ static const int _dispatch_mgr_sched_qos2prio[] = { [_DISPATCH_QOS_CLASS_USER_INITIATED] = 37, [_DISPATCH_QOS_CLASS_USER_INTERACTIVE] = 47, }; +#endif static void _dispatch_mgr_sched_init(void *ctxt DISPATCH_UNUSED) @@ -4056,7 +4060,7 @@ _dispatch_queue_push_override(dispatch_queue_t dq, dispatch_queue_t tq, _dispatch_queue_push(rq, dc, 0); #else - (void)dq; (void)tq; (void)p; + (void)dq; (void)tq; (void)p; (void)owning; #endif } diff --git a/src/shims/.dirstamp b/src/shims/.dirstamp deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/src/shims/hw_config.h b/src/shims/hw_config.h index 2b85d4a60af18..be559b47f432e 100644 --- a/src/shims/hw_config.h +++ b/src/shims/hw_config.h @@ -81,6 +81,15 @@ static inline uint32_t _dispatch_hw_get_config(_dispatch_hw_config_t c) { uint32_t val = 1; +#if defined(__linux__) + switch (c) { + case _dispatch_hw_config_logical_cpus: + case _dispatch_hw_config_physical_cpus: + return sysconf(_SC_NPROCESSORS_CONF); + case _dispatch_hw_config_active_cpus: + return sysconf(_SC_NPROCESSORS_ONLN); + } +#else const char *name = NULL; int r; #if defined(__APPLE__) @@ -106,6 +115,7 @@ _dispatch_hw_get_config(_dispatch_hw_config_t c) if (r > 0) val = (uint32_t)r; #endif } +#endif return val; } diff --git a/src/shims/linux_stubs.c b/src/shims/linux_stubs.c index 02b87e7499523..e62ee8fbde7ab 100644 --- a/src/shims/linux_stubs.c +++ b/src/shims/linux_stubs.c @@ -24,13 +24,10 @@ */ #include - +#include #include #include "pthread.h" - -#define program_invocation_short_name "hi" - #include "os/linux_base.h" #include "internal.h" @@ -38,52 +35,30 @@ #undef LINUX_PORT_ERROR #define LINUX_PORT_ERROR() do { printf("LINUX_PORT_ERROR_CALLED %s:%d: %s\n",__FILE__,__LINE__,__FUNCTION__); abort(); } while (0) -void _dispatch_mach_msg_dispose() { LINUX_PORT_ERROR(); } - -unsigned long _dispatch_mach_probe(dispatch_mach_t dm) { - LINUX_PORT_ERROR(); -} - dispatch_block_t _dispatch_block_create(dispatch_block_flags_t flags, voucher_t voucher, pthread_priority_t priority, dispatch_block_t block) { LINUX_PORT_ERROR(); } -void _dispatch_mach_invoke() { LINUX_PORT_ERROR(); } - -size_t _dispatch_mach_msg_debug(dispatch_mach_msg_t dmsg, char* buf, size_t bufsiz) { - LINUX_PORT_ERROR(); -} -void _dispatch_mach_dispose() { LINUX_PORT_ERROR(); } -void _dispatch_mach_msg_invoke() { LINUX_PORT_ERROR(); } - unsigned long _dispatch_runloop_queue_probe(dispatch_queue_t dq) { LINUX_PORT_ERROR(); } void _dispatch_runloop_queue_xref_dispose() { LINUX_PORT_ERROR(); } -void strlcpy() { LINUX_PORT_ERROR(); } void _dispatch_runloop_queue_dispose() { LINUX_PORT_ERROR(); } char* mach_error_string(mach_msg_return_t x) { LINUX_PORT_ERROR(); } - void mach_vm_deallocate() { LINUX_PORT_ERROR(); } -mach_port_t pthread_mach_thread_np() { - return (mach_port_t)pthread_self(); +mach_port_t pthread_mach_thread_np(void) { + return (pid_t)syscall(SYS_gettid); } - -mach_port_t mach_task_self() { +mach_port_t mach_task_self(void) { return (mach_port_t)pthread_self(); } -int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, - void *newp, size_t newlen) { - LINUX_PORT_ERROR(); -} - /* * Stubbed out static data */ diff --git a/src/shims/linux_stubs.h b/src/shims/linux_stubs.h index 2f34339491d28..4b2f53204bab9 100644 --- a/src/shims/linux_stubs.h +++ b/src/shims/linux_stubs.h @@ -22,9 +22,6 @@ #ifndef __DISPATCH__STUBS__INTERNAL #define __DISPATCH__STUBS__INTERNAL -int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, - void *newp, size_t newlen); - mach_port_t pthread_mach_thread_np(); mach_port_t mach_task_self(); diff --git a/src/source.c b/src/source.c index f5685c4233f36..f149254e241f2 100644 --- a/src/source.c +++ b/src/source.c @@ -604,11 +604,13 @@ _dispatch_source_kevent_resume(dispatch_source_t ds, uint32_t new_flags) _dispatch_debug("kevent-source[%p]: rearmed kevent[%p]", ds, ds->ds_dkev); return; +#if HAVE_MACH case EVFILT_MACHPORT: if (ds->ds_pending_data_mask & DISPATCH_MACH_RECV_MESSAGE) { new_flags |= DISPATCH_MACH_RECV_MESSAGE; // emulate EV_DISPATCH } break; +#endif } if ((ds->ds_atomic_flags & DSF_DELETED) || _dispatch_kevent_resume(ds->ds_dkev, new_flags, 0)) { @@ -931,6 +933,7 @@ _dispatch_kevent_hash(uint64_t ident, short filter) MACH_PORT_INDEX(ident) : ident); #else value = ident; + (void)filter; #endif return DSL_HASH((uintptr_t)value); } @@ -1025,6 +1028,10 @@ _dispatch_kevent_resume(dispatch_kevent_t dk, uint32_t new_flags, } return r; } +#if !HAVE_MACH + (void)new_flags; + (void)del_flags; +#endif } static long @@ -4766,13 +4773,10 @@ _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz) return offset; } +#if HAVE_MACH static size_t _dispatch_mach_debug_attr(dispatch_mach_t dm, char* buf, size_t bufsiz) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return (size_t)0; -#else dispatch_queue_t target = dm->do_targetq; return dsnprintf(buf, bufsiz, "target = %s[%p], receive = 0x%x, " "send = 0x%x, send-possible = 0x%x%s, checkin = 0x%x%s, " @@ -4786,15 +4790,10 @@ _dispatch_mach_debug_attr(dispatch_mach_t dm, char* buf, size_t bufsiz) dm->dm_refs->dm_checkin ? " (pending)" : "", dm->dm_refs->dm_sending, dm->dm_refs->dm_disconnect_cnt, (bool)(dm->ds_atomic_flags & DSF_CANCELED)); -#endif } size_t _dispatch_mach_debug(dispatch_mach_t dm, char* buf, size_t bufsiz) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return (size_t)0; -#else size_t offset = 0; offset += dsnprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dm->dq_label && !dm->dm_cancel_handler_called ? dm->dq_label : @@ -4803,8 +4802,8 @@ _dispatch_mach_debug(dispatch_mach_t dm, char* buf, size_t bufsiz) offset += _dispatch_mach_debug_attr(dm, &buf[offset], bufsiz - offset); offset += dsnprintf(&buf[offset], bufsiz - offset, "}"); return offset; -#endif } +#endif #if DISPATCH_DEBUG DISPATCH_NOINLINE diff --git a/src/source_internal.h b/src/source_internal.h index 6e8f40f5a2f9e..5ac4e24ae49d4 100644 --- a/src/source_internal.h +++ b/src/source_internal.h @@ -38,6 +38,7 @@ #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 ! @@ -68,6 +69,7 @@ enum { DISPATCH_MACH_RECV_MESSAGE_DIRECT_ONCE = 0x20, DISPATCH_MACH_RECV_NO_SENDERS = 0x40, }; +#endif enum { DISPATCH_TIMER_WALL_CLOCK = 0x4, @@ -201,6 +203,7 @@ struct dispatch_source_s { unsigned long ds_pending_data; }; +#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 { @@ -258,6 +261,7 @@ struct dispatch_mach_msg_s { char dmsg_buf[0]; }; }; +#endif #if TARGET_OS_EMBEDDED #define DSL_HASH_SIZE 64u // must be a power of two @@ -275,6 +279,7 @@ void _dispatch_source_set_interval(dispatch_source_t ds, uint64_t interval); void _dispatch_source_set_event_handler_with_context_f(dispatch_source_t ds, void *ctxt, dispatch_function_t handler); +#if HAVE_MACH void _dispatch_mach_dispose(dispatch_mach_t dm); void _dispatch_mach_invoke(dispatch_mach_t dm, dispatch_object_t dou, dispatch_invoke_flags_t flags); @@ -287,6 +292,7 @@ void _dispatch_mach_msg_invoke(dispatch_mach_msg_t dmsg, dispatch_object_t dou, size_t _dispatch_mach_msg_debug(dispatch_mach_msg_t dmsg, char* buf, size_t bufsiz); void _dispatch_mach_barrier_invoke(void *ctxt); +#endif unsigned long _dispatch_mgr_wakeup(dispatch_queue_t dq); void _dispatch_mgr_thread(dispatch_queue_t dq, dispatch_object_t dou, diff --git a/src/transform.c b/src/transform.c index 98da41fef9250..d82134bc201d8 100644 --- a/src/transform.c +++ b/src/transform.c @@ -22,6 +22,14 @@ #ifdef __APPLE__ #include +#elif __linux__ +#include +#define OSLittleEndian __LITTLE_ENDIAN +#define OSBigEndian __BIG_ENDIAN +#define OSSwapLittleToHostInt16 le16toh +#define OSSwapBigToHostInt16 be16toh +#define OSSwapHostToLittleInt16 htole16 +#define OSSwapHostToBigInt16 htobe16 #endif #if defined(__LITTLE_ENDIAN__) @@ -30,6 +38,8 @@ #elif defined(__BIG_ENDIAN__) #define DISPATCH_DATA_FORMAT_TYPE_UTF16_HOST DISPATCH_DATA_FORMAT_TYPE_UTF16BE #define DISPATCH_DATA_FORMAT_TYPE_UTF16_REV DISPATCH_DATA_FORMAT_TYPE_UTF16LE +#else +#error Unsupported Endianness #endif enum { @@ -197,29 +207,19 @@ _dispatch_transform_detect_utf(dispatch_data_t data) static uint16_t _dispatch_transform_swap_to_host(uint16_t x, int32_t byteOrder) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return x; -#else if (byteOrder == OSLittleEndian) { - return OSSwapLittleToHostInt16(x); + return OSSwapLittleToHostInt16(x); } return OSSwapBigToHostInt16(x); -#endif } static uint16_t _dispatch_transform_swap_from_host(uint16_t x, int32_t byteOrder) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return x; -#else if (byteOrder == OSLittleEndian) { return OSSwapHostToLittleInt16(x); } return OSSwapHostToBigInt16(x); -#endif } #pragma mark - @@ -531,45 +531,25 @@ _dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder) static dispatch_data_t _dispatch_transform_from_utf16le(dispatch_data_t data) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return (dispatch_data_t)0; -#else return _dispatch_transform_from_utf16(data, OSLittleEndian); -#endif } static dispatch_data_t _dispatch_transform_from_utf16be(dispatch_data_t data) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return (dispatch_data_t)0; -#else return _dispatch_transform_from_utf16(data, OSBigEndian); -#endif } static dispatch_data_t _dispatch_transform_to_utf16le(dispatch_data_t data) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return (dispatch_data_t)0; -#else return _dispatch_transform_to_utf16(data, OSLittleEndian); -#endif } static dispatch_data_t _dispatch_transform_to_utf16be(dispatch_data_t data) { -#ifdef __LINUX_PORT_HDD__ - LINUX_PORT_ERROR(); - return (dispatch_data_t)0; -#else return _dispatch_transform_to_utf16(data, OSBigEndian); -#endif } #pragma mark -