1 /* 2 * Copyright 2008-2013, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 #ifndef _KERNEL_SYSCALL_RESTART_H 6 #define _KERNEL_SYSCALL_RESTART_H 7 8 9 #include <OS.h> 10 11 #include <thread.h> 12 13 14 /*! Helper function for syscalls with relative timeout. 15 Converts the given relative timeout to an absolute timeout or retrieves 16 the value from the syscall restart parameters, if the syscall has been 17 restarted. A negative value means infinite timeout. 18 */ 19 static inline void 20 syscall_restart_handle_timeout_pre(bigtime_t& timeout) 21 { 22 // If restarted, get the timeout from the restart parameters. Otherwise 23 // convert relative timeout to an absolute one. 24 Thread* thread = thread_get_current_thread(); 25 if ((thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0) 26 timeout = *(bigtime_t*)thread->syscall_restart.parameters; 27 else if (timeout >= 0) { 28 timeout += system_time(); 29 // deal with overflow 30 if (timeout < 0) 31 timeout = B_INFINITE_TIMEOUT; 32 } 33 } 34 35 36 /*! Helper function for syscalls with flags + timeout. 37 If necessary converts the given timeout to an absolute timeout or retrieves 38 the value from the syscall restart parameters, if the syscall has been 39 restarted. 40 */ 41 static inline void 42 syscall_restart_handle_timeout_pre(uint32& flags, bigtime_t& timeout) 43 { 44 // If restarted, get the timeout from the restart parameters. Otherwise 45 // convert relative timeout to an absolute one. Note that we preserve 46 // relative 0 us timeouts, so that the syscall can still decide whether to 47 // return B_WOULD_BLOCK instead of B_TIMED_OUT. 48 Thread* thread = thread_get_current_thread(); 49 if ((thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0) { 50 timeout = *(bigtime_t*)thread->syscall_restart.parameters; 51 if (timeout > 0 && (flags & B_RELATIVE_TIMEOUT) != 0) 52 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; 53 } else if ((flags & B_RELATIVE_TIMEOUT) != 0) { 54 if (timeout > 0 && timeout < B_INFINITE_TIMEOUT) { 55 timeout += system_time(); 56 // deal with overflow 57 if (timeout < 0) 58 timeout = B_INFINITE_TIMEOUT; 59 60 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; 61 } 62 } 63 } 64 65 66 static inline status_t 67 syscall_restart_handle_timeout_post(status_t error, bigtime_t timeout) 68 { 69 if (error == B_INTERRUPTED) { 70 // interrupted -- store timeout and set flag for syscall restart 71 Thread* thread = thread_get_current_thread(); 72 *(bigtime_t*)thread->syscall_restart.parameters = timeout; 73 atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL); 74 } 75 76 return error; 77 } 78 79 80 static inline status_t 81 syscall_restart_handle_post(status_t error) 82 { 83 if (error == B_INTERRUPTED) { 84 // interrupted -- set flag for syscall restart 85 Thread* thread = thread_get_current_thread(); 86 atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL); 87 } 88 89 return error; 90 } 91 92 93 static inline bool 94 syscall_restart_is_restarted() 95 { 96 Thread* thread = thread_get_current_thread(); 97 98 return (thread->flags & THREAD_FLAGS_SYSCALL) != 0 99 && (thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0; 100 } 101 102 103 /*! Returns whether or not a function has been called via a syscall. The flag 104 to determine this is currently only used where actually needed, such as 105 ioctl(). 106 107 TODO: this function is actually needed as part of the public API for ioctl() 108 */ 109 static inline bool 110 is_called_via_syscall(void) 111 { 112 Thread* thread = thread_get_current_thread(); 113 return (thread->flags & THREAD_FLAGS_SYSCALL) != 0; 114 } 115 116 117 struct SyscallFlagUnsetter { 118 SyscallFlagUnsetter() 119 { 120 fThread = thread_get_current_thread(); 121 fWasSyscall = (atomic_and(&fThread->flags, ~THREAD_FLAGS_SYSCALL) 122 & THREAD_FLAGS_SYSCALL) != 0; 123 } 124 125 ~SyscallFlagUnsetter() 126 { 127 if (fWasSyscall) 128 atomic_or(&fThread->flags, THREAD_FLAGS_SYSCALL); 129 } 130 131 private: 132 Thread* fThread; 133 bool fWasSyscall; 134 }; 135 136 137 template<typename Type> 138 struct SyscallRestartWrapper { 139 SyscallRestartWrapper(Type initialValue = 0) 140 : fResult(initialValue) 141 { 142 fThread = thread_get_current_thread(); 143 atomic_or(&fThread->flags, THREAD_FLAGS_SYSCALL); 144 } 145 146 ~SyscallRestartWrapper() 147 { 148 if (fResult == B_INTERRUPTED) { 149 // interrupted -- set flag for syscall restart 150 atomic_or(&fThread->flags, THREAD_FLAGS_RESTART_SYSCALL); 151 } 152 153 atomic_and(&fThread->flags, ~THREAD_FLAGS_SYSCALL); 154 } 155 156 SyscallRestartWrapper<Type>& operator=(const Type& other) 157 { 158 fResult = other; 159 return *this; 160 } 161 162 operator Type() const { return fResult; } 163 164 private: 165 Type fResult; 166 Thread* fThread; 167 }; 168 169 170 #endif // _KERNEL_SYSCALL_RESTART_H 171