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