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*/
19static inline void
20syscall_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*/
41static inline void
42syscall_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
66static inline status_t
67syscall_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
80static inline status_t
81syscall_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
93static inline bool
94syscall_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*/
109static inline bool
110is_called_via_syscall(void)
111{
112	Thread* thread = thread_get_current_thread();
113	return (thread->flags & THREAD_FLAGS_SYSCALL) != 0;
114}
115
116
117struct 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
131private:
132	Thread*	fThread;
133	bool	fWasSyscall;
134};
135
136
137template<typename Type>
138struct 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
164private:
165	Type	fResult;
166	Thread*	fThread;
167};
168
169
170#endif	// _KERNEL_SYSCALL_RESTART_H
171