* Copyright 2008-2013, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_SYSCALL_RESTART_H
#define _KERNEL_SYSCALL_RESTART_H
#include <OS.h>
#include <thread.h>
Converts the given relative timeout to an absolute timeout or retrieves
the value from the syscall restart parameters, if the syscall has been
restarted. A negative value means infinite timeout.
*/
static inline void
syscall_restart_handle_timeout_pre(bigtime_t& timeout)
{
Thread* thread = thread_get_current_thread();
if ((thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0)
timeout = *(bigtime_t*)thread->syscall_restart.parameters;
else if (timeout >= 0) {
timeout += system_time();
if (timeout < 0)
timeout = B_INFINITE_TIMEOUT;
}
}
If necessary converts the given timeout to an absolute timeout or retrieves
the value from the syscall restart parameters, if the syscall has been
restarted.
*/
static inline void
syscall_restart_handle_timeout_pre(uint32& flags, bigtime_t& timeout)
{
Thread* thread = thread_get_current_thread();
if ((thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0) {
timeout = *(bigtime_t*)thread->syscall_restart.parameters;
if (timeout > 0 && (flags & B_RELATIVE_TIMEOUT) != 0)
flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT;
} else if ((flags & B_RELATIVE_TIMEOUT) != 0) {
if (timeout > 0 && timeout != B_INFINITE_TIMEOUT) {
timeout += system_time();
if (timeout < 0)
timeout = B_INFINITE_TIMEOUT;
flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT;
}
}
}
static inline status_t
syscall_restart_handle_timeout_post(status_t error, bigtime_t timeout)
{
if (error == B_INTERRUPTED) {
Thread* thread = thread_get_current_thread();
*(bigtime_t*)thread->syscall_restart.parameters = timeout;
atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL);
}
return error;
}
static inline status_t
syscall_restart_handle_post(status_t error)
{
if (error == B_INTERRUPTED) {
Thread* thread = thread_get_current_thread();
atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL);
}
return error;
}
static inline bool
syscall_restart_is_restarted()
{
Thread* thread = thread_get_current_thread();
return (thread->flags & THREAD_FLAGS_SYSCALL) != 0
&& (thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0;
}
to determine this is currently only used where actually needed, such as
ioctl().
TODO: this function is actually needed as part of the public API for ioctl()
*/
static inline bool
is_called_via_syscall(void)
{
Thread* thread = thread_get_current_thread();
return (thread->flags & THREAD_FLAGS_SYSCALL) != 0;
}
struct SyscallFlagUnsetter {
SyscallFlagUnsetter()
{
fThread = thread_get_current_thread();
fWasSyscall = (atomic_and(&fThread->flags, ~THREAD_FLAGS_SYSCALL)
& THREAD_FLAGS_SYSCALL) != 0;
}
~SyscallFlagUnsetter()
{
if (fWasSyscall)
atomic_or(&fThread->flags, THREAD_FLAGS_SYSCALL);
}
private:
Thread* fThread;
bool fWasSyscall;
};
template<typename Type>
struct SyscallRestartWrapper {
SyscallRestartWrapper(Type initialValue = 0)
: fResult(initialValue)
{
fThread = thread_get_current_thread();
atomic_or(&fThread->flags, THREAD_FLAGS_SYSCALL);
}
~SyscallRestartWrapper()
{
if (fResult == B_INTERRUPTED) {
atomic_or(&fThread->flags, THREAD_FLAGS_RESTART_SYSCALL);
}
atomic_and(&fThread->flags, ~THREAD_FLAGS_SYSCALL);
}
SyscallRestartWrapper<Type>& operator=(const Type& other)
{
fResult = other;
return *this;
}
operator Type() const { return fResult; }
private:
Type fResult;
Thread* fThread;
};
#endif