]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOTimerEventSource.cpp
xnu-6153.141.1.tar.gz
[apple/xnu.git] / iokit / Kernel / IOTimerEventSource.cpp
index e42fa436c09a9124f0a267ef168de7b776359c08..59eb6a0a26af9b6b58376389d532573e8a431a4e 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
@@ -41,16 +41,18 @@ __END_DECLS
 
 #include <IOKit/IOTimeStamp.h>
 #include <IOKit/IOKitDebug.h>
-
 #if CONFIG_DTRACE
 #include <mach/sdt.h>
 #endif
 
+#include <libkern/Block.h>
+
+
 #define super IOEventSource
 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
-OSMetaClassDefineReservedUnused(IOTimerEventSource, 0);
-OSMetaClassDefineReservedUnused(IOTimerEventSource, 1);
-OSMetaClassDefineReservedUnused(IOTimerEventSource, 2);
+OSMetaClassDefineReservedUsed(IOTimerEventSource, 0);
+OSMetaClassDefineReservedUsed(IOTimerEventSource, 1);
+OSMetaClassDefineReservedUsed(IOTimerEventSource, 2);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
@@ -88,297 +90,478 @@ do { \
 
 #endif /* IOKITSTATS */
 
-// 
+//
 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
-// not a subclassed implementation. 
+// not a subclassed implementation.
 //
 
 // Timeout handler function. This function is called by the kernel when
 // the timeout interval expires.
 //
-void IOTimerEventSource::timeout(void *self)
+
+__inline__ void
+IOTimerEventSource::invokeAction(IOTimerEventSource::Action _action, IOTimerEventSource * ts,
+    OSObject * _owner, IOWorkLoop * _workLoop)
 {
-    IOTimerEventSource *me = (IOTimerEventSource *) self;
-
-    IOStatisticsTimeout();
-
-    if (me->enabled && me->action)
-    {
-        IOWorkLoop *
-        wl = me->workLoop;
-        if (wl)
-        {
-            Action doit;
-            wl->closeGate();
-            IOStatisticsCloseGate();
-            doit = (Action) me->action;
-            if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime))
-            {
-               bool    trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
-               
-               if (trace)
-                       IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
-                                                                                        (uintptr_t) doit, (uintptr_t) me->owner);
-                               
-                (*doit)(me->owner, me);
+       bool    trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
+
+       if (trace) {
+               IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
+                   VM_KERNEL_ADDRHIDE(_action), VM_KERNEL_ADDRHIDE(_owner));
+       }
+
+       if (kActionBlock & flags) {
+               ((IOTimerEventSource::ActionBlock) actionBlock)(ts);
+       } else {
+               (*_action)(_owner, ts);
+       }
+
 #if CONFIG_DTRACE
-               DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop);
+       DTRACE_TMR3(iotescallout__expire, Action, _action, OSObject, _owner, void, _workLoop);
 #endif
-                
-                               if (trace)
-                       IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
-                                                                                  (uintptr_t) doit, (uintptr_t) me->owner);
-            }
-            IOStatisticsOpenGate();
-            wl->openGate();
-        }
-    }
+
+       if (trace) {
+               IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
+                   VM_KERNEL_UNSLIDE(_action), VM_KERNEL_ADDRHIDE(_owner));
+       }
 }
 
