1 /* 2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <time.h> 8 9 #include <errno.h> 10 11 #include <new> 12 13 #include <AutoDeleter.h> 14 #include <syscall_utils.h> 15 16 #include <errno_private.h> 17 #include <syscalls.h> 18 #include <thread_defs.h> 19 #include <user_timer_defs.h> 20 21 #include <libroot_private.h> 22 #include <pthread_private.h> 23 #include <time_private.h> 24 #include <user_thread.h> 25 26 27 static void 28 info_to_itimerspec(const user_timer_info& info, itimerspec& spec) 29 { 30 bigtime_to_timespec(info.interval, spec.it_interval); 31 32 // A remaining_time of B_INFINITE_TIMEOUT means the timer isn't scheduled. 33 if (info.remaining_time != B_INFINITE_TIMEOUT) { 34 bigtime_to_timespec(info.remaining_time, spec.it_value); 35 } else { 36 spec.it_value.tv_sec = 0; 37 spec.it_value.tv_nsec = 0; 38 } 39 } 40 41 42 static bool 43 itimerspec_to_bigtimes(const itimerspec& spec, bigtime_t& _nextTime, 44 bigtime_t& _interval) 45 { 46 if (!timespec_to_bigtime(spec.it_interval, _interval) 47 || !timespec_to_bigtime(spec.it_value, _nextTime)) { 48 return false; 49 } 50 51 if (_nextTime == 0) 52 _nextTime = B_INFINITE_TIMEOUT; 53 54 return true; 55 } 56 57 58 static status_t 59 timer_thread_entry(void* _entry, void* _value) 60 { 61 // init a pthread 62 pthread_thread thread; 63 __init_pthread(&thread, NULL, NULL); 64 thread.flags |= THREAD_DETACHED; 65 66 get_user_thread()->pthread = &thread; 67 68 // we have been started with deferred signals -- undefer them 69 undefer_signals(); 70 71 // prepare the arguments 72 union sigval value; 73 value.sival_ptr = _value; 74 void (*entry)(union sigval) = (void (*)(union sigval))_entry; 75 76 // call the entry function 77 entry(value); 78 79 return B_OK; 80 } 81 82 83 // #pragma mark - 84 85 86 int 87 timer_create(clockid_t clockID, struct sigevent* event, timer_t* _timer) 88 { 89 // create a timer object 90 __timer_t* timer = new(std::nothrow) __timer_t; 91 if (timer == NULL) 92 RETURN_AND_SET_ERRNO(ENOMEM); 93 ObjectDeleter<__timer_t> timerDeleter(timer); 94 95 // If the notification method is SIGEV_THREAD, initialize thread creation 96 // attributes. 97 bool isThreadEvent = event != NULL && event->sigev_notify == SIGEV_THREAD; 98 thread_creation_attributes threadAttributes; 99 if (isThreadEvent) { 100 status_t error = __pthread_init_creation_attributes( 101 event->sigev_notify_attributes, NULL, &timer_thread_entry, 102 (void*)event->sigev_notify_function, event->sigev_value.sival_ptr, 103 "timer notify", &threadAttributes); 104 if (error != B_OK) 105 RETURN_AND_SET_ERRNO(error); 106 107 threadAttributes.flags |= THREAD_CREATION_FLAG_DEFER_SIGNALS; 108 } 109 110 // create the timer 111 int32 timerID = _kern_create_timer(clockID, -1, 0, event, 112 isThreadEvent ? &threadAttributes : NULL); 113 if (timerID < 0) 114 RETURN_AND_SET_ERRNO(timerID); 115 116 // init the object members 117 timer->SetTo(timerID, -1); 118 119 *_timer = timerDeleter.Detach(); 120 return 0; 121 } 122 123 124 int 125 timer_delete(timer_t timer) 126 { 127 status_t error = _kern_delete_timer(timer->id, timer->thread); 128 if (error != B_OK) 129 RETURN_AND_SET_ERRNO(error); 130 131 delete timer; 132 return 0; 133 } 134 135 136 int 137 timer_gettime(timer_t timer, struct itimerspec* value) 138 { 139 user_timer_info info; 140 status_t error = _kern_get_timer(timer->id, timer->thread, &info); 141 if (error != B_OK) 142 RETURN_AND_SET_ERRNO(error); 143 144 info_to_itimerspec(info, *value); 145 146 return 0; 147 } 148 149 150 int 151 timer_settime(timer_t timer, int flags, const struct itimerspec* value, 152 struct itimerspec* oldValue) 153 { 154 // translate new value 155 bigtime_t nextTime; 156 bigtime_t interval; 157 if (!itimerspec_to_bigtimes(*value, nextTime, interval)) 158 RETURN_AND_SET_ERRNO(EINVAL); 159 160 uint32 timeoutFlags = (flags & TIMER_ABSTIME) != 0 161 ? B_ABSOLUTE_TIMEOUT : B_RELATIVE_TIMEOUT; 162 163 // set the timer 164 user_timer_info oldInfo; 165 status_t error = _kern_set_timer(timer->id, timer->thread, nextTime, 166 interval, timeoutFlags, oldValue != NULL ? &oldInfo : NULL); 167 if (error != B_OK) 168 RETURN_AND_SET_ERRNO(error); 169 170 // translate old info back 171 if (oldValue != NULL) 172 info_to_itimerspec(oldInfo, *oldValue); 173 174 return 0; 175 } 176 177 178 int 179 timer_getoverrun(timer_t timer) 180 { 181 user_timer_info info; 182 status_t error = _kern_get_timer(timer->id, timer->thread, &info); 183 if (error != B_OK) 184 RETURN_AND_SET_ERRNO(error); 185 186 return info.overrun_count; 187 } 188