xref: /haiku/headers/private/kernel/syscall_restart.h (revision 7a74a5df454197933bc6e80a542102362ee98703)
1 /*
2  * Copyright 2008-2011, 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) {
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 struct SyscallFlagUnsetter {
104 	SyscallFlagUnsetter()
105 	{
106 		fThread = thread_get_current_thread();
107 		fWasSyscall = (atomic_and(&fThread->flags, ~THREAD_FLAGS_SYSCALL)
108 			& THREAD_FLAGS_SYSCALL) != 0;
109 	}
110 
111 	~SyscallFlagUnsetter()
112 	{
113 		if (fWasSyscall)
114 			atomic_or(&fThread->flags, THREAD_FLAGS_SYSCALL);
115 	}
116 
117 private:
118 	Thread*	fThread;
119 	bool	fWasSyscall;
120 };
121 
122 
123 template<typename Type>
124 struct SyscallRestartWrapper {
125 	SyscallRestartWrapper(Type initialValue = 0)
126 		: fResult(initialValue)
127 	{
128 		fThread = thread_get_current_thread();
129 		atomic_or(&fThread->flags, THREAD_FLAGS_SYSCALL);
130 	}
131 
132 	~SyscallRestartWrapper()
133 	{
134 		if (fResult == B_INTERRUPTED) {
135 			// interrupted -- set flag for syscall restart
136 			atomic_or(&fThread->flags, THREAD_FLAGS_RESTART_SYSCALL);
137 		}
138 
139 		atomic_and(&fThread->flags, ~THREAD_FLAGS_SYSCALL);
140 	}
141 
142 	SyscallRestartWrapper<Type>& operator=(const Type& other)
143 	{
144 		fResult = other;
145 		return *this;
146 	}
147 
148 	operator Type() const	{ return fResult; }
149 
150 private:
151 	Type	fResult;
152 	Thread*	fThread;
153 };
154 
155 
156 #endif	// _KERNEL_SYSCALL_RESTART_H
157