-void IOTimerEventSource::timeoutAndRelease(void * self, void * c)
+void
+IOTimerEventSource::timeout(void *self)
 {
-    IOTimerEventSource *me = (IOTimerEventSource *) self;
+       IOTimerEventSource *me = (IOTimerEventSource *) self;
+
+       IOStatisticsTimeout();
+
+       if (me->enabled && me->action) {
+               IOWorkLoop *
+                   wl = me->workLoop;
+               if (wl) {
+                       Action doit;
+                       wl->closeGate();
+                       IOStatisticsCloseGate();
+                       doit = (Action) me->action;
+                       if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime)) {
+                               me->invokeAction(doit, me, me->owner, me->workLoop);
+                       }
+                       IOStatisticsOpenGate();
+                       wl->openGate();
+               }
+       }
+}
+
+void
+IOTimerEventSource::timeoutAndRelease(void * self, void * c)
+{
+       IOTimerEventSource *me = (IOTimerEventSource *) self;
        /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count"
-          must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
+        *  must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
        SInt32 count = (SInt32) (long) c;
 
-    IOStatisticsTimeout();
-       
-    if (me->enabled && me->action)
-    {
-        IOWorkLoop *
-        wl = me->reserved->workLoop;
-        if (wl)
-        {
-            Action doit;
-            wl->closeGate();
-            IOStatisticsCloseGate();
-            doit = (Action) me->action;
-            if (doit && (me->reserved->calloutGeneration == count))
-            {
-               bool    trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
-               
-               if (trace)
-                       IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
-                                                                                        (uintptr_t) doit, (uintptr_t) me->owner);
-                               
-                (*doit)(me->owner, me);
-#if CONFIG_DTRACE
-               DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop);
-#endif
-                
-                               if (trace)
-                       IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
-                                                                                  (uintptr_t) doit, (uintptr_t) me->owner);
-            }
-            IOStatisticsOpenGate();
-            wl->openGate();
-        }
-    }
-
-    me->reserved->workLoop->release();
-    me->release();
+       IOStatisticsTimeout();
+
+       if (me->enabled && me->action) {
+               IOWorkLoop *
+                   wl = me->reserved->workLoop;
+               if (wl) {
+                       Action doit;
+                       wl->closeGate();
+                       IOStatisticsCloseGate();
+                       doit = (Action) me->action;
+                       if (doit && (me->reserved->calloutGeneration == count)) {
+                               me->invokeAction(doit, me, me->owner, me->workLoop);
+                       }
+                       IOStatisticsOpenGate();
+                       wl->openGate();
+               }
+       }
+
+       me->reserved->workLoop->release();
+       me->release();
+}
+
+// -- work loop delivery
+
+bool
+IOTimerEventSource::checkForWork()
+{
+       Action doit;
+
+       if (reserved
+           && (reserved->calloutGenerationSignaled == reserved->calloutGeneration)
+           && enabled && (doit = (Action) action)) {
+               reserved->calloutGenerationSignaled = ~reserved->calloutGeneration;
+               invokeAction(doit, this, owner, workLoop);
+       }
+
+       return false;
+}
+
+void
+IOTimerEventSource::timeoutSignaled(void * self, void * c)
+{
+       IOTimerEventSource *me = (IOTimerEventSource *) self;
+
+       me->reserved->calloutGenerationSignaled = (SInt32)(long) c;
+       if (me->enabled) {
+               me->signalWorkAvailable();
+       }
 }
 
-void IOTimerEventSource::setTimeoutFunc()
+// --
+
+void
+IOTimerEventSource::setTimeoutFunc()
 {
-    // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
-    // not a subclassed implementation
-    reserved = IONew(ExpansionData, 1);
-    calloutEntry = (void *) thread_call_allocate((thread_call_func_t) &IOTimerEventSource::timeoutAndRelease,
-                                                 (thread_call_param_t) this);
+       thread_call_priority_t pri;
+       uint32_t options;
+
+       if (reserved) {
+               panic("setTimeoutFunc already %p, %p", this, reserved);
+       }
+
+       // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
+       // not a subclassed implementation
+       reserved = IONew(ExpansionData, 1);
+       reserved->calloutGenerationSignaled = ~reserved->calloutGeneration;
+       options = abstime;
+       abstime = 0;
+
+       thread_call_options_t tcoptions = 0;
+       thread_call_func_t    func      = NULL;
+
+       switch (kIOTimerEventSourceOptionsPriorityMask & options) {
+       case kIOTimerEventSourceOptionsPriorityHigh:
+               pri = THREAD_CALL_PRIORITY_HIGH;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityKernel:
+               pri = THREAD_CALL_PRIORITY_KERNEL;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityKernelHigh:
+               pri = THREAD_CALL_PRIORITY_KERNEL_HIGH;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityUser:
+               pri = THREAD_CALL_PRIORITY_USER;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityLow:
+               pri = THREAD_CALL_PRIORITY_LOW;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityWorkLoop:
+               pri = THREAD_CALL_PRIORITY_KERNEL;
+               tcoptions |= THREAD_CALL_OPTIONS_SIGNAL;
+               if (kIOTimerEventSourceOptionsAllowReenter & options) {
+                       break;
+               }
+               func = &IOTimerEventSource::timeoutSignaled;
+               break;
+
+       default:
+               break;
+       }
+
+       assertf(func, "IOTimerEventSource options 0x%x", options);
+       if (!func) {
+               return;                                              // init will fail
+       }
+       if (THREAD_CALL_OPTIONS_SIGNAL & tcoptions) {
+               flags |= kActive;
+       } else {
+               flags |= kPassive;
+       }
+
+       if (!(kIOTimerEventSourceOptionsAllowReenter & options)) {
+               tcoptions |= THREAD_CALL_OPTIONS_ONCE;
+       }
+
+       calloutEntry = (void *) thread_call_allocate_with_options(func,
+           (thread_call_param_t) this, pri, tcoptions);
+       assert(calloutEntry);
 }
 
