2  * Copyright (c) 2015 Apple Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. Please obtain a copy of the License at 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this 
  13  * The Original Code and all software distributed under the License are 
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  * Please see the License for the specific language governing rights and 
  19  * limitations under the License. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  23 #include <sys/cdefs.h> 
  24 #include <sys/types.h> 
  25 #include <sys/work_interval.h> 
  27 #include <mach/mach.h> 
  28 #include <mach/mach_time.h> 
  29 #include <mach/port.h> 
  31 #include <sys/errno.h> 
  34 struct work_interval 
{ 
  36         uint64_t work_interval_id
; 
  37         uint32_t create_flags
; 
  41 extern uint64_t __thread_selfid(void); 
  43 /* Create a new work interval handle (currently for the current thread only). */ 
  45 work_interval_create(work_interval_t 
*interval_handle
, uint32_t create_flags
) 
  48         work_interval_t handle
; 
  50         if (interval_handle 
== NULL
) { 
  55         struct work_interval_create_params create_params 
= { 
  56                 .wicp_create_flags 
= create_flags
, 
  59         ret 
= __work_interval_ctl(WORK_INTERVAL_OPERATION_CREATE2
, 0, 
  60                                   &create_params
, sizeof(create_params
)); 
  65         handle 
= malloc(sizeof(*handle
)); 
  71         handle
->thread_id 
= __thread_selfid(); 
  72         handle
->work_interval_id 
= create_params
.wicp_id
; 
  73         handle
->create_flags 
= create_params
.wicp_create_flags
; 
  74         handle
->wi_port 
= create_params
.wicp_port
; 
  76         *interval_handle 
= handle
; 
  81 work_interval_notify(work_interval_t interval_handle
, uint64_t start
, 
  82                      uint64_t finish
, uint64_t deadline
, uint64_t next_start
, 
  83                      uint32_t notify_flags
) 
  86         uint64_t work_interval_id
; 
  87         struct work_interval_notification notification 
= { 
  91                 .next_start 
= next_start
, 
  92                 .notify_flags 
= notify_flags
 
  95         if (interval_handle 
== NULL
) { 
 100         notification
.create_flags 
= interval_handle
->create_flags
; 
 101         work_interval_id 
= interval_handle
->work_interval_id
; 
 103         ret 
= __work_interval_ctl(WORK_INTERVAL_OPERATION_NOTIFY
, work_interval_id
, 
 104                                   ¬ification
, sizeof(notification
)); 
 109 work_interval_notify_simple(work_interval_t interval_handle
, uint64_t start
, 
 110                             uint64_t deadline
, uint64_t next_start
) 
 112         return work_interval_notify(interval_handle
, start
, mach_absolute_time(), 
 113                                     deadline
, next_start
, 0); 
 117 work_interval_destroy(work_interval_t interval_handle
) 
 119         if (interval_handle 
== NULL
) { 
 124         if (interval_handle
->create_flags 
& WORK_INTERVAL_FLAG_JOINABLE
) { 
 125                 mach_port_t wi_port 
= interval_handle
->wi_port
; 
 128                  * A joinable work interval's lifetime is tied to the port lifetime. 
 129                  * When the last port reference is destroyed, the work interval 
 130                  * is destroyed via no-senders notification. 
 132                  * Note however that after destroy it can no longer be notified 
 133                  * because the userspace token is gone. 
 135                  * Additionally, this function does not cause the thread to un-join 
 138                 kern_return_t kr 
= mach_port_deallocate(mach_task_self(), wi_port
); 
 140                 if (kr 
!= KERN_SUCCESS
) { 
 142                          * If the deallocate fails, then someone got their port 
 143                          * lifecycle wrong and over-released a port right. 
 145                          * Return an error so the client can assert on this, 
 146                          * and still find the port name in the interval handle. 
 152                 interval_handle
->wi_port 
= MACH_PORT_NULL
; 
 153                 interval_handle
->work_interval_id 
= 0; 
 155                 free(interval_handle
); 
 158                 uint64_t work_interval_id 
= interval_handle
->work_interval_id
; 
 160                 int ret 
= __work_interval_ctl(WORK_INTERVAL_OPERATION_DESTROY
, 
 161                                               work_interval_id
, NULL
, 0); 
 163                 interval_handle
->work_interval_id 
= 0; 
 165                 int saved_errno 
= errno
; 
 166                 free(interval_handle
); 
 173 work_interval_join(work_interval_t interval_handle
) 
 175         if (interval_handle 
== NULL
) { 
 180         if ((interval_handle
->create_flags 
& WORK_INTERVAL_FLAG_JOINABLE
) == 0) { 
 185         mach_port_t wi_port 
= interval_handle
->wi_port
; 
 187         if (!MACH_PORT_VALID(wi_port
)) { 
 192         return work_interval_join_port(wi_port
); 
 196 work_interval_join_port(mach_port_t port
) 
 198         if (port 
== MACH_PORT_NULL
) { 
 203         return __work_interval_ctl(WORK_INTERVAL_OPERATION_JOIN
, 
 204                                    (uint64_t)port
, NULL
, 0); 
 208 work_interval_leave(void) 
 210         return __work_interval_ctl(WORK_INTERVAL_OPERATION_JOIN
, 
 211                                    (uint64_t)MACH_PORT_NULL
, NULL
, 0); 
 215 work_interval_copy_port(work_interval_t interval_handle
, mach_port_t 
*port
) 
 222         if (interval_handle 
== NULL
) { 
 223                 *port 
= MACH_PORT_NULL
; 
 228         if ((interval_handle
->create_flags 
& WORK_INTERVAL_FLAG_JOINABLE
) == 0) { 
 229                 *port 
= MACH_PORT_NULL
; 
 234         mach_port_t wi_port 
= interval_handle
->wi_port
; 
 236         kern_return_t kr 
= mach_port_mod_refs(mach_task_self(), wi_port
, 
 237                                               MACH_PORT_RIGHT_SEND
, 1); 
 239         if (kr 
!= KERN_SUCCESS
) { 
 240                 *port 
= MACH_PORT_NULL
;