xref: /haiku/headers/private/kernel/syscall_restart.h (revision 3d4afef9cba2f328e238089d4609d00d4b1524f3)
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