-bool IOTimerEventSource::init(OSObject *inOwner, Action inAction)
+bool
+IOTimerEventSource::init(OSObject *inOwner, Action inAction)
 {
-    if (!super::init(inOwner, (IOEventSource::Action) inAction) )
-        return false;
+       if (!super::init(inOwner, (IOEventSource::Action) inAction)) {
+               return false;
+       }
 
-    setTimeoutFunc();
-    if (!calloutEntry)
-        return false;
+       setTimeoutFunc();
+       if (!calloutEntry) {
+               return false;
+       }
 
-    IOStatisticsInitializeCounter();
+       IOStatisticsInitializeCounter();
 
-    return true;
+       return true;
+}
+
+bool
+IOTimerEventSource::init(uint32_t options, OSObject *inOwner, Action inAction)
+{
+       abstime = options;
+       return init(inOwner, inAction);
 }
 
 IOTimerEventSource *
-IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
+IOTimerEventSource::timerEventSource(uint32_t inOptions, OSObject *inOwner, Action inAction)
+{
+       IOTimerEventSource *me = new IOTimerEventSource;
+
+       if (me && !me->init(inOptions, inOwner, inAction)) {
+               me->release();
+               return NULL;
+       }
+
+       return me;
+}
+
+IOTimerEventSource *
+IOTimerEventSource::timerEventSource(uint32_t options, OSObject *inOwner, ActionBlock _action)
 {
-    IOTimerEventSource *me = new IOTimerEventSource;
+       IOTimerEventSource * tes;
+       tes = IOTimerEventSource::timerEventSource(options, inOwner, (Action) NULL);
+       if (tes) {
+               tes->setActionBlock((IOEventSource::ActionBlock) _action);
+       }
 
-    if (me && !me->init(inOwner, inAction)) {
-        me->release();
-        return 0;
-    }
+       return tes;
+}
+
+#define _thread_call_cancel(tc)   ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc)))
 
-    return me;
+IOTimerEventSource *
+IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
+{
+       return IOTimerEventSource::timerEventSource(
+               kIOTimerEventSourceOptionsPriorityKernelHigh,
+               inOwner, inAction);
 }
 
-void IOTimerEventSource::free()
+void
+IOTimerEventSource::free()
 {
-    if (calloutEntry) {
-        cancelTimeout();
-        thread_call_free((thread_call_t) calloutEntry);    
-    }
+       if (calloutEntry) {
+               __assert_only bool freed;
+
+               cancelTimeout();
+
+               freed = thread_call_free((thread_call_t) calloutEntry);
+               assert(freed);
+       }
 
-    if (reserved)
-        IODelete(reserved, ExpansionData, 1);
+       if (reserved) {
+               IODelete(reserved, ExpansionData, 1);
+       }
 
-    super::free();
+       super::free();
 }
 
-void IOTimerEventSource::cancelTimeout()
+void
+IOTimerEventSource::cancelTimeout()
 {
-    if (reserved)
-        reserved->calloutGeneration++;
-    bool active = thread_call_cancel((thread_call_t) calloutEntry);
-    AbsoluteTime_to_scalar(&abstime) = 0;
-    if (active && reserved)
-    {
-        release();
-        workLoop->release();
-    }
+       if (reserved) {
+               reserved->calloutGeneration++;
+       }
+       bool active = _thread_call_cancel((thread_call_t) calloutEntry);
+       AbsoluteTime_to_scalar(&abstime) = 0;
+       if (active && reserved && (kPassive & flags)) {
+               release();
+               workLoop->release();
+       }
 }
 
-void IOTimerEventSource::enable()
+void
+IOTimerEventSource::enable()
 {
-    super::enable();
-    if (kIOReturnSuccess != wakeAtTime(abstime))
-        super::disable(); // Problem re-scheduling timeout ignore enable
+       super::enable();
+       if (kIOReturnSuccess != wakeAtTime(abstime)) {
+               super::disable(); // Problem re-scheduling timeout ignore enable
+       }
 }
 
-void IOTimerEventSource::disable()
+void
+IOTimerEventSource::disable()
 {
-    if (reserved)
-        reserved->calloutGeneration++;
-    bool active = thread_call_cancel((thread_call_t) calloutEntry);
-    super::disable();
-    if (active && reserved)
-    {
-        release();
-        workLoop->release();
-    }
+       if (reserved) {
+               reserved->calloutGeneration++;
+       }
+       bool active = _thread_call_cancel((thread_call_t) calloutEntry);
+       super::disable();
+       if (active && reserved && (kPassive & flags)) {
+               release();
+               workLoop->release();
+       }
 }
 
-IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
+IOReturn
+IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
 {
-    return setTimeout(ticks, kTickScale);
+       return setTimeout(ticks, kTickScale);
 }
 
-IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
+IOReturn
+IOTimerEventSource::setTimeoutMS(UInt32 ms)
 {
-    return setTimeout(ms, kMillisecondScale);
+       return setTimeout(ms, kMillisecondScale);
 }
 
-IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
+IOReturn
+IOTimerEventSource::setTimeoutUS(UInt32 us)
 {
-    return setTimeout(us, kMicrosecondScale);
+       return setTimeout(us, kMicrosecondScale);
 }
 
-IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
+IOReturn
+IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
 {
-    AbsoluteTime end;
+       AbsoluteTime end;
 
-    clock_interval_to_deadline(interval, scale_factor, &end);
-    return wakeAtTime(end);
+       clock_interval_to_deadline(interval, scale_factor, &end);
+       return wakeAtTime(end);
 }
 
 #if !defined(__LP64__)
-IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval)
+IOReturn
+IOTimerEventSource::setTimeout(mach_timespec_t interval)
 {
-    AbsoluteTime end, nsecs;
+       AbsoluteTime end, nsecs;
 
-    clock_interval_to_absolutetime_interval
-        (interval.tv_nsec, kNanosecondScale, &nsecs);
-    clock_interval_to_deadline
-        (interval.tv_sec, NSEC_PER_SEC, &end);
-    ADD_ABSOLUTETIME(&end, &nsecs);
+       clock_interval_to_absolutetime_interval
+       (interval.tv_nsec, kNanosecondScale, &nsecs);
+       clock_interval_to_deadline
+       (interval.tv_sec, NSEC_PER_SEC, &end);
+       ADD_ABSOLUTETIME(&end, &nsecs);
 
-    return wakeAtTime(end);
+       return wakeAtTime(end);
 }
 #endif
 
-IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
+IOReturn
+IOTimerEventSource::setTimeout(AbsoluteTime interval)
 {
-    AbsoluteTime end;
-
-    clock_get_uptime(&end);
-    ADD_ABSOLUTETIME(&end, &interval);
+       AbsoluteTime end;
+       clock_absolutetime_interval_to_deadline(interval, &end);
+       return wakeAtTime(end);
+}
 
-    return wakeAtTime(end);
+IOReturn
+IOTimerEventSource::setTimeout(uint32_t options,
+    AbsoluteTime abstime, AbsoluteTime leeway)
+{
+       AbsoluteTime end;
+       if (options & kIOTimeOptionsContinuous) {
+               clock_continuoustime_interval_to_deadline(abstime, &end);
+       } else {
+               clock_absolutetime_interval_to_deadline(abstime, &end);
+       }
+
+       return wakeAtTime(options, end, leeway);
 }
 
-IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
+IOReturn
+IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
 {
-    return wakeAtTime(ticks, kTickScale);
+       return wakeAtTime(ticks, kTickScale);
 }
 
-IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
+IOReturn
+IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
 {
-    return wakeAtTime(ms, kMillisecondScale);
+       return wakeAtTime(ms, kMillisecondScale);
 }
 
-IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
+IOReturn
+IOTimerEventSource::wakeAtTimeUS(UInt32 us)
 {
-    return wakeAtTime(us, kMicrosecondScale);
+       return wakeAtTime(us, kMicrosecondScale);
 }
 
-IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
+IOReturn
+IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
 {
-    AbsoluteTime end;
-    clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
+       AbsoluteTime end;
+       clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
 
-    return wakeAtTime(end);
+       return wakeAtTime(end);
 }
 
 #if !defined(__LP64__)
-IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
+IOReturn
+IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
 {
-    AbsoluteTime end, nsecs;
+       AbsoluteTime end, nsecs;
 
-    clock_interval_to_absolutetime_interval
-        (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
-    clock_interval_to_absolutetime_interval
-        (inAbstime.tv_sec, kSecondScale, &end);
-    ADD_ABSOLUTETIME(&end, &nsecs);
+       clock_interval_to_absolutetime_interval
+       (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
+       clock_interval_to_absolutetime_interval
+       (inAbstime.tv_sec, kSecondScale, &end);
+       ADD_ABSOLUTETIME(&end, &nsecs);
 
-    return wakeAtTime(end);
+       return wakeAtTime(end);
 }
 #endif
 
-void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
+void
+IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
+{
+       super::setWorkLoop(inWorkLoop);
+       if (enabled && AbsoluteTime_to_scalar(&abstime) && workLoop) {
+               wakeAtTime(abstime);
+       }
+}
+
+IOReturn
+IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
 {
-    super::setWorkLoop(inWorkLoop);
-    if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
-        wakeAtTime(abstime);
+       return wakeAtTime(0, inAbstime, 0);
 }
 
-IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
+IOReturn
+IOTimerEventSource::wakeAtTime(uint32_t options, AbsoluteTime inAbstime, AbsoluteTime leeway)
 {
-    if (!action)
-        return kIOReturnNoResources;
-
-    abstime = inAbstime;
-    if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop )
-    {
-        if (reserved)
-        {
-            retain();
-            workLoop->retain();
-            reserved->workLoop = workLoop;
-            reserved->calloutGeneration++;
-            if (thread_call_enter1_delayed((thread_call_t) calloutEntry, 
-                    (void *) reserved->calloutGeneration, inAbstime))
-            {
-                release();
-                workLoop->release();
-            }
-        }
-        else
-            thread_call_enter_delayed((thread_call_t) calloutEntry, inAbstime);
-    }
-
-    return kIOReturnSuccess;
+       if (!action) {
+               return kIOReturnNoResources;
+       }
+
+       abstime = inAbstime;
+       if (enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop) {
+               uint32_t tcoptions = 0;
+
+               if (kIOTimeOptionsWithLeeway & options) {
+                       tcoptions |= THREAD_CALL_DELAY_LEEWAY;
+               }
+               if (kIOTimeOptionsContinuous & options) {
+                       tcoptions |= THREAD_CALL_CONTINUOUS;
+               }
+
+               if (reserved) {
+                       if (kPassive & flags) {
+                               retain();
+                               workLoop->retain();
+                       }
+                       reserved->workLoop = workLoop;
+                       reserved->calloutGeneration++;
+                       if (thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
+                           (void *)(uintptr_t) reserved->calloutGeneration, inAbstime, leeway, tcoptions)
+                           && (kPassive & flags)) {
+                               release();
+                               workLoop->release();
+                       }
+               } else {
+                       thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
+                           NULL, inAbstime, leeway, tcoptions);
+               }
+       }
+
+       return kIOReturnSuccess;
 }