diff --git a/build/jam/MainBuildRules b/build/jam/MainBuildRules index e96b74cfb7..69d6597d3d 100644 --- a/build/jam/MainBuildRules +++ b/build/jam/MainBuildRules @@ -314,6 +314,11 @@ rule CreateAsmStructOffsetsHeader header : source } } + # Turn off "invalid use of offsetof()" macro warning. We use offsetof() also + # for non-PODs. Since we're using the same compiler for the whole kernel and + # don't do virtual inheritence, that works well enough. + flags += -Wno-invalid-offsetof ; + # locate object, search for source, and set on target variables Depends $(header) : $(source) ; diff --git a/headers/os/kernel/OS.h b/headers/os/kernel/OS.h index eb34da5cf7..a12deed5cd 100644 --- a/headers/os/kernel/OS.h +++ b/headers/os/kernel/OS.h @@ -311,6 +311,8 @@ typedef struct { #define B_REAL_TIME_PRIORITY 120 #define B_SYSTEM_TIMEBASE 0 + /* time base for snooze_*(), compatible with the clockid_t constants defined + in */ #define B_FIRST_REAL_TIME_PRIORITY B_REAL_TIME_DISPLAY_PRIORITY @@ -739,6 +741,11 @@ extern int32 is_computer_on(void); extern double is_computer_on_fire(void); +/* signal related functions */ +int send_signal(thread_id threadID, unsigned int signal); +void set_signal_stack(void* base, size_t size); + + /* WARNING: Experimental API! */ enum { diff --git a/headers/os/kernel/debugger.h b/headers/os/kernel/debugger.h index 12add40d00..5b86c706d0 100644 --- a/headers/os/kernel/debugger.h +++ b/headers/os/kernel/debugger.h @@ -151,7 +151,7 @@ typedef enum { B_DEBUG_MESSAGE_CLEAR_WATCHPOINT, // clear a watchpoint B_DEBUG_MESSAGE_SET_SIGNAL_MASKS, // set/get a thread's masks of signals B_DEBUG_MESSAGE_GET_SIGNAL_MASKS, // the debugger is interested in - B_DEBUG_MESSAGE_SET_SIGNAL_HANDLER, // set/get a thread's signal handler for + B_DEBUG_MESSAGE_SET_SIGNAL_HANDLER, // set/get the team's signal handler for B_DEBUG_MESSAGE_GET_SIGNAL_HANDLER, // a signal B_DEBUG_MESSAGE_PREPARE_HANDOVER, // prepares the debugged team for being @@ -356,7 +356,6 @@ typedef struct { // B_DEBUG_MESSAGE_SET_SIGNAL_HANDLER typedef struct { - thread_id thread; // the thread int signal; // the signal struct sigaction handler; // the new signal handler } debug_nub_set_signal_handler; @@ -365,7 +364,6 @@ typedef struct { typedef struct { port_id reply_port; // port to send the reply to - thread_id thread; // the thread int signal; // the signal } debug_nub_get_signal_handler; diff --git a/headers/posix/arch/x86/signal.h b/headers/posix/arch/x86/signal.h index 0d69b5225d..6d04281499 100644 --- a/headers/posix/arch/x86/signal.h +++ b/headers/posix/arch/x86/signal.h @@ -90,7 +90,7 @@ typedef struct mmx_regs { unsigned char mm7[10]; unsigned char _reserved_154_159[6]; } mmx_regs; - + typedef struct xmmx_regs { unsigned char xmm0[16]; unsigned char xmm1[16]; @@ -105,7 +105,7 @@ typedef struct xmmx_regs { typedef struct new_extended_regs { unsigned short fp_control; unsigned short fp_status; - unsigned short fp_tag; + unsigned short fp_tag; unsigned short fp_opcode; unsigned long fp_eip; unsigned short fp_cs; @@ -128,7 +128,7 @@ typedef struct extended_regs { old_extended_regs old_format; new_extended_regs new_format; } state; - unsigned long format; + unsigned long format; } extended_regs; struct vregs { @@ -141,9 +141,11 @@ struct vregs { unsigned long ebp; unsigned long _reserved_1; extended_regs xregs; - unsigned long _reserved_2[3]; + unsigned long edi; + unsigned long esi; + unsigned long ebx; }; - + #endif /* __INTEL__ */ #endif /* _ARCH_SIGNAL_H_ */ diff --git a/headers/posix/limits.h b/headers/posix/limits.h index 5c69bc9240..8fd8ea1eff 100644 --- a/headers/posix/limits.h +++ b/headers/posix/limits.h @@ -71,6 +71,12 @@ #define _POSIX_STREAM_MAX (8) #define _POSIX_TTY_NAME_MAX (256) #define _POSIX_TZNAME_MAX (3) +#define _POSIX_SEM_VALUE_MAX INT_MAX +#define _POSIX_SIGQUEUE_MAX 32 +#define _POSIX_RTSIG_MAX 8 +#define _POSIX_CLOCKRES_MIN 20000000 +#define _POSIX_TIMER_MAX 32 +#define _POSIX_DELAYTIMER_MAX 32 #define _POSIX2_LINE_MAX (2048) diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h index 3004de2f34..cc5b900dfd 100644 --- a/headers/posix/pthread.h +++ b/headers/posix/pthread.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010 Haiku Inc. All Rights Reserved. + * Copyright 2001-2011 Haiku, Inc. All Rights Reserved. * Distributed under the terms of the Haiku License. */ #ifndef _PTHREAD_H_ @@ -8,66 +8,10 @@ #include #include +#include #include -typedef struct _pthread_thread *pthread_t; -typedef struct _pthread_attr *pthread_attr_t; -typedef struct _pthread_mutex pthread_mutex_t; -typedef struct _pthread_mutexattr *pthread_mutexattr_t; -typedef struct _pthread_cond pthread_cond_t; -typedef struct _pthread_condattr *pthread_condattr_t; -typedef int pthread_key_t; -typedef struct _pthread_once pthread_once_t; -typedef struct _pthread_rwlock pthread_rwlock_t; -typedef struct _pthread_rwlockattr *pthread_rwlockattr_t; -typedef struct _pthread_spinlock pthread_spinlock_t; -/* -typedef struct _pthread_barrier *pthread_barrier_t; -typedef struct _pthread_barrierattr *pthread_barrierattr_t; -*/ - -struct _pthread_mutex { - uint32_t flags; - int32_t lock; - int32_t unused; - int32_t owner; - int32_t owner_count; -}; - -struct _pthread_cond { - uint32_t flags; - int32_t unused; - pthread_mutex_t *mutex; - int32_t waiter_count; - int32_t lock; -}; - -struct _pthread_once { - int32_t state; -}; - -struct _pthread_rwlock { - uint32_t flags; - int32_t owner; - union { - struct { - int32_t sem; - } shared; - struct { - int32_t lock_sem; - int32_t lock_count; - int32_t reader_count; - int32_t writer_count; - void* waiters[2]; - } local; - }; -}; - -struct _pthread_spinlock { - int32_t lock; -}; - #define PTHREAD_MUTEX_DEFAULT 0 #define PTHREAD_MUTEX_NORMAL 1 #define PTHREAD_MUTEX_ERRORCHECK 2 @@ -278,7 +222,6 @@ extern int pthread_equal(pthread_t t1, pthread_t t2); extern void pthread_exit(void *value_ptr); extern int pthread_join(pthread_t thread, void **_value); extern pthread_t pthread_self(void); -extern int pthread_kill(pthread_t thread, int sig); extern int pthread_getconcurrency(void); extern int pthread_setconcurrency(int newLevel); diff --git a/headers/posix/setjmp.h b/headers/posix/setjmp.h index cbae76b37a..30b24ea0be 100644 --- a/headers/posix/setjmp.h +++ b/headers/posix/setjmp.h @@ -15,8 +15,7 @@ typedef struct __jmp_buf_tag { __jmp_buf regs; /* saved registers, stack & program pointer */ - int mask_was_saved; - sigset_t saved_mask; + sigset_t inverted_signal_mask; } jmp_buf[1]; typedef jmp_buf sigjmp_buf; diff --git a/headers/posix/signal.h b/headers/posix/signal.h index ab3cdb0cdc..c43aef770a 100644 --- a/headers/posix/signal.h +++ b/headers/posix/signal.h @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 Haiku Inc. All Rights Reserved. + * Copyright 2002-2011, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _SIGNAL_H_ @@ -10,48 +10,73 @@ typedef int sig_atomic_t; -typedef __haiku_int32 sigset_t; - -typedef void (*sighandler_t)(int); - /* GNU-like signal handler typedef */ - -typedef void (*__signal_func_ptr)(int); - /* deprecated, for compatibility with BeOS only */ +typedef __haiku_uint64 sigset_t; /* macros defining the standard signal handling behavior */ -#define SIG_DFL ((sighandler_t)0) /* "default" signal behaviour */ -#define SIG_IGN ((sighandler_t)1) /* ignore signal */ -#define SIG_ERR ((sighandler_t)-1) /* an error occurred during signal processing */ -#define SIG_HOLD ((sighandler_t)3) /* the signal was hold */ +#define SIG_DFL ((__sighandler_t)0) /* "default" signal behaviour */ +#define SIG_IGN ((__sighandler_t)1) /* ignore signal */ +#define SIG_ERR ((__sighandler_t)-1) /* an error occurred during signal + processing */ +#define SIG_HOLD ((__sighandler_t)3) /* the signal was hold */ -/* TODO: Support this structure, or more precisely the SA_SIGINFO flag. To do - * this properly we need real-time signal support. Both are commented out for - * the time being to not make "configure" scripts think we do support them. */ -#if 0 -typedef struct { - int si_signo; /* signal number */ - int si_code; /* signal code */ - int si_errno; /* if non zero, an error number associated with this signal */ - pid_t si_pid; /* sending process ID */ - uid_t si_uid; /* real user ID of sending process */ - void *si_addr; /* address of faulting instruction */ - int si_status; /* exit value or signal */ - long si_band; /* band event for SIGPOLL */ +/* macros specifying the event notification type (sigevent::sigev_notify) */ +#define SIGEV_NONE 0 /* no notification */ +#define SIGEV_SIGNAL 1 /* notify via queued signal */ +#define SIGEV_THREAD 2 /* notify via function called in new thread */ + + +union sigval { + int sival_int; + void* sival_ptr; +}; + +struct sigevent { + int sigev_notify; /* notification type */ + int sigev_signo; /* signal number */ + union sigval sigev_value; /* user-defined signal value */ + void (*sigev_notify_function)(union sigval); + /* notification function in case of + SIGEV_THREAD */ + pthread_attr_t* sigev_notify_attributes; + /* pthread creation attributes in case of + SIGEV_THREAD */ +}; + +typedef struct __siginfo_t { + int si_signo; /* signal number */ + int si_code; /* signal code */ + int si_errno; /* if non zero, an error number associated with + this signal */ + pid_t si_pid; /* sending process ID */ + uid_t si_uid; /* real user ID of sending process */ + void* si_addr; /* address of faulting instruction */ + int si_status; /* exit value or signal */ + long si_band; /* band event for SIGPOLL */ + union sigval si_value; /* signal value */ } siginfo_t; -#endif /* 0 */ -/* - * structure used by sigaction() - * - * Note: the 'sa_userdata' field is a non-POSIX extension. - * See the documentation for more info on this. - */ + +/* signal handler function types */ +typedef void (*__sighandler_t)(int); +typedef void (*__siginfo_handler_t)(int, siginfo_t*, void*); + +#ifdef __USE_GNU +typedef __sighandler_t sighandler_t; + /* GNU-like signal handler typedef */ +#endif + + +/* structure used by sigaction() */ struct sigaction { - sighandler_t sa_handler; - sigset_t sa_mask; - int sa_flags; - void *sa_userdata; /* will be passed to the signal handler */ + union { + __sighandler_t sa_handler; + __siginfo_handler_t sa_sigaction; + }; + sigset_t sa_mask; + int sa_flags; + void* sa_userdata; /* will be passed to the signal + handler, BeOS extension */ }; /* values for sa_flags */ @@ -61,7 +86,7 @@ struct sigaction { #define SA_NODEFER 0x08 #define SA_RESTART 0x10 #define SA_ONSTACK 0x20 -/* #define SA_SIGINFO 0x40 */ +#define SA_SIGINFO 0x40 #define SA_NOMASK SA_NODEFER #define SA_STACK SA_ONSTACK #define SA_ONESHOT SA_RESETHAND @@ -73,20 +98,13 @@ struct sigaction { #define MINSIGSTKSZ 4096 #define SIGSTKSZ 16384 -/* - * for signals using an alternate stack - */ +/* for signals using an alternate stack */ typedef struct stack_t { - void *ss_sp; + void* ss_sp; size_t ss_size; int ss_flags; } stack_t; -typedef struct sigstack { - int ss_onstack; - void *ss_sp; -} sigstack; - /* for the 'how' arg of sigprocmask() */ #define SIG_BLOCK 1 #define SIG_UNBLOCK 2 @@ -99,91 +117,166 @@ typedef struct sigstack { * some consistency with UN*X conventions so that things * like "kill -9" do what you expect. */ -#define SIGHUP 1 /* hangup -- tty is gone! */ -#define SIGINT 2 /* interrupt */ -#define SIGQUIT 3 /* `quit' special character typed in tty */ -#define SIGILL 4 /* illegal instruction */ -#define SIGCHLD 5 /* child process exited */ -#define SIGABRT 6 /* abort() called, dont' catch */ -#define SIGPIPE 7 /* write to a pipe w/no readers */ -#define SIGFPE 8 /* floating point exception */ -#define SIGKILL 9 /* kill a team (not catchable) */ -#define SIGSTOP 10 /* suspend a thread (not catchable) */ -#define SIGSEGV 11 /* segmentation violation (read: invalid pointer) */ -#define SIGCONT 12 /* continue execution if suspended */ -#define SIGTSTP 13 /* `stop' special character typed in tty */ -#define SIGALRM 14 /* an alarm has gone off (see alarm()) */ -#define SIGTERM 15 /* termination requested */ -#define SIGTTIN 16 /* read of tty from bg process */ -#define SIGTTOU 17 /* write to tty from bg process */ -#define SIGUSR1 18 /* app defined signal 1 */ -#define SIGUSR2 19 /* app defined signal 2 */ -#define SIGWINCH 20 /* tty window size changed */ -#define SIGKILLTHR 21 /* be specific: kill just the thread, not team */ -#define SIGTRAP 22 /* Trace/breakpoint trap */ -#define SIGPOLL 23 /* Pollable event */ -#define SIGPROF 24 /* Profiling timer expired */ -#define SIGSYS 25 /* Bad system call */ -#define SIGURG 26 /* High bandwidth data is available at socket */ -#define SIGVTALRM 27 /* Virtual timer expired */ -#define SIGXCPU 28 /* CPU time limit exceeded */ -#define SIGXFSZ 29 /* File size limit exceeded */ +#define SIGHUP 1 /* hangup -- tty is gone! */ +#define SIGINT 2 /* interrupt */ +#define SIGQUIT 3 /* `quit' special character typed in tty */ +#define SIGILL 4 /* illegal instruction */ +#define SIGCHLD 5 /* child process exited */ +#define SIGABRT 6 /* abort() called, dont' catch */ +#define SIGPIPE 7 /* write to a pipe w/no readers */ +#define SIGFPE 8 /* floating point exception */ +#define SIGKILL 9 /* kill a team (not catchable) */ +#define SIGSTOP 10 /* suspend a thread (not catchable) */ +#define SIGSEGV 11 /* segmentation violation (read: invalid pointer) */ +#define SIGCONT 12 /* continue execution if suspended */ +#define SIGTSTP 13 /* `stop' special character typed in tty */ +#define SIGALRM 14 /* an alarm has gone off (see alarm()) */ +#define SIGTERM 15 /* termination requested */ +#define SIGTTIN 16 /* read of tty from bg process */ +#define SIGTTOU 17 /* write to tty from bg process */ +#define SIGUSR1 18 /* app defined signal 1 */ +#define SIGUSR2 19 /* app defined signal 2 */ +#define SIGWINCH 20 /* tty window size changed */ +#define SIGKILLTHR 21 /* be specific: kill just the thread, not team */ +#define SIGTRAP 22 /* Trace/breakpoint trap */ +#define SIGPOLL 23 /* Pollable event */ +#define SIGPROF 24 /* Profiling timer expired */ +#define SIGSYS 25 /* Bad system call */ +#define SIGURG 26 /* High bandwidth data is available at socket */ +#define SIGVTALRM 27 /* Virtual timer expired */ +#define SIGXCPU 28 /* CPU time limit exceeded */ +#define SIGXFSZ 29 /* File size limit exceeded */ +#define SIGBUS 30 /* access to undefined portion of a memory object */ +#define SIGRESERVED1 31 /* reserved for future use */ +#define SIGRESERVED2 32 /* reserved for future use */ -#define SIGBUS SIGSEGV /* for old style code */ +#define SIGRTMIN (__signal_get_sigrtmin()) + /* lowest realtime signal number */ +#define SIGRTMAX (__signal_get_sigrtmax()) + /* greatest realtime signal number */ -/* - * Signal numbers 30-32 are currently free but may be used in future - * releases. Use them at your own peril (if you do use them, at least - * be smart and use them backwards from signal 32). - */ -#define MAX_SIGNO 32 /* the most signals that a single thread can reference */ -#define __signal_max 29 /* the largest signal number that is actually defined */ -#define NSIG (__signal_max+1) - /* the number of defined signals */ +#define __MAX_SIGNO 64 /* greatest possible signal number, can be used (+1) + as size of static arrays */ +#define NSIG (__MAX_SIGNO + 1) + /* BSD extension, size of the sys_siglist table, + obsolete */ + + +/* Signal code values appropriate for siginfo_t::si_code: */ +/* any signal */ +#define SI_USER 0 /* signal sent by user */ +#define SI_QUEUE 1 /* signal sent by sigqueue() */ +#define SI_TIMER 2 /* signal sent on timer_settime() timeout */ +#define SI_ASYNCIO 3 /* signal sent on asynchronous I/O completion */ +#define SI_MESGQ 4 /* signal sent on arrival of message on empty + message queue */ +/* SIGILL */ +#define ILL_ILLOPC 10 /* illegal opcode */ +#define ILL_ILLOPN 11 /* illegal operand */ +#define ILL_ILLADR 12 /* illegal addressing mode */ +#define ILL_ILLTRP 13 /* illegal trap */ +#define ILL_PRVOPC 14 /* privileged opcode */ +#define ILL_PRVREG 15 /* privileged register */ +#define ILL_COPROC 16 /* coprocessor error */ +#define ILL_BADSTK 17 /* internal stack error */ +/* SIGFPE */ +#define FPE_INTDIV 20 /* integer division by zero */ +#define FPE_INTOVF 21 /* integer overflow */ +#define FPE_FLTDIV 22 /* floating-point division by zero */ +#define FPE_FLTOVF 23 /* floating-point overflow */ +#define FPE_FLTUND 24 /* floating-point underflow */ +#define FPE_FLTRES 25 /* floating-point inexact result */ +#define FPE_FLTINV 26 /* invalid floating-point operation */ +#define FPE_FLTSUB 27 /* subscript out of range */ +/* SIGSEGV */ +#define SEGV_MAPERR 30 /* address not mapped to object */ +#define SEGV_ACCERR 31 /* invalid permissions for mapped object */ +/* SIGBUS */ +#define BUS_ADRALN 40 /* invalid address alignment */ +#define BUS_ADRERR 41 /* nonexistent physical address */ +#define BUS_OBJERR 42 /* object-specific hardware error */ +/* SIGTRAP */ +#define TRAP_BRKPT 50 /* process breakpoint */ +#define TRAP_TRACE 51 /* process trace trap. */ +/* SIGCHLD */ +#define CLD_EXITED 60 /* child exited */ +#define CLD_KILLED 61 /* child terminated abnormally without core dump */ +#define CLD_DUMPED 62 /* child terminated abnormally with core dump */ +#define CLD_TRAPPED 63 /* traced child trapped */ +#define CLD_STOPPED 64 /* child stopped */ +#define CLD_CONTINUED 65 /* stopped child continued */ +/* SIGPOLL */ +#define POLL_IN 70 /* input available */ +#define POLL_OUT 71 /* output available */ +#define POLL_MSG 72 /* input message available */ +#define POLL_ERR 73 /* I/O error */ +#define POLL_PRI 74 /* high priority input available */ +#define POLL_HUP 75 /* device disconnected */ /* the global table of text strings containing descriptions for each signal */ -extern const char * const sys_siglist[NSIG]; +extern const char* const sys_siglist[NSIG]; + /* BSD extension, obsolete, use strsignal() instead */ #ifdef __cplusplus extern "C" { #endif -sighandler_t signal(int sig, sighandler_t signalHandler); -sighandler_t sigset(int sig, sighandler_t signalHandler); -int raise(int sig); -int kill(pid_t pid, int sig); -int send_signal(pid_t tid, unsigned int sig); -int killpg(pid_t processGroupID, int sig); -int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); -int siginterrupt(int sig, int flag); -int sigprocmask(int how, const sigset_t *set, sigset_t *oset); -int sigpending(sigset_t *set); -int sigsuspend(const sigset_t *mask); -int sigwait(const sigset_t *set, int *sig); +/* signal management (actions and block masks) */ +__sighandler_t signal(int signal, __sighandler_t signalHandler); +int sigaction(int signal, const struct sigaction* action, + struct sigaction* oldAction); +__sighandler_t sigset(int signal, __sighandler_t signalHandler); +int sigignore(int signal); +int siginterrupt(int signal, int flag); -int sigemptyset(sigset_t *set); -int sigfillset(sigset_t *set); -int sigaddset(sigset_t *set, int signo); -int sigdelset(sigset_t *set, int signo); -int sigismember(const sigset_t *set, int signo); -int sigignore(int signo); -int sighold(int signo); -int sigrelse(int signo); -int sigpause(int signo); +int sigprocmask(int how, const sigset_t* set, sigset_t* oldSet); +int pthread_sigmask(int how, const sigset_t* set, sigset_t* oldSet); +int sighold(int signal); +int sigrelse(int signal); -void set_signal_stack(void *ptr, size_t size); -int sigaltstack(const stack_t *ss, stack_t *oss); +/* sending signals */ +int raise(int signal); +int kill(pid_t pid, int signal); +int killpg(pid_t processGroupID, int signal); +int sigqueue(pid_t pid, int signal, const union sigval userValue); +int pthread_kill(pthread_t thread, int signal); + +/* querying and waiting for signals */ +int sigpending(sigset_t* set); +int sigsuspend(const sigset_t* mask); +int sigpause(int signal); +int sigwait(const sigset_t* set, int* _signal); +int sigwaitinfo(const sigset_t* set, siginfo_t* info); +int sigtimedwait(const sigset_t* set, siginfo_t* info, + const struct timespec* timeout); + +/* setting the per-thread signal stack */ +int sigaltstack(const stack_t* stack, stack_t* oldStack); + +/* signal set (sigset_t) manipulation */ +int sigemptyset(sigset_t* set); +int sigfillset(sigset_t* set); +int sigaddset(sigset_t* set, int signal); +int sigdelset(sigset_t* set, int signal); +int sigismember(const sigset_t* set, int signal); + +/* printing signal names */ +void psiginfo(const siginfo_t* info, const char* message); +void psignal(int signal, const char* message); + +/* implementation private */ +int __signal_get_sigrtmin(); +int __signal_get_sigrtmax(); -/* pthread extension : equivalent of sigprocmask() */ -int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); #ifdef __cplusplus } #endif + /* TODO: move this into the documentation! * ================================================== * !!! SPECIAL NOTES CONCERNING NON-POSIX EXTENSIONS: @@ -205,7 +298,7 @@ int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); * handling. It also allows an opportunity, via the 'sigaction' struct, to * enable additional data to be passed to the handler. For example: * void - * my_signal_handler(int sig, char *userData, vregs regs) + * my_signal_handler(int sig, char* userData, vregs* regs) * { * . . . * } @@ -213,7 +306,7 @@ int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); * struct sigaction sa; * char data_buffer[32]; * - * sa.sa_handler = (sighandler_t)my_signal_handler; + * sa.sa_handler = (__sighandler_t)my_signal_handler; * sigemptyset(&sa.sa_mask); * sa.sa_userdata = userData; * @@ -223,8 +316,9 @@ int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); * The two additional arguments available to the signal handler are extensions * to the Posix standard. This feature was introduced by the BeOS and retained * by Haiku. However, to remain compatible with Posix and ANSI C, the type - * of the sa_handler field is defined as 'sighandler_t'. This requires the handler - * to be cast when assigned to the sa_handler field, as in the example above. + * of the sa_handler field is defined as '__sighandler_t'. This requires the + * handler to be cast when assigned to the sa_handler field, as in the example + * above. * * The 3 arguments that Haiku provides to signal handlers are as follows: * 1) The first argument is the (usual) signal number. @@ -234,10 +328,14 @@ int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); * * 3) The third argument is a pointer to a vregs struct (defined below). * The vregs struct contains the contents of the volatile registers at - * the time the signal was delivered to your thread. You can change the fields - * of the structure. After your signal handler completes, the OS uses this struct - * to reload the registers for your thread (privileged registers are not loaded - * of course). The vregs struct is of course terribly machine dependent. + * the time the signal was delivered to your thread. You can change the + * fields of the structure. After your signal handler completes, the OS uses + * this struct to reload the registers for your thread (privileged registers + * are not loaded of course). The vregs struct is of course terribly machine + * dependent. + * Note that in BeOS the vregs argument was passed by value, not by pointer. + * While Haiku retains binary compability with code compiled for BeOS, code + * built under Haiku must use the pointer argument. */ /* @@ -245,11 +343,22 @@ int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); * * signal handlers get this as the last argument */ - typedef struct vregs vregs; + /* BeOS extension */ + /* include architecture specific definitions */ #include __HAIKU_ARCH_HEADER(signal.h) +typedef struct vregs mcontext_t; + +typedef struct __ucontext_t { + struct __ucontext_t* uc_link; + sigset_t uc_sigmask; + stack_t uc_stack; + mcontext_t uc_mcontext; +} ucontext_t; + + #endif /* _SIGNAL_H_ */ diff --git a/headers/posix/sys/types.h b/headers/posix/sys/types.h index d5ad169443..13c78301d9 100644 --- a/headers/posix/sys/types.h +++ b/headers/posix/sys/types.h @@ -52,6 +52,70 @@ typedef char* caddr_t; typedef __haiku_addr_t addr_t; typedef __haiku_int32 key_t; +typedef __haiku_std_int32 clockid_t; +typedef struct __timer_t* timer_t; + + +/* pthread types */ + +typedef struct _pthread_thread *pthread_t; +typedef struct _pthread_attr *pthread_attr_t; +typedef struct _pthread_mutex pthread_mutex_t; +typedef struct _pthread_mutexattr *pthread_mutexattr_t; +typedef struct _pthread_cond pthread_cond_t; +typedef struct _pthread_condattr *pthread_condattr_t; +typedef int pthread_key_t; +typedef struct _pthread_once pthread_once_t; +typedef struct _pthread_rwlock pthread_rwlock_t; +typedef struct _pthread_rwlockattr *pthread_rwlockattr_t; +typedef struct _pthread_spinlock pthread_spinlock_t; +/* +typedef struct _pthread_barrier *pthread_barrier_t; +typedef struct _pthread_barrierattr *pthread_barrierattr_t; +*/ + +struct _pthread_mutex { + __haiku_std_uint32 flags; + __haiku_std_int32 lock; + __haiku_std_int32 unused; + __haiku_std_int32 owner; + __haiku_std_int32 owner_count; +}; + +struct _pthread_cond { + __haiku_std_uint32 flags; + __haiku_std_int32 unused; + pthread_mutex_t* mutex; + __haiku_std_int32 waiter_count; + __haiku_std_int32 lock; +}; + +struct _pthread_once { + __haiku_std_int32 state; +}; + +struct _pthread_rwlock { + __haiku_std_uint32 flags; + __haiku_std_int32 owner; + union { + struct { + __haiku_std_int32 sem; + } shared; + struct { + __haiku_std_int32 lock_sem; + __haiku_std_int32 lock_count; + __haiku_std_int32 reader_count; + __haiku_std_int32 writer_count; + void* waiters[2]; + } local; + }; +}; + +struct _pthread_spinlock { + __haiku_std_int32 lock; +}; + + #include #include #include diff --git a/headers/posix/sys/wait.h b/headers/posix/sys/wait.h index 7c9c09503c..6b3217309b 100644 --- a/headers/posix/sys/wait.h +++ b/headers/posix/sys/wait.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2010 Haiku Inc. All Rights Reserved. + * Copyright 2004-2011, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _SYS_WAIT_H @@ -28,16 +28,12 @@ #define WIFCORED(value) ((value) & 0x10000) #define WIFCONTINUED(value) ((value) & 0x20000) -/* TODO: waitid() is part of the real-time signal extension. Uncomment when - * implemented! */ -#if 0 /* ID types for waitid() */ typedef enum { P_ALL, /* wait for any children, ignore ID */ P_PID, /* wait for the child whose process ID matches */ P_PGID /* wait for any child whose process group ID matches */ } idtype_t; -#endif /* 0 */ #ifdef __cplusplus @@ -46,7 +42,7 @@ extern "C" { extern pid_t wait(int *_status); extern pid_t waitpid(pid_t pid, int *_status, int options); -/* extern int waitid(idtype_t idType, id_t id, siginfo_t *info, int options); */ +extern int waitid(idtype_t idType, id_t id, siginfo_t *info, int options); #ifdef __cplusplus } diff --git a/headers/posix/time.h b/headers/posix/time.h index af1ca5757e..856c6d5c8b 100644 --- a/headers/posix/time.h +++ b/headers/posix/time.h @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 Haiku Inc. All Rights Reserved. + * Copyright 2005-2011, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _TIME_H_ @@ -9,17 +9,33 @@ #include +struct sigevent; /* defined in */ + + typedef __haiku_int32 clock_t; typedef __haiku_int32 time_t; typedef __haiku_int32 suseconds_t; typedef __haiku_uint32 useconds_t; -#define CLOCKS_PER_SEC 1000 + +#define CLOCKS_PER_SEC 1000000 #define CLK_TCK CLOCKS_PER_SEC #define MAX_TIMESTR 70 /* maximum length of a string returned by asctime(), and ctime() */ +#define CLOCK_MONOTONIC ((clockid_t)0) + /* system-wide monotonic clock (aka system time) */ +#define CLOCK_REALTIME ((clockid_t)-1) + /* system-wide real time clock */ +#define CLOCK_PROCESS_CPUTIME_ID ((clockid_t)-2) + /* clock measuring the used CPU time of the current process */ +#define CLOCK_THREAD_CPUTIME_ID ((clockid_t)-3) + /* clock measuring the used CPU time of the current thread */ + +#define TIMER_ABSTIME 1 /* absolute timer flag */ + + struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* and nanoseconds */ @@ -72,6 +88,23 @@ extern size_t strftime(char *buffer, size_t maxSize, const char *format, const struct tm *tm); extern char *strptime(const char *buf, const char *format, struct tm *tm); +/* clock functions */ +int clock_getres(clockid_t clockID, struct timespec* resolution); +int clock_gettime(clockid_t clockID, struct timespec* time); +int clock_settime(clockid_t clockID, const struct timespec* time); +int clock_nanosleep(clockid_t clockID, int flags, + const struct timespec* time, struct timespec* remainingTime); +int clock_getcpuclockid(pid_t pid, clockid_t* _clockID); + +/* timer functions */ +int timer_create(clockid_t clockID, struct sigevent* event, + timer_t* timerID); +int timer_delete(timer_t timerID); +int timer_gettime(timer_t timerID, struct itimerspec* value); +int timer_settime(timer_t timerID, int flags, + const struct itimerspec* value, struct itimerspec* oldValue); +int timer_getoverrun(timer_t timerID); + /* special timezone support */ extern void tzset(void); diff --git a/headers/posix/unistd.h b/headers/posix/unistd.h index e9de223637..8380ae65a6 100644 --- a/headers/posix/unistd.h +++ b/headers/posix/unistd.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2010 Haiku Inc. All Rights Reserved. + * Copyright 2004-2011 Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _UNISTD_H_ @@ -45,9 +45,13 @@ #define _POSIX_THREAD_ATTR_STACKADDR (-1) /* currently unsupported */ #define _POSIX_THREAD_ATTR_STACKSIZE (200809L) #define _POSIX_THREAD_PRIORITY_SCHEDULING (-1) /* currently unsupported */ -#define _POSIX_REALTIME_SIGNALS (-1) /* currently unsupported */ +#define _POSIX_REALTIME_SIGNALS (200809L) #define _POSIX_MEMORY_PROTECTION (200809L) -#define _POSIX_SEM_VALUE_MAX INT_MAX +#define _POSIX_MONOTONIC_CLOCK (200809L) +#define _POSIX_TIMERS (200809L) +#define _POSIX_CPUTIME (200809L) +#define _POSIX_THREAD_CPUTIME (200809L) + /* pathconf() constants */ /* BeOS supported values, do not touch */ @@ -119,6 +123,14 @@ #define _SC_THREAD_PRIORITY_SCHEDULING 50 #define _SC_REALTIME_SIGNALS 51 #define _SC_MEMORY_PROTECTION 52 +#define _SC_SIGQUEUE_MAX 53 +#define _SC_RTSIG_MAX 54 +#define _SC_MONOTONIC_CLOCK 55 +#define _SC_DELAYTIMER_MAX 56 +#define _SC_TIMER_MAX 57 +#define _SC_TIMERS 58 +#define _SC_CPUTIME 59 +#define _SC_THREAD_CPUTIME 60 /* confstr() constants */ diff --git a/headers/private/kernel/DPC.h b/headers/private/kernel/DPC.h new file mode 100644 index 0000000000..9267910822 --- /dev/null +++ b/headers/private/kernel/DPC.h @@ -0,0 +1,114 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DPC_H +#define _KERNEL_DPC_H + + +#include + +#include + +#include + +#include + + +namespace BKernel { + + +class DPCQueue; + + +class DPCCallback : public DoublyLinkedListLinkImpl { +public: + DPCCallback(); + virtual ~DPCCallback(); + + virtual void DoDPC(DPCQueue* queue) = 0; + +private: + friend class DPCQueue; + +private: + DPCQueue* fInQueue; +}; + + +class FunctionDPCCallback : public DPCCallback { +public: + FunctionDPCCallback(DPCQueue* owner); + + void SetTo(void (*function)(void*), void* argument); + + virtual void DoDPC(DPCQueue* queue); + +private: + DPCQueue* fOwner; + void (*fFunction)(void*); + void* fArgument; +}; + + +class DPCQueue { +public: + DPCQueue(); + ~DPCQueue(); + + static DPCQueue* DefaultQueue(int priority); + + status_t Init(const char* name, int32 priority, + uint32 reservedSlots); + void Close(bool cancelPending); + + status_t Add(DPCCallback* callback, + bool schedulerLocked); + status_t Add(void (*function)(void*), void* argument, + bool schedulerLocked); + bool Cancel(DPCCallback* callback); + + thread_id Thread() const + { return fThreadID; } + +public: + // conceptually package private + void Recycle(FunctionDPCCallback* callback); + +private: + typedef DoublyLinkedList CallbackList; + +private: + static status_t _ThreadEntry(void* data); + status_t _Thread(); + + bool _IsClosed() const + { return fThreadID < 0; } + +private: + spinlock fLock; + thread_id fThreadID; + CallbackList fCallbacks; + CallbackList fUnusedFunctionCallbacks; + ConditionVariable fPendingCallbacksCondition; + DPCCallback* fCallbackInProgress; + ConditionVariable* fCallbackDoneCondition; +}; + + +} // namespace BKernel + + +using BKernel::DPCCallback; +using BKernel::DPCQueue; +using BKernel::FunctionDPCCallback; + + +__BEGIN_DECLS + +void dpc_init(); + +__END_DECLS + + +#endif // _KERNEL_DPC_H diff --git a/headers/private/kernel/UserEvent.h b/headers/private/kernel/UserEvent.h new file mode 100644 index 0000000000..1b8ceb70b2 --- /dev/null +++ b/headers/private/kernel/UserEvent.h @@ -0,0 +1,109 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_USER_EVENT_H +#define _KERNEL_USER_EVENT_H + + +#include + +#include + +#include +#include + + +namespace BKernel { + + +struct Team; +struct Thread; + + +struct UserEvent { + virtual ~UserEvent(); + + virtual status_t Fire() = 0; +}; + + +struct SignalEvent : UserEvent { + virtual ~SignalEvent(); + + void SetUserValue(union sigval userValue); + +protected: + struct EventSignal; + +protected: + SignalEvent(EventSignal* signal); + +protected: + EventSignal* fSignal; +}; + + +struct TeamSignalEvent : SignalEvent { + static TeamSignalEvent* Create(Team* team, uint32 signalNumber, + int32 signalCode, int32 errorCode); + + virtual status_t Fire(); + +private: + TeamSignalEvent(Team* team, + EventSignal* signal); + +private: + Team* fTeam; +}; + + +struct ThreadSignalEvent : SignalEvent { + static ThreadSignalEvent* Create(Thread* thread, uint32 signalNumber, + int32 signalCode, int32 errorCode, + pid_t sendingTeam); + + virtual status_t Fire(); + +private: + ThreadSignalEvent(Thread* thread, + EventSignal* signal); + +private: + Thread* fThread; +}; + + +struct CreateThreadEvent : UserEvent, private DPCCallback { + ~CreateThreadEvent(); + + static CreateThreadEvent* Create( + const ThreadCreationAttributes& attributes); + + virtual status_t Fire(); + +private: + CreateThreadEvent( + const ThreadCreationAttributes& attributes); + + virtual void DoDPC(DPCQueue* queue); + +private: + ThreadCreationAttributes fCreationAttributes; + char fThreadName[B_OS_NAME_LENGTH]; + bool fPendingDPC; +}; + + +} // namespace BKernel + + +using BKernel::CreateThreadEvent; +using BKernel::SignalEvent; +using BKernel::TeamSignalEvent; +using BKernel::ThreadSignalEvent; +using BKernel::UserEvent; + + +#endif // _KERNEL_USER_EVENT_H diff --git a/headers/private/kernel/UserTimer.h b/headers/private/kernel/UserTimer.h new file mode 100644 index 0000000000..2f439c1b88 --- /dev/null +++ b/headers/private/kernel/UserTimer.h @@ -0,0 +1,273 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_USER_TIMER_H +#define _KERNEL_USER_TIMER_H + + +#include +#include + +#include + +#include +#include +#include + + +struct thread_creation_attributes; + + +namespace BKernel { + + +struct UserEvent; +struct Team; + + +struct UserTimer : DoublyLinkedListLinkImpl { + UserTimer(); + virtual ~UserTimer(); + + int32 ID() const + { return fID; } + void SetID(int32 id) + { fID = id; } + + void SetEvent(UserEvent* event) + { fEvent = event; } + + virtual void Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, + bigtime_t& _oldInterval) = 0; + void Cancel(); + + virtual void GetInfo(bigtime_t& _remainingTime, + bigtime_t& _interval, + uint32& _overrunCount) = 0; + +protected: + static int32 HandleTimerHook(struct timer* timer); + virtual void HandleTimer(); + + inline void UpdatePeriodicStartTime(); + inline void CheckPeriodicOverrun(bigtime_t now); + +protected: + int32 fID; + timer fTimer; + UserEvent* fEvent; + bigtime_t fNextTime; + bigtime_t fInterval; + uint32 fOverrunCount; + bool fScheduled; // fTimer scheduled +}; + + +struct SystemTimeUserTimer : public UserTimer { + virtual void Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, + bigtime_t& _oldInterval); + virtual void GetInfo(bigtime_t& _remainingTime, + bigtime_t& _interval, + uint32& _overrunCount); + +protected: + virtual void HandleTimer(); + + void ScheduleKernelTimer(bigtime_t now, + bool checkPeriodicOverrun); +}; + + +struct RealTimeUserTimer : public SystemTimeUserTimer { + virtual void Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, + bigtime_t& _oldInterval); + + void TimeWarped(); + +private: + bigtime_t fRealTimeOffset; + bool fAbsolute; + +protected: + virtual void HandleTimer(); + +public: + // conceptually package private + DoublyLinkedListLink fGlobalListLink; +}; + + +struct TeamTimeUserTimer : public UserTimer { + TeamTimeUserTimer(team_id teamID); + ~TeamTimeUserTimer(); + + virtual void Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, + bigtime_t& _oldInterval); + virtual void GetInfo(bigtime_t& _remainingTime, + bigtime_t& _interval, + uint32& _overrunCount); + + void Deactivate(); + + void Update(Thread* unscheduledThread); + void TimeWarped(bigtime_t changedBy); + +protected: + virtual void HandleTimer(); + +private: + void _Update(bool unscheduling); + +private: + team_id fTeamID; + Team* fTeam; + int32 fRunningThreads; + bool fAbsolute; + +public: + // conceptually package private + DoublyLinkedListLink fCPUTimeListLink; +}; + + +struct TeamUserTimeUserTimer : public UserTimer { + TeamUserTimeUserTimer(team_id teamID); + ~TeamUserTimeUserTimer(); + + virtual void Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, + bigtime_t& _oldInterval); + virtual void GetInfo(bigtime_t& _remainingTime, + bigtime_t& _interval, + uint32& _overrunCount); + + void Deactivate(); + void Check(); + +private: + team_id fTeamID; + Team* fTeam; + +public: + // conceptually package private + DoublyLinkedListLink fCPUTimeListLink; +}; + + +struct ThreadTimeUserTimer : public UserTimer { + ThreadTimeUserTimer(thread_id threadID); + ~ThreadTimeUserTimer(); + + virtual void Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, + bigtime_t& _oldInterval); + virtual void GetInfo(bigtime_t& _remainingTime, + bigtime_t& _interval, + uint32& _overrunCount); + + void Deactivate(); + + void Start(); + void Stop(); + void TimeWarped(bigtime_t changedBy); + +protected: + virtual void HandleTimer(); + +private: + thread_id fThreadID; + Thread* fThread; // != NULL only when active + bool fAbsolute; + +public: + // conceptually package private + DoublyLinkedListLink fCPUTimeListLink; +}; + + +struct UserTimerList { + UserTimerList(); + ~UserTimerList(); + + UserTimer* TimerFor(int32 id) const; + void AddTimer(UserTimer* timer); + void RemoveTimer(UserTimer* timer) + { fTimers.Remove(timer); } + int32 DeleteTimers(bool userDefinedOnly); + +private: + typedef DoublyLinkedList TimerList; + +private: + TimerList fTimers; +}; + + +typedef DoublyLinkedList > RealTimeUserTimerList; + +typedef DoublyLinkedList > TeamTimeUserTimerList; + +typedef DoublyLinkedList > TeamUserTimeUserTimerList; + +typedef DoublyLinkedList > ThreadTimeUserTimerList; + + +} // namespace BKernel + + +using BKernel::RealTimeUserTimer; +using BKernel::RealTimeUserTimerList; +using BKernel::SystemTimeUserTimer; +using BKernel::TeamUserTimeUserTimer; +using BKernel::TeamUserTimeUserTimerList; +using BKernel::TeamTimeUserTimer; +using BKernel::TeamTimeUserTimerList; +using BKernel::ThreadTimeUserTimer; +using BKernel::ThreadTimeUserTimerList; +using BKernel::UserTimer; +using BKernel::UserTimerList; + + +__BEGIN_DECLS + +status_t user_timer_create_thread_timers(Team* team, Thread* thread); +status_t user_timer_create_team_timers(Team* team); + +status_t user_timer_get_clock(clockid_t clockID, bigtime_t& _time); +void user_timer_real_time_clock_changed(); + +void user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread); +void user_timer_continue_cpu_timers(Thread* thread, + Thread* previousThread); +void user_timer_check_team_user_timers(Team* team); + +status_t _user_get_clock(clockid_t clockID, bigtime_t* _time); +status_t _user_set_clock(clockid_t clockID, bigtime_t time); + +int32 _user_create_timer(clockid_t clockID, thread_id threadID, + uint32 flags, const struct sigevent* event, + const thread_creation_attributes* threadAttributes); +status_t _user_delete_timer(int32 timerID, thread_id threadID); +status_t _user_get_timer(int32 timerID, thread_id threadID, + struct user_timer_info* info); +status_t _user_set_timer(int32 timerID, thread_id threadID, + bigtime_t startTime, bigtime_t interval, uint32 flags, + struct user_timer_info* oldInfo); + +__END_DECLS + + +#endif // _KERNEL_USER_TIMER_H diff --git a/headers/private/kernel/arch/thread.h b/headers/private/kernel/arch/thread.h index 6efad95dec..b19066a520 100644 --- a/headers/private/kernel/arch/thread.h +++ b/headers/private/kernel/arch/thread.h @@ -21,16 +21,16 @@ status_t arch_team_init_team_struct(Team *t, bool kernel); status_t arch_thread_init_thread_struct(Thread *t); status_t arch_thread_init_tls(Thread *thread); void arch_thread_context_switch(Thread *t_from, Thread *t_to); -status_t arch_thread_init_kthread_stack(Thread *t, - int (*start_func)(void), void (*entry_func)(void), void (*exit_func)(void)); +void arch_thread_init_kthread_stack(Thread *thread, void *stack, + void *stackTop, void (*function)(void*), const void *data); void arch_thread_dump_info(void *info); status_t arch_thread_enter_userspace(Thread *t, addr_t entry, void *args1, void *args2); bool arch_on_signal_stack(Thread *thread); -status_t arch_setup_signal_frame(Thread *t, struct sigaction *sa, - int signal, int signalMask); -int64 arch_restore_signal_frame(void); +status_t arch_setup_signal_frame(Thread *thread, struct sigaction *action, + struct signal_frame_data *signalFrameData); +int64 arch_restore_signal_frame(struct signal_frame_data* signalFrameData); void arch_store_fork_frame(struct arch_fork_arg *arg); void arch_restore_fork_frame(struct arch_fork_arg *arg); diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index 4f84cc7a35..53a86ab586 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -95,6 +95,32 @@ #define IA32_MTR_WRITE_BACK 6 +// EFLAGS register +#define X86_EFLAGS_CARRY 0x00000001 +#define X86_EFLAGS_RESERVED1 0x00000002 +#define X86_EFLAGS_PARITY 0x00000004 +#define X86_EFLAGS_AUXILIARY_CARRY 0x00000010 +#define X86_EFLAGS_ZERO 0x00000040 +#define X86_EFLAGS_SIGN 0x00000080 +#define X86_EFLAGS_TRAP 0x00000100 +#define X86_EFLAGS_INTERRUPT 0x00000200 +#define X86_EFLAGS_DIRECTION 0x00000400 +#define X86_EFLAGS_OVERFLOW 0x00000800 +#define X86_EFLAGS_IO_PRIVILEG_LEVEL 0x00003000 +#define X86_EFLAGS_IO_PRIVILEG_LEVEL_SHIFT 12 +#define X86_EFLAGS_NESTED_TASK 0x00004000 +#define X86_EFLAGS_RESUME 0x00010000 +#define X86_EFLAGS_V86_MODE 0x00020000 +#define X86_EFLAGS_ALIGNMENT_CHECK 0x00040000 +#define X86_EFLAGS_VIRTUAL_INTERRUPT 0x00080000 +#define X86_EFLAGS_VIRTUAL_INTERRUPT_PENDING 0x00100000 +#define X86_EFLAGS_ID 0x00200000 + +#define X86_EFLAGS_USER_FLAGS (X86_EFLAGS_CARRY | X86_EFLAGS_PARITY \ + | X86_EFLAGS_AUXILIARY_CARRY | X86_EFLAGS_ZERO | X86_EFLAGS_SIGN \ + | X86_EFLAGS_DIRECTION | X86_EFLAGS_OVERFLOW) + + // iframe types #define IFRAME_TYPE_SYSCALL 0x1 #define IFRAME_TYPE_OTHER 0x2 @@ -276,7 +302,6 @@ void x86_context_switch(struct arch_thread* oldState, struct arch_thread* newState); void x86_userspace_thread_exit(void); void x86_end_userspace_thread_exit(void); -void x86_enter_userspace(addr_t entry, addr_t stackTop); void x86_swap_pgdir(uint32 newPageDir); void i386_set_tss_and_kstack(addr_t kstack); void i386_fnsave(void* fpuState); diff --git a/headers/private/kernel/arch/x86/arch_thread.h b/headers/private/kernel/arch/x86/arch_thread.h index 648006b454..ff1f42a3cc 100644 --- a/headers/private/kernel/arch/x86/arch_thread.h +++ b/headers/private/kernel/arch/x86/arch_thread.h @@ -24,9 +24,6 @@ uint32 x86_next_page_directory(Thread *from, Thread *to); void x86_restart_syscall(struct iframe* frame); -void i386_return_from_signal(); -void i386_end_return_from_signal(); - // override empty macro #undef arch_syscall_64_bit_return_value void arch_syscall_64_bit_return_value(void); diff --git a/headers/private/kernel/condition_variable.h b/headers/private/kernel/condition_variable.h index f6dd46984c..9f4c0366b6 100644 --- a/headers/private/kernel/condition_variable.h +++ b/headers/private/kernel/condition_variable.h @@ -56,18 +56,18 @@ public: void Publish(const void* object, const char* objectType); - void Unpublish(bool threadsLocked = false); + void Unpublish(bool schedulerLocked = false); - inline void NotifyOne(bool threadsLocked = false, + inline void NotifyOne(bool schedulerLocked = false, status_t result = B_OK); - inline void NotifyAll(bool threadsLocked = false, + inline void NotifyAll(bool schedulerLocked = false, status_t result = B_OK); static void NotifyOne(const void* object, - bool threadsLocked = false, + bool schedulerLocked = false, status_t result = B_OK); static void NotifyAll(const void* object, - bool threadsLocked = false, + bool schedulerLocked = false, status_t result = B_OK); // (both methods) caller must ensure that // the variable is not unpublished @@ -86,7 +86,7 @@ public: void Dump() const; private: - void _Notify(bool all, bool threadsLocked, + void _Notify(bool all, bool schedulerLocked, status_t result); void _NotifyLocked(bool all, status_t result); @@ -124,16 +124,16 @@ ConditionVariableEntry::~ConditionVariableEntry() inline void -ConditionVariable::NotifyOne(bool threadsLocked, status_t result) +ConditionVariable::NotifyOne(bool schedulerLocked, status_t result) { - _Notify(false, threadsLocked, result); + _Notify(false, schedulerLocked, result); } inline void -ConditionVariable::NotifyAll(bool threadsLocked, status_t result) +ConditionVariable::NotifyAll(bool schedulerLocked, status_t result) { - _Notify(true, threadsLocked, result); + _Notify(true, schedulerLocked, result); } diff --git a/headers/private/kernel/cpu.h b/headers/private/kernel/cpu.h index 89cd96012e..850e666913 100644 --- a/headers/private/kernel/cpu.h +++ b/headers/private/kernel/cpu.h @@ -51,6 +51,7 @@ typedef struct cpu_ent { jmp_buf fault_jump_buffer; Thread* running_thread; + Thread* previous_thread; bool invoke_scheduler; bool invoke_scheduler_if_idle; bool disabled; diff --git a/headers/private/kernel/elf.h b/headers/private/kernel/elf.h index bb8e9e7cb9..292cb9f818 100644 --- a/headers/private/kernel/elf.h +++ b/headers/private/kernel/elf.h @@ -16,6 +16,12 @@ struct kernel_args; +struct elf_symbol_info { + addr_t address; + size_t size; +}; + + #ifdef __cplusplus extern "C" { #endif @@ -34,6 +40,7 @@ status_t elf_debug_lookup_user_symbol_address(Team* team, addr_t address, addr_t *_baseAddress, const char **_symbolName, const char **_imageName, bool *_exactMatch); addr_t elf_debug_lookup_symbol(const char* searchName); +status_t elf_lookup_kernel_symbol(const char* name, elf_symbol_info* info); struct elf_image_info* elf_get_kernel_image(); status_t elf_get_image_info_for_address(addr_t address, image_info* info); image_id elf_create_memory_image(const char* imageName, addr_t text, diff --git a/headers/private/kernel/kscheduler.h b/headers/private/kernel/kscheduler.h index 9e19b28fda..f106ac86c1 100644 --- a/headers/private/kernel/kscheduler.h +++ b/headers/private/kernel/kscheduler.h @@ -18,27 +18,59 @@ struct SchedulerListener; struct scheduler_ops { + /*! Enqueues the thread in the ready-to-run queue. + The caller must hold the scheduler lock (with disabled interrupts). + */ void (*enqueue_in_run_queue)(Thread* thread); + + /*! Selects a thread from the ready-to-run queue and, if that's not the + calling thread, switches the current CPU's context to run the selected + thread. + If it's the same thread, the thread will just continue to run. + In either case, unless the thread is dead or is sleeping/waiting + indefinitely, the function will eventually return. + The caller must hold the scheduler lock (with disabled interrupts). + */ void (*reschedule)(void); + + /*! Sets the given thread's priority. + The thread may be running or may be in the ready-to-run queue. + The caller must hold the scheduler lock (with disabled interrupts). + */ void (*set_thread_priority)(Thread* thread, int32 priority); bigtime_t (*estimate_max_scheduling_latency)(Thread* thread); - void (*on_thread_create)(Thread* thread); - // called when the thread structure is first created - - // initialization of per-thread housekeeping data structures should - // be done here - void (*on_thread_init)(Thread* thread); - // called when a thread structure is initialized and made ready for - // use - should be used to reset the housekeeping data structures - // if needed - void (*on_thread_destroy)(Thread* thread); - // called when a thread structure is freed - freeing up any allocated - // mem on the scheduler's part should be done here + /*! Called when the Thread structure is first created. + Per-thread housekeeping resources can be allocated. + Interrupts must be enabled. + */ + status_t (*on_thread_create)(Thread* thread, bool idleThread); + /*! Called when a Thread structure is initialized and made ready for + use. + The per-thread housekeeping data structures are reset, if needed. + The caller must hold the scheduler lock (with disabled interrupts). + */ + void (*on_thread_init)(Thread* thread); + + /*! Called when a Thread structure is freed. + Frees up any per-thread resources allocated on the scheduler's part. The + function may be called even if on_thread_create() failed. + Interrupts must be enabled. + */ + void (*on_thread_destroy)(Thread* thread); + + /*! Called in the early boot process to start thread scheduling on the + current CPU. + The function is called once for each CPU. + Interrupts must be disabled, but the caller must not hold the scheduler + lock. + */ void (*start)(void); }; extern struct scheduler_ops* gScheduler; +extern spinlock gSchedulerLock; #define scheduler_enqueue_in_run_queue(thread) \ gScheduler->enqueue_in_run_queue(thread) @@ -46,8 +78,8 @@ extern struct scheduler_ops* gScheduler; gScheduler->set_thread_priority(thread, priority) #define scheduler_reschedule() gScheduler->reschedule() #define scheduler_start() gScheduler->start() -#define scheduler_on_thread_create(thread) \ - gScheduler->on_thread_create(thread) +#define scheduler_on_thread_create(thread, idleThread) \ + gScheduler->on_thread_create(thread, idleThread) #define scheduler_on_thread_init(thread) \ gScheduler->on_thread_init(thread) #define scheduler_on_thread_destroy(thread) \ @@ -73,7 +105,7 @@ status_t _user_analyze_scheduling(bigtime_t from, bigtime_t until, void* buffer, /*! Reschedules, if necessary. - The thread spinlock must be held. + The caller must hold the scheduler lock (with disabled interrupts). */ static inline void scheduler_reschedule_if_necessary_locked() @@ -91,9 +123,11 @@ scheduler_reschedule_if_necessary() { if (are_interrupts_enabled()) { cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + acquire_spinlock(&gSchedulerLock); + scheduler_reschedule_if_necessary_locked(); - RELEASE_THREAD_LOCK(); + + release_spinlock(&gSchedulerLock); restore_interrupts(state); } } diff --git a/headers/private/kernel/ksignal.h b/headers/private/kernel/ksignal.h index 088f3bb70d..b7ec3c3bab 100644 --- a/headers/private/kernel/ksignal.h +++ b/headers/private/kernel/ksignal.h @@ -1,57 +1,240 @@ /* - * Copyright 2003-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2003-2008, Axel Dörfler, axeld@pinc-software.de. + * All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _KERNEL_SIGNAL_H #define _KERNEL_SIGNAL_H -#include #include +#include + +#include + +#include +#include +#include + namespace BKernel { + struct ProcessGroup; + struct Team; struct Thread; } +using BKernel::ProcessGroup; +using BKernel::Team; using BKernel::Thread; -#define KILL_SIGNALS ((1L << (SIGKILL - 1)) | (1L << (SIGKILLTHR - 1))) +#define KILL_SIGNALS \ + (((sigset_t)1 << (SIGKILL - 1)) | ((sigset_t)1 << (SIGKILLTHR - 1))) -#define SIGNAL_TO_MASK(signal) (1LL << (signal - 1)) +#define SYSCALL_RESTART_PARAMETER_SIZE 32 -// additional send_signal_etc() flag -#define SIGNAL_FLAG_TEAMS_LOCKED (0x10000) - // interrupts are disabled and team lock is held -#define SIGNAL_FLAG_DONT_RESTART_SYSCALL (0x20000) +// kernel-internal signals +#define SIGNAL_CANCEL_THREAD 63 + // Cancel a thread. Non-blockable. +#define SIGNAL_CONTINUE_THREAD 64 + // Continue a thread. Used by resume_thread(). Non-blockable, prevents + // syscall restart. + + +struct signal_frame_data { + siginfo_t info; + ucontext_t context; + void* user_data; + void* handler; + bool siginfo_handler; + int32 thread_flags; + uint64 syscall_restart_return_value; + uint8 syscall_restart_parameters[SYSCALL_RESTART_PARAMETER_SIZE]; +}; + + +namespace BKernel { + + +struct QueuedSignalsCounter : BReferenceable { + QueuedSignalsCounter(int32 limit); + + bool Increment(); + void Decrement() { ReleaseReference(); } + +private: + int32 fLimit; +}; + + +struct Signal : KernelReferenceable, DoublyLinkedListLinkImpl { +public: + Signal(); + // cheap no-init constructor + Signal(const Signal& other); + Signal(uint32 number, int32 signalCode, + int32 errorCode, pid_t sendingProcess); + virtual ~Signal(); + + static status_t CreateQueuable(const Signal& signal, + bool queuingRequired, + Signal*& _signalToQueue); + + void SetTo(uint32 number); + + uint32 Number() const { return fNumber; } + void SetNumber(uint32 number) + { fNumber = number; } + + int32 Priority() const; + + int32 SignalCode() const + { return fSignalCode; } + int32 ErrorCode() const + { return fErrorCode; } + pid_t SendingProcess() const + { return fSendingProcess; } + + uid_t SendingUser() const + { return fSendingUser; } + void SetSendingUser(uid_t user) + { fSendingUser = user; } + + int32 Status() const + { return fStatus; } + void SetStatus(int32 status) + { fStatus = status; } + + int32 PollBand() const + { return fPollBand; } + void SetPollBand(int32 pollBand) + { fPollBand = pollBand; } + + void* Address() const + { return fAddress; } + void SetAddress(void* address) + { fAddress = address; } + + union sigval UserValue() const + { return fUserValue; } + void SetUserValue(union sigval userValue) + { fUserValue = userValue; } + + bool IsPending() const + { return fPending; } + void SetPending(bool pending) + { fPending = pending; } + + virtual void Handled(); + +protected: + virtual void LastReferenceReleased(); + +private: + QueuedSignalsCounter* fCounter; + uint32 fNumber; + int32 fSignalCode; + int32 fErrorCode; // error code associated with the + // signal + pid_t fSendingProcess; + uid_t fSendingUser; + int32 fStatus; // exit value + int32 fPollBand; // for SIGPOLL + void* fAddress; + union sigval fUserValue; + bool fPending; +}; + + +struct PendingSignals { + PendingSignals(); + ~PendingSignals(); + + sigset_t AllSignals() const + { return fQueuedSignalsMask + | fUnqueuedSignalsMask; } + + int32 HighestSignalPriority(sigset_t nonBlocked) + const; + + void Clear(); + void AddSignal(int32 signal) + { fUnqueuedSignalsMask + |= SIGNAL_TO_MASK(signal); } + void AddSignal(Signal* signal); + void RemoveSignal(int32 signal) + { RemoveSignals(SIGNAL_TO_MASK(signal)); } + void RemoveSignal(Signal* signal); + void RemoveSignals(sigset_t mask); + + Signal* DequeueSignal(sigset_t nonBlocked, + Signal& buffer); + +private: + typedef DoublyLinkedList SignalList; + +private: + int32 _GetHighestPrioritySignal(sigset_t nonBlocked, + Signal*& _queuedSignal, + int32& _unqueuedSignal) const; + void _UpdateQueuedSignalMask(); + +private: + sigset_t fQueuedSignalsMask; + sigset_t fUnqueuedSignalsMask; + SignalList fQueuedSignals; +}; + + +} // namespace BKernel + + +using BKernel::PendingSignals; +using BKernel::QueuedSignalsCounter; +using BKernel::Signal; #ifdef __cplusplus extern "C" { #endif -extern bool handle_signals(Thread *thread); -extern bool is_kill_signal_pending(void); -extern int has_signals_pending(void *_thread); -extern bool is_signal_blocked(int signal); +void handle_signals(Thread* thread); +bool is_team_signal_blocked(Team* team, int signal); +void signal_get_user_stack(addr_t address, stack_t* stack); -extern void update_current_thread_signals_flag(); +status_t send_signal_to_thread_locked(Thread* thread, uint32 signalNumber, + Signal* signal, uint32 flags); +status_t send_signal_to_thread(Thread* thread, const Signal& signal, + uint32 flags); +status_t send_signal_to_thread_id(thread_id threadID, const Signal& signal, + uint32 flags); -extern int sigaction_etc(thread_id threadID, int signal, - const struct sigaction *newAction, struct sigaction *oldAction); +status_t send_signal_to_team_locked(Team* team, uint32 signalNumber, + Signal* signal, uint32 flags); +status_t send_signal_to_team(Team* team, const Signal& signal, uint32 flags); +status_t send_signal_to_team_id(team_id teamID, const Signal& signal, + uint32 flags); -extern status_t _user_send_signal(pid_t tid, uint sig); -extern status_t _user_sigprocmask(int how, const sigset_t *set, - sigset_t *oldSet); -extern status_t _user_sigaction(int sig, const struct sigaction *newAction, +status_t send_signal_to_process_group_locked(ProcessGroup* group, + const Signal& signal, uint32 flags); +status_t send_signal_to_process_group(pid_t groupID, const Signal& signal, + uint32 flags); + +status_t _user_send_signal(int32 id, uint32 signal, + const union sigval* userValue, uint32 flags); +status_t _user_set_signal_mask(int how, const sigset_t *set, sigset_t *oldSet); +status_t _user_sigaction(int sig, const struct sigaction *newAction, struct sigaction *oldAction); -extern bigtime_t _user_set_alarm(bigtime_t time, uint32 mode); -extern status_t _user_sigwait(const sigset_t *set, int *_signal); -extern status_t _user_sigsuspend(const sigset_t *mask); -extern status_t _user_sigpending(sigset_t *set); -extern status_t _user_set_signal_stack(const stack_t *newUserStack, +bigtime_t _user_set_alarm(bigtime_t time, uint32 mode); +status_t _user_sigwait(const sigset_t *set, siginfo_t *info, uint32 flags, + bigtime_t timeout); +status_t _user_sigsuspend(const sigset_t *mask); +status_t _user_sigpending(sigset_t *set); +status_t _user_set_signal_stack(const stack_t *newUserStack, stack_t *oldUserStack); +int64 _user_restore_signal_frame(struct signal_frame_data* signalFrameData); #ifdef __cplusplus } diff --git a/headers/private/kernel/lock.h b/headers/private/kernel/lock.h index e48cd8ec10..4381d21146 100644 --- a/headers/private/kernel/lock.h +++ b/headers/private/kernel/lock.h @@ -1,5 +1,5 @@ /* - * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. * @@ -144,11 +144,11 @@ extern status_t mutex_switch_from_read_lock(rw_lock* from, mutex* to); extern status_t _rw_lock_read_lock(rw_lock* lock); extern status_t _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags, bigtime_t timeout); -extern void _rw_lock_read_unlock(rw_lock* lock, bool threadsLocked); -extern void _rw_lock_write_unlock(rw_lock* lock, bool threadsLocked); +extern void _rw_lock_read_unlock(rw_lock* lock, bool schedulerLocked); +extern void _rw_lock_write_unlock(rw_lock* lock, bool schedulerLocked); -extern status_t _mutex_lock(mutex* lock, bool threadsLocked); -extern void _mutex_unlock(mutex* lock, bool threadsLocked); +extern status_t _mutex_lock(mutex* lock, bool schedulerLocked); +extern void _mutex_unlock(mutex* lock, bool schedulerLocked); extern status_t _mutex_trylock(mutex* lock); extern status_t _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout); diff --git a/headers/private/kernel/real_time_clock.h b/headers/private/kernel/real_time_clock.h index a9ba0043cc..ccfd1177a0 100644 --- a/headers/private/kernel/real_time_clock.h +++ b/headers/private/kernel/real_time_clock.h @@ -22,6 +22,8 @@ struct kernel_args; extern "C" { #endif +void set_real_time_clock_usecs(bigtime_t currentTime); + status_t rtc_init(struct kernel_args *args); bigtime_t rtc_boot_time(void); // Returns the time at which the system was booted in microseconds since Jan 1, 1970 UTC. @@ -34,7 +36,7 @@ void rtc_secs_to_tm(uint32 seconds, struct tm *t); uint32 get_timezone_offset(void); bigtime_t _user_system_time(void); -status_t _user_set_real_time_clock(uint32 time); +status_t _user_set_real_time_clock(bigtime_t time); status_t _user_set_timezone(int32 timezoneOffset, const char *name, size_t nameLength); status_t _user_get_timezone(int32 *_timezoneOffset, char* name, diff --git a/headers/private/kernel/team.h b/headers/private/kernel/team.h index b15e99ed27..50afdee411 100644 --- a/headers/private/kernel/team.h +++ b/headers/private/kernel/team.h @@ -22,12 +22,11 @@ extern "C" { status_t team_init(struct kernel_args *args); status_t wait_for_team(team_id id, status_t *returnCode); -void team_remove_team(Team *team); -port_id team_shutdown_team(Team *team, cpu_status& state); + +void team_remove_team(Team *team, pid_t& _signalGroup); +port_id team_shutdown_team(Team *team); void team_delete_team(Team *team, port_id debuggerPort); -struct process_group *team_get_process_group_locked( - struct process_session *session, pid_t id); -void team_delete_process_group(struct process_group *group); + Team *team_get_kernel_team(void); team_id team_get_kernel_team_id(void); team_id team_get_current_team_id(void); @@ -42,15 +41,11 @@ Team *team_get_team_struct_locked(team_id id); int32 team_max_teams(void); int32 team_used_teams(void); -typedef bool (*team_iterator_callback)(Team* team, void* cookie); -Team* team_iterate_through_teams(team_iterator_callback callback, - void* cookie); - thread_id load_image_etc(int32 argCount, const char* const* args, const char* const* env, int32 priority, team_id parentID, uint32 flags); void team_set_job_control_state(Team* team, job_control_state newState, - int signal, bool threadsLocked); + Signal* signal, bool threadsLocked); void team_set_controlling_tty(int32 index); int32 team_get_controlling_tty(); status_t team_set_foreground_process_group(int32 ttyIndex, pid_t processGroup); @@ -61,7 +56,7 @@ status_t stop_watching_team(team_id team, void (*hook)(team_id, void *), void *data); struct user_thread* team_allocate_user_thread(Team* team); -void team_free_user_thread(Thread* thread); +void team_free_user_thread(Team* team, struct user_thread* userThread); bool team_associate_data(AssociatedData* data); bool team_dissociate_data(AssociatedData* data); @@ -73,8 +68,7 @@ thread_id _user_load_image(const char* const* flatArgs, size_t flatArgsSize, status_t _user_wait_for_team(team_id id, status_t *_returnCode); void _user_exit_team(status_t returnValue); status_t _user_kill_team(thread_id thread); -thread_id _user_wait_for_child(thread_id child, uint32 flags, int32 *_reason, - status_t *_returnCode); +pid_t _user_wait_for_child(thread_id child, uint32 flags, siginfo_t* info); status_t _user_exec(const char *path, const char* const* flatArgs, size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask); thread_id _user_fork(void); diff --git a/headers/private/kernel/thread.h b/headers/private/kernel/thread.h index 7991f43119..c38657f8ac 100644 --- a/headers/private/kernel/thread.h +++ b/headers/private/kernel/thread.h @@ -19,6 +19,7 @@ #include +struct arch_fork_arg; struct kernel_args; struct select_info; struct thread_creation_attributes; @@ -31,6 +32,43 @@ struct thread_creation_attributes; #define THREAD_NAME_CHANGED 0x04 +namespace BKernel { + + +struct ThreadCreationAttributes : thread_creation_attributes { + // when calling from kernel only + team_id team; + Thread* thread; + sigset_t signal_mask; + size_t additional_stack_size; // additional space in the stack + // area after the TLS region, not + // used as thread stack + thread_func kernelEntry; + void* kernelArgument; + arch_fork_arg* forkArgs; // If non-NULL, the userland thread + // will be started with this + // register context. + +public: + ThreadCreationAttributes() {} + // no-init constructor + ThreadCreationAttributes( + thread_func function, const char* name, + int32 priority, void* arg, + team_id team = -1, Thread* thread = NULL); + + status_t InitFromUserAttributes( + const thread_creation_attributes* + userAttributes, + char* nameBuffer); +}; + + +} // namespace BKernel + +using BKernel::ThreadCreationAttributes; + + #ifdef __cplusplus extern "C" { #endif @@ -61,9 +99,6 @@ void thread_set_io_priority(int32 priority); #define thread_get_current_thread arch_thread_get_current_thread -Thread *thread_get_thread_struct(thread_id id); -Thread *thread_get_thread_struct_locked(thread_id id); - static thread_id thread_get_current_thread_id(void); static inline thread_id thread_get_current_thread_id(void) @@ -75,18 +110,21 @@ thread_get_current_thread_id(void) static inline bool thread_is_idle_thread(Thread *thread) { - return thread->entry == NULL; + return thread->priority == B_IDLE_PRIORITY; } -typedef bool (*thread_iterator_callback)(Thread* thread, void* cookie); -Thread* thread_iterate_through_threads(thread_iterator_callback callback, - void* cookie); +thread_id allocate_thread_id(); +thread_id peek_next_thread_id(); -thread_id allocate_thread_id(void); -thread_id peek_next_thread_id(void); +status_t thread_enter_userspace_new_team(Thread* thread, addr_t entryFunction, + void* argument1, void* argument2); +status_t thread_create_user_stack(Team* team, Thread* thread, void* stackBase, + size_t stackSize, size_t additionalSize); +thread_id thread_create_thread(const ThreadCreationAttributes& attributes, + bool kernel); thread_id spawn_kernel_thread_etc(thread_func, const char *name, int32 priority, - void *args, team_id team, thread_id threadID); + void *args, team_id team); status_t wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, status_t *_returnCode); @@ -99,7 +137,6 @@ status_t thread_block(); status_t thread_block_with_timeout(uint32 timeoutFlags, bigtime_t timeout); status_t thread_block_with_timeout_locked(uint32 timeoutFlags, bigtime_t timeout); -void thread_unblock(status_t threadID, status_t status); // used in syscalls.c status_t _user_set_thread_priority(thread_id thread, int32 newPriority); @@ -109,8 +146,10 @@ status_t _user_resume_thread(thread_id thread); status_t _user_rename_thread(thread_id thread, const char *name); thread_id _user_spawn_thread(struct thread_creation_attributes* attributes); status_t _user_wait_for_thread(thread_id id, status_t *_returnCode); -status_t _user_snooze_etc(bigtime_t timeout, int timebase, uint32 flags); +status_t _user_snooze_etc(bigtime_t timeout, int timebase, uint32 flags, + bigtime_t* _remainingTime); status_t _user_kill_thread(thread_id thread); +status_t _user_cancel_thread(thread_id threadID, void (*cancelFunction)(int)); void _user_thread_yield(void); void _user_exit_thread(status_t return_value); bool _user_has_data(thread_id thread); @@ -135,20 +174,41 @@ int _user_setrlimit(int resource, const struct rlimit * rlp); #endif -/*! - \a thread must be the current thread. - Thread lock can be, but doesn't need to be held. +/*! Checks whether the current thread would immediately be interrupted when + blocking it with the given wait/interrupt flags. + + The caller must hold the scheduler lock. + + \param thread The current thread. + \param flags Wait/interrupt flags to be considered. Relevant are: + - \c B_CAN_INTERRUPT: The thread can be interrupted by any non-blocked + signal. Implies \c B_KILL_CAN_INTERRUPT (specified or not). + - \c B_KILL_CAN_INTERRUPT: The thread can be interrupted by a kill + signal. + \return \c true, if the thread would be interrupted, \c false otherwise. */ static inline bool thread_is_interrupted(Thread* thread, uint32 flags) { - return ((flags & B_CAN_INTERRUPT) - && (thread->sig_pending & ~thread->sig_block_mask) != 0) - || ((flags & B_KILL_CAN_INTERRUPT) - && (thread->sig_pending & KILL_SIGNALS)); + sigset_t pendingSignals = thread->AllPendingSignals(); + return ((flags & B_CAN_INTERRUPT) != 0 + && (pendingSignals & ~thread->sig_block_mask) != 0) + || ((flags & B_KILL_CAN_INTERRUPT) != 0 + && (pendingSignals & KILL_SIGNALS) != 0); } +/*! Checks wether the given thread is currently blocked (i.e. still waiting for + something). + + If a stable answer is required, the caller must hold the scheduler lock. + Alternatively, if waiting is not interruptible and cannot time out, holding + the client lock held when calling thread_prepare_to_block() and the + unblocking functions works as well. + + \param thread The thread in question. + \return \c true, if the thread is blocked, \c false otherwise. +*/ static inline bool thread_is_blocked(Thread* thread) { @@ -156,9 +216,109 @@ thread_is_blocked(Thread* thread) } -/*! - \a thread must be the current thread. - Thread lock can be, but doesn't need to be locked. +/*! Prepares the current thread for waiting. + + This is the first of two steps necessary to block the current thread + (IOW, to let it wait for someone else to unblock it or optionally time out + after a specified delay). The process consists of two steps to avoid race + conditions in case a lock other than the scheduler lock is involved. + + Usually the thread waits for some condition to change and this condition is + something reflected in the caller's data structures which should be + protected by a client lock the caller knows about. E.g. in the semaphore + code that lock is a per-semaphore spinlock that protects the semaphore data, + including the semaphore count and the queue of waiting threads. For certain + low-level locking primitives (e.g. mutexes) that client lock is the + scheduler lock itself, which simplifies things a bit. + + If a client lock other than the scheduler lock is used, this function must + be called with that lock being held. Afterwards that lock should be dropped + and the function that actually blocks the thread shall be invoked + (thread_block[_locked]() or thread_block_with_timeout[_locked]()). In + between these two steps no functionality that uses the thread blocking API + for this thread shall be used. + + When the caller determines that the condition for unblocking the thread + occurred, it calls thread_unblock_locked() to unblock the thread. At that + time one of locks that are held when calling thread_prepare_to_block() must + be held. Usually that would be the client lock. In two cases it generally + isn't, however, since the unblocking code doesn't know about the client + lock: 1. When thread_block_with_timeout[_locked]() had been used and the + timeout occurs. 2. When thread_prepare_to_block() had been called with one + or both of the \c B_CAN_INTERRUPT or \c B_KILL_CAN_INTERRUPT flags specified + and someone calls thread_interrupt() that is supposed to wake up the thread. + In either of these two cases only the scheduler lock is held by the + unblocking code. A timeout can only happen after + thread_block_with_timeout_locked() has been called, but an interruption is + possible at any time. The client code must deal with those situations. + + Generally blocking and unblocking threads proceed in the following manner: + + Blocking thread: + - Acquire client lock. + - Check client condition and decide whether blocking is necessary. + - Modify some client data structure to indicate that this thread is now + waiting. + - Release client lock (unless client lock is the scheduler lock). + - Block. + - Acquire client lock (unless client lock is the scheduler lock). + - Check client condition and compare with block result. E.g. if the wait was + interrupted or timed out, but the client condition indicates success, it + may be considered a success after all, since usually that happens when + another thread concurrently changed the client condition and also tried + to unblock the waiting thread. It is even necessary when that other + thread changed the client data structures in a way that associate some + resource with the unblocked thread, or otherwise the unblocked thread + would have to reverse that here. + - If still necessary -- i.e. not already taken care of by an unblocking + thread -- modify some client structure to indicate that the thread is no + longer waiting, so it isn't erroneously unblocked later. + + Unblocking thread: + - Acquire client lock. + - Check client condition and decide whether a blocked thread can be woken + up. + - Check the client data structure that indicates whether one or more threads + are waiting and which thread(s) need(s) to be woken up. + - Unblock respective thread(s). + - Possibly change some client structure, so that an unblocked thread can + decide whether a concurrent timeout/interruption can be ignored, or + simply so that it doesn't have to do any more cleanup. + + Note that in the blocking thread the steps after blocking are strictly + required only if timeouts or interruptions are possible. If they are not, + the blocking thread can only be woken up explicitly by an unblocking thread, + which could already take care of all the necessary client data structure + modifications, so that the blocking thread wouldn't have to do that. + + Note that the client lock can but does not have to be a spinlock. + A mutex, a semaphore, or anything that doesn't try to use the thread + blocking API for the calling thread when releasing the lock is fine. + In particular that means in principle thread_prepare_to_block() can be + called with interrupts enabled. + + Care must be taken when the wait can be interrupted or can time out, + especially with a client lock that uses the thread blocking API. After a + blocked thread has been interrupted or the the time out occurred it cannot + acquire the client lock (or any other lock using the thread blocking API) + without first making sure that the thread doesn't still appears to be + waiting to other client code. Otherwise another thread could try to unblock + it which could erroneously unblock the thread while already waiting on the + client lock. So usually when interruptions or timeouts are possible a + spinlock needs to be involved. + + \param thread The current thread. + \param flags The blocking flags. Relevant are: + - \c B_CAN_INTERRUPT: The thread can be interrupted by any non-blocked + signal. Implies \c B_KILL_CAN_INTERRUPT (specified or not). + - \c B_KILL_CAN_INTERRUPT: The thread can be interrupted by a kill + signal. + \param type The type of object the thread will be blocked at. Informative/ + for debugging purposes. Must be one of the \c THREAD_BLOCK_TYPE_* + constants. \c THREAD_BLOCK_TYPE_OTHER implies that \a object is a + string. + \param object The object the thread will be blocked at. Informative/for + debugging purposes. */ static inline void thread_prepare_to_block(Thread* thread, uint32 flags, uint32 type, @@ -173,11 +333,27 @@ thread_prepare_to_block(Thread* thread, uint32 flags, uint32 type, } +/*! Blocks the current thread. + + The thread is blocked until someone else unblock it. Must be called after a + call to thread_prepare_to_block(). If the thread has already been unblocked + after the previous call to thread_prepare_to_block(), this function will + return immediately. Cf. the documentation of thread_prepare_to_block() for + more details. + + The caller must hold the scheduler lock. + + \param thread The current thread. + \return The error code passed to the unblocking function. thread_interrupt() + uses \c B_INTERRUPTED. By convention \c B_OK means that the wait was + successful while another error code indicates a failure (what that means + depends on the client code). +*/ static inline status_t thread_block_locked(Thread* thread) { if (thread->wait.status == 1) { - // check for signals, if interruptable + // check for signals, if interruptible if (thread_is_interrupted(thread, thread->wait.flags)) { thread->wait.status = B_INTERRUPTED; } else { @@ -190,6 +366,19 @@ thread_block_locked(Thread* thread) } +/*! Unblocks the specified blocked thread. + + If the thread is no longer waiting (e.g. because thread_unblock_locked() has + already been called in the meantime), this function does not have any + effect. + + The caller must hold the scheduler lock and the client lock (might be the + same). + + \param thread The thread to be unblocked. + \param status The unblocking status. That's what the unblocked thread's + call to thread_block_locked() will return. +*/ static inline void thread_unblock_locked(Thread* thread, status_t status) { @@ -202,6 +391,29 @@ thread_unblock_locked(Thread* thread, status_t status) } +/*! Interrupts the specified blocked thread, if possible. + + The function checks whether the thread can be interrupted and, if so, calls + \code thread_unblock_locked(thread, B_INTERRUPTED) \endcode. Otherwise the + function is a no-op. + + The caller must hold the scheduler lock. Normally thread_unblock_locked() + also requires the client lock to be held, but in this case the caller + usually doesn't know it. This implies that the client code needs to take + special care, if waits are interruptible. See thread_prepare_to_block() for + more information. + + \param thread The thread to be interrupted. + \param kill If \c false, the blocked thread is only interrupted, when the + flag \c B_CAN_INTERRUPT was specified for the blocked thread. If + \c true, it is only interrupted, when at least one of the flags + \c B_CAN_INTERRUPT or \c B_KILL_CAN_INTERRUPT was specified for the + blocked thread. + \return \c B_OK, if the thread is interruptible and thread_unblock_locked() + was called, \c B_NOT_ALLOWED otherwise. \c B_OK doesn't imply that the + thread actually has been interrupted -- it could have been unblocked + before already. +*/ static inline status_t thread_interrupt(Thread* thread, bool kill) { diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index 2097c1fe22..017da64352 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -10,30 +10,23 @@ #ifndef _ASSEMBLER -#include +#include #include #include +#include +#include #include -#include #include #include #include +#include #include #include +#include #include -extern spinlock gThreadSpinlock; -#define GRAB_THREAD_LOCK() acquire_spinlock(&gThreadSpinlock) -#define RELEASE_THREAD_LOCK() release_spinlock(&gThreadSpinlock) - -extern spinlock gTeamSpinlock; - // NOTE: TEAM lock can be held over a THREAD lock acquisition, - // but not the other way (to avoid deadlock) -#define GRAB_TEAM_LOCK() acquire_spinlock(&gTeamSpinlock) -#define RELEASE_TEAM_LOCK() release_spinlock(&gTeamSpinlock) - enum additional_thread_state { THREAD_STATE_FREE_ON_RESCHED = 7, // free the thread structure upon reschedule // THREAD_STATE_BIRTH // thread is being created @@ -43,9 +36,11 @@ enum additional_thread_state { #define THREAD_MAX_SET_PRIORITY B_REAL_TIME_PRIORITY enum team_state { - TEAM_STATE_NORMAL, // normal state - TEAM_STATE_BIRTH, // being contructed - TEAM_STATE_DEATH // being killed + TEAM_STATE_NORMAL, // normal state + TEAM_STATE_BIRTH, // being constructed + TEAM_STATE_SHUTDOWN, // still lives, but is going down + TEAM_STATE_DEATH // only the Team object still exists, threads are + // gone }; #define TEAM_FLAG_EXEC_DONE 0x01 @@ -71,33 +66,14 @@ struct xsi_sem_context; // defined in xsi_semaphore.cpp namespace BKernel { struct Team; struct Thread; + struct ProcessGroup; } -struct death_entry { +struct thread_death_entry { struct list_link link; - pid_t group_id; thread_id thread; status_t status; - uint16 reason; - uint16 signal; -}; - -struct process_session { - pid_t id; - int32 group_count; - int32 controlling_tty; // index of the controlling tty, - // -1 if none - pid_t foreground_group; -}; - -struct process_group { - struct process_group *next; // next in hash - struct process_session *session; - pid_t id; - int32 refs; - BKernel::Team *teams; - bool orphaned; }; struct team_loading_info { @@ -122,7 +98,9 @@ struct team_watcher { struct job_control_entry : DoublyLinkedListLinkImpl { job_control_state state; // current team job control state thread_id thread; // main thread ID == team ID + uint16 signal; // signal causing the current state bool has_group_ref; + uid_t signaling_user; // valid while state != JOB_CONTROL_STATE_DEAD BKernel::Team* team; @@ -130,8 +108,8 @@ struct job_control_entry : DoublyLinkedListLinkImpl { // valid when state == JOB_CONTROL_STATE_DEAD pid_t group_id; status_t status; - uint16 reason; - uint16 signal; + uint16 reason; // reason for the team's demise, one of the + // CLD_* values defined in job_control_entry(); ~job_control_entry(); @@ -215,41 +193,67 @@ typedef bool (*page_fault_callback)(addr_t address, addr_t faultAddress, namespace BKernel { -struct Team : AssociatedDataOwner { - Team *next; // next in hash - Team *siblings_next; - Team *parent; - Team *children; - Team *group_next; - team_id id; + +template +struct TeamThreadIteratorEntry + : DoublyLinkedListLinkImpl > { + typedef IDType id_type; + typedef TeamThreadIteratorEntry iterator_type; + + id_type id; // -1 for iterator entries, >= 0 for actual elements + bool visible; // the entry is publicly visible +}; + + +struct Team : TeamThreadIteratorEntry, KernelReferenceable, + AssociatedDataOwner { + DoublyLinkedListLink global_list_link; + Team *hash_next; // next in hash + Team *siblings_next; // next in parent's list; protected by + // parent's fLock + Team *parent; // write-protected by both parent (if any) + // and this team's fLock + Team *children; // protected by this team's fLock; + // adding/removing a child also requires the + // child's fLock + Team *group_next; // protected by the group's lock + + int64 serial_number; // immutable after adding team to hash + + // process group info -- write-protected by both the group's lock, the + // team's lock, and the team's parent's lock pid_t group_id; pid_t session_id; - struct process_group *group; - char name[B_OS_NAME_LENGTH]; - char args[64]; // contents for the team_info::args field + ProcessGroup *group; + int num_threads; // number of threads in this team int state; // current team state, see above int32 flags; struct io_context *io_context; struct realtime_sem_context *realtime_sem_context; struct xsi_sem_context *xsi_sem_context; - struct team_death_entry *death_entry; + struct team_death_entry *death_entry; // protected by fLock struct list dead_threads; int dead_threads_count; + // protected by the team's fLock team_dead_children dead_children; team_job_control_children stopped_children; team_job_control_children continued_children; + + // protected by the parent team's fLock struct job_control_entry* job_control_entry; VMAddressSpace *address_space; - Thread *main_thread; - Thread *thread_list; - struct team_loading_info *loading_info; - struct list image_list; + Thread *main_thread; // protected by fLock and the scheduler + // lock (and the thread's lock), immutable + // after first set + Thread *thread_list; // protected by fLock and the scheduler lock + struct team_loading_info *loading_info; // protected by fLock + struct list image_list; // protected by sImageMutex struct list watcher_list; - struct list sem_list; - struct list port_list; + struct list sem_list; // protected by sSemsSpinlock + struct list port_list; // protected by sPortsLock struct arch_team arch_info; addr_t user_data; @@ -260,9 +264,13 @@ struct Team : AssociatedDataOwner { struct team_debug_info debug_info; + // protected by scheduler lock bigtime_t dead_threads_kernel_time; bigtime_t dead_threads_user_time; + bigtime_t cpu_clock_offset; + // user group information; protected by fLock, the *_uid/*_gid fields also + // by the scheduler lock uid_t saved_set_uid; uid_t real_uid; uid_t effective_uid; @@ -271,44 +279,181 @@ struct Team : AssociatedDataOwner { gid_t effective_gid; gid_t* supplementary_groups; int supplementary_group_count; + + // Exit status information. Set when the first terminal event occurs, + // immutable afterwards. Protected by fLock. + struct { + uint16 reason; // reason for the team's demise, one of the + // CLD_* values defined in + uint16 signal; // signal killing the team + uid_t signaling_user; // real UID of the signal sender + status_t status; // exit status, if normal team exit + bool initialized; // true when the state has been initialized + } exit; + +public: + ~Team(); + + static Team* Create(team_id id, const char* name, + bool kernel); + static Team* Get(team_id id); + static Team* GetAndLock(team_id id); + + bool Lock() + { mutex_lock(&fLock); return true; } + bool TryLock() + { return mutex_trylock(&fLock) == B_OK; } + void Unlock() + { mutex_unlock(&fLock); } + + void UnlockAndReleaseReference() + { Unlock(); ReleaseReference(); } + + void LockTeamAndParent(bool dontLockParentIfKernel); + void UnlockTeamAndParent(); + void LockTeamAndProcessGroup(); + void UnlockTeamAndProcessGroup(); + void LockTeamParentAndProcessGroup(); + void UnlockTeamParentAndProcessGroup(); + void LockProcessGroup() + { LockTeamAndProcessGroup(); Unlock(); } + + const char* Name() const { return fName; } + void SetName(const char* name); + + const char* Args() const { return fArgs; } + void SetArgs(const char* args); + void SetArgs(const char* path, + const char* const* otherArgs, + int otherArgCount); + + BKernel::QueuedSignalsCounter* QueuedSignalsCounter() const + { return fQueuedSignalsCounter; } + sigset_t PendingSignals() const + { return fPendingSignals.AllSignals(); } + + void AddPendingSignal(int signal) + { fPendingSignals.AddSignal(signal); } + void AddPendingSignal(Signal* signal) + { fPendingSignals.AddSignal(signal); } + void RemovePendingSignal(int signal) + { fPendingSignals.RemoveSignal(signal); } + void RemovePendingSignal(Signal* signal) + { fPendingSignals.RemoveSignal(signal); } + void RemovePendingSignals(sigset_t mask) + { fPendingSignals.RemoveSignals(mask); } + void ResetSignalsOnExec(); + + inline int32 HighestPendingSignalPriority( + sigset_t nonBlocked) const; + inline Signal* DequeuePendingSignal(sigset_t nonBlocked, + Signal& buffer); + + struct sigaction& SignalActionFor(int32 signal) + { return fSignalActions[signal - 1]; } + void InheritSignalActions(Team* parent); + + // user timers -- protected by fLock + UserTimer* UserTimerFor(int32 id) const + { return fUserTimers.TimerFor(id); } + status_t AddUserTimer(UserTimer* timer); + void RemoveUserTimer(UserTimer* timer); + void DeleteUserTimers(bool userDefinedOnly); + + bool CheckAddUserDefinedTimer(); + void UserDefinedTimersRemoved(int32 count); + + void UserTimerActivated(TeamTimeUserTimer* timer) + { fCPUTimeUserTimers.Add(timer); } + void UserTimerActivated(TeamUserTimeUserTimer* timer) + { fUserTimeUserTimers.Add(timer); } + void UserTimerDeactivated(TeamTimeUserTimer* timer) + { fCPUTimeUserTimers.Remove(timer); } + void UserTimerDeactivated( + TeamUserTimeUserTimer* timer) + { fUserTimeUserTimers.Remove(timer); } + void DeactivateCPUTimeUserTimers(); + // both total and user CPU timers + bool HasActiveCPUTimeUserTimers() const + { return !fCPUTimeUserTimers.IsEmpty(); } + bool HasActiveUserTimeUserTimers() const + { return !fUserTimeUserTimers.IsEmpty(); } + TeamTimeUserTimerList::ConstIterator + CPUTimeUserTimerIterator() const + { return fCPUTimeUserTimers.GetIterator(); } + inline TeamUserTimeUserTimerList::ConstIterator + UserTimeUserTimerIterator() const; + + bigtime_t CPUTime(bool ignoreCurrentRun) const; + bigtime_t UserCPUTime() const; + +private: + Team(team_id id, bool kernel); + +private: + mutex fLock; + char fName[B_OS_NAME_LENGTH]; + char fArgs[64]; + // contents for the team_info::args field + + BKernel::QueuedSignalsCounter* fQueuedSignalsCounter; + BKernel::PendingSignals fPendingSignals; + // protected by scheduler lock + struct sigaction fSignalActions[MAX_SIGNAL_NUMBER]; + // indexed signal - 1, protected by fLock + + UserTimerList fUserTimers; // protected by fLock + TeamTimeUserTimerList fCPUTimeUserTimers; + // protected by scheduler lock + TeamUserTimeUserTimerList fUserTimeUserTimers; + vint32 fUserDefinedTimerCount; // accessed atomically }; -struct Thread { +struct Thread : TeamThreadIteratorEntry, KernelReferenceable { int32 flags; // summary of events relevant in interrupt // handlers (signals pending, user debugging // enabled, etc.) - Thread *all_next; - Thread *team_next; - Thread *queue_next; /* i.e. run queue, release queue, etc. */ - timer alarm; - thread_id id; - char name[B_OS_NAME_LENGTH]; - int32 priority; - int32 next_priority; - int32 io_priority; - int32 state; - int32 next_state; - struct cpu_ent *cpu; - struct cpu_ent *previous_cpu; - int32 pinned_to_cpu; + int64 serial_number; // immutable after adding thread to hash + Thread *hash_next; // protected by thread hash lock + Thread *team_next; // protected by team lock and fLock + Thread *queue_next; // protected by scheduler lock + timer alarm; // protected by scheduler lock + char name[B_OS_NAME_LENGTH]; // protected by fLock + int32 priority; // protected by scheduler lock + int32 next_priority; // protected by scheduler lock + int32 io_priority; // protected by fLock + int32 state; // protected by scheduler lock + int32 next_state; // protected by scheduler lock + struct cpu_ent *cpu; // protected by scheduler lock + struct cpu_ent *previous_cpu; // protected by scheduler lock + int32 pinned_to_cpu; // only accessed by this thread or in the + // scheduler, when thread is not running - sigset_t sig_pending; - sigset_t sig_block_mask; - sigset_t sig_temp_enabled; - struct sigaction sig_action[32]; - addr_t signal_stack_base; - size_t signal_stack_size; - bool signal_stack_enabled; + sigset_t sig_block_mask; // protected by scheduler lock, + // only modified by the thread itself + sigset_t sigsuspend_original_unblocked_mask; + // non-0 after a return from _user_sigsuspend(), containing the inverted + // original signal mask, reset in handle_signals(); only accessed by + // this thread + ucontext_t* user_signal_context; // only accessed by this thread + addr_t signal_stack_base; // only accessed by this thread + size_t signal_stack_size; // only accessed by this thread + bool signal_stack_enabled; // only accessed by this thread - bool in_kernel; - bool was_yielded; - struct scheduler_thread_data* scheduler_data; + bool in_kernel; // protected by time_lock, only written by + // this thread + bool was_yielded; // protected by scheduler lock + struct scheduler_thread_data* scheduler_data; // protected by scheduler lock - struct user_thread* user_thread; + struct user_thread* user_thread; // write-protected by fLock, only + // modified by the thread itself and + // thus freely readable by it + + void (*cancel_function)(int); struct { - uint8 parameters[32]; + uint8 parameters[SYSCALL_RESTART_PARAMETER_SIZE]; } syscall_restart; struct { @@ -322,13 +467,15 @@ struct Thread { struct PrivateConditionVariableEntry *condition_variable_entry; struct { - sem_id write_sem; - sem_id read_sem; + sem_id write_sem; // acquired by writers before writing + sem_id read_sem; // release by writers after writing, acquired + // by this thread when reading thread_id sender; int32 code; size_t size; void* buffer; - } msg; + } msg; // write_sem/read_sem are protected by fLock when accessed by + // others, the other fields are protected by write_sem/read_sem union { addr_t fault_handler; @@ -340,50 +487,304 @@ struct Thread { int32 page_faults_allowed; /* this field may only stay in debug builds in the future */ - thread_entry_func entry; - void *args1, *args2; - BKernel::Team *team; + BKernel::Team *team; // protected by team lock, thread lock, scheduler + // lock struct { - sem_id sem; - status_t status; - uint16 reason; - uint16 signal; - struct list waiters; + sem_id sem; // immutable after thread creation + status_t status; // accessed only by this thread + struct list waiters; // protected by fLock } exit; - struct select_info *select_infos; + struct select_info *select_infos; // protected by fLock struct thread_debug_info debug_info; // stack - area_id kernel_stack_area; - addr_t kernel_stack_base; - addr_t kernel_stack_top; - area_id user_stack_area; - addr_t user_stack_base; - size_t user_stack_size; + area_id kernel_stack_area; // immutable after thread creation + addr_t kernel_stack_base; // immutable after thread creation + addr_t kernel_stack_top; // immutable after thread creation + area_id user_stack_area; // protected by thread lock + addr_t user_stack_base; // protected by thread lock + size_t user_stack_size; // protected by thread lock addr_t user_local_storage; // usually allocated at the safe side of the stack int kernel_errno; // kernel "errno" differs from its userspace alter ego - bigtime_t user_time; - bigtime_t kernel_time; - bigtime_t last_time; + // user_time, kernel_time, and last_time are only written by the thread + // itself, so they can be read by the thread without lock. Holding the + // scheduler lock and checking that the thread does not run also guarantees + // that the times will not change. + spinlock time_lock; + bigtime_t user_time; // protected by time_lock + bigtime_t kernel_time; // protected by time_lock + bigtime_t last_time; // protected by time_lock + bigtime_t cpu_clock_offset; // protected by scheduler lock void (*post_interrupt_callback)(void*); void* post_interrupt_data; - // architecture dependant section + // architecture dependent section struct arch_thread arch_info; + +public: + Thread() {} + // dummy for the idle threads + Thread(const char *name, thread_id threadID, + struct cpu_ent *cpu); + ~Thread(); + + static status_t Create(const char* name, Thread*& _thread); + + static Thread* Get(thread_id id); + static Thread* GetAndLock(thread_id id); + static Thread* GetDebug(thread_id id); + // in kernel debugger only + + static bool IsAlive(thread_id id); + + void* operator new(size_t size); + void* operator new(size_t, void* pointer); + void operator delete(void* pointer, size_t size); + + status_t Init(bool idleThread); + + bool Lock() + { mutex_lock(&fLock); return true; } + bool TryLock() + { return mutex_trylock(&fLock) == B_OK; } + void Unlock() + { mutex_unlock(&fLock); } + + void UnlockAndReleaseReference() + { Unlock(); ReleaseReference(); } + + bool IsAlive() const; + + bool IsRunning() const + { return cpu != NULL; } + // scheduler lock must be held + + sigset_t ThreadPendingSignals() const + { return fPendingSignals.AllSignals(); } + inline sigset_t AllPendingSignals() const; + void AddPendingSignal(int signal) + { fPendingSignals.AddSignal(signal); } + void AddPendingSignal(Signal* signal) + { fPendingSignals.AddSignal(signal); } + void RemovePendingSignal(int signal) + { fPendingSignals.RemoveSignal(signal); } + void RemovePendingSignal(Signal* signal) + { fPendingSignals.RemoveSignal(signal); } + void RemovePendingSignals(sigset_t mask) + { fPendingSignals.RemoveSignals(mask); } + void ResetSignalsOnExec(); + + inline int32 HighestPendingSignalPriority( + sigset_t nonBlocked) const; + inline Signal* DequeuePendingSignal(sigset_t nonBlocked, + Signal& buffer); + + // user timers -- protected by fLock + UserTimer* UserTimerFor(int32 id) const + { return fUserTimers.TimerFor(id); } + status_t AddUserTimer(UserTimer* timer); + void RemoveUserTimer(UserTimer* timer); + void DeleteUserTimers(bool userDefinedOnly); + + void UserTimerActivated(ThreadTimeUserTimer* timer) + { fCPUTimeUserTimers.Add(timer); } + void UserTimerDeactivated(ThreadTimeUserTimer* timer) + { fCPUTimeUserTimers.Remove(timer); } + void DeactivateCPUTimeUserTimers(); + bool HasActiveCPUTimeUserTimers() const + { return !fCPUTimeUserTimers.IsEmpty(); } + ThreadTimeUserTimerList::ConstIterator + CPUTimeUserTimerIterator() const + { return fCPUTimeUserTimers.GetIterator(); } + + inline bigtime_t CPUTime(bool ignoreCurrentRun) const; + +private: + mutex fLock; + + BKernel::PendingSignals fPendingSignals; + // protected by scheduler lock + + UserTimerList fUserTimers; // protected by fLock + ThreadTimeUserTimerList fCPUTimeUserTimers; + // protected by scheduler lock }; + +struct ProcessSession : BReferenceable { + pid_t id; + int32 controlling_tty; // index of the controlling tty, + // -1 if none + pid_t foreground_group; + +public: + ProcessSession(pid_t id); + ~ProcessSession(); + + bool Lock() + { mutex_lock(&fLock); return true; } + bool TryLock() + { return mutex_trylock(&fLock) == B_OK; } + void Unlock() + { mutex_unlock(&fLock); } + +private: + mutex fLock; +}; + + +struct ProcessGroup : KernelReferenceable { + struct ProcessGroup *next; // next in hash + pid_t id; + BKernel::Team *teams; + +public: + ProcessGroup(pid_t id); + ~ProcessGroup(); + + static ProcessGroup* Get(pid_t id); + + bool Lock() + { mutex_lock(&fLock); return true; } + bool TryLock() + { return mutex_trylock(&fLock) == B_OK; } + void Unlock() + { mutex_unlock(&fLock); } + + ProcessSession* Session() const + { return fSession; } + void Publish(ProcessSession* session); + void PublishLocked(ProcessSession* session); + + bool IsOrphaned() const; + + void ScheduleOrphanedCheck(); + void UnsetOrphanedCheck(); + +public: + SinglyLinkedListLink fOrphanedCheckListLink; + +private: + mutex fLock; + ProcessSession* fSession; + bool fInOrphanedCheckList; // protected by + // sOrphanedCheckLock +}; + +typedef SinglyLinkedList > ProcessGroupList; + + +/*! \brief Allows to iterate through all teams. +*/ +struct TeamListIterator { + TeamListIterator(); + ~TeamListIterator(); + + Team* Next(); + +private: + TeamThreadIteratorEntry fEntry; +}; + + +/*! \brief Allows to iterate through all threads. +*/ +struct ThreadListIterator { + ThreadListIterator(); + ~ThreadListIterator(); + + Thread* Next(); + +private: + TeamThreadIteratorEntry fEntry; +}; + + +inline int32 +Team::HighestPendingSignalPriority(sigset_t nonBlocked) const +{ + return fPendingSignals.HighestSignalPriority(nonBlocked); +} + + +inline Signal* +Team::DequeuePendingSignal(sigset_t nonBlocked, Signal& buffer) +{ + return fPendingSignals.DequeueSignal(nonBlocked, buffer); +} + + +inline TeamUserTimeUserTimerList::ConstIterator +Team::UserTimeUserTimerIterator() const +{ + return fUserTimeUserTimers.GetIterator(); +} + + +inline sigset_t +Thread::AllPendingSignals() const +{ + return fPendingSignals.AllSignals() | team->PendingSignals(); +} + + +inline int32 +Thread::HighestPendingSignalPriority(sigset_t nonBlocked) const +{ + return fPendingSignals.HighestSignalPriority(nonBlocked); +} + + +inline Signal* +Thread::DequeuePendingSignal(sigset_t nonBlocked, Signal& buffer) +{ + return fPendingSignals.DequeueSignal(nonBlocked, buffer); +} + + +/*! Returns the thread's current total CPU time (kernel + user + offset). + + The caller must hold the scheduler lock. + + \param ignoreCurrentRun If \c true and the thread is currently running, + don't add the time since the last time \c last_time was updated. Should + be used in "thread unscheduled" scheduler callbacks, since although the + thread is still running at that time, its time has already been stopped. + \return The thread's current total CPU time. +*/ +inline bigtime_t +Thread::CPUTime(bool ignoreCurrentRun) const +{ + bigtime_t time = user_time + kernel_time + cpu_clock_offset; + + // If currently running, also add the time since the last check, unless + // requested otherwise. + if (!ignoreCurrentRun && cpu != NULL) + time += system_time() - last_time; + + return time; +} + + } // namespace BKernel using BKernel::Team; +using BKernel::TeamListIterator; using BKernel::Thread; +using BKernel::ThreadListIterator; +using BKernel::ProcessSession; +using BKernel::ProcessGroup; +using BKernel::ProcessGroupList; struct thread_queue { diff --git a/headers/private/kernel/timer.h b/headers/private/kernel/timer.h index 254bafca33..2789b56493 100644 --- a/headers/private/kernel/timer.h +++ b/headers/private/kernel/timer.h @@ -1,4 +1,4 @@ -/* +/* ** Copyright 2003-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. ** Distributed under the terms of the OpenBeOS License. */ @@ -15,8 +15,21 @@ extern "C" { struct kernel_args; -#define B_TIMER_ACQUIRE_THREAD_LOCK 0x8000 -#define B_TIMER_FLAGS B_TIMER_ACQUIRE_THREAD_LOCK +#define B_TIMER_REAL_TIME_BASE 0x2000 + // For an absolute timer the given time is interpreted as a real-time, not + // as a system time. Note that setting the real-time clock will cause the + // timer to be updated -- it will expire according to the new clock. + // Relative timers are unaffected by this flag. +#define B_TIMER_USE_TIMER_STRUCT_TIMES 0x4000 + // For add_timer(): Use the timer::schedule_time (absolute time) and + // timer::period values instead of the period parameter. +#define B_TIMER_ACQUIRE_SCHEDULER_LOCK 0x8000 + // The timer hook is invoked with the scheduler lock held. When invoking + // cancel_timer() with the scheduler lock held, too, this helps to avoid + // race conditions. +#define B_TIMER_FLAGS \ + (B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK \ + | B_TIMER_REAL_TIME_BASE) /* Timer info structure */ struct timer_info { @@ -32,6 +45,8 @@ typedef struct timer_info timer_info; /* kernel functions */ status_t timer_init(struct kernel_args *); +void timer_init_post_rtc(void); +void timer_real_time_clock_changed(); int32 timer_interrupt(void); #ifdef __cplusplus diff --git a/headers/private/kernel/user_debugger.h b/headers/private/kernel/user_debugger.h index cf9252271c..777483388b 100644 --- a/headers/private/kernel/user_debugger.h +++ b/headers/private/kernel/user_debugger.h @@ -36,25 +36,26 @@ using BKernel::Thread; // // Locking policy: // 1) When accessing the structure it must be made sure, that the structure, -// (i.e. the struct team it lives in) isn't deleted. Thus one either needs to -// acquire the global team lock, or one accesses the structure from a thread -// of that team. +// (i.e. the struct Team it lives in) isn't deleted. Thus one either needs to +// get a team reference, lock the team, or one accesses the structure from a +// thread of that team. // 2) Access to the `flags' field is atomic. Reading via atomic_get() // requires no further locks (in addition to 1) that is). Writing requires -// `lock' being held and must be done atomically, too +// `lock' to be held and must be done atomically, too // (atomic_{set,and,or}()). Reading with `lock' being held doesn't need to // be done atomically. -// 3) Access to all other fields (read or write) requires `lock' being held. +// 3) Access to all other fields (read or write) requires `lock' to be held. +// 4) Locking order is scheduler lock -> Team -> Thread -> team_debug_info::lock +// -> thread_debug_info::lock. // struct team_debug_info { spinlock lock; // Guards the remaining fields. Should always be the innermost lock - // to be acquired/released. + // to be acquired/released, save for thread_debug_info::lock. int32 flags; - // Set atomically. So reading atomically is OK, even when the team - // lock is not held (at least if it is certain, that the team struct - // won't go). + // Set atomically. So reading atomically is OK, even when the lock is + // not held (at least if it is certain, that the team struct won't go). team_id debugger_team; port_id debugger_port; @@ -71,12 +72,13 @@ struct team_debug_info { // counter incremented whenever an image is created/deleted struct ConditionVariable* debugger_changed_condition; - // Set whenever someone is going (or planning) to change the debugger. - // If one wants to do the same, one has to wait for this condition. - // Both threads lock (outer) and team debug info lock (inner) have to - // be held when accessing this field. After setting to a condition - // variable the thread won't be deleted (until unsetting it) -- it might - // be removed from the team hash table, though. + // Set to a condition variable when going to change the debugger. Anyone + // who wants to change the debugger as well, needs to wait until the + // condition variable is unset again (waiting for the condition and + // rechecking again). The field and the condition variable is protected + // by 'lock'. After setting the a condition variable the team is + // guaranteed not to be deleted (until it is unset) it might be removed + // from the team hash table, though. struct BreakpointManager* breakpoint_manager; // manages hard- and software breakpoints @@ -84,11 +86,31 @@ struct team_debug_info { struct arch_team_debug_info arch_info; }; +// Thread related debugging data. +// +// Locking policy: +// 1) When accessing the structure it must be made sure, that the structure, +// (i.e. the struct Thread it lives in) isn't deleted. Thus one either needs +// to get a thread reference, lock the thread, or one accesses the structure +// of the current thread. +// 2) Access to the `flags' field is atomic. Reading via atomic_get() +// requires no further locks (in addition to 1) that is). Writing requires +// `lock' to be held and must be done atomically, too +// (atomic_{set,and,or}()). Reading with `lock' being held doesn't need to +// be done atomically. +// 3) Access to all other fields (read or write) requires `lock' to be held. +// 4) Locking order is scheduler lock -> Team -> Thread -> team_debug_info::lock +// -> thread_debug_info::lock. +// struct thread_debug_info { + spinlock lock; + // Guards the remaining fields. Should always be the innermost lock + // to be acquired/released. + int32 flags; - // Set atomically. So reading atomically is OK, even when the thread - // lock is not held (at least if it is certain, that the thread struct - // won't go). + // Set atomically. So reading atomically is OK, even when the lock is + // not held (at least if it is certain, that the thread struct won't + // go). port_id debug_port; // the port the thread is waiting on for commands from the nub thread @@ -238,7 +260,7 @@ void user_debug_stop_thread(); void user_debug_team_created(team_id teamID); void user_debug_team_deleted(team_id teamID, port_id debuggerPort); void user_debug_team_exec(); -void user_debug_update_new_thread_flags(thread_id threadID); +void user_debug_update_new_thread_flags(Thread* thread); void user_debug_thread_created(thread_id threadID); void user_debug_thread_deleted(team_id teamID, thread_id threadID); void user_debug_thread_exiting(Thread* thread); diff --git a/headers/private/kernel/usergroup.h b/headers/private/kernel/usergroup.h index 5ddbdb8d20..dac1c94348 100644 --- a/headers/private/kernel/usergroup.h +++ b/headers/private/kernel/usergroup.h @@ -25,7 +25,6 @@ extern "C" { // kernel private functions void inherit_parent_user_and_group(Team* team, Team* parent); -void inherit_parent_user_and_group_locked(Team* team, Team* parent); status_t update_set_id_user_and_group(Team* team, const char* file); // syscalls diff --git a/headers/private/kernel/util/AutoLock.h b/headers/private/kernel/util/AutoLock.h index 07b091493f..12f9b972b4 100644 --- a/headers/private/kernel/util/AutoLock.h +++ b/headers/private/kernel/util/AutoLock.h @@ -177,6 +177,10 @@ public: typedef AutoLocker ThreadCPUPinner; +typedef AutoLocker TeamLocker; +typedef AutoLocker ThreadLocker; + + } // namespace BPrivate using BPrivate::AutoLocker; @@ -188,5 +192,8 @@ using BPrivate::InterruptsLocker; using BPrivate::SpinLocker; using BPrivate::InterruptsSpinLocker; using BPrivate::ThreadCPUPinner; +using BPrivate::TeamLocker; +using BPrivate::ThreadLocker; + #endif // KERNEL_UTIL_AUTO_LOCKER_H diff --git a/headers/private/kernel/util/KernelReferenceable.h b/headers/private/kernel/util/KernelReferenceable.h new file mode 100644 index 0000000000..928075cbd0 --- /dev/null +++ b/headers/private/kernel/util/KernelReferenceable.h @@ -0,0 +1,29 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_UTIL_KERNEL_REFERENCEABLE_H +#define _KERNEL_UTIL_KERNEL_REFERENCEABLE_H + + +#include + +#include + + +namespace BKernel { + + +struct KernelReferenceable : BReferenceable, DeferredDeletable { +protected: + virtual void LastReferenceReleased(); +}; + + +} // namespace BKernel + + +using BKernel::KernelReferenceable; + + +#endif /* _KERNEL_UTIL_KERNEL_REFERENCEABLE_H */ diff --git a/headers/private/libroot/libroot_private.h b/headers/private/libroot/libroot_private.h index eaa9bf2276..7a3357b614 100644 --- a/headers/private/libroot/libroot_private.h +++ b/headers/private/libroot/libroot_private.h @@ -37,12 +37,11 @@ void __init_heap_post_env(void); void __init_time(void); void __arch_init_time(struct real_time_data *data, bool setDefaults); bigtime_t __arch_get_system_time_offset(struct real_time_data *data); +bigtime_t __get_system_time_offset(); void __init_pwd_backend(void); void __reinit_pwd_backend_after_fork(void); void* __arch_get_caller(void); -void __init_pthread(void); - #ifdef __cplusplus } diff --git a/headers/private/libroot/pthread_private.h b/headers/private/libroot/pthread_private.h index 6477d30576..f50bd28e0e 100644 --- a/headers/private/libroot/pthread_private.h +++ b/headers/private/libroot/pthread_private.h @@ -11,6 +11,17 @@ #include + +// _pthread_thread::flags values +#define THREAD_DETACHED 0x01 +#define THREAD_DEAD 0x02 +#define THREAD_CANCELED 0x04 +#define THREAD_CANCEL_ENABLED 0x08 +#define THREAD_CANCEL_ASYNCHRONOUS 0x10 + + +struct thread_creation_attributes; + // The public *_t types are only pointers to these structures // This way, we are completely free to change them, which might be // necessary in the future (not only due to the incomplete implementation @@ -55,9 +66,6 @@ typedef struct _pthread_thread { void *(*entry)(void*); void *entry_argument; void *exit_value; - int cancel_state; - int cancel_type; - bool cancelled; struct pthread_key_data specific[PTHREAD_KEYS_MAX]; struct __pthread_cleanup_handler *cleanup_handlers; } pthread_thread; @@ -69,7 +77,13 @@ extern "C" { void __pthread_key_call_destructors(pthread_thread *thread); void __pthread_destroy_thread(void); -pthread_thread *__allocate_pthread(void *data); +pthread_thread *__allocate_pthread(void* (*entry)(void*), void *data); +void __init_pthread(pthread_thread* thread, void* (*entry)(void*), void* data); +status_t __pthread_init_creation_attributes( + const pthread_attr_t* pthreadAttributes, pthread_t thread, + status_t (*entryFunction)(void*, void*), void* argument1, + void* argument2, const char* name, + struct thread_creation_attributes* attributes); #ifdef __cplusplus } diff --git a/headers/private/libroot/signal_private.h b/headers/private/libroot/signal_private.h new file mode 100644 index 0000000000..5380aa92c7 --- /dev/null +++ b/headers/private/libroot/signal_private.h @@ -0,0 +1,118 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _LIBROOT_SIGNAL_PRIVATE_H +#define _LIBROOT_SIGNAL_PRIVATE_H + + +#include +#include + +#include + + +#define MAX_SIGNAL_NUMBER_BEOS 29 + + +typedef __haiku_int32 sigset_t_beos; + +struct sigaction_beos { + __sighandler_t sa_handler; + sigset_t_beos sa_mask; + int sa_flags; + void* sa_userdata; +}; + + +static inline sigset_t_beos +to_beos_sigset(sigset_t set) +{ + // restrict to BeOS signals + sigset_t_beos beosSet = (sigset_t_beos)(set + & SIGNAL_RANGE_TO_MASK(1, MAX_SIGNAL_NUMBER_BEOS)); + + // if SIGBUS is set, set SIGSEGV, since they have the same number in BeOS + if ((set & SIGNAL_TO_MASK(SIGBUS)) != 0) + beosSet |= SIGNAL_TO_MASK(SIGSEGV); + + return beosSet; +} + + +static inline sigset_t +from_beos_sigset(sigset_t_beos beosSet) +{ + sigset_t set = beosSet; + + // if SIGSEGV is set, set SIGBUS, since they have the same number in BeOS + if ((set & SIGNAL_TO_MASK(SIGSEGV)) != 0) + set |= SIGNAL_TO_MASK(SIGBUS); + + return set; +} + + +__BEGIN_DECLS + + +__sighandler_t __signal_beos(int signal, __sighandler_t signalHandler); +__sighandler_t __signal(int signal, __sighandler_t signalHandler); + +int __sigaction_beos(int signal, const struct sigaction_beos* beosAction, + struct sigaction_beos* beosOldAction); +int __sigaction(int signal, const struct sigaction* action, + struct sigaction* oldAction); + +__sighandler_t __sigset_beos(int signal, __sighandler_t signalHandler); +__sighandler_t __sigset(int signal, __sighandler_t signalHandler); + +int __sigignore_beos(int signal); +int __sigignore(int signal); + +int __sighold_beos(int signal); +int __sighold(int signal); + +int __sigrelse_beos(int signal); +int __sigrelse(int signal); + +int __sigpause_beos(int signal); +int __sigpause(int signal); + +int __siginterrupt_beos(int signal, int flag); +int __siginterrupt(int signal, int flag); + +int __pthread_sigmask_beos(int how, const sigset_t_beos* beosSet, + sigset_t_beos* beosOldSet); +int __sigprocmask_beos(int how, const sigset_t_beos* beosSet, + sigset_t_beos* beosOldSet); + +int __pthread_sigmask(int how, const sigset_t* set, sigset_t* oldSet); +int __sigprocmask(int how, const sigset_t* set, sigset_t* oldSet); + +int __sigpending_beos(sigset_t_beos* beosSet); +int __sigpending(sigset_t* set); + +int __sigsuspend_beos(const sigset_t_beos* beosMask); +int __sigsuspend(const sigset_t* mask); + +int __sigwait_beos(const sigset_t_beos* beosSet, int* _signal); +int __sigwait(const sigset_t* set, int* _signal); + +int __sigemptyset_beos(sigset_t_beos* set); +int __sigfillset_beos(sigset_t_beos* set); +int __sigismember_beos(const sigset_t_beos* set, int signal); +int __sigaddset_beos(sigset_t_beos* set, int signal); +int __sigdelset_beos(sigset_t_beos* set, int signal); + +int __sigemptyset(sigset_t* set); +int __sigfillset(sigset_t* set); +int __sigismember(const sigset_t* set, int signal); +int __sigaddset(sigset_t* set, int signal); +int __sigdelset(sigset_t* set, int signal); + + +__END_DECLS + + +#endif // _LIBROOT_SIGNAL_PRIVATE_H diff --git a/headers/private/libroot/time_private.h b/headers/private/libroot/time_private.h new file mode 100644 index 0000000000..0cfd9faa6d --- /dev/null +++ b/headers/private/libroot/time_private.h @@ -0,0 +1,88 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _LIBROOT_TIME_PRIVATE_H +#define _LIBROOT_TIME_PRIVATE_H + + +#include +#include +#include +#include + +#include + +#include + +#define CLOCKS_PER_SEC_BEOS 1000 +#define CLK_TCK_BEOS CLOCKS_PER_SEC_BEOS + +#define MICROSECONDS_PER_CLOCK_TICK (1000000 / CLOCKS_PER_SEC) +#define MICROSECONDS_PER_CLOCK_TICK_BEOS (1000000 / CLOCKS_PER_SEC_BEOS) + + +struct __timer_t { + int32 id; + thread_id thread; + + void SetTo(int32 id, thread_id thread) + { + this->id = id; + this->thread = thread; + } +}; + + +static inline void +bigtime_to_timespec(bigtime_t time, timespec& spec) +{ + spec.tv_sec = time / 1000000; + spec.tv_nsec = (time % 1000000) * 1000; +} + + +static inline bool +timespec_to_bigtime(const timespec& spec, bigtime_t& _time) +{ + if (spec.tv_sec < 0 || spec.tv_nsec < 0 || spec.tv_nsec >= 1000000000) + return false; + + _time = (bigtime_t)spec.tv_sec * 1000000 + (spec.tv_nsec + 999) / 1000; + + return true; +} + + +static inline bool +timeval_to_timespec(const timeval& val, timespec& spec) +{ + if (val.tv_sec < 0 || val.tv_usec < 0 || val.tv_usec >= 1000000) + return false; + + spec.tv_sec = val.tv_sec; + spec.tv_nsec = val.tv_usec * 1000; + + return true; +} + + +static inline void +timespec_to_timeval(const timespec& spec, timeval& val) +{ + val.tv_sec = spec.tv_sec; + val.tv_usec = spec.tv_nsec / 1000; +} + + +__BEGIN_DECLS + + +clock_t __clock_beos(void); +clock_t __clock(void); + + +__END_DECLS + + +#endif // _LIBROOT_TIME_PRIVATE_H diff --git a/headers/private/libroot/times_private.h b/headers/private/libroot/times_private.h new file mode 100644 index 0000000000..00b1218fee --- /dev/null +++ b/headers/private/libroot/times_private.h @@ -0,0 +1,23 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _LIBROOT_TIMES_PRIVATE_H +#define _LIBROOT_TIMES_PRIVATE_H + + +#include +#include + + +__BEGIN_DECLS + + +clock_t __times_beos(struct tms* tms); +clock_t __times(struct tms* tms); + + +__END_DECLS + + +#endif // _LIBROOT_TIMES_PRIVATE_H diff --git a/headers/private/libroot/unistd_private.h b/headers/private/libroot/unistd_private.h new file mode 100644 index 0000000000..9d41c000f7 --- /dev/null +++ b/headers/private/libroot/unistd_private.h @@ -0,0 +1,23 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _LIBROOT_UNISTD_PRIVATE_H +#define _LIBROOT_UNISTD_PRIVATE_H + + +#include +#include + + +__BEGIN_DECLS + + +long __sysconf_beos(int name); +long __sysconf(int name); + + +__END_DECLS + + +#endif // _LIBROOT_UNISTD_PRIVATE_H diff --git a/headers/private/shared/syscall_utils.h b/headers/private/shared/syscall_utils.h index 3498b5bb26..f5890e87f9 100644 --- a/headers/private/shared/syscall_utils.h +++ b/headers/private/shared/syscall_utils.h @@ -1,18 +1,38 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef _SYSCALL_UTILS_H #define _SYSCALL_UTILS_H + #define RETURN_AND_SET_ERRNO(err) \ do { \ - __typeof(err) raseResult = (err); \ - if (raseResult < 0) { \ - errno = raseResult; \ + __typeof(err) __result = (err); \ + if (__result < 0) { \ + errno = __result; \ return -1; \ } \ - return raseResult; \ - } while (false) + return __result; \ + } while (0) + +#define RETURN_AND_TEST_CANCEL(err) \ + do { \ + __typeof(err) __result = (err); \ + pthread_testcancel(); \ + return __result; \ + } while (0) + +#define RETURN_AND_SET_ERRNO_TEST_CANCEL(err) \ + do { \ + __typeof(err) __result = (err); \ + pthread_testcancel(); \ + if (__result < 0) { \ + errno = __result; \ + return -1; \ + } \ + return __result; \ + } while (0) + #endif // _SYSCALL_UTILS_H diff --git a/headers/private/system/arch/x86/arch_commpage_defs.h b/headers/private/system/arch/x86/arch_commpage_defs.h index a61678dd03..5f27f676ee 100644 --- a/headers/private/system/arch/x86/arch_commpage_defs.h +++ b/headers/private/system/arch/x86/arch_commpage_defs.h @@ -12,6 +12,10 @@ #define COMMPAGE_ENTRY_X86_SYSCALL (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 0) #define COMMPAGE_ENTRY_X86_MEMCPY (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 1) #define COMMPAGE_ENTRY_X86_MEMSET (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 2) +#define COMMPAGE_ENTRY_X86_SIGNAL_HANDLER \ + (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 3) +#define COMMPAGE_ENTRY_X86_SIGNAL_HANDLER_BEOS \ + (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 4) #define ARCH_USER_COMMPAGE_ADDR (0xffff0000) diff --git a/headers/private/system/signal_defs.h b/headers/private/system/signal_defs.h new file mode 100644 index 0000000000..b3e6a7eb65 --- /dev/null +++ b/headers/private/system/signal_defs.h @@ -0,0 +1,43 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _SYSTEM_SIGNAL_DEFS_H +#define _SYSTEM_SIGNAL_DEFS_H + + +#include +#include + + +// The total number of signals a process may have queued at receivers at any +// time. +#define MAX_QUEUED_SIGNALS _POSIX_SIGQUEUE_MAX + +// realtime signal number range +#define SIGNAL_REALTIME_MIN 33 +#define SIGNAL_REALTIME_MAX 40 + +// greatest actually supported signal number +#define MAX_SIGNAL_NUMBER SIGNAL_REALTIME_MAX + +// additional send_signal_etc() flags +#define SIGNAL_FLAG_QUEUING_REQUIRED (0x10000) + // force signal queuing, i.e. fail instead of falling back to unqueued + // signals, when queuing isn't possible +#define SIGNAL_FLAG_SEND_TO_THREAD (0x20000) + // interpret the the given ID as a thread_id rather than a team_id (syscall + // use only) + +// additional sigaction::sa_flags flag +#define SA_BEOS_COMPATIBLE_HANDLER 0x80000000 + // BeOS compatible signal handler, i.e. the vregs argument is passed + // per-value, not per-pointer + +#define SIGNAL_TO_MASK(signal) ((sigset_t)1 << ((signal) - 1)) +#define SIGNAL_RANGE_TO_MASK(first, last) \ + ((((SIGNAL_TO_MASK(last) - 1) << 1) | 1) & ~(SIGNAL_TO_MASK(first) - 1)) + // Note: The last mask computation looks that way to avoid an overflow for + // last == 64. + +#endif /* _SYSTEM_SIGNAL_DEFS_H */ diff --git a/headers/private/system/syscalls.h b/headers/private/system/syscalls.h index cd510bdf2d..09488ba3af 100644 --- a/headers/private/system/syscalls.h +++ b/headers/private/system/syscalls.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2010, Haiku Inc. All rights reserved. + * Copyright 2004-2011, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _SYSTEM_SYSCALLS_H @@ -35,8 +35,10 @@ struct _sem_t; struct sembuf; union semun; struct sigaction; +struct signal_frame_data; struct stat; struct system_profiler_parameters; +struct user_timer_info; struct disk_device_job_progress_info; struct partitionable_space_data; @@ -134,8 +136,8 @@ extern void __NO_RETURN _kern_exit_team(status_t returnValue); extern status_t _kern_kill_team(team_id team); extern team_id _kern_get_current_team(); extern status_t _kern_wait_for_team(team_id team, status_t *_returnCode); -extern thread_id _kern_wait_for_child(thread_id child, uint32 flags, - int32 *_reason, status_t *_returnCode); +extern pid_t _kern_wait_for_child(thread_id child, uint32 flags, + siginfo_t* info); extern status_t _kern_exec(const char *path, const char* const* flatArgs, size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask); @@ -155,6 +157,8 @@ extern status_t _kern_set_thread_priority(thread_id thread, int32 newPriority); extern status_t _kern_kill_thread(thread_id thread); extern void _kern_exit_thread(status_t returnValue); +extern status_t _kern_cancel_thread(thread_id threadID, + void (*cancelFunction)(int)); extern void _kern_thread_yield(void); extern status_t _kern_wait_for_thread(thread_id thread, status_t *_returnCode); @@ -163,7 +167,8 @@ extern status_t _kern_send_data(thread_id thread, int32 code, const void *buffer, size_t bufferSize); extern int32 _kern_receive_data(thread_id *_sender, void *buffer, size_t bufferSize); -extern int64 _kern_restore_signal_frame(); +extern int64 _kern_restore_signal_frame( + struct signal_frame_data* signalFrameData); extern status_t _kern_get_thread_info(thread_id id, thread_info *info); extern status_t _kern_get_next_thread_info(team_id team, int32 *cookie, @@ -198,13 +203,14 @@ extern ssize_t _kern_getgroups(int groupCount, gid_t* groupList); extern status_t _kern_setgroups(int groupCount, const gid_t* groupList); // signal functions -extern status_t _kern_send_signal(pid_t tid, uint sig); -extern status_t _kern_sigprocmask(int how, const sigset_t *set, +extern status_t _kern_send_signal(int32 id, uint32 signal, + const union sigval* userValue, uint32 flags); +extern status_t _kern_set_signal_mask(int how, const sigset_t *set, sigset_t *oldSet); extern status_t _kern_sigaction(int sig, const struct sigaction *action, struct sigaction *oldAction); -extern bigtime_t _kern_set_alarm(bigtime_t time, uint32 mode); -extern status_t _kern_sigwait(const sigset_t *set, int *_signal); +extern status_t _kern_sigwait(const sigset_t *set, siginfo_t *info, + uint32 flags, bigtime_t timeout); extern status_t _kern_sigsuspend(const sigset_t *mask); extern status_t _kern_sigpending(sigset_t *set); extern status_t _kern_set_signal_stack(const stack_t *newStack, @@ -370,7 +376,7 @@ extern status_t _kern_stop_watching(dev_t device, ino_t node, port_id port, uint32 token); // time functions -extern status_t _kern_set_real_time_clock(uint32 time); +extern status_t _kern_set_real_time_clock(bigtime_t time); extern status_t _kern_set_timezone(int32 timezoneOffset, const char *name, size_t nameLength); extern status_t _kern_get_timezone(int32 *_timezoneOffset, char *name, @@ -378,8 +384,23 @@ extern status_t _kern_get_timezone(int32 *_timezoneOffset, char *name, extern status_t _kern_set_real_time_clock_is_gmt(bool isGMT); extern status_t _kern_get_real_time_clock_is_gmt(bool *_isGMT); +extern status_t _kern_get_clock(clockid_t clockID, bigtime_t* _time); +extern status_t _kern_set_clock(clockid_t clockID, bigtime_t time); + extern bigtime_t _kern_system_time(); -extern status_t _kern_snooze_etc(bigtime_t time, int timebase, int32 flags); +extern status_t _kern_snooze_etc(bigtime_t time, int timebase, int32 flags, + bigtime_t* _remainingTime); + +extern int32 _kern_create_timer(clockid_t clockID, thread_id threadID, + uint32 flags, const struct sigevent* event, + const struct thread_creation_attributes* + threadAttributes); +extern status_t _kern_delete_timer(int32 timerID, thread_id threadID); +extern status_t _kern_get_timer(int32 timerID, thread_id threadID, + struct user_timer_info* info); +extern status_t _kern_set_timer(int32 timerID, thread_id threadID, + bigtime_t startTime, bigtime_t interval, uint32 flags, + struct user_timer_info* oldInfo); // area functions extern area_id _kern_create_area(const char *name, void **address, diff --git a/headers/private/system/thread_defs.h b/headers/private/system/thread_defs.h index ab990190e2..9f1b527b8e 100644 --- a/headers/private/system/thread_defs.h +++ b/headers/private/system/thread_defs.h @@ -1,18 +1,16 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef _SYSTEM_THREAD_DEFS_H #define _SYSTEM_THREAD_DEFS_H + +#include + #include -#define THREAD_RETURN_EXIT 0x1 -#define THREAD_RETURN_INTERRUPTED 0x2 -#define THREAD_STOPPED 0x3 -#define THREAD_CONTINUED 0x4 - /** Size of the stack given to teams in user space */ #define USER_STACK_GUARD_PAGES 4 // 16 kB #define USER_MAIN_THREAD_STACK_SIZE (16 * 1024 * 1024 \ @@ -39,18 +37,21 @@ enum { }; +#define THREAD_CREATION_FLAG_DEFER_SIGNALS 0x01 + // create the thread with signals deferred, i.e. with + // user_thread::defer_signals set to 1 + + struct thread_creation_attributes { - int32 (*entry)(thread_func, void *); + int32 (*entry)(void*, void*); const char* name; int32 priority; void* args1; void* args2; void* stack_address; size_t stack_size; - - // when calling from kernel only - team_id team; - thread_id thread; + pthread_t pthread; + uint32 flags; }; #endif /* _SYSTEM_THREAD_DEFS_H */ diff --git a/headers/private/system/tls.h b/headers/private/system/tls.h index ebb470d859..f2105da1fb 100644 --- a/headers/private/system/tls.h +++ b/headers/private/system/tls.h @@ -19,7 +19,6 @@ enum { TLS_ERRNO_SLOT, TLS_ON_EXIT_THREAD_SLOT, TLS_USER_THREAD_SLOT, - TLS_PTHREAD_SLOT, // Note: these entries can safely be changed between // releases; 3rd party code always calls tls_allocate() diff --git a/headers/private/system/user_thread_defs.h b/headers/private/system/user_thread_defs.h index 0bdd1f791a..ccf80e8b01 100644 --- a/headers/private/system/user_thread_defs.h +++ b/headers/private/system/user_thread_defs.h @@ -1,18 +1,24 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef _SYSTEM_USER_THREAD_DEFS_H #define _SYSTEM_USER_THREAD_DEFS_H + +#include +#include + #include struct user_thread { + pthread_t pthread; // pthread pointer + uint32 flags; + status_t wait_status; // wait status for thread blocking int32 defer_signals; // counter; 0 == signals allowed - uint32 pending_signals; // signals that are pending, when + sigset_t pending_signals; // signals that are pending, when // signals are deferred - status_t wait_status; }; diff --git a/headers/private/system/user_timer_defs.h b/headers/private/system/user_timer_defs.h new file mode 100644 index 0000000000..fdb2b782b2 --- /dev/null +++ b/headers/private/system/user_timer_defs.h @@ -0,0 +1,50 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _SYSTEM_USER_TIMER_DEFS_H +#define _SYSTEM_USER_TIMER_DEFS_H + + +#include +#include + +#include + + +#define CLOCK_PROCESS_USER_CPUTIME_ID ((clockid_t)-4) + /* clock measuring the used user CPU time of the current process */ + +// limits +#define MAX_USER_TIMERS_PER_TEAM _POSIX_TIMER_MAX + // maximum numbers of user-defined user timers (timer_create()) +#define MAX_USER_TIMER_OVERRUN_COUNT INT_MAX + // cap value of a timer's overrun counter + +#if MAX_USER_TIMER_OVERRUN_COUNT < _POSIX_DELAYTIMER_MAX +# error "MAX_USER_TIMER_OVERRUN_COUNT < _POSIX_DELAYTIMER_MAX" +#endif + +#define USER_TIMER_REAL_TIME_ID 0 + // predefined ID for the real time timer +#define USER_TIMER_TEAM_TOTAL_TIME_ID 1 + // predefined ID for the team's total (kernel + user) time timer +#define USER_TIMER_TEAM_USER_TIME_ID 2 + // predefined ID for the team's user time timer +#define USER_TIMER_FIRST_USER_DEFINED_ID 3 + // first ID assigned to a user-defined timer (timer_create()) + +// _kern_create_user_timer() flag: +#define USER_TIMER_SIGNAL_THREAD 0x01 + // send the signal to the thread instead of the team (valid only for thread + // timers) + + +struct user_timer_info { + bigtime_t remaining_time; + bigtime_t interval; + uint32 overrun_count; +}; + + +#endif /* _SYSTEM_USER_TIMER_DEFS_H */ diff --git a/src/add-ons/kernel/debugger/invalidate_on_exit/invalidate_on_exit.cpp b/src/add-ons/kernel/debugger/invalidate_on_exit/invalidate_on_exit.cpp index 68cee2dd71..189247c348 100644 --- a/src/add-ons/kernel/debugger/invalidate_on_exit/invalidate_on_exit.cpp +++ b/src/add-ons/kernel/debugger/invalidate_on_exit/invalidate_on_exit.cpp @@ -50,7 +50,7 @@ std_ops(int32 op, ...) if (thread < B_OK) return thread; - send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); + resume_thread(thread); return B_OK; } else if (op == B_MODULE_UNINIT) { // deleting the sem will also cause the thread to exit @@ -76,7 +76,7 @@ static struct debugger_module_info sModuleInfo = { NULL }; -module_info *modules[] = { +module_info *modules[] = { (module_info *)&sModuleInfo, NULL }; diff --git a/src/add-ons/kernel/debugger/run_on_exit/run_on_exit.cpp b/src/add-ons/kernel/debugger/run_on_exit/run_on_exit.cpp index 7bcc488a32..1987fd03c8 100644 --- a/src/add-ons/kernel/debugger/run_on_exit/run_on_exit.cpp +++ b/src/add-ons/kernel/debugger/run_on_exit/run_on_exit.cpp @@ -103,7 +103,7 @@ std_ops(int32 op, ...) if (thread < B_OK) return thread; - send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); + resume_thread(thread); add_debugger_command_etc("on_exit", &add_run_on_exit_command, "Adds a command to be run when leaving the kernel debugger", @@ -136,7 +136,7 @@ static struct debugger_module_info sModuleInfo = { NULL }; -module_info *modules[] = { +module_info *modules[] = { (module_info *)&sModuleInfo, NULL }; diff --git a/src/apps/terminal/TermApp.cpp b/src/apps/terminal/TermApp.cpp index 37d885ca08..c72fa6aaf1 100644 --- a/src/apps/terminal/TermApp.cpp +++ b/src/apps/terminal/TermApp.cpp @@ -77,7 +77,7 @@ TermApp::ReadyToRun() // a shell exits. struct sigaction action; #ifdef __HAIKU__ - action.sa_handler = (sighandler_t)_SigChildHandler; + action.sa_handler = (__sighandler_t)_SigChildHandler; #else action.sa_handler = (__signal_func_ptr)_SigChildHandler; #endif diff --git a/src/bin/debug/profile/profile.cpp b/src/bin/debug/profile/profile.cpp index 0878237c07..1d2ddfcca9 100644 --- a/src/bin/debug/profile/profile.cpp +++ b/src/bin/debug/profile/profile.cpp @@ -653,7 +653,7 @@ profile_all(const char* const* programArgs, int programArgCount) // install signal handlers so we can exit gracefully struct sigaction action; - action.sa_handler = (sighandler_t)signal_handler; + action.sa_handler = (__sighandler_t)signal_handler; sigemptyset(&action.sa_mask); action.sa_userdata = NULL; if (sigaction(SIGHUP, &action, NULL) < 0 diff --git a/src/bin/debug/scheduling_recorder/scheduling_recorder.cpp b/src/bin/debug/scheduling_recorder/scheduling_recorder.cpp index 04a64e3e71..0be34b541f 100644 --- a/src/bin/debug/scheduling_recorder/scheduling_recorder.cpp +++ b/src/bin/debug/scheduling_recorder/scheduling_recorder.cpp @@ -116,7 +116,7 @@ public: // install signal handlers so we can exit gracefully struct sigaction action; - action.sa_handler = (sighandler_t)_SignalHandler; + action.sa_handler = (__sighandler_t)_SignalHandler; sigemptyset(&action.sa_mask); action.sa_userdata = this; if (sigaction(SIGHUP, &action, NULL) < 0 diff --git a/src/bin/debug/strace/MemoryReader.cpp b/src/bin/debug/strace/MemoryReader.cpp index 8b57c189a0..df593b6fc7 100644 --- a/src/bin/debug/strace/MemoryReader.cpp +++ b/src/bin/debug/strace/MemoryReader.cpp @@ -1,8 +1,9 @@ /* - * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ + #include #include #include @@ -11,27 +12,41 @@ #include "MemoryReader.h" -// constructor -MemoryReader::MemoryReader(port_id nubPort) - : fNubPort(nubPort), - fReplyPort(-1) + +MemoryReader::MemoryReader() + : + fNubPort(-1), + fReplyPort(-1) { - fReplyPort = create_port(1, "memory reader reply"); - if (fReplyPort < 0) { - fprintf(stderr, "Failed to create memory reader reply port: %s\n", - strerror(fReplyPort)); - exit(1); - } } -// constructor + MemoryReader::~MemoryReader() { if (fReplyPort >= 0) delete_port(fReplyPort); } -// Read + +status_t +MemoryReader::Init(port_id nubPort) +{ + if (fReplyPort >= 0) + delete_port(fReplyPort); + + fNubPort = nubPort; + + fReplyPort = create_port(1, "memory reader reply"); + if (fReplyPort < 0) { + fprintf(stderr, "Failed to create memory reader reply port: %s\n", + strerror(fReplyPort)); + return fReplyPort; + } + + return B_OK; +} + + status_t MemoryReader::Read(void *_address, void *_buffer, int32 size, int32 &bytesRead) { @@ -63,7 +78,7 @@ MemoryReader::Read(void *_address, void *_buffer, int32 size, int32 &bytesRead) return B_OK; } -// _Read + status_t MemoryReader::_Read(void *address, void *buffer, int32 size, int32 &bytesRead) { diff --git a/src/bin/debug/strace/MemoryReader.h b/src/bin/debug/strace/MemoryReader.h index 028be82ba8..b625b9b909 100644 --- a/src/bin/debug/strace/MemoryReader.h +++ b/src/bin/debug/strace/MemoryReader.h @@ -1,24 +1,31 @@ /* - * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef STRACE_MEMORY_READER_H #define STRACE_MEMORY_READER_H + #include + class MemoryReader { public: - MemoryReader(port_id nubPort); - ~MemoryReader(); + MemoryReader(); + ~MemoryReader(); - status_t Read(void *address, void *buffer, int32 size, int32 &bytesRead); + status_t Init(port_id nubPort); + + status_t Read(void *address, void *buffer, int32 size, + int32 &bytesRead); private: - status_t _Read(void *address, void *buffer, int32 size, int32 &bytesRead); + status_t _Read(void *address, void *buffer, int32 size, + int32 &bytesRead); - port_id fNubPort; - port_id fReplyPort; +private: + port_id fNubPort; + port_id fReplyPort; }; diff --git a/src/bin/debug/strace/strace.cpp b/src/bin/debug/strace/strace.cpp index b808f33a84..dbb332f2fb 100644 --- a/src/bin/debug/strace/strace.cpp +++ b/src/bin/debug/strace/strace.cpp @@ -1,8 +1,9 @@ /* - * Copyright 2005-2008, Ingo Weinhold, bonefish@users.sf.net. + * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ + #include #include #include @@ -25,10 +26,12 @@ #include "Syscall.h" #include "TypeHandler.h" + using std::map; using std::string; using std::vector; + extern void get_syscalls0(vector &syscalls); extern void get_syscalls1(vector &syscalls); extern void get_syscalls2(vector &syscalls); @@ -50,9 +53,11 @@ extern void get_syscalls17(vector &syscalls); extern void get_syscalls18(vector &syscalls); extern void get_syscalls19(vector &syscalls); + extern const char *__progname; static const char *kCommandName = __progname; + // usage static const char *kUsage = "Usage: %s [ ] [ | ]\n" @@ -74,6 +79,8 @@ static const char *kUsage = " -r - Don't print syscall return values.\n" " -s - Also trace all threads spawned by the supplied thread,\n" " respectively the loaded executable's main thread.\n" +" -t - Also recursively trace all teams created by a traced\n" +" thread or team.\n" " -T - Trace all threads of the supplied or loaded executable's\n" " team. If an ID is supplied, it is interpreted as a team\n" " ID.\n" @@ -82,6 +89,7 @@ static const char *kUsage = " -g - turns off signal tracing.\n" ; + // terminal color escape sequences // (http://www.dee.ufcg.edu.br/~rrbrandt/tools/ansi.html) static const char *kTerminalTextNormal = "\33[0m"; @@ -89,38 +97,6 @@ static const char *kTerminalTextRed = "\33[31m"; static const char *kTerminalTextMagenta = "\33[35m"; static const char *kTerminalTextBlue = "\33[34m"; -static const char *kSignalName[] = { - /* 0 */ "SIG0", - /* 1 */ "SIGHUP", - /* 2 */ "SIGINT", - /* 3 */ "SIGQUIT", - /* 4 */ "SIGILL", - /* 5 */ "SIGCHLD", - /* 6 */ "SIGABRT", - /* 7 */ "SIGPIPE", - /* 8 */ "SIGFPE", - /* 9 */ "SIGKILL", - /* 10 */ "SIGSTOP", - /* 11 */ "SIGSEGV", - /* 12 */ "SIGCONT", - /* 13 */ "SIGTSTP", - /* 14 */ "SIGALRM", - /* 15 */ "SIGTERM", - /* 16 */ "SIGTTIN", - /* 17 */ "SIGTTOU", - /* 18 */ "SIGUSR1", - /* 19 */ "SIGUSR2", - /* 20 */ "SIGWINCH", - /* 21 */ "SIGKILLTHR", - /* 22 */ "SIGTRAP", - /* 23 */ "SIGPOLL", - /* 24 */ "SIGPROF", - /* 25 */ "SIGSYS", - /* 26 */ "SIGURG", - /* 27 */ "SIGVTALRM", - /* 28 */ "SIGXCPU", - /* 29 */ "SIGXFSZ", -}; // command line args static int sArgc; @@ -130,26 +106,73 @@ static const char *const *sArgv; static vector sSyscallVector; static map sSyscallMap; -// print_usage -void + +struct Team { + Team(team_id id) + : + fID(id), + fNubPort(-1) + { + } + + team_id ID() const + { + return fID; + } + + port_id NubPort() const + { + return fNubPort; + } + + MemoryReader& GetMemoryReader() + { + return fMemoryReader; + } + + status_t InstallDebugger(port_id debuggerPort, bool traceTeam, + bool traceChildTeams, bool traceSignal) + { + fNubPort = install_team_debugger(fID, debuggerPort); + if (fNubPort < 0) { + fprintf(stderr, "%s: Failed to install team debugger: %s\n", + kCommandName, strerror(fNubPort)); + return fNubPort; + } + + // set team debugging flags + int32 teamDebugFlags = (traceTeam ? B_TEAM_DEBUG_POST_SYSCALL : 0) + | (traceChildTeams ? B_TEAM_DEBUG_TEAM_CREATION : 0) + | (traceSignal ? B_TEAM_DEBUG_SIGNALS : 0); + set_team_debugging_flags(fNubPort, teamDebugFlags); + + return fMemoryReader.Init(fNubPort); + } + +private: + team_id fID; + port_id fNubPort; + MemoryReader fMemoryReader; +}; + + +static void print_usage(bool error) { // print usage fprintf((error ? stderr : stdout), kUsage, kCommandName); } -// print_usage_and_exit -static -void + +static void print_usage_and_exit(bool error) { print_usage(error); exit(error ? 1 : 0); } -// get_id -static -bool + +static bool get_id(const char *str, int32 &id) { int32 len = strlen(str); @@ -162,7 +185,7 @@ get_id(const char *str, int32 &id) return true; } -// get_syscall + Syscall * get_syscall(const char *name) { @@ -173,7 +196,7 @@ get_syscall(const char *name) return i->second; } -// patch_syscalls + static void patch_syscalls() { @@ -185,9 +208,8 @@ patch_syscalls() patch_ioctl(); } -// init_syscalls -static -void + +static void init_syscalls() { // init the syscall vector @@ -222,9 +244,8 @@ init_syscalls() patch_syscalls(); } -// print_to_string -static -void + +static void print_to_string(char **_buffer, int32 *_length, const char *format, ...) { va_list list; @@ -236,9 +257,8 @@ print_to_string(char **_buffer, int32 *_length, const char *format, ...) *_length -= length; } -// print_syscall -static -void + +static void print_syscall(FILE *outputFile, debug_post_syscall &message, MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags, bool printReturnValue, bool colorize, bool decimal) @@ -324,21 +344,20 @@ print_syscall(FILE *outputFile, debug_post_syscall &message, _kern_debug_output(buffer); } -static -const char * + +static const char * signal_name(int signal) { if (signal >= 0 && signal < NSIG) - return kSignalName[signal]; + return strsignal(signal); static char buffer[32]; sprintf(buffer, "%d", signal); return buffer; } -// print_signal -static -void + +static void print_signal(FILE *outputFile, debug_signal_received &message, bool colorize) { @@ -364,7 +383,7 @@ print_signal(FILE *outputFile, debug_signal_received &message, _kern_debug_output(buffer); } -// main + int main(int argc, const char *const *argv) { @@ -383,6 +402,7 @@ main(int argc, const char *const *argv) bool printReturnValues = true; bool traceChildThreads = false; bool traceTeam = false; + bool traceChildTeams = false; bool traceSignal = true; bool serialOutput = false; FILE *outputFile = stdout; @@ -433,6 +453,8 @@ main(int argc, const char *const *argv) printReturnValues = false; } else if (strcmp(arg, "-s") == 0) { traceChildThreads = true; + } else if (strcmp(arg, "-t") == 0) { + traceChildTeams = true; } else if (strcmp(arg, "-T") == 0) { traceTeam = true; } else if (strcmp(arg, "-g") == 0) { @@ -488,29 +510,29 @@ main(int argc, const char *const *argv) colorize = false; // get thread/team to be debugged - thread_id thread = -1; - team_id team = -1; + thread_id threadID = -1; + team_id teamID = -1; if (programArgCount > 1 - || !get_id(*programArgs, (traceTeam ? team : thread))) { + || !get_id(*programArgs, (traceTeam ? teamID : threadID))) { // we've been given an executable and need to load it - thread = load_program(programArgs, programArgCount, traceLoading); - if (thread < 0) { + threadID = load_program(programArgs, programArgCount, traceLoading); + if (threadID < 0) { fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, - programArgs[0], strerror(thread)); + programArgs[0], strerror(threadID)); exit(1); } } // get the team ID, if we have none yet - if (team < 0) { + if (teamID < 0) { thread_info threadInfo; - status_t error = get_thread_info(thread, &threadInfo); + status_t error = get_thread_info(threadID, &threadInfo); if (error != B_OK) { fprintf(stderr, "%s: Failed to get info for thread %ld: %s\n", - kCommandName, thread, strerror(error)); + kCommandName, threadID, strerror(error)); exit(1); } - team = threadInfo.team; + teamID = threadInfo.team; } // create a debugger port @@ -522,34 +544,36 @@ main(int argc, const char *const *argv) } // install ourselves as the team debugger - port_id nubPort = install_team_debugger(team, debuggerPort); - if (nubPort < 0) { - fprintf(stderr, "%s: Failed to install team debugger: %s\n", - kCommandName, strerror(nubPort)); - exit(1); + typedef map TeamMap; + TeamMap debuggedTeams; + port_id nubPort; + + { + Team* team = new Team(teamID); + status_t error = team->InstallDebugger(debuggerPort, traceTeam, + traceChildTeams, traceSignal); + if (error != B_OK) + exit(1); + + debuggedTeams[team->ID()] = team; + + nubPort = team->NubPort(); } - // set team debugging flags - int32 teamDebugFlags = (traceTeam ? B_TEAM_DEBUG_POST_SYSCALL : 0) - | (traceSignal ? B_TEAM_DEBUG_SIGNALS : 0); - set_team_debugging_flags(nubPort, teamDebugFlags); - // set thread debugging flags - if (thread >= 0) { + if (threadID >= 0) { int32 threadDebugFlags = 0; if (!traceTeam) { threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL | (traceChildThreads ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0); } - set_thread_debugging_flags(nubPort, thread, threadDebugFlags); + set_thread_debugging_flags(nubPort, threadID, threadDebugFlags); // resume the target thread to be sure, it's running - resume_thread(thread); + resume_thread(threadID); } - MemoryReader memoryReader(nubPort); - // debug loop while (true) { bool quitLoop = false; @@ -570,6 +594,13 @@ main(int argc, const char *const *argv) switch (code) { case B_DEBUGGER_MESSAGE_POST_SYSCALL: { + TeamMap::iterator it = debuggedTeams.find(message.origin.team); + if (it == debuggedTeams.end()) + break; + + Team* team = it->second; + MemoryReader& memoryReader = team->GetMemoryReader(); + print_syscall(outputFile, message.post_syscall, memoryReader, printArguments, contentsFlags, printReturnValues, colorize, decimalFormat); @@ -578,10 +609,8 @@ main(int argc, const char *const *argv) case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: { - if (traceSignal) { - print_signal(outputFile, message.signal_received, - colorize); - } + if (traceSignal) + print_signal(outputFile, message.signal_received, colorize); break; } @@ -592,17 +621,50 @@ main(int argc, const char *const *argv) case B_DEBUGGER_MESSAGE_SINGLE_STEP: case B_DEBUGGER_MESSAGE_PRE_SYSCALL: case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: - case B_DEBUGGER_MESSAGE_TEAM_CREATED: case B_DEBUGGER_MESSAGE_THREAD_CREATED: case B_DEBUGGER_MESSAGE_THREAD_DELETED: case B_DEBUGGER_MESSAGE_IMAGE_CREATED: case B_DEBUGGER_MESSAGE_IMAGE_DELETED: break; - case B_DEBUGGER_MESSAGE_TEAM_DELETED: - // the debugged team is gone - quitLoop = true; + case B_DEBUGGER_MESSAGE_TEAM_CREATED: + { + if (!traceChildTeams) + break; + + Team* team = new(std::nothrow) Team( + message.team_created.new_team); + if (team == NULL) { + fprintf(stderr, "%s: Out of memory!\n", kCommandName); + break; + } + + status_t error = team->InstallDebugger(debuggerPort, true, true, + traceSignal); + if (error != B_OK) { + delete team; + break; + } + + debuggedTeams[team->ID()] = team; break; + } + + case B_DEBUGGER_MESSAGE_TEAM_DELETED: + { + // a debugged team is gone + TeamMap::iterator it = debuggedTeams.find(message.origin.team); + if (it == debuggedTeams.end()) + break; + + Team* team = it->second; + debuggedTeams.erase(it); + delete team; + + // if all debugged teams are gone, we're done + quitLoop = debuggedTeams.empty(); + break; + } } if (quitLoop) diff --git a/src/bin/gdb/libiberty/config.h b/src/bin/gdb/libiberty/config.h index 694d1fbba3..837f5fdd82 100644 --- a/src/bin/gdb/libiberty/config.h +++ b/src/bin/gdb/libiberty/config.h @@ -114,7 +114,7 @@ /* #undef HAVE_ON_EXIT */ /* Define to 1 if you have the `psignal' function. */ -/* #undef HAVE_PSIGNAL */ +#define HAVE_PSIGNAL 1 /* Define to 1 if you have the `pstat_getdynamic' function. */ /* #undef HAVE_PSTAT_GETDYNAMIC */ diff --git a/src/kits/network/socket.cpp b/src/kits/network/socket.cpp index 5aa32eb855..9daf8ec2cb 100644 --- a/src/kits/network/socket.cpp +++ b/src/kits/network/socket.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -176,7 +177,8 @@ connect(int socket, const struct sockaddr *address, socklen_t addressLength) addressLength = sizeof(struct sockaddr_in); } - RETURN_AND_SET_ERRNO(_kern_connect(socket, address, addressLength)); + RETURN_AND_SET_ERRNO_TEST_CANCEL( + _kern_connect(socket, address, addressLength)); } @@ -205,6 +207,9 @@ accept(int socket, struct sockaddr *_address, socklen_t *_addressLength) } int acceptSocket = _kern_accept(socket, address, &addressLength); + + pthread_testcancel(); + if (acceptSocket < 0) { errno = acceptSocket; return -1; @@ -224,7 +229,7 @@ accept(int socket, struct sockaddr *_address, socklen_t *_addressLength) extern "C" ssize_t recv(int socket, void *data, size_t length, int flags) { - RETURN_AND_SET_ERRNO(_kern_recv(socket, data, length, flags)); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recv(socket, data, length, flags)); } @@ -248,6 +253,9 @@ recvfrom(int socket, void *data, size_t length, int flags, ssize_t bytesReceived = _kern_recvfrom(socket, data, length, flags, address, &addressLength); + + pthread_testcancel(); + if (bytesReceived < 0) { errno = bytesReceived; return -1; @@ -267,14 +275,14 @@ recvfrom(int socket, void *data, size_t length, int flags, extern "C" ssize_t recvmsg(int socket, struct msghdr *message, int flags) { - RETURN_AND_SET_ERRNO(_kern_recvmsg(socket, message, flags)); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recvmsg(socket, message, flags)); } extern "C" ssize_t send(int socket, const void *data, size_t length, int flags) { - RETURN_AND_SET_ERRNO(_kern_send(socket, data, length, flags)); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_send(socket, data, length, flags)); } @@ -290,15 +298,15 @@ sendto(int socket, const void *data, size_t length, int flags, addressLength = sizeof(struct sockaddr_in); } - RETURN_AND_SET_ERRNO(_kern_sendto(socket, data, length, flags, address, - addressLength)); + RETURN_AND_SET_ERRNO_TEST_CANCEL( + _kern_sendto(socket, data, length, flags, address, addressLength)); } extern "C" ssize_t sendmsg(int socket, const struct msghdr *message, int flags) { - RETURN_AND_SET_ERRNO(_kern_sendmsg(socket, message, flags)); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_sendmsg(socket, message, flags)); } diff --git a/src/libs/posix_error_mapper/Jamfile b/src/libs/posix_error_mapper/Jamfile index e843480cce..4203401086 100644 --- a/src/libs/posix_error_mapper/Jamfile +++ b/src/libs/posix_error_mapper/Jamfile @@ -14,4 +14,5 @@ StaticLibrary libposix_error_mapper.a : pthread_spinlock.cpp pthread_thread.cpp signal.cpp + time.cpp ; diff --git a/src/libs/posix_error_mapper/pthread_thread.cpp b/src/libs/posix_error_mapper/pthread_thread.cpp index 7fcca73b96..4d66a91e70 100644 --- a/src/libs/posix_error_mapper/pthread_thread.cpp +++ b/src/libs/posix_error_mapper/pthread_thread.cpp @@ -1,10 +1,11 @@ /* - * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include +#include #include "posix_error_mapper.h" diff --git a/src/libs/posix_error_mapper/signal.cpp b/src/libs/posix_error_mapper/signal.cpp index eac3a8012b..bfaf4e0ce9 100644 --- a/src/libs/posix_error_mapper/signal.cpp +++ b/src/libs/posix_error_mapper/signal.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2010-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -13,3 +13,8 @@ WRAPPER_FUNCTION(int, pthread_sigmask, (int how, const sigset_t *set, sigset_t *oldSet), return B_TO_POSITIVE_ERROR(sReal_pthread_sigmask(how, set, oldSet)); ) + + +WRAPPER_FUNCTION(int, sigwait, (const sigset_t *set, int *signal), + return B_TO_POSITIVE_ERROR(sReal_sigwait(set, signal)); +) diff --git a/src/libs/posix_error_mapper/time.cpp b/src/libs/posix_error_mapper/time.cpp new file mode 100644 index 0000000000..fee45a0aef --- /dev/null +++ b/src/libs/posix_error_mapper/time.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include "posix_error_mapper.h" + + +WRAPPER_FUNCTION(int, clock_nanosleep, + (clockid_t clockID, int flags, const struct timespec* time, + struct timespec* remainingTime), + return B_TO_POSITIVE_ERROR(sReal_clock_nanosleep(clockID, flags, time, + remainingTime)); +) + + +WRAPPER_FUNCTION(int, clock_getcpuclockid, + (pid_t pid, clockid_t* _clockID), + return B_TO_POSITIVE_ERROR(sReal_clock_getcpuclockid(pid, _clockID)); +) diff --git a/src/system/kernel/DPC.cpp b/src/system/kernel/DPC.cpp new file mode 100644 index 0000000000..7d64ca4a84 --- /dev/null +++ b/src/system/kernel/DPC.cpp @@ -0,0 +1,327 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + + +#define NORMAL_PRIORITY B_NORMAL_PRIORITY +#define HIGH_PRIORITY B_URGENT_DISPLAY_PRIORITY +#define REAL_TIME_PRIORITY B_FIRST_REAL_TIME_PRIORITY + +#define DEFAULT_QUEUE_SLOT_COUNT 64 + + +static DPCQueue sNormalPriorityQueue; +static DPCQueue sHighPriorityQueue; +static DPCQueue sRealTimePriorityQueue; + + +// #pragma mark - FunctionDPCCallback + + +FunctionDPCCallback::FunctionDPCCallback(DPCQueue* owner) + : + fOwner(owner) +{ +} + + +void +FunctionDPCCallback::SetTo(void (*function)(void*), void* argument) +{ + fFunction = function; + fArgument = argument; +} + + +void +FunctionDPCCallback::DoDPC(DPCQueue* queue) +{ + fFunction(fArgument); + + if (fOwner != NULL) + fOwner->Recycle(this); +} + + +// #pragma mark - DPCCallback + + +DPCCallback::DPCCallback() + : + fInQueue(NULL) +{ +} + + +DPCCallback::~DPCCallback() +{ +} + + +// #pragma mark - DPCQueue + + +DPCQueue::DPCQueue() + : + fThreadID(-1), + fCallbackInProgress(NULL), + fCallbackDoneCondition(NULL) +{ + B_INITIALIZE_SPINLOCK(&fLock); + + fPendingCallbacksCondition.Init(this, "dpc queue"); +} + + +DPCQueue::~DPCQueue() +{ + // close, if not closed yet + { + InterruptsSpinLocker locker(fLock); + if (!_IsClosed()) { + locker.Unlock(); + Close(false); + } + } + + // delete function callbacks + while (DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead()) + delete callback; +} + + +/*static*/ DPCQueue* +DPCQueue::DefaultQueue(int priority) +{ + if (priority <= NORMAL_PRIORITY) + return &sNormalPriorityQueue; + + if (priority <= HIGH_PRIORITY) + return &sHighPriorityQueue; + + return &sRealTimePriorityQueue; +} + + +status_t +DPCQueue::Init(const char* name, int32 priority, uint32 reservedSlots) +{ + // create function callbacks + for (uint32 i = 0; i < reservedSlots; i++) { + FunctionDPCCallback* callback + = new(std::nothrow) FunctionDPCCallback(this); + if (callback == NULL) + return B_NO_MEMORY; + + fUnusedFunctionCallbacks.Add(callback); + } + + // spawn the thread + fThreadID = spawn_kernel_thread(&_ThreadEntry, name, priority, this); + if (fThreadID < 0) + return fThreadID; + + resume_thread(fThreadID); + + return B_OK; +} + + +void +DPCQueue::Close(bool cancelPending) +{ + InterruptsSpinLocker locker(fLock); + + if (_IsClosed()) + return; + + // If requested, dequeue all pending callbacks + if (cancelPending) + fCallbacks.MakeEmpty(); + + // mark the queue closed + thread_id thread = fThreadID; + fThreadID = -1; + + locker.Unlock(); + + // wake up the thread and wait for it + fPendingCallbacksCondition.NotifyAll(); + wait_for_thread(thread, NULL); +} + + +status_t +DPCQueue::Add(DPCCallback* callback, bool schedulerLocked) +{ + // queue the callback, if the queue isn't closed already + InterruptsSpinLocker locker(fLock); + + if (_IsClosed()) + return B_NOT_INITIALIZED; + + bool wasEmpty = fCallbacks.IsEmpty(); + fCallbacks.Add(callback); + callback->fInQueue = this; + + locker.Unlock(); + + // notify the condition variable, if necessary + if (wasEmpty) + fPendingCallbacksCondition.NotifyAll(schedulerLocked); + + return B_OK; +} + + +status_t +DPCQueue::Add(void (*function)(void*), void* argument, bool schedulerLocked) +{ + if (function == NULL) + return B_BAD_VALUE; + + // get a free callback + InterruptsSpinLocker locker(fLock); + + DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead(); + if (callback == NULL) + return B_NO_MEMORY; + + locker.Unlock(); + + // init the callback + FunctionDPCCallback* functionCallback + = static_cast(callback); + functionCallback->SetTo(function, argument); + + // add it + status_t error = Add(functionCallback, schedulerLocked); + if (error != B_OK) + Recycle(functionCallback); + + return error; +} + + +bool +DPCQueue::Cancel(DPCCallback* callback) +{ + InterruptsSpinLocker locker(fLock); + + // If the callback is queued, remove it. + if (callback->fInQueue == this) { + fCallbacks.Remove(callback); + return true; + } + + // The callback is not queued. If it isn't in progress, we're done, too. + if (callback != fCallbackInProgress) + return false; + + // The callback is currently being executed. We need to wait for it to be + // done. + + // Set the respective condition, if not set yet. For the unlikely case that + // there are multiple threads trying to cancel the callback at the same + // time, the condition variable of the first thread will be used. + ConditionVariable condition; + if (fCallbackDoneCondition == NULL) + fCallbackDoneCondition = &condition; + + // add our wait entry + ConditionVariableEntry waitEntry; + fCallbackDoneCondition->Add(&waitEntry); + + // wait + locker.Unlock(); + waitEntry.Wait(); + + return false; +} + + +void +DPCQueue::Recycle(FunctionDPCCallback* callback) +{ + InterruptsSpinLocker locker(fLock); + fUnusedFunctionCallbacks.Insert(callback, false); +} + + +/*static*/ status_t +DPCQueue::_ThreadEntry(void* data) +{ + return ((DPCQueue*)data)->_Thread(); +} + + +status_t +DPCQueue::_Thread() +{ + while (true) { + InterruptsSpinLocker locker(fLock); + + // get the next pending callback + DPCCallback* callback = fCallbacks.RemoveHead(); + if (callback == NULL) { + // nothing is pending -- wait unless the queue is already closed + if (_IsClosed()) + break; + + ConditionVariableEntry waitEntry; + fPendingCallbacksCondition.Add(&waitEntry); + + locker.Unlock(); + waitEntry.Wait(); + + continue; + } + + callback->fInQueue = NULL; + fCallbackInProgress = callback; + + // call the callback + locker.Unlock(); + callback->DoDPC(this); + locker.Lock(); + + fCallbackInProgress = NULL; + + // wake up threads waiting for the callback to be done + ConditionVariable* doneCondition = fCallbackDoneCondition; + fCallbackDoneCondition = NULL; + locker.Unlock(); + if (doneCondition != NULL) + doneCondition->NotifyAll(); + } + + return B_OK; +} + + +// #pragma mark - kernel private + + +void +dpc_init() +{ + // create the default queues + new(&sNormalPriorityQueue) DPCQueue; + new(&sHighPriorityQueue) DPCQueue; + new(&sRealTimePriorityQueue) DPCQueue; + + if (sNormalPriorityQueue.Init("dpc: normal priority", NORMAL_PRIORITY, + DEFAULT_QUEUE_SLOT_COUNT) != B_OK + || sHighPriorityQueue.Init("dpc: high priority", HIGH_PRIORITY, + DEFAULT_QUEUE_SLOT_COUNT) != B_OK + || sRealTimePriorityQueue.Init("dpc: real-time priority", + REAL_TIME_PRIORITY, DEFAULT_QUEUE_SLOT_COUNT) != B_OK) { + panic("Failed to create default DPC queues!"); + } +} diff --git a/src/system/kernel/Jamfile b/src/system/kernel/Jamfile index be743c5614..bfc433f38a 100644 --- a/src/system/kernel/Jamfile +++ b/src/system/kernel/Jamfile @@ -28,6 +28,7 @@ KernelMergeObject kernel_core.o : commpage.cpp condition_variable.cpp cpu.cpp + DPC.cpp elf.cpp heap.cpp image.cpp @@ -50,7 +51,9 @@ KernelMergeObject kernel_core.o : team.cpp thread.cpp timer.cpp + UserEvent.cpp usergroup.cpp + UserTimer.cpp wait_for_objects.cpp # locks diff --git a/src/system/kernel/TeamThreadTables.h b/src/system/kernel/TeamThreadTables.h new file mode 100644 index 0000000000..5816e934e1 --- /dev/null +++ b/src/system/kernel/TeamThreadTables.h @@ -0,0 +1,184 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef KERNEL_TEAM_THREAD_TABLES_H +#define KERNEL_TEAM_THREAD_TABLES_H + + +#include + + +namespace BKernel { + + +template +struct TeamThreadTable { +public: + typedef typename Element::id_type id_type; + typedef typename Element::iterator_type IteratorEntry; + + struct Iterator { + Iterator() + : + fNext(NULL) + { + } + + Iterator(IteratorEntry* nextEntry) + { + _SetNext(nextEntry); + } + + bool HasNext() const + { + return fNext != NULL; + } + + Element* Next() + { + Element* result = fNext; + if (result != NULL) + _SetNext(result->GetDoublyLinkedListLink()->next); + + return result; + } + + private: + void _SetNext(IteratorEntry* entry) + { + while (entry != NULL) { + if (entry->id >= 0) { + fNext = static_cast(entry); + return; + } + + entry = entry->GetDoublyLinkedListLink()->next; + } + + fNext = NULL; + } + + private: + Element* fNext; + }; + +public: + TeamThreadTable() + : + fNextSerialNumber(1) + { + } + + status_t Init(size_t initialSize) + { + return fTable.Init(initialSize); + } + + void Insert(Element* element) + { + element->serial_number = fNextSerialNumber++; + fTable.InsertUnchecked(element); + fList.Add(element); + } + + void Remove(Element* element) + { + fTable.RemoveUnchecked(element); + fList.Remove(element); + } + + Element* Lookup(id_type id, bool visibleOnly = true) const + { + Element* element = fTable.Lookup(id); + return element != NULL && (!visibleOnly || element->visible) + ? element : NULL; + } + + /*! Gets an iterator. + The iterator iterates through all, including invisible, entries! + */ + Iterator GetIterator() + { + return Iterator(fList.Head()); + } + + void InsertIteratorEntry(IteratorEntry* entry) + { + // add to front + entry->id = -1; + entry->visible = false; + fList.Add(entry, false); + } + + void RemoveIteratorEntry(IteratorEntry* entry) + { + fList.Remove(entry); + } + + Element* NextElement(IteratorEntry* entry, bool visibleOnly = true) + { + if (entry == fList.Tail()) + return NULL; + + IteratorEntry* nextEntry = entry; + + while (true) { + nextEntry = nextEntry->GetDoublyLinkedListLink()->next; + if (nextEntry == NULL) { + // end of list -- requeue entry at the end and return NULL + fList.Remove(entry); + fList.Add(entry); + return NULL; + } + + if (nextEntry->id >= 0 && (!visibleOnly || nextEntry->visible)) { + // found an element -- requeue entry after element + Element* element = static_cast(nextEntry); + fList.Remove(entry); + fList.InsertAfter(nextEntry, entry); + return element; + } + } + } + +private: + struct HashDefinition { + typedef id_type KeyType; + typedef Element ValueType; + + size_t HashKey(id_type key) const + { + return key; + } + + size_t Hash(Element* value) const + { + return HashKey(value->id); + } + + bool Compare(id_type key, Element* value) const + { + return value->id == key; + } + + Element*& GetLink(Element* value) const + { + return value->hash_next; + } + }; + + typedef BOpenHashTable ElementTable; + typedef DoublyLinkedList List; + +private: + ElementTable fTable; + List fList; + int64 fNextSerialNumber; +}; + + +} // namespace BKernel + + +#endif // KERNEL_TEAM_THREAD_TABLES_H diff --git a/src/system/kernel/UserEvent.cpp b/src/system/kernel/UserEvent.cpp new file mode 100644 index 0000000000..7d890d9cf5 --- /dev/null +++ b/src/system/kernel/UserEvent.cpp @@ -0,0 +1,243 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include +#include + + +// #pragma mark - UserEvent + + +UserEvent::~UserEvent() +{ +} + + +// #pragma mark - SignalEvent + + +struct SignalEvent::EventSignal : Signal { + EventSignal(uint32 number, int32 signalCode, int32 errorCode, + pid_t sendingProcess) + : + Signal(number, signalCode, errorCode, sendingProcess), + fInUse(false) + { + } + + bool IsInUse() const + { + return fInUse; + } + + void SetInUse(bool inUse) + { + fInUse = inUse; + } + + virtual void Handled() + { + // mark not-in-use + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + fInUse = false; + schedulerLocker.Unlock(); + + Signal::Handled(); + } + +private: + bool fInUse; +}; + + +SignalEvent::SignalEvent(EventSignal* signal) + : + fSignal(signal) +{ +} + + +SignalEvent::~SignalEvent() +{ + fSignal->ReleaseReference(); +} + + +void +SignalEvent::SetUserValue(union sigval userValue) +{ + fSignal->SetUserValue(userValue); +} + + +// #pragma mark - TeamSignalEvent + + +TeamSignalEvent::TeamSignalEvent(Team* team, EventSignal* signal) + : + SignalEvent(signal), + fTeam(team) +{ +} + + +/*static*/ TeamSignalEvent* +TeamSignalEvent::Create(Team* team, uint32 signalNumber, int32 signalCode, + int32 errorCode) +{ + // create the signal + EventSignal* signal = new(std::nothrow) EventSignal(signalNumber, + signalCode, errorCode, team->id); + if (signal == NULL) + return NULL; + + // create the event + TeamSignalEvent* event = new TeamSignalEvent(team, signal); + if (event == NULL) { + delete signal; + return NULL; + } + + return event; +} + + +status_t +TeamSignalEvent::Fire() +{ + // called with the scheduler lock held + if (fSignal->IsInUse()) + return B_BUSY; + + fSignal->AcquireReference(); + // one reference is transferred to send_signal_to_team_locked + status_t error = send_signal_to_team_locked(fTeam, fSignal->Number(), + fSignal, B_DO_NOT_RESCHEDULE); + if (error == B_OK) { + // Mark the signal in-use. There are situations (for certain signals), + // in which send_signal_to_team_locked() succeeds without queuing the + // signal. + fSignal->SetInUse(fSignal->IsPending()); + } + + return error; +} + + +// #pragma mark - ThreadSignalEvent + + +ThreadSignalEvent::ThreadSignalEvent(Thread* thread, EventSignal* signal) + : + SignalEvent(signal), + fThread(thread) +{ +} + + +/*static*/ ThreadSignalEvent* +ThreadSignalEvent::Create(Thread* thread, uint32 signalNumber, int32 signalCode, + int32 errorCode, pid_t sendingTeam) +{ + // create the signal + EventSignal* signal = new(std::nothrow) EventSignal(signalNumber, + signalCode, errorCode, sendingTeam); + if (signal == NULL) + return NULL; + + // create the event + ThreadSignalEvent* event = new ThreadSignalEvent(thread, signal); + if (event == NULL) { + delete signal; + return NULL; + } + + return event; +} + + +status_t +ThreadSignalEvent::Fire() +{ + // called with the scheduler lock held + if (fSignal->IsInUse()) + return B_BUSY; + + fSignal->AcquireReference(); + // one reference is transferred to send_signal_to_team_locked + status_t error = send_signal_to_thread_locked(fThread, fSignal->Number(), + fSignal, B_DO_NOT_RESCHEDULE); + if (error == B_OK) { + // Mark the signal in-use. There are situations (for certain signals), + // in which send_signal_to_team_locked() succeeds without queuing the + // signal. + fSignal->SetInUse(fSignal->IsPending()); + } + + return error; +} + + +// #pragma mark - UserEvent + + +CreateThreadEvent::CreateThreadEvent(const ThreadCreationAttributes& attributes) + : + fCreationAttributes(attributes), + fPendingDPC(false) +{ + // attributes.name is a pointer to a temporary buffer. Copy the name into + // our own buffer and replace the name pointer. + strlcpy(fThreadName, attributes.name, sizeof(fThreadName)); + fCreationAttributes.name = fThreadName; +} + + +CreateThreadEvent::~CreateThreadEvent() +{ + // cancel the DPC to be on the safe side + DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Cancel(this); +} + + +/*static*/ CreateThreadEvent* +CreateThreadEvent::Create(const ThreadCreationAttributes& attributes) +{ + return new(std::nothrow) CreateThreadEvent(attributes); +} + + +status_t +CreateThreadEvent::Fire() +{ + if (fPendingDPC) + return B_BUSY; + + fPendingDPC = true; + + DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this, true); + + return B_OK; +} + + +void +CreateThreadEvent::DoDPC(DPCQueue* queue) +{ + // We're no longer queued in the DPC queue, so we can be reused. + { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + fPendingDPC = false; + } + + // create the thread + thread_id threadID = thread_create_thread(fCreationAttributes, false); + if (threadID >= 0) + resume_thread(threadID); +} diff --git a/src/system/kernel/UserTimer.cpp b/src/system/kernel/UserTimer.cpp new file mode 100644 index 0000000000..8c0cabec15 --- /dev/null +++ b/src/system/kernel/UserTimer.cpp @@ -0,0 +1,1786 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + + +// Minimum interval length in microseconds for a periodic timer. This is not a +// restriction on the user timer interval length itself, but the minimum time +// span by which we advance the start time for kernel timers. A shorted user +// timer interval will result in the overrun count to be increased every time +// the kernel timer is rescheduled. +static const bigtime_t kMinPeriodicTimerInterval = 100; + +static RealTimeUserTimerList sAbsoluteRealTimeTimers; +static spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER; + + +// #pragma mark - TimerLocker + + +namespace { + +struct TimerLocker { + Team* team; + Thread* thread; + + TimerLocker() + : + team(NULL), + thread(NULL) + { + } + + ~TimerLocker() + { + Unlock(); + } + + void Lock(Team* team, Thread* thread) + { + this->team = team; + team->Lock(); + + this->thread = thread; + + if (thread != NULL) { + thread->AcquireReference(); + thread->Lock(); + } + + // We don't check thread->team != team here, since this method can be + // called for new threads not added to the team yet. + } + + status_t LockAndGetTimer(thread_id threadID, int32 timerID, + UserTimer*& _timer) + { + team = thread_get_current_thread()->team; + team->Lock(); + + if (threadID >= 0) { + thread = Thread::GetAndLock(threadID); + if (thread == NULL) + return B_BAD_THREAD_ID; + if (thread->team != team) + return B_NOT_ALLOWED; + } + + UserTimer* timer = thread != NULL + ? thread->UserTimerFor(timerID) : team->UserTimerFor(timerID); + if (timer == NULL) + return B_BAD_VALUE; + + _timer = timer; + return B_OK; + } + + void Unlock() + { + if (thread != NULL) { + thread->UnlockAndReleaseReference(); + thread = NULL; + } + if (team != NULL) { + team->Unlock(); + team = NULL; + } + } +}; + +} // unnamed namespace + + +// #pragma mark - UserTimer + + +UserTimer::UserTimer() + : + fID(-1), + fEvent(NULL), + fNextTime(0), + fInterval(0), + fOverrunCount(0), + fScheduled(false) +{ + // mark the timer unused + fTimer.user_data = this; +} + + +UserTimer::~UserTimer() +{ + delete fEvent; +} + + +/*! \fn UserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, + bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) + Cancels the timer, if it is already scheduled, and optionally schedules it + with new parameters. + + The caller must not hold the scheduler lock. + + \param nextTime The time at which the timer should go off the next time. If + \c B_INFINITE_TIMEOUT, the timer will not be scheduled. Whether the + value is interpreted as absolute or relative time, depends on \c flags. + \param interval If >0 , the timer will be scheduled to fire + periodically every \a interval microseconds. Otherwise it will fire + only once at \a nextTime. If \a nextTime is \c B_INFINITE_TIMEOUT, it + will fire never in either case. + \param flags Bitwise OR of flags. Currently \c B_ABSOLUTE_TIMEOUT and + \c B_RELATIVE_TIMEOUT are supported, indicating whether \a nextTime is + an absolute or relative time. + \param _oldRemainingTime Return variable that will be set to the + microseconds remaining to the time for which the timer was scheduled + next before the call. If it wasn't scheduled, the variable is set to + \c B_INFINITE_TIMEOUT. + \param _oldInterval Return variable that will be set to the interval in + microseconds the timer was to be scheduled periodically. If the timer + wasn't periodic, the variable is set to \c 0. +*/ + + +/*! Cancels the timer, if it is scheduled. + + The caller must not hold the scheduler lock. +*/ +void +UserTimer::Cancel() +{ + bigtime_t oldNextTime; + bigtime_t oldInterval; + return Schedule(B_INFINITE_TIMEOUT, 0, 0, oldNextTime, oldInterval); +} + + +/*! \fn UserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, + uint32& _overrunCount) + Return information on the current timer. + + The caller must not hold the scheduler lock. + + \param _remainingTime Return variable that will be set to the microseconds + remaining to the time for which the timer was scheduled next before the + call. If it wasn't scheduled, the variable is set to + \c B_INFINITE_TIMEOUT. + \param _interval Return variable that will be set to the interval in + microseconds the timer is to be scheduled periodically. If the timer + isn't periodic, the variable is set to \c 0. + \param _overrunCount Return variable that will be set to the number of times + the timer went off, but its event couldn't be delivered, since it's + previous delivery hasn't been handled yet. +*/ + + +/*static*/ int32 +UserTimer::HandleTimerHook(struct timer* timer) +{ + ((UserTimer*)timer->user_data)->HandleTimer(); + return B_HANDLED_INTERRUPT; +} + + +void +UserTimer::HandleTimer() +{ + if (fEvent != NULL) { + // fire the event and update the overrun count, if necessary + status_t error = fEvent->Fire(); + if (error == B_BUSY) { + if (fOverrunCount < MAX_USER_TIMER_OVERRUN_COUNT) + fOverrunCount++; + } + } + + // Since we don't use periodic kernel timers, it isn't scheduled anymore. + // If the timer is periodic, the derived class' version will schedule it + // again. + fScheduled = false; +} + + +/*! Updates the start time for a periodic timer after it expired, enforcing + sanity limits and updating \c fOverrunCount, if necessary. + + The caller must not hold the scheduler lock. +*/ +void +UserTimer::UpdatePeriodicStartTime() +{ + if (fInterval < kMinPeriodicTimerInterval) { + bigtime_t skip = (kMinPeriodicTimerInterval + fInterval - 1) / fInterval; + fNextTime += skip * fInterval; + + // One interval is the normal advance, so don't consider it skipped. + skip--; + + if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT) + fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT; + else + fOverrunCount += skip; + } else + fNextTime += fInterval; +} + + +/*! Checks whether the timer start time lies too much in the past and, if so, + adjusts it and updates \c fOverrunCount. + + The caller must not hold the scheduler lock. + + \param now The current time. +*/ +void +UserTimer::CheckPeriodicOverrun(bigtime_t now) +{ + if (fNextTime + fInterval > now) + return; + + // The start time is a full interval or more in the past. Skip those + // intervals. + bigtime_t skip = (now - fNextTime) / fInterval; + fNextTime += skip * fInterval; + + if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT) + fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT; + else + fOverrunCount += skip; +} + + +// #pragma mark - SystemTimeUserTimer + + +void +SystemTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + // get the current time + bigtime_t now = system_time(); + + // Cancel the old timer, if still scheduled, and get the previous values. + if (fScheduled) { + cancel_timer(&fTimer); + + _oldRemainingTime = fNextTime - now; + _oldInterval = fInterval; + + fScheduled = false; + } else { + _oldRemainingTime = B_INFINITE_TIMEOUT; + _oldInterval = 0; + } + + // schedule the new timer + fNextTime = nextTime; + fInterval = interval; + fOverrunCount = 0; + + if (nextTime == B_INFINITE_TIMEOUT) + return; + + if ((flags & B_RELATIVE_TIMEOUT) != 0) + fNextTime += now; + + ScheduleKernelTimer(now, fInterval > 0); +} + + +void +SystemTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, + uint32& _overrunCount) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + if (fScheduled) { + _remainingTime = fNextTime - system_time(); + _interval = fInterval; + } else { + _remainingTime = B_INFINITE_TIMEOUT; + _interval = 0; + } + + _overrunCount = fOverrunCount; +} + + +void +SystemTimeUserTimer::HandleTimer() +{ + UserTimer::HandleTimer(); + + // if periodic, reschedule the kernel timer + if (fInterval > 0) { + UpdatePeriodicStartTime(); + ScheduleKernelTimer(system_time(), true); + } +} + + +/*! Schedules the kernel timer. + + The caller must hold the scheduler lock. + + \param now The current system time to be used. + \param checkPeriodicOverrun If \c true, calls CheckPeriodicOverrun() first, + i.e. the start time will be adjusted to not lie too much in the past. +*/ +void +SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now, + bool checkPeriodicOverrun) +{ + // If periodic, check whether the start time is too far in the past. + if (checkPeriodicOverrun) + CheckPeriodicOverrun(now); + + uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER + | B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK; + // We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions + // between setting/canceling the timer and the event handler. + + fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0); + fTimer.period = 0; + + add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, timerFlags); + + fScheduled = true; +} + + +// #pragma mark - RealTimeUserTimer + + +void +RealTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + // get the current time + bigtime_t now = system_time(); + + // Cancel the old timer, if still scheduled, and get the previous values. + if (fScheduled) { + cancel_timer(&fTimer); + + _oldRemainingTime = fNextTime - now; + _oldInterval = fInterval; + + if (fAbsolute) { + SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); + sAbsoluteRealTimeTimers.Remove(this); + } + + fScheduled = false; + } else { + _oldRemainingTime = B_INFINITE_TIMEOUT; + _oldInterval = 0; + } + + // schedule the new timer + fNextTime = nextTime; + fInterval = interval; + fOverrunCount = 0; + + if (nextTime == B_INFINITE_TIMEOUT) + return; + + fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0; + + if (fAbsolute) { + fRealTimeOffset = rtc_boot_time(); + fNextTime -= fRealTimeOffset; + + // If periodic, check whether the start time is too far in the past. + if (fInterval > 0) + CheckPeriodicOverrun(now); + + // add the absolute timer to the global list + SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); + sAbsoluteRealTimeTimers.Insert(this); + } else + fNextTime += now; + + ScheduleKernelTimer(now, false); +} + + +/*! Called when the real-time clock has been changed. + + The caller must hold the scheduler lock. Optionally the caller may also + hold \c sAbsoluteRealTimeTimersLock. +*/ +void +RealTimeUserTimer::TimeWarped() +{ + ASSERT(fScheduled && fAbsolute); + + // get the new real-time offset + bigtime_t oldRealTimeOffset = fRealTimeOffset; + fRealTimeOffset = rtc_boot_time(); + if (fRealTimeOffset == oldRealTimeOffset) + return; + + // cancel the kernel timer and reschedule it + cancel_timer(&fTimer); + + fNextTime += oldRealTimeOffset - fRealTimeOffset; + + ScheduleKernelTimer(system_time(), fInterval > 0); +} + + +void +RealTimeUserTimer::HandleTimer() +{ + SystemTimeUserTimer::HandleTimer(); + + // remove from global list, if no longer scheduled + if (!fScheduled && fAbsolute) { + SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); + sAbsoluteRealTimeTimers.Remove(this); + } +} + + +// #pragma mark - TeamTimeUserTimer + + +TeamTimeUserTimer::TeamTimeUserTimer(team_id teamID) + : + fTeamID(teamID), + fTeam(NULL) +{ +} + + +TeamTimeUserTimer::~TeamTimeUserTimer() +{ + ASSERT(fTeam == NULL); +} + + +void +TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + // get the current time, but only if needed + bool nowValid = fTeam != NULL; + bigtime_t now = nowValid ? fTeam->CPUTime(false) : 0; + + // Cancel the old timer, if still scheduled, and get the previous values. + if (fTeam != NULL) { + if (fScheduled) { + cancel_timer(&fTimer); + fScheduled = false; + } + + _oldRemainingTime = fNextTime - now; + _oldInterval = fInterval; + + fTeam->UserTimerDeactivated(this); + fTeam->ReleaseReference(); + fTeam = NULL; + } else { + _oldRemainingTime = B_INFINITE_TIMEOUT; + _oldInterval = 0; + } + + // schedule the new timer + fNextTime = nextTime; + fInterval = interval; + fOverrunCount = 0; + + if (fNextTime == B_INFINITE_TIMEOUT) + return; + + // Get the team. If it doesn't exist anymore, just don't schedule the + // timer anymore. + fTeam = Team::Get(fTeamID); + if (fTeam == NULL) + return; + + fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0; + + // convert relative to absolute timeouts + if (!fAbsolute) { + if (!nowValid) + now = fTeam->CPUTime(false); + fNextTime += now; + } + + fTeam->UserTimerActivated(this); + + // schedule/udpate the kernel timer + Update(NULL); +} + + +void +TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, + uint32& _overrunCount) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + if (fTeam != NULL) { + _remainingTime = fNextTime - fTeam->CPUTime(false); + _interval = fInterval; + } else { + _remainingTime = B_INFINITE_TIMEOUT; + _interval = 0; + } + + _overrunCount = fOverrunCount; +} + + +/*! Deactivates the timer, if it is activated. + + The caller must hold the scheduler lock. +*/ +void +TeamTimeUserTimer::Deactivate() +{ + if (fTeam == NULL) + return; + + // unschedule, if scheduled + if (fScheduled) { + cancel_timer(&fTimer); + fScheduled = false; + } + + // deactivate + fTeam->UserTimerDeactivated(this); + fTeam->ReleaseReference(); + fTeam = NULL; +} + + +/*! Starts/stops the timer as necessary, if it is active. + + Called whenever threads of the team whose CPU time is referred to by the + timer are scheduled or unscheduled (or leave the team), or when the timer + was just set. Schedules a kernel timer for the remaining time, respectively + cancels it. + + The caller must hold the scheduler lock. + + \param unscheduledThread If not \c NULL, this is the thread that is + currently running and which is in the process of being unscheduled. +*/ +void +TeamTimeUserTimer::Update(Thread* unscheduledThread) +{ + if (fTeam == NULL) + return; + + // determine how many of the team's threads are currently running + fRunningThreads = 0; + int32 cpuCount = smp_get_num_cpus(); + for (int32 i = 0; i < cpuCount; i++) { + Thread* thread = gCPU[i].running_thread; + if (thread != unscheduledThread && thread->team == fTeam) + fRunningThreads++; + } + + _Update(unscheduledThread != NULL); +} + + +/*! Called when the team's CPU time clock which this timer refers to has been + set. + + The caller must hold the scheduler lock. + + \param changedBy The value by which the clock has changed. +*/ +void +TeamTimeUserTimer::TimeWarped(bigtime_t changedBy) +{ + if (fTeam == NULL || changedBy == 0) + return; + + // If this is a relative timer, adjust fNextTime by the value the clock has + // changed. + if (!fAbsolute) + fNextTime += changedBy; + + // reschedule the kernel timer + _Update(false); +} + + +void +TeamTimeUserTimer::HandleTimer() +{ + UserTimer::HandleTimer(); + + // If the timer is not periodic, it is no longer active. Otherwise + // reschedule the kernel timer. + if (fTeam != NULL) { + if (fInterval == 0) { + fTeam->UserTimerDeactivated(this); + fTeam->ReleaseReference(); + fTeam = NULL; + } else { + UpdatePeriodicStartTime(); + _Update(false); + } + } +} + + +/*! Schedules/cancels the kernel timer as necessary. + + \c fRunningThreads must be up-to-date. + The caller must hold the scheduler lock. + + \param unscheduling \c true, when the current thread is in the process of + being unscheduled. +*/ +void +TeamTimeUserTimer::_Update(bool unscheduling) +{ + // unschedule the kernel timer, if scheduled + if (fScheduled) + cancel_timer(&fTimer); + + // if no more threads are running, we're done + if (fRunningThreads == 0) { + fScheduled = false; + return; + } + + // There are still threads running. Reschedule the kernel timer. + bigtime_t now = fTeam->CPUTime(unscheduling); + + // If periodic, check whether the start time is too far in the past. + if (fInterval > 0) + CheckPeriodicOverrun(now); + + if (fNextTime > now) { + fTimer.schedule_time = system_time() + + (fNextTime - now + fRunningThreads - 1) / fRunningThreads; + // check for overflow + if (fTimer.schedule_time < 0) + fTimer.schedule_time = B_INFINITE_TIMEOUT; + } else + fTimer.schedule_time = 0; + fTimer.period = 0; + // We reschedule periodic timers manually in HandleTimer() to avoid + // rounding errors. + + add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, + B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES + | B_TIMER_ACQUIRE_SCHEDULER_LOCK); + // We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions + // between setting/canceling the timer and the event handler. + // We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which + // our base class expects. + + fScheduled = true; +} + + +// #pragma mark - TeamUserTimeUserTimer + + +TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID) + : + fTeamID(teamID), + fTeam(NULL) +{ +} + + +TeamUserTimeUserTimer::~TeamUserTimeUserTimer() +{ + ASSERT(fTeam == NULL); +} + + +void +TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + // get the current time, but only if needed + bool nowValid = fTeam != NULL; + bigtime_t now = nowValid ? fTeam->UserCPUTime() : 0; + + // Cancel the old timer, if still active, and get the previous values. + if (fTeam != NULL) { + _oldRemainingTime = fNextTime - now; + _oldInterval = fInterval; + + fTeam->UserTimerDeactivated(this); + fTeam->ReleaseReference(); + fTeam = NULL; + } else { + _oldRemainingTime = B_INFINITE_TIMEOUT; + _oldInterval = 0; + } + + // schedule the new timer + fNextTime = nextTime; + fInterval = interval; + fOverrunCount = 0; + + if (fNextTime == B_INFINITE_TIMEOUT) + return; + + // Get the team. If it doesn't exist anymore, just don't schedule the + // timer anymore. + fTeam = Team::Get(fTeamID); + if (fTeam == NULL) + return; + + // convert relative to absolute timeouts + if ((flags & B_RELATIVE_TIMEOUT) != 0) { + if (!nowValid) + now = fTeam->CPUTime(false); + fNextTime += now; + } + + fTeam->UserTimerActivated(this); + + // fire the event, if already timed out + Check(); +} + + +void +TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, + uint32& _overrunCount) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + if (fTeam != NULL) { + _remainingTime = fNextTime - fTeam->UserCPUTime(); + _interval = fInterval; + } else { + _remainingTime = B_INFINITE_TIMEOUT; + _interval = 0; + } + + _overrunCount = fOverrunCount; +} + + +/*! Deactivates the timer, if it is activated. + + The caller must hold the scheduler lock. +*/ +void +TeamUserTimeUserTimer::Deactivate() +{ + if (fTeam == NULL) + return; + + // deactivate + fTeam->UserTimerDeactivated(this); + fTeam->ReleaseReference(); + fTeam = NULL; +} + + +/*! Checks whether the timer is up, firing an event, if so. + + The caller must hold the scheduler lock. +*/ +void +TeamUserTimeUserTimer::Check() +{ + if (fTeam == NULL) + return; + + // check whether we need to fire the event yet + bigtime_t now = fTeam->UserCPUTime(); + if (now < fNextTime) + return; + + HandleTimer(); + + // If the timer is not periodic, it is no longer active. Otherwise compute + // the event time. + if (fInterval == 0) { + fTeam->UserTimerDeactivated(this); + fTeam->ReleaseReference(); + fTeam = NULL; + return; + } + + // First validate fNextTime, then increment it, so that fNextTime is > now + // (CheckPeriodicOverrun() only makes it > now - fInterval). + CheckPeriodicOverrun(now); + fNextTime += fInterval; + fScheduled = true; +} + + +// #pragma mark - ThreadTimeUserTimer + + +ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID) + : + fThreadID(threadID), + fThread(NULL) +{ +} + + +ThreadTimeUserTimer::~ThreadTimeUserTimer() +{ + ASSERT(fThread == NULL); +} + + +void +ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, + uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + // get the current time, but only if needed + bool nowValid = fThread != NULL; + bigtime_t now = nowValid ? fThread->CPUTime(false) : 0; + + // Cancel the old timer, if still scheduled, and get the previous values. + if (fThread != NULL) { + if (fScheduled) { + cancel_timer(&fTimer); + fScheduled = false; + } + + _oldRemainingTime = fNextTime - now; + _oldInterval = fInterval; + + fThread->UserTimerDeactivated(this); + fThread->ReleaseReference(); + fThread = NULL; + } else { + _oldRemainingTime = B_INFINITE_TIMEOUT; + _oldInterval = 0; + } + + // schedule the new timer + fNextTime = nextTime; + fInterval = interval; + fOverrunCount = 0; + + if (fNextTime == B_INFINITE_TIMEOUT) + return; + + // Get the thread. If it doesn't exist anymore, just don't schedule the + // timer anymore. + fThread = Thread::Get(fThreadID); + if (fThread == NULL) + return; + + fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0; + + // convert relative to absolute timeouts + if (!fAbsolute) { + if (!nowValid) + now = fThread->CPUTime(false); + fNextTime += now; + } + + fThread->UserTimerActivated(this); + + // If the thread is currently running, also schedule a kernel timer. + if (fThread->cpu != NULL) + Start(); +} + + +void +ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, + uint32& _overrunCount) +{ + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + if (fThread != NULL) { + _remainingTime = fNextTime - fThread->CPUTime(false); + _interval = fInterval; + } else { + _remainingTime = B_INFINITE_TIMEOUT; + _interval = 0; + } + + _overrunCount = fOverrunCount; +} + + +/*! Deactivates the timer, if it is activated. + + The caller must hold the scheduler lock. +*/ +void +ThreadTimeUserTimer::Deactivate() +{ + if (fThread == NULL) + return; + + // unschedule, if scheduled + if (fScheduled) { + cancel_timer(&fTimer); + fScheduled = false; + } + + // deactivate + fThread->UserTimerDeactivated(this); + fThread->ReleaseReference(); + fThread = NULL; +} + + +/*! Starts the timer, if it is active. + + Called when the thread whose CPU time is referred to by the timer is + scheduled, or, when the timer was just set and the thread is already + running. Schedules a kernel timer for the remaining time. + + The caller must hold the scheduler lock. +*/ +void +ThreadTimeUserTimer::Start() +{ + if (fThread == NULL) + return; + + ASSERT(!fScheduled); + + // add the kernel timer + bigtime_t now = fThread->CPUTime(false); + + // If periodic, check whether the start time is too far in the past. + if (fInterval > 0) + CheckPeriodicOverrun(now); + + if (fNextTime > now) { + fTimer.schedule_time = system_time() + fNextTime - now; + // check for overflow + if (fTimer.schedule_time < 0) + fTimer.schedule_time = B_INFINITE_TIMEOUT; + } else + fTimer.schedule_time = 0; + fTimer.period = 0; + + uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER + | B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK; + // We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions + // between setting/canceling the timer and the event handler. + + add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags); + + fScheduled = true; +} + + +/*! Stops the timer, if it is active. + + Called when the thread whose CPU time is referred to by the timer is + unscheduled, or, when the timer is canceled. + + The caller must hold the scheduler lock. +*/ +void +ThreadTimeUserTimer::Stop() +{ + if (fThread == NULL) + return; + + ASSERT(fScheduled); + + // cancel the kernel timer + cancel_timer(&fTimer); + fScheduled = false; + + // TODO: To avoid odd race conditions, we should check the current time of + // the thread (ignoring the time since last_time) and manually fire the + // user event, if necessary. +} + + +/*! Called when the team's CPU time clock which this timer refers to has been + set. + + The caller must hold the scheduler lock. + + \param changedBy The value by which the clock has changed. +*/ +void +ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy) +{ + if (fThread == NULL || changedBy == 0) + return; + + // If this is a relative timer, adjust fNextTime by the value the clock has + // changed. + if (!fAbsolute) + fNextTime += changedBy; + + // reschedule the kernel timer + if (fScheduled) { + Stop(); + Start(); + } +} + + +void +ThreadTimeUserTimer::HandleTimer() +{ + UserTimer::HandleTimer(); + + if (fThread != NULL) { + // If the timer is periodic, reschedule the kernel timer. Otherwise it + // is no longer active. + if (fInterval > 0) { + UpdatePeriodicStartTime(); + Start(); + } else { + fThread->UserTimerDeactivated(this); + fThread->ReleaseReference(); + fThread = NULL; + } + } +} + + +// #pragma mark - UserTimerList + + +UserTimerList::UserTimerList() +{ +} + + +UserTimerList::~UserTimerList() +{ + ASSERT(fTimers.IsEmpty()); +} + + +/*! Returns the user timer with the given ID. + + \param id The timer's ID + \return The user timer with the given ID or \c NULL, if there is no such + timer. +*/ +UserTimer* +UserTimerList::TimerFor(int32 id) const +{ + // TODO: Use a more efficient data structure. E.g. a sorted array. + for (TimerList::ConstIterator it = fTimers.GetIterator(); + UserTimer* timer = it.Next();) { + if (timer->ID() == id) + return timer; + } + + return NULL; +} + + +/*! Adds the given user timer and assigns it an ID. + + \param timer The timer to be added. +*/ +void +UserTimerList::AddTimer(UserTimer* timer) +{ + int32 id = timer->ID(); + if (id < 0) { + // user-defined timer -- find an usused ID + id = USER_TIMER_FIRST_USER_DEFINED_ID; + UserTimer* insertAfter = NULL; + for (TimerList::Iterator it = fTimers.GetIterator(); + UserTimer* other = it.Next();) { + if (other->ID() > id) + break; + if (other->ID() == id) + id++; + insertAfter = other; + } + + // insert the timer + timer->SetID(id); + fTimers.InsertAfter(insertAfter, timer); + } else { + // default timer -- find the insertion point + UserTimer* insertAfter = NULL; + for (TimerList::Iterator it = fTimers.GetIterator(); + UserTimer* other = it.Next();) { + if (other->ID() > id) + break; + if (other->ID() == id) { + panic("UserTimerList::AddTimer(): timer with ID %" B_PRId32 + " already exists!", id); + } + insertAfter = other; + } + + // insert the timer + fTimers.InsertAfter(insertAfter, timer); + } +} + + +/*! Deletes all (or all user-defined) user timers. + + \param userDefinedOnly If \c true, only the user-defined timers are deleted, + otherwise all timers are deleted. + \return The number of user-defined timers that were removed and deleted. +*/ +int32 +UserTimerList::DeleteTimers(bool userDefinedOnly) +{ + int32 userDefinedCount = 0; + + for (TimerList::Iterator it = fTimers.GetIterator(); + UserTimer* timer = it.Next();) { + if (timer->ID() < USER_TIMER_FIRST_USER_DEFINED_ID) { + if (userDefinedOnly) + continue; + } else + userDefinedCount++; + + // remove, cancel, and delete the timer + it.Remove(); + timer->Cancel(); + delete timer; + } + + return userDefinedCount; +} + + +// #pragma mark - private + + +static int32 +create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread, + uint32 flags, const struct sigevent& event, + ThreadCreationAttributes* threadAttributes, bool isDefaultEvent) +{ + // create the timer object + UserTimer* timer; + switch (clockID) { + case CLOCK_MONOTONIC: + timer = new(std::nothrow) SystemTimeUserTimer; + break; + + case CLOCK_REALTIME: + timer = new(std::nothrow) RealTimeUserTimer; + break; + + case CLOCK_THREAD_CPUTIME_ID: + timer = new(std::nothrow) ThreadTimeUserTimer( + thread_get_current_thread()->id); + break; + + case CLOCK_PROCESS_CPUTIME_ID: + if (team == NULL) + return B_BAD_VALUE; + timer = new(std::nothrow) TeamTimeUserTimer(team->id); + break; + + case CLOCK_PROCESS_USER_CPUTIME_ID: + if (team == NULL) + return B_BAD_VALUE; + timer = new(std::nothrow) TeamUserTimeUserTimer(team->id); + break; + + default: + { + // The clock ID is a ID of the team whose CPU time the clock refers + // to. Check whether the team exists and we have permission to + // access its clock. + if (clockID <= 0) + return B_BAD_VALUE; + if (clockID == team_get_kernel_team_id()) + return B_NOT_ALLOWED; + + Team* timedTeam = Team::GetAndLock(clockID); + if (timedTeam == NULL) + return B_BAD_VALUE; + + uid_t uid = geteuid(); + uid_t teamUID = timedTeam->effective_uid; + + timedTeam->UnlockAndReleaseReference(); + + if (uid != 0 && uid != teamUID) + return B_NOT_ALLOWED; + + timer = new(std::nothrow) TeamTimeUserTimer(clockID); + break; + } + } + + if (timer == NULL) + return B_NO_MEMORY; + ObjectDeleter timerDeleter(timer); + + if (timerID >= 0) + timer->SetID(timerID); + + SignalEvent* signalEvent = NULL; + + switch (event.sigev_notify) { + case SIGEV_NONE: + // the timer's event remains NULL + break; + + case SIGEV_SIGNAL: + { + if (event.sigev_signo <= 0 || event.sigev_signo > MAX_SIGNAL_NUMBER) + return B_BAD_VALUE; + + if (thread != NULL && (flags & USER_TIMER_SIGNAL_THREAD) != 0) { + // The signal shall be sent to the thread. + signalEvent = ThreadSignalEvent::Create(thread, + event.sigev_signo, SI_TIMER, 0, team->id); + } else { + // The signal shall be sent to the team. + signalEvent = TeamSignalEvent::Create(team, event.sigev_signo, + SI_TIMER, 0); + } + + if (signalEvent == NULL) + return B_NO_MEMORY; + + timer->SetEvent(signalEvent); + break; + } + + case SIGEV_THREAD: + { + if (threadAttributes == NULL) + return B_BAD_VALUE; + + CreateThreadEvent* event + = CreateThreadEvent::Create(*threadAttributes); + if (event == NULL) + return B_NO_MEMORY; + + timer->SetEvent(event); + break; + } + + default: + return B_BAD_VALUE; + } + + // add it to the team/thread + TimerLocker timerLocker; + timerLocker.Lock(team, thread); + + status_t error = thread != NULL + ? thread->AddUserTimer(timer) : team->AddUserTimer(timer); + if (error != B_OK) + return error; + + // set a signal event's user value + if (signalEvent != NULL) { + // If no sigevent structure was given, use the timer ID. + union sigval signalValue = event.sigev_value; + if (isDefaultEvent) + signalValue.sival_int = timer->ID(); + + signalEvent->SetUserValue(signalValue); + } + + return timerDeleter.Detach()->ID(); +} + + +/*! Called when the CPU time clock of the given thread has been set. + + The caller must hold the scheduler lock. + + \param thread The thread whose CPU time clock has been set. + \param changedBy The value by which the CPU time clock has changed + (new = old + changedBy). +*/ +static void +thread_clock_changed(Thread* thread, bigtime_t changedBy) +{ + for (ThreadTimeUserTimerList::ConstIterator it + = thread->CPUTimeUserTimerIterator(); + ThreadTimeUserTimer* timer = it.Next();) { + timer->TimeWarped(changedBy); + } +} + + +/*! Called when the CPU time clock of the given team has been set. + + The caller must hold the scheduler lock. + + \param team The team whose CPU time clock has been set. + \param changedBy The value by which the CPU time clock has changed + (new = old + changedBy). +*/ +static void +team_clock_changed(Team* team, bigtime_t changedBy) +{ + for (TeamTimeUserTimerList::ConstIterator it + = team->CPUTimeUserTimerIterator(); + TeamTimeUserTimer* timer = it.Next();) { + timer->TimeWarped(changedBy); + } +} + + +// #pragma mark - kernel private + + +/*! Creates the pre-defined user timers for the given thread. + The thread may not have been added to its team yet, hence the team must be + passed + + \param team The thread's (future) team. + \param thread The thread whose pre-defined timers shall be created. + \return \c B_OK, when everything when fine, another error code otherwise. +*/ +status_t +user_timer_create_thread_timers(Team* team, Thread* thread) +{ + // create a real time user timer + struct sigevent event; + event.sigev_notify = SIGEV_SIGNAL; + event.sigev_signo = SIGALRM; + + int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID, + team, thread, USER_TIMER_SIGNAL_THREAD, event, NULL, true); + if (timerID < 0) + return timerID; + + return B_OK; +} + + +/*! Creates the pre-defined user timers for the given team. + + \param team The team whose pre-defined timers shall be created. + \return \c B_OK, when everything when fine, another error code otherwise. +*/ +status_t +user_timer_create_team_timers(Team* team) +{ + // create a real time user timer + struct sigevent event; + event.sigev_notify = SIGEV_SIGNAL; + event.sigev_signo = SIGALRM; + + int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID, + team, NULL, 0, event, NULL, true); + if (timerID < 0) + return timerID; + + // create a total CPU time user timer + event.sigev_notify = SIGEV_SIGNAL; + event.sigev_signo = SIGPROF; + + timerID = create_timer(CLOCK_PROCESS_CPUTIME_ID, + USER_TIMER_TEAM_TOTAL_TIME_ID, team, NULL, 0, event, NULL, true); + if (timerID < 0) + return timerID; + + // create a user CPU time user timer + event.sigev_notify = SIGEV_SIGNAL; + event.sigev_signo = SIGVTALRM; + + timerID = create_timer(CLOCK_PROCESS_USER_CPUTIME_ID, + USER_TIMER_TEAM_USER_TIME_ID, team, NULL, 0, event, NULL, true); + if (timerID < 0) + return timerID; + + return B_OK; +} + + +status_t +user_timer_get_clock(clockid_t clockID, bigtime_t& _time) +{ + switch (clockID) { + case CLOCK_MONOTONIC: + _time = system_time(); + return B_OK; + + case CLOCK_REALTIME: + _time = real_time_clock_usecs(); + return B_OK; + + case CLOCK_THREAD_CPUTIME_ID: + { + Thread* thread = thread_get_current_thread(); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + _time = thread->CPUTime(false); + return B_OK; + } + + case CLOCK_PROCESS_USER_CPUTIME_ID: + { + Team* team = thread_get_current_thread()->team; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + _time = team->UserCPUTime(); + return B_OK; + } + + case CLOCK_PROCESS_CPUTIME_ID: + default: + { + // get the ID of the target team (or the respective placeholder) + team_id teamID; + if (clockID == CLOCK_PROCESS_CPUTIME_ID) { + teamID = B_CURRENT_TEAM; + } else { + if (clockID < 0) + return B_BAD_VALUE; + if (clockID == team_get_kernel_team_id()) + return B_NOT_ALLOWED; + + teamID = clockID; + } + + // get the team + Team* team = Team::Get(teamID); + if (team == NULL) + return B_BAD_VALUE; + BReference teamReference(team, true); + + // get the time + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + _time = team->CPUTime(false); + + return B_OK; + } + } +} + + +void +user_timer_real_time_clock_changed() +{ + // we need to update all absolute real-time timers + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); + + for (RealTimeUserTimerList::Iterator it + = sAbsoluteRealTimeTimers.GetIterator(); + RealTimeUserTimer* timer = it.Next();) { + timer->TimeWarped(); + } +} + + +void +user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread) +{ + // stop thread timers + for (ThreadTimeUserTimerList::ConstIterator it + = thread->CPUTimeUserTimerIterator(); + ThreadTimeUserTimer* timer = it.Next();) { + timer->Stop(); + } + + // update team timers + if (nextThread == NULL || nextThread->team != thread->team) { + for (TeamTimeUserTimerList::ConstIterator it + = thread->team->CPUTimeUserTimerIterator(); + TeamTimeUserTimer* timer = it.Next();) { + timer->Update(thread); + } + } +} + + +void +user_timer_continue_cpu_timers(Thread* thread, Thread* previousThread) +{ + // update team timers + if (previousThread == NULL || previousThread->team != thread->team) { + for (TeamTimeUserTimerList::ConstIterator it + = thread->team->CPUTimeUserTimerIterator(); + TeamTimeUserTimer* timer = it.Next();) { + timer->Update(NULL); + } + } + + // start thread timers + for (ThreadTimeUserTimerList::ConstIterator it + = thread->CPUTimeUserTimerIterator(); + ThreadTimeUserTimer* timer = it.Next();) { + timer->Start(); + } +} + + +void +user_timer_check_team_user_timers(Team* team) +{ + for (TeamUserTimeUserTimerList::ConstIterator it + = team->UserTimeUserTimerIterator(); + TeamUserTimeUserTimer* timer = it.Next();) { + timer->Check(); + } +} + + +// #pragma mark - syscalls + + +status_t +_user_get_clock(clockid_t clockID, bigtime_t* userTime) +{ + // get the time + bigtime_t time; + status_t error = user_timer_get_clock(clockID, time); + if (error != B_OK) + return error; + + // copy the value back to userland + if (userTime == NULL || !IS_USER_ADDRESS(userTime) + || user_memcpy(userTime, &time, sizeof(time)) != B_OK) { + return B_BAD_ADDRESS; + } + + return B_OK; +} + + +status_t +_user_set_clock(clockid_t clockID, bigtime_t time) +{ + switch (clockID) { + case CLOCK_MONOTONIC: + return B_BAD_VALUE; + + case CLOCK_REALTIME: + // only root may set the time + if (geteuid() != 0) + return B_NOT_ALLOWED; + + set_real_time_clock_usecs(time); + return B_OK; + + case CLOCK_THREAD_CPUTIME_ID: + { + Thread* thread = thread_get_current_thread(); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + bigtime_t diff = time - thread->CPUTime(false); + thread->cpu_clock_offset += diff; + + thread_clock_changed(thread, diff); + return B_OK; + } + + case CLOCK_PROCESS_USER_CPUTIME_ID: + // not supported -- this clock is an Haiku-internal extension + return B_BAD_VALUE; + + case CLOCK_PROCESS_CPUTIME_ID: + default: + { + // get the ID of the target team (or the respective placeholder) + team_id teamID; + if (clockID == CLOCK_PROCESS_CPUTIME_ID) { + teamID = B_CURRENT_TEAM; + } else { + if (clockID < 0) + return B_BAD_VALUE; + if (clockID == team_get_kernel_team_id()) + return B_NOT_ALLOWED; + + teamID = clockID; + } + + // get the team + Team* team = Team::Get(teamID); + if (team == NULL) + return B_BAD_VALUE; + BReference teamReference(team, true); + + // set the time offset + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + bigtime_t diff = time - team->CPUTime(false); + team->cpu_clock_offset += diff; + + team_clock_changed(team, diff); + return B_OK; + } + } + + return B_OK; +} + + +int32 +_user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags, + const struct sigevent* userEvent, + const thread_creation_attributes* userThreadAttributes) +{ + // copy the sigevent structure from userland + struct sigevent event; + if (userEvent != NULL) { + if (!IS_USER_ADDRESS(userEvent) + || user_memcpy(&event, userEvent, sizeof(event)) != B_OK) { + return B_BAD_ADDRESS; + } + } else { + // none given -- use defaults + event.sigev_notify = SIGEV_SIGNAL; + event.sigev_signo = SIGALRM; + } + + // copy thread creation attributes from userland, if specified + char nameBuffer[B_OS_NAME_LENGTH]; + ThreadCreationAttributes threadAttributes; + if (event.sigev_notify == SIGEV_THREAD) { + status_t error = threadAttributes.InitFromUserAttributes( + userThreadAttributes, nameBuffer); + if (error != B_OK) + return error; + } + + // get team and thread + Team* team = thread_get_current_thread()->team; + Thread* thread = NULL; + if (threadID >= 0) { + thread = Thread::GetAndLock(threadID); + if (thread == NULL) + return B_BAD_THREAD_ID; + + thread->Unlock(); + } + BReference threadReference(thread, true); + + // create the timer + return create_timer(clockID, -1, team, thread, flags, event, + userThreadAttributes != NULL ? &threadAttributes : NULL, + userEvent == NULL); +} + + +status_t +_user_delete_timer(int32 timerID, thread_id threadID) +{ + // can only delete user-defined timers + if (timerID < USER_TIMER_FIRST_USER_DEFINED_ID) + return B_BAD_VALUE; + + // get the timer + TimerLocker timerLocker; + UserTimer* timer; + status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer); + if (error != B_OK) + return error; + + // cancel, remove, and delete it + timer->Cancel(); + + if (threadID >= 0) + timerLocker.thread->RemoveUserTimer(timer); + else + timerLocker.team->RemoveUserTimer(timer); + + delete timer; + + return B_OK; +} + + +status_t +_user_get_timer(int32 timerID, thread_id threadID, + struct user_timer_info* userInfo) +{ + // get the timer + TimerLocker timerLocker; + UserTimer* timer; + status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer); + if (error != B_OK) + return error; + + // get the info + user_timer_info info; + timer->GetInfo(info.remaining_time, info.interval, info.overrun_count); + + // Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid + // value. + if (info.remaining_time <= 0) + info.remaining_time = 1; + + timerLocker.Unlock(); + + // copy it back to userland + if (userInfo != NULL + && (!IS_USER_ADDRESS(userInfo) + || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { + return B_BAD_ADDRESS; + } + + return B_OK; +} + + +status_t +_user_set_timer(int32 timerID, thread_id threadID, bigtime_t startTime, + bigtime_t interval, uint32 flags, struct user_timer_info* userOldInfo) +{ + // check the values + if (startTime < 0 || interval < 0) + return B_BAD_VALUE; + + // get the timer + TimerLocker timerLocker; + UserTimer* timer; + status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer); + if (error != B_OK) + return error; + + // schedule the timer + user_timer_info oldInfo; + timer->Schedule(startTime, interval, flags, oldInfo.remaining_time, + oldInfo.interval); + + // Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid + // value. + if (oldInfo.remaining_time <= 0) + oldInfo.remaining_time = 1; + + timerLocker.Unlock(); + + // copy back the old info + if (userOldInfo != NULL + && (!IS_USER_ADDRESS(userOldInfo) + || user_memcpy(userOldInfo, &oldInfo, sizeof(oldInfo)) != B_OK)) { + return B_BAD_ADDRESS; + } + + return B_OK; +} diff --git a/src/system/kernel/arch/arm/arch_thread.cpp b/src/system/kernel/arch/arm/arch_thread.cpp index 46e63f58c1..42ba0589c8 100644 --- a/src/system/kernel/arch/arm/arch_thread.cpp +++ b/src/system/kernel/arch/arm/arch_thread.cpp @@ -63,47 +63,11 @@ arch_thread_init_thread_struct(Thread *thread) } -status_t -arch_thread_init_kthread_stack(Thread *t, int (*start_func)(void), - void (*entry_func)(void), void (*exit_func)(void)) +void +arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop, + void (*function)(void*), const void* data) { -/* addr_t *kstack = (addr_t *)t->kernel_stack_base; - addr_t *kstackTop = (addr_t *)t->kernel_stack_base; - - // clear the kernel stack -#ifdef DEBUG_KERNEL_STACKS -# ifdef STACK_GROWS_DOWNWARDS - memset((void *)((addr_t)kstack + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE), 0, - KERNEL_STACK_SIZE); -# else - memset(kstack, 0, KERNEL_STACK_SIZE); -# endif -#else - memset(kstack, 0, KERNEL_STACK_SIZE); -#endif - - // space for frame pointer and return address, and stack frames must be - // 16 byte aligned - kstackTop -= 2; - kstackTop = (addr_t*)((addr_t)kstackTop & ~0xf); - - // LR, CR, r2, r13-r31, f13-f31, as pushed by m68k_context_switch() - kstackTop -= 22 + 2 * 19; - - // let LR point to m68k_kernel_thread_root() - kstackTop[0] = (addr_t)&m68k_kernel_thread_root; - - // the arguments of m68k_kernel_thread_root() are the functions to call, - // provided in registers r13-r15 - kstackTop[3] = (addr_t)entry_func; - kstackTop[4] = (addr_t)start_func; - kstackTop[5] = (addr_t)exit_func; - - // save this stack position - t->arch_info.sp = (void *)kstackTop; -*/ #warning ARM:WRITEME - return B_OK; } @@ -157,14 +121,15 @@ arch_on_signal_stack(Thread *thread) status_t -arch_setup_signal_frame(Thread *thread, struct sigaction *sa, int sig, int sigMask) +arch_setup_signal_frame(Thread *thread, struct sigaction *sa, + struct signal_frame_data *signalFrameData) { return B_ERROR; } int64 -arch_restore_signal_frame(void) +arch_restore_signal_frame(struct signal_frame_data* signalFrameData) { return 0; } diff --git a/src/system/kernel/arch/m68k/arch_debug.cpp b/src/system/kernel/arch/m68k/arch_debug.cpp index 5ddbcdab10..eaa3819d41 100644 --- a/src/system/kernel/arch/m68k/arch_debug.cpp +++ b/src/system/kernel/arch/m68k/arch_debug.cpp @@ -129,7 +129,7 @@ stack_trace(int argc, char **argv) } else { // TODO: Add support for stack traces of other threads. /* thread_id id = strtoul(argv[1], NULL, 0); - thread = thread_get_thread_struct_locked(id); + thread = Thread::GetDebug(id); if (thread == NULL) { kprintf("could not find thread %ld\n", id); return 0; diff --git a/src/system/kernel/arch/m68k/arch_int.cpp b/src/system/kernel/arch/m68k/arch_int.cpp index 0569f0a14c..a982a75e8a 100644 --- a/src/system/kernel/arch/m68k/arch_int.cpp +++ b/src/system/kernel/arch/m68k/arch_int.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -296,18 +297,19 @@ dprintf("handling I/O interrupts done\n"); int state = disable_interrupts(); if (thread->cpu->invoke_scheduler) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); scheduler_reschedule(); - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); restore_interrupts(state); } else if (hardwareInterrupt && thread->post_interrupt_callback != NULL) { - restore_interrupts(state); void (*callback)(void*) = thread->post_interrupt_callback; void* data = thread->post_interrupt_data; thread->post_interrupt_callback = NULL; thread->post_interrupt_data = NULL; + restore_interrupts(state); + callback(data); } diff --git a/src/system/kernel/arch/m68k/arch_thread.cpp b/src/system/kernel/arch/m68k/arch_thread.cpp index b5e0e8fdfd..36d196c21e 100644 --- a/src/system/kernel/arch/m68k/arch_thread.cpp +++ b/src/system/kernel/arch/m68k/arch_thread.cpp @@ -146,10 +146,11 @@ arch_thread_init_thread_struct(Thread *thread) } -status_t -arch_thread_init_kthread_stack(Thread *t, int (*start_func)(void), - void (*entry_func)(void), void (*exit_func)(void)) +void +arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop, + void (*function)(void*), const void* data) { +#if 0 addr_t *kstack = (addr_t *)t->kernel_stack_base; addr_t *kstackTop = (addr_t *)t->kernel_stack_base; @@ -186,6 +187,9 @@ arch_thread_init_kthread_stack(Thread *t, int (*start_func)(void), t->arch_info.sp = (void *)kstackTop; return B_OK; +#else + panic("arch_thread_init_kthread_stack(): Implement me!"); +#endif } @@ -237,14 +241,15 @@ arch_on_signal_stack(Thread *thread) status_t -arch_setup_signal_frame(Thread *thread, struct sigaction *sa, int sig, int sigMask) +arch_setup_signal_frame(Thread *thread, struct sigaction *sa, + struct signal_frame_data *signalFrameData) { return B_ERROR; } int64 -arch_restore_signal_frame(void) +arch_restore_signal_frame(struct signal_frame_data* signalFrameData) { return 0; } diff --git a/src/system/kernel/arch/mipsel/arch_thread.cpp b/src/system/kernel/arch/mipsel/arch_thread.cpp index 0730a98a34..5cc0a2da0e 100644 --- a/src/system/kernel/arch/mipsel/arch_thread.cpp +++ b/src/system/kernel/arch/mipsel/arch_thread.cpp @@ -86,12 +86,11 @@ arch_thread_init_thread_struct(Thread *thread) } -status_t -arch_thread_init_kthread_stack(Thread *t, int (*start_func)(void), - void (*entry_func)(void), void (*exit_func)(void)) +void +arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop, + void (*function)(void*), const void* data) { #warning IMPLEMENT arch_thread_init_kthread_stack - return B_ERROR; } @@ -136,8 +135,8 @@ arch_on_signal_stack(Thread *thread) status_t -arch_setup_signal_frame(Thread *thread, struct sigaction *sa, int sig, - int sigMask) +arch_setup_signal_frame(Thread *thread, struct sigaction *sa, + struct signal_frame_data *signalFrameData) { #warning IMPLEMENT arch_setup_signal_frame return B_ERROR; @@ -145,7 +144,7 @@ arch_setup_signal_frame(Thread *thread, struct sigaction *sa, int sig, int64 -arch_restore_signal_frame(void) +arch_restore_signal_frame(struct signal_frame_data* signalFrameData) { #warning IMPLEMENT arch_restore_signal_frame return 0; diff --git a/src/system/kernel/arch/ppc/arch_debug.cpp b/src/system/kernel/arch/ppc/arch_debug.cpp index 99c8c304d1..597e37bcce 100644 --- a/src/system/kernel/arch/ppc/arch_debug.cpp +++ b/src/system/kernel/arch/ppc/arch_debug.cpp @@ -129,7 +129,7 @@ stack_trace(int argc, char **argv) } else { // TODO: Add support for stack traces of other threads. /* thread_id id = strtoul(argv[1], NULL, 0); - thread = thread_get_thread_struct_locked(id); + thread = Thread::GetDebug(id); if (thread == NULL) { kprintf("could not find thread %ld\n", id); return 0; diff --git a/src/system/kernel/arch/ppc/arch_int.cpp b/src/system/kernel/arch/ppc/arch_int.cpp index 6830136478..61ede968ef 100644 --- a/src/system/kernel/arch/ppc/arch_int.cpp +++ b/src/system/kernel/arch/ppc/arch_int.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -243,18 +244,19 @@ dprintf("handling I/O interrupts done\n"); cpu_status state = disable_interrupts(); if (thread->cpu->invoke_scheduler) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); scheduler_reschedule(); - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); restore_interrupts(state); } else if (thread->post_interrupt_callback != NULL) { - restore_interrupts(state); void (*callback)(void*) = thread->post_interrupt_callback; void* data = thread->post_interrupt_data; thread->post_interrupt_callback = NULL; thread->post_interrupt_data = NULL; + restore_interrupts(state); + callback(data); } diff --git a/src/system/kernel/arch/ppc/arch_thread.cpp b/src/system/kernel/arch/ppc/arch_thread.cpp index 5b3d471216..c9856cb009 100644 --- a/src/system/kernel/arch/ppc/arch_thread.cpp +++ b/src/system/kernel/arch/ppc/arch_thread.cpp @@ -114,10 +114,11 @@ arch_thread_init_thread_struct(Thread *thread) } -status_t -arch_thread_init_kthread_stack(Thread *t, int (*start_func)(void), - void (*entry_func)(void), void (*exit_func)(void)) +void +arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop, + void (*function)(void*), const void* data) { +#if 0 addr_t *kstack = (addr_t *)t->kernel_stack_base; addr_t *kstackTop = (addr_t *)t->kernel_stack_top; @@ -154,6 +155,9 @@ arch_thread_init_kthread_stack(Thread *t, int (*start_func)(void), t->arch_info.sp = (void *)kstackTop; return B_OK; +#else + panic("arch_thread_init_kthread_stack(): Implement me!"); +#endif } @@ -212,14 +216,15 @@ arch_on_signal_stack(Thread *thread) status_t -arch_setup_signal_frame(Thread *thread, struct sigaction *sa, int sig, int sigMask) +arch_setup_signal_frame(Thread *thread, struct sigaction *sa, + struct signal_frame_data *signalFrameData) { return B_ERROR; } int64 -arch_restore_signal_frame(void) +arch_restore_signal_frame(struct signal_frame_data* signalFrameData) { return 0; } diff --git a/src/system/kernel/arch/x86/Jamfile b/src/system/kernel/arch/x86/Jamfile index 7c3d75a491..ecb9706398 100644 --- a/src/system/kernel/arch/x86/Jamfile +++ b/src/system/kernel/arch/x86/Jamfile @@ -45,11 +45,13 @@ KernelMergeObject kernel_arch_x86.o : pic.cpp syscall.S vm86.cpp + x86_signals.cpp + x86_signals_asm.S + x86_syscalls.cpp # paging x86_physical_page_mapper.cpp x86_physical_page_mapper_large_memory.cpp - x86_syscalls.cpp X86PagingMethod.cpp X86PagingStructures.cpp X86VMTranslationMap.cpp diff --git a/src/system/kernel/arch/x86/arch_commpage.cpp b/src/system/kernel/arch/x86/arch_commpage.cpp index 581b0788d4..8b9cc0bdb6 100644 --- a/src/system/kernel/arch/x86/arch_commpage.cpp +++ b/src/system/kernel/arch/x86/arch_commpage.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2007, Travis Geiselbrecht. All rights reserved. * Distributed under the terms of the MIT License. */ @@ -7,6 +7,7 @@ #include +#include "x86_signals.h" #include "x86_syscalls.h" @@ -23,5 +24,8 @@ arch_commpage_init_post_cpus(void) // select the optimum syscall mechanism and patch the commpage x86_initialize_commpage_syscall(); + // initialize the signal handler code in the commpage + x86_initialize_commpage_signal_handler(); + return B_OK; } diff --git a/src/system/kernel/arch/x86/arch_debug.cpp b/src/system/kernel/arch/x86/arch_debug.cpp index 1da75efaad..d3c6b8b1e9 100644 --- a/src/system/kernel/arch/x86/arch_debug.cpp +++ b/src/system/kernel/arch/x86/arch_debug.cpp @@ -388,7 +388,7 @@ setup_for_thread(char *arg, Thread **_thread, uint32 *_ebp, if (arg != NULL) { thread_id id = strtoul(arg, NULL, 0); - thread = thread_get_thread_struct_locked(id); + thread = Thread::GetDebug(id); if (thread == NULL) { kprintf("could not find thread %ld\n", id); return false; @@ -855,7 +855,7 @@ dump_iframes(int argc, char **argv) thread = thread_get_current_thread(); } else if (argc == 2) { thread_id id = strtoul(argv[1], NULL, 0); - thread = thread_get_thread_struct_locked(id); + thread = Thread::GetDebug(id); if (thread == NULL) { kprintf("could not find thread %ld\n", id); return 0; @@ -925,7 +925,7 @@ cmd_in_context(int argc, char** argv) return 0; // get the thread - Thread* thread = thread_get_thread_struct_locked(threadID); + Thread* thread = Thread::GetDebug(threadID); if (thread == NULL) { kprintf("Could not find thread with ID \"%s\".\n", threadIDString); return 0; diff --git a/src/system/kernel/arch/x86/arch_int.cpp b/src/system/kernel/arch/x86/arch_int.cpp index 69aa5da153..fe746b342b 100644 --- a/src/system/kernel/arch/x86/arch_int.cpp +++ b/src/system/kernel/arch/x86/arch_int.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -244,7 +245,10 @@ static void unexpected_exception(struct iframe* frame) { debug_exception_type type; - int signal; + uint32 signalNumber; + int32 signalCode; + addr_t signalAddress = 0; + int32 signalError = B_ERROR; if (IFRAME_IS_VM86(frame)) { x86_vm86_return((struct vm86_iframe *)frame, (frame->vector == 13) ? @@ -255,42 +259,62 @@ unexpected_exception(struct iframe* frame) switch (frame->vector) { case 0: // Divide Error Exception (#DE) type = B_DIVIDE_ERROR; - signal = SIGFPE; + signalNumber = SIGFPE; + signalCode = FPE_INTDIV; + signalAddress = frame->eip; break; case 4: // Overflow Exception (#OF) type = B_OVERFLOW_EXCEPTION; - signal = SIGTRAP; + signalNumber = SIGFPE; + signalCode = FPE_INTOVF; + signalAddress = frame->eip; break; case 5: // BOUND Range Exceeded Exception (#BR) type = B_BOUNDS_CHECK_EXCEPTION; - signal = SIGTRAP; + signalNumber = SIGTRAP; + signalCode = SI_USER; break; case 6: // Invalid Opcode Exception (#UD) type = B_INVALID_OPCODE_EXCEPTION; - signal = SIGILL; + signalNumber = SIGILL; + signalCode = ILL_ILLOPC; + signalAddress = frame->eip; break; case 13: // General Protection Exception (#GP) type = B_GENERAL_PROTECTION_FAULT; - signal = SIGILL; + signalNumber = SIGILL; + signalCode = ILL_PRVOPC; // or ILL_PRVREG + signalAddress = frame->eip; break; case 16: // x87 FPU Floating-Point Error (#MF) type = B_FLOATING_POINT_EXCEPTION; - signal = SIGFPE; + signalNumber = SIGFPE; + signalCode = FPE_FLTDIV; + // TODO: Determine the correct cause via the FPU status + // register! + signalAddress = frame->eip; break; case 17: // Alignment Check Exception (#AC) type = B_ALIGNMENT_EXCEPTION; - signal = SIGTRAP; + signalNumber = SIGBUS; + signalCode = BUS_ADRALN; + // TODO: Also get the address (from where?). Since we don't enable + // alignment checking this exception should never happen, though. + signalError = EFAULT; break; case 19: // SIMD Floating-Point Exception (#XF) type = B_FLOATING_POINT_EXCEPTION; - signal = SIGFPE; + signalNumber = SIGFPE; + signalCode = FPE_FLTDIV; + // TODO: Determine the correct cause via the MXCSR register! + signalAddress = frame->eip; break; default: @@ -306,12 +330,15 @@ unexpected_exception(struct iframe* frame) // If the thread has a signal handler for the signal, we simply send it // the signal. Otherwise we notify the user debugger first. - if (sigaction(signal, NULL, &action) == 0 - && action.sa_handler != SIG_DFL - && action.sa_handler != SIG_IGN) { - send_signal(thread->id, signal); - } else if (user_debug_exception_occurred(type, signal)) - send_signal(team_get_current_team_id(), signal); + if ((sigaction(signalNumber, NULL, &action) == 0 + && action.sa_handler != SIG_DFL + && action.sa_handler != SIG_IGN) + || user_debug_exception_occurred(type, signalNumber)) { + Signal signal(signalNumber, signalCode, signalError, + thread->team->id); + signal.SetAddress((void*)signalAddress); + send_signal_to_thread(thread, signal, 0); + } } else { char name[32]; panic("Unexpected exception \"%s\" occurred in kernel mode! " @@ -500,18 +527,19 @@ hardware_interrupt(struct iframe* frame) cpu_status state = disable_interrupts(); if (thread->cpu->invoke_scheduler) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); scheduler_reschedule(); - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); restore_interrupts(state); } else if (thread->post_interrupt_callback != NULL) { - restore_interrupts(state); void (*callback)(void*) = thread->post_interrupt_callback; void* data = thread->post_interrupt_data; thread->post_interrupt_callback = NULL; thread->post_interrupt_data = NULL; + restore_interrupts(state); + callback(data); } } diff --git a/src/system/kernel/arch/x86/arch_interrupts.S b/src/system/kernel/arch/x86/arch_interrupts.S index f0fdca87f7..dca676c8cc 100644 --- a/src/system/kernel/arch/x86/arch_interrupts.S +++ b/src/system/kernel/arch/x86/arch_interrupts.S @@ -22,6 +22,18 @@ #include "syscall_table.h" +#define LOCK_THREAD_TIME() \ + lea THREAD_time_lock(%edi), %eax; \ + pushl %eax; \ + call acquire_spinlock; + /* leave spinlock address on stack for UNLOCK_THREAD_TIME() */ + +#define UNLOCK_THREAD_TIME() \ + /* spinlock address still on stack from */ \ + /* LOCK_THREAD_TIME() */ \ + call release_spinlock; \ + addl $4, %esp; + #define UPDATE_THREAD_USER_TIME_COMMON() \ movl %eax, %ebx; /* save for later */ \ movl %edx, %ecx; \ @@ -37,19 +49,33 @@ movl %ecx, (THREAD_last_time + 4)(%edi); \ \ /* thread->in_kernel = true; */ \ - movb $1, THREAD_in_kernel(%edi) + movb $1, THREAD_in_kernel(%edi); #define UPDATE_THREAD_USER_TIME() \ + LOCK_THREAD_TIME() \ call system_time; \ - UPDATE_THREAD_USER_TIME_COMMON() + UPDATE_THREAD_USER_TIME_COMMON() \ + UNLOCK_THREAD_TIME() #define UPDATE_THREAD_USER_TIME_PUSH_TIME() \ call system_time; \ push %edx; \ push %eax; \ - UPDATE_THREAD_USER_TIME_COMMON() + \ + LOCK_THREAD_TIME() \ + \ + /* recover the system time, note that */ \ + /* LOCK_THREAD_TIME() leaves an address on the stack */ \ + movl 4(%esp), %eax; \ + movl 8(%esp), %edx; \ + \ + UPDATE_THREAD_USER_TIME_COMMON() \ + \ + UNLOCK_THREAD_TIME() #define UPDATE_THREAD_KERNEL_TIME() \ + LOCK_THREAD_TIME() \ + \ call system_time; \ \ movl %eax, %ebx; /* save for later */ \ @@ -66,7 +92,9 @@ movl %ecx, (THREAD_last_time + 4)(%edi); \ \ /* thread->in_kernel = false; */ \ - movb $0, THREAD_in_kernel(%edi) + movb $0, THREAD_in_kernel(%edi); \ + \ + UNLOCK_THREAD_TIME() \ #define PUSH_IFRAME_BOTTOM(iframeType) \ pusha; \ @@ -798,32 +826,22 @@ FUNCTION(x86_sysenter): FUNCTION_END(x86_sysenter) -/*! Is copied to the signal stack call to restore the original frame when - the signal handler exits. - The copying code (in arch_thread.c::arch_setup_signal_frame()) copies - everything between the i386_return_from_signal and i386_end_return_from_signal - symbols. -*/ -FUNCTION(i386_return_from_signal): - addl $12, %esp // Flushes the 3 arguments to sa_handler - movl $SYSCALL_RESTORE_SIGNAL_FRAME, %eax - // This syscall will restore the cpu context to the - // one existing before calling the signal handler - movl $0, %ecx - lea 4(%esp), %edx - int $99 - ret -FUNCTION_END(i386_return_from_signal) -SYMBOL(i386_end_return_from_signal): +/*! \fn void x86_return_to_userland(iframe* frame) + \brief Returns to the userland environment given by \a frame. + Before returning to userland all potentially necessary kernel exit work is + done. -/*! void i386_restore_frame_from_syscall(struct iframe iframe); - Pops the regs of the iframe from the stack to make it current and then - return to userland. - Interrupts are disabled. + \a frame must point to a location somewhere on the caller's stack (e.g. a + local variable). + The function must be called with interrupts disabled. + + \param frame The iframe defining the userland environment. */ -FUNCTION(i386_restore_frame_from_syscall): - lea 4(%esp), %ebp // iframe to %ebp +FUNCTION(x86_return_to_userland): + // get the iframe* parameter + movl 4(%esp), %ebp + movl %ebp, %esp // check, if any kernel exit work has to be done movl %dr3, %edi @@ -835,7 +853,7 @@ FUNCTION(i386_restore_frame_from_syscall): // update the thread's kernel time and return UPDATE_THREAD_KERNEL_TIME() POP_IFRAME_AND_RETURN() -FUNCTION_END(i386_restore_frame_from_syscall) +FUNCTION_END(x86_return_to_userland) /* status_t x86_vm86_enter(struct vm86_iframe *frame) */ diff --git a/src/system/kernel/arch/x86/arch_thread.cpp b/src/system/kernel/arch/x86/arch_thread.cpp index b3457d6c36..59ad054453 100644 --- a/src/system/kernel/arch/x86/arch_thread.cpp +++ b/src/system/kernel/arch/x86/arch_thread.cpp @@ -22,11 +22,13 @@ #include #include #include +#include #include #include #include "paging/X86PagingStructures.h" #include "paging/X86VMTranslationMap.h" +#include "x86_signals.h" #include "x86_syscalls.h" @@ -66,7 +68,7 @@ class RestartSyscall : public AbstractTraceEntry { // from arch_interrupts.S extern "C" void i386_stack_init(struct farcall *interrupt_stack_offset); -extern "C" void i386_restore_frame_from_syscall(struct iframe frame); +extern "C" void x86_return_to_userland(iframe* frame); // from arch_cpu.c extern void (*gX86SwapFPUFunc)(void *oldState, const void *newState); @@ -135,6 +137,48 @@ get_current_iframe(void) } +static inline void +set_fs_register(uint32 segment) +{ + asm("movl %0,%%fs" :: "r" (segment)); +} + + +static void +set_tls_context(Thread *thread) +{ + int entry = smp_get_current_cpu() + TLS_BASE_SEGMENT; + + set_segment_descriptor_base(&gGDT[entry], thread->user_local_storage); + set_fs_register((entry << 3) | DPL_USER); +} + + +/*! Returns to the userland environment given by \a frame for a thread not + having been userland before. + + Before returning to userland all potentially necessary kernel exit work is + done. + + \param thread The current thread. + \param frame The iframe defining the userland environment. Must point to a + location somewhere on the caller's stack (e.g. a local variable). +*/ +static void +initial_return_to_userland(Thread* thread, iframe* frame) +{ + // disable interrupts and set up CPU specifics for this thread + disable_interrupts(); + + i386_set_tss_and_kstack(thread->kernel_stack_top); + set_tls_context(thread); + x86_set_syscall_stack(thread->kernel_stack_top); + + // return to userland + x86_return_to_userland(frame); +} + + /*! \brief Returns the current thread's topmost (i.e. most recent) userland->kernel transition iframe (usually the first one, save for @@ -206,23 +250,6 @@ x86_next_page_directory(Thread *from, Thread *to) } -static inline void -set_fs_register(uint32 segment) -{ - asm("movl %0,%%fs" :: "r" (segment)); -} - - -static void -set_tls_context(Thread *thread) -{ - int entry = smp_get_current_cpu() + TLS_BASE_SEGMENT; - - set_segment_descriptor_base(&gGDT[entry], thread->user_local_storage); - set_fs_register((entry << 3) | DPL_USER); -} - - void x86_restart_syscall(struct iframe* frame) { @@ -241,20 +268,19 @@ x86_restart_syscall(struct iframe* frame) } -static uint32 * -get_signal_stack(Thread *thread, struct iframe *frame, int signal) +static uint8* +get_signal_stack(Thread* thread, struct iframe* frame, struct sigaction* action) { // use the alternate signal stack if we should and can if (thread->signal_stack_enabled - && (thread->sig_action[signal - 1].sa_flags & SA_ONSTACK) != 0 + && (action->sa_flags & SA_ONSTACK) != 0 && (frame->user_esp < thread->signal_stack_base || frame->user_esp >= thread->signal_stack_base + thread->signal_stack_size)) { - return (uint32 *)(thread->signal_stack_base - + thread->signal_stack_size); + return (uint8*)(thread->signal_stack_base + thread->signal_stack_size); } - return (uint32 *)frame->user_esp; + return (uint8*)frame->user_esp; } @@ -277,57 +303,40 @@ arch_thread_init_thread_struct(Thread *thread) } -status_t -arch_thread_init_kthread_stack(Thread *t, int (*start_func)(void), - void (*entry_func)(void), void (*exit_func)(void)) +/*! Prepares the given thread's kernel stack for executing its entry function. + + \param thread The thread. + \param stack The usable bottom of the thread's kernel stack. + \param stackTop The usable top of the thread's kernel stack. + \param function The entry function the thread shall execute. + \param data Pointer to be passed to the entry function. +*/ +void +arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop, + void (*function)(void*), const void* data) { - addr_t *kstack = (addr_t *)t->kernel_stack_base; - addr_t *kstack_top = (addr_t *)t->kernel_stack_top; - int i; + addr_t* stackTop = (addr_t*)_stackTop; - TRACE(("arch_thread_initialize_kthread_stack: kstack 0x%p, start_func 0x%p, entry_func 0x%p\n", - kstack, start_func, entry_func)); + TRACE(("arch_thread_init_kthread_stack: stack top %p, function %, data: " + "%p\n", stackTop, function, data)); - // clear the kernel stack -#ifdef DEBUG_KERNEL_STACKS -# ifdef STACK_GROWS_DOWNWARDS - memset((void *)((addr_t)kstack + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE), 0, - KERNEL_STACK_SIZE); -# else - memset(kstack, 0, KERNEL_STACK_SIZE); -# endif -#else - memset(kstack, 0, KERNEL_STACK_SIZE); -#endif + // push the function argument, a pointer to the data + *--stackTop = (addr_t)data; - // set the final return address to be thread_kthread_exit - kstack_top--; - *kstack_top = (unsigned int)exit_func; + // push a dummy return address for the function + *--stackTop = 0; - // set the return address to be the start of the first function - kstack_top--; - *kstack_top = (unsigned int)start_func; + // push the function address -- that's the return address used after the + // context switch + *--stackTop = (addr_t)function; - // set the return address to be the start of the entry (thread setup) - // function - kstack_top--; - *kstack_top = (unsigned int)entry_func; - - // simulate pushfl -// kstack_top--; -// *kstack_top = 0x00; // interrupts still disabled after the switch - - // simulate initial popad - for (i = 0; i < 8; i++) { - kstack_top--; - *kstack_top = 0; - } + // simulate pushad as done by x86_context_switch() + for (int i = 0; i < 8; i++) + *--stackTop = 0; // save the stack position - t->arch_info.current_stack.esp = kstack_top; - t->arch_info.current_stack.ss = (addr_t *)KERNEL_DATA_SEG; - - return B_OK; + thread->arch_info.current_stack.esp = stackTop; + thread->arch_info.current_stack.ss = (addr_t*)KERNEL_DATA_SEG; } @@ -411,20 +420,19 @@ arch_thread_dump_info(void *info) } -/** Sets up initial thread context and enters user space - */ - +/*! Sets up initial thread context and enters user space +*/ status_t -arch_thread_enter_userspace(Thread *t, addr_t entry, void *args1, - void *args2) +arch_thread_enter_userspace(Thread* thread, addr_t entry, void* args1, + void* args2) { - addr_t stackTop = t->user_stack_base + t->user_stack_size; + addr_t stackTop = thread->user_stack_base + thread->user_stack_size; uint32 codeSize = (addr_t)x86_end_userspace_thread_exit - (addr_t)x86_userspace_thread_exit; uint32 args[3]; - TRACE(("arch_thread_enter_uspace: entry 0x%lx, args %p %p, ustack_top 0x%lx\n", - entry, args1, args2, stackTop)); + TRACE(("arch_thread_enter_userspace: entry 0x%lx, args %p %p, " + "ustack_top 0x%lx\n", entry, args1, args2, stackTop)); // copy the little stub that calls exit_thread() when the thread entry // function returns, as well as the arguments of the entry function @@ -441,20 +449,22 @@ arch_thread_enter_userspace(Thread *t, addr_t entry, void *args1, if (user_memcpy((void *)stackTop, args, sizeof(args)) < B_OK) return B_BAD_ADDRESS; - thread_at_kernel_exit(); - // also disables interrupts + // prepare the user iframe + iframe frame = {}; + frame.type = IFRAME_TYPE_SYSCALL; + frame.gs = USER_DATA_SEG; + // frame.fs not used -- we call set_tls_context() below + frame.es = USER_DATA_SEG; + frame.ds = USER_DATA_SEG; + frame.eip = entry; + frame.cs = USER_CODE_SEG; + frame.flags = X86_EFLAGS_RESERVED1 | X86_EFLAGS_INTERRUPT + | (3 << X86_EFLAGS_IO_PRIVILEG_LEVEL_SHIFT); + frame.user_esp = stackTop; + frame.user_ss = USER_DATA_SEG; - // install user breakpoints, if any - if ((t->flags & THREAD_FLAGS_BREAKPOINTS_DEFINED) != 0) - x86_init_user_debug_at_kernel_exit(NULL); - - i386_set_tss_and_kstack(t->kernel_stack_top); - - // set the CPU dependent GDT entry for TLS - set_tls_context(t); - - x86_set_syscall_stack(t->kernel_stack_top); - x86_enter_userspace(entry, stackTop); + // return to userland + initial_return_to_userland(thread, &frame); return B_OK; // never gets here @@ -472,9 +482,35 @@ arch_on_signal_stack(Thread *thread) } +/*! Sets up the user iframe for invoking a signal handler. + + The function fills in the remaining fields of the given \a signalFrameData, + copies it to the thread's userland stack (the one on which the signal shall + be handled), and sets up the user iframe so that when returning to userland + a wrapper function is executed that calls the user-defined signal handler. + When the signal handler returns, the wrapper function shall call the + "restore signal frame" syscall with the (possibly modified) signal frame + data. + + The following fields of the \a signalFrameData structure still need to be + filled in: + - \c context.uc_stack: The stack currently used by the thread. + - \c context.uc_mcontext: The current userland state of the registers. + - \c syscall_restart_return_value: Architecture specific use. On x86 the + value of eax and edx which are overwritten by the syscall return value. + + Furthermore the function needs to set \c thread->user_signal_context to the + userland pointer to the \c ucontext_t on the user stack. + + \param thread The current thread. + \param action The signal action specified for the signal to be handled. + \param signalFrameData A partially initialized structure of all the data + that need to be copied to userland. + \return \c B_OK on success, another error code, if something goes wrong. +*/ status_t -arch_setup_signal_frame(Thread *thread, struct sigaction *action, - int signal, int signalMask) +arch_setup_signal_frame(Thread* thread, struct sigaction* action, + struct signal_frame_data* signalFrameData) { struct iframe *frame = get_current_iframe(); if (!IFRAME_IS_USER(frame)) { @@ -482,136 +518,94 @@ arch_setup_signal_frame(Thread *thread, struct sigaction *action, return B_BAD_VALUE; } - uint32 *signalCode; - uint32 *userRegs; - struct vregs regs; - uint32 buffer[6]; - status_t status; + // In case of a BeOS compatible handler map SIGBUS to SIGSEGV, since they + // had the same signal number. + if ((action->sa_flags & SA_BEOS_COMPATIBLE_HANDLER) != 0 + && signalFrameData->info.si_signo == SIGBUS) { + signalFrameData->info.si_signo = SIGSEGV; + } - // start stuffing stuff on the user stack - uint32* userStack = get_signal_stack(thread, frame, signal); + // store the register state in signalFrameData->context.uc_mcontext + signalFrameData->context.uc_mcontext.eip = frame->eip; + signalFrameData->context.uc_mcontext.eflags = frame->flags; + signalFrameData->context.uc_mcontext.eax = frame->eax; + signalFrameData->context.uc_mcontext.ecx = frame->ecx; + signalFrameData->context.uc_mcontext.edx = frame->edx; + signalFrameData->context.uc_mcontext.ebp = frame->ebp; + signalFrameData->context.uc_mcontext.esp = frame->user_esp; + signalFrameData->context.uc_mcontext.edi = frame->edi; + signalFrameData->context.uc_mcontext.esi = frame->esi; + signalFrameData->context.uc_mcontext.ebx = frame->ebx; + i386_fnsave((void *)(&signalFrameData->context.uc_mcontext.xregs)); - // copy syscall restart info onto the user stack - userStack -= (sizeof(thread->syscall_restart.parameters) + 12 + 3) / 4; - uint32 threadFlags = atomic_and(&thread->flags, - ~(THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_64_BIT_SYSCALL_RETURN)); - if (user_memcpy(userStack, &threadFlags, 4) < B_OK - || user_memcpy(userStack + 1, &frame->orig_eax, 4) < B_OK - || user_memcpy(userStack + 2, &frame->orig_edx, 4) < B_OK) + // fill in signalFrameData->context.uc_stack + signal_get_user_stack(frame->user_esp, &signalFrameData->context.uc_stack); + + // store orig_eax/orig_edx in syscall_restart_return_value + signalFrameData->syscall_restart_return_value + = (uint64)frame->orig_edx << 32 | frame->orig_eax; + + // get the stack to use -- that's either the current one or a special signal + // stack + uint8* userStack = get_signal_stack(thread, frame, action); + + // copy the signal frame data onto the stack + userStack -= sizeof(*signalFrameData); + signal_frame_data* userSignalFrameData = (signal_frame_data*)userStack; + if (user_memcpy(userSignalFrameData, signalFrameData, + sizeof(*signalFrameData)) != B_OK) { return B_BAD_ADDRESS; - status = user_memcpy(userStack + 3, thread->syscall_restart.parameters, - sizeof(thread->syscall_restart.parameters)); - if (status < B_OK) - return status; + } - // store the saved regs onto the user stack - regs.eip = frame->eip; - regs.eflags = frame->flags; - regs.eax = frame->eax; - regs.ecx = frame->ecx; - regs.edx = frame->edx; - regs.ebp = frame->ebp; - regs.esp = frame->esp; - regs._reserved_1 = frame->user_esp; - regs._reserved_2[0] = frame->edi; - regs._reserved_2[1] = frame->esi; - regs._reserved_2[2] = frame->ebx; - i386_fnsave((void *)(®s.xregs)); + // prepare the user stack frame for a function call to the signal handler + // wrapper function + uint32 stackFrame[2] = { + frame->eip, // return address + (addr_t)userSignalFrameData, // parameter: pointer to signal frame data + }; - userStack -= (sizeof(struct vregs) + 3) / 4; - userRegs = userStack; - status = user_memcpy(userRegs, ®s, sizeof(regs)); - if (status < B_OK) - return status; + userStack -= sizeof(stackFrame); + if (user_memcpy(userStack, stackFrame, sizeof(stackFrame)) != B_OK) + return B_BAD_ADDRESS; - // now store a code snippet on the stack - userStack -= ((uint32)i386_end_return_from_signal + 3 - - (uint32)i386_return_from_signal) / 4; - signalCode = userStack; - status = user_memcpy(signalCode, (const void *)&i386_return_from_signal, - ((uint32)i386_end_return_from_signal - - (uint32)i386_return_from_signal)); - if (status < B_OK) - return status; + // Update Thread::user_signal_context, now that everything seems to have + // gone fine. + thread->user_signal_context = &userSignalFrameData->context; - // now set up the final part - buffer[0] = (uint32)signalCode; // return address when sa_handler done - buffer[1] = signal; // arguments to sa_handler - buffer[2] = (uint32)action->sa_userdata; - buffer[3] = (uint32)userRegs; - - buffer[4] = signalMask; // Old signal mask to restore - buffer[5] = (uint32)userRegs; // Int frame + extra regs to restore - - userStack -= sizeof(buffer) / 4; - - status = user_memcpy(userStack, buffer, sizeof(buffer)); - if (status < B_OK) - return status; - - frame->user_esp = (uint32)userStack; - frame->eip = (uint32)action->sa_handler; + // Adjust the iframe's esp and eip, so that the thread will continue with + // the prepared stack, executing the signal handler wrapper function. + frame->user_esp = (addr_t)userStack; + frame->eip = x86_get_user_signal_handler_wrapper( + (action->sa_flags & SA_BEOS_COMPATIBLE_HANDLER) != 0); return B_OK; } int64 -arch_restore_signal_frame(void) +arch_restore_signal_frame(struct signal_frame_data* signalFrameData) { - Thread *thread = thread_get_current_thread(); - struct iframe *frame = get_current_iframe(); - int32 signalMask; - uint32 *userStack; - struct vregs* regsPointer; - struct vregs regs; + struct iframe* frame = get_current_iframe(); TRACE(("### arch_restore_signal_frame: entry\n")); - userStack = (uint32 *)frame->user_esp; - if (user_memcpy(&signalMask, &userStack[0], 4) < B_OK - || user_memcpy(®sPointer, &userStack[1], 4) < B_OK - || user_memcpy(®s, regsPointer, sizeof(vregs)) < B_OK) { - return B_BAD_ADDRESS; - } + frame->orig_eax = (uint32)signalFrameData->syscall_restart_return_value; + frame->orig_edx + = (uint32)(signalFrameData->syscall_restart_return_value >> 32); - uint32* syscallRestartInfo - = (uint32*)regsPointer + (sizeof(struct vregs) + 3) / 4; - uint32 threadFlags; - if (user_memcpy(&threadFlags, syscallRestartInfo, 4) < B_OK - || user_memcpy(&frame->orig_eax, syscallRestartInfo + 1, 4) < B_OK - || user_memcpy(&frame->orig_edx, syscallRestartInfo + 2, 4) < B_OK - || user_memcpy(thread->syscall_restart.parameters, - syscallRestartInfo + 3, - sizeof(thread->syscall_restart.parameters)) < B_OK) { - return B_BAD_ADDRESS; - } + frame->eip = signalFrameData->context.uc_mcontext.eip; + frame->flags = (frame->flags & ~(uint32)X86_EFLAGS_USER_FLAGS) + | (signalFrameData->context.uc_mcontext.eflags & X86_EFLAGS_USER_FLAGS); + frame->eax = signalFrameData->context.uc_mcontext.eax; + frame->ecx = signalFrameData->context.uc_mcontext.ecx; + frame->edx = signalFrameData->context.uc_mcontext.edx; + frame->ebp = signalFrameData->context.uc_mcontext.ebp; + frame->user_esp = signalFrameData->context.uc_mcontext.esp; + frame->edi = signalFrameData->context.uc_mcontext.edi; + frame->esi = signalFrameData->context.uc_mcontext.esi; + frame->ebx = signalFrameData->context.uc_mcontext.ebx; - // set restart/64bit return value flags from previous syscall - atomic_and(&thread->flags, - ~(THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_64_BIT_SYSCALL_RETURN)); - atomic_or(&thread->flags, threadFlags - & (THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_64_BIT_SYSCALL_RETURN)); - - // TODO: Verify that just restoring the old signal mask is right! Bash for - // instance changes the procmask in a signal handler. Those changes are - // lost the way we do it. - atomic_set(&thread->sig_block_mask, signalMask); - update_current_thread_signals_flag(); - - frame->eip = regs.eip; - frame->flags = regs.eflags; - frame->eax = regs.eax; - frame->ecx = regs.ecx; - frame->edx = regs.edx; - frame->ebp = regs.ebp; - frame->esp = regs.esp; - frame->user_esp = regs._reserved_1; - frame->edi = regs._reserved_2[0]; - frame->esi = regs._reserved_2[1]; - frame->ebx = regs._reserved_2[2]; - - i386_frstor((void *)(®s.xregs)); + i386_frstor((void*)(&signalFrameData->context.uc_mcontext.xregs)); TRACE(("### arch_restore_signal_frame: exit\n")); @@ -637,27 +631,21 @@ arch_store_fork_frame(struct arch_fork_arg *arg) } -/** Restores the frame from a forked team as specified by the provided - * arch_fork_arg structure. - * Needs to be called from within the child team, ie. instead of - * arch_thread_enter_uspace() as thread "starter". - * This function does not return to the caller, but will enter userland - * in the child team at the same position where the parent team left of. - */ +/*! Restores the frame from a forked team as specified by the provided + arch_fork_arg structure. + Needs to be called from within the child team, i.e. instead of + arch_thread_enter_userspace() as thread "starter". + This function does not return to the caller, but will enter userland + in the child team at the same position where the parent team left of. + \param arg The architecture specific fork arguments including the + environment to restore. Must point to a location somewhere on the + caller's stack. +*/ void -arch_restore_fork_frame(struct arch_fork_arg *arg) +arch_restore_fork_frame(struct arch_fork_arg* arg) { - Thread *thread = thread_get_current_thread(); - - disable_interrupts(); - - i386_set_tss_and_kstack(thread->kernel_stack_top); - - // set the CPU dependent GDT entry for TLS (set the current %fs register) - set_tls_context(thread); - - i386_restore_frame_from_syscall(arg->iframe); + initial_return_to_userland(thread_get_current_thread(), &arg->iframe); } diff --git a/src/system/kernel/arch/x86/arch_user_debugger.cpp b/src/system/kernel/arch/x86/arch_user_debugger.cpp index eb3bcf690e..3a2673c367 100644 --- a/src/system/kernel/arch/x86/arch_user_debugger.cpp +++ b/src/system/kernel/arch/x86/arch_user_debugger.cpp @@ -781,18 +781,16 @@ x86_init_user_debug_at_kernel_exit(struct iframe *frame) // disable kernel breakpoints disable_breakpoints(); - GRAB_THREAD_LOCK(); + // install the user breakpoints GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info); arch_team_debug_info &teamInfo = thread->team->debug_info.arch_info; - // install the user breakpoints install_breakpoints(teamInfo); atomic_or(&thread->flags, THREAD_FLAGS_BREAKPOINTS_INSTALLED); RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info); - RELEASE_THREAD_LOCK(); } @@ -815,20 +813,19 @@ x86_exit_user_debug_at_kernel_entry() if (!(thread->flags & THREAD_FLAGS_BREAKPOINTS_INSTALLED)) return; - GRAB_THREAD_LOCK(); - // disable user breakpoints disable_breakpoints(); // install kernel breakpoints Team* kernelTeam = team_get_kernel_team(); + GRAB_TEAM_DEBUG_INFO_LOCK(kernelTeam->debug_info); + install_breakpoints(kernelTeam->debug_info.arch_info); - RELEASE_TEAM_DEBUG_INFO_LOCK(kernelTeam->debug_info); atomic_and(&thread->flags, ~THREAD_FLAGS_BREAKPOINTS_INSTALLED); - RELEASE_THREAD_LOCK(); + RELEASE_TEAM_DEBUG_INFO_LOCK(kernelTeam->debug_info); } @@ -927,7 +924,8 @@ x86_handle_debug_exception(struct iframe *frame) // We need to ignore the exception now and send a single-step // notification later, when the thread wants to return from the // kernel. - InterruptsSpinLocker threadLocker(gThreadSpinlock); + InterruptsSpinLocker threadDebugInfoLocker( + thread->debug_info.lock); // Check whether the team is still being debugged and set // the B_THREAD_DEBUG_NOTIFY_SINGLE_STEP and diff --git a/src/system/kernel/arch/x86/arch_x86.S b/src/system/kernel/arch/x86/arch_x86.S index 056f170300..a0c60ff2af 100644 --- a/src/system/kernel/arch/x86/arch_x86.S +++ b/src/system/kernel/arch/x86/arch_x86.S @@ -157,27 +157,6 @@ FUNCTION_END(x86_userspace_thread_exit) SYMBOL(x86_end_userspace_thread_exit): -/* void x86_enter_userspace(addr_t entry, addr_t stackTop); */ -FUNCTION(x86_enter_userspace): - movl 4(%esp), %eax // get entry point - movl 8(%esp), %ebx // get user stack - movl $USER_DATA_SEG, %ecx - movw %cx, %ds - movw %cx, %es - //movw $0x33 + cpu_num, %fs -> fs points to the TLS storage (CPU dependent segment) - movw %cx, %gs - - xorl %ebp, %ebp // this is the last stack frame - we don't need one on return - // (%ebp marks the beginning of this stack frame) - pushl %ecx // user data segment - pushl %ebx // user stack - pushl $(1 << 9) | 2 // user flags - pushl $USER_CODE_SEG // user code segment - pushl %eax // user IP - iret -FUNCTION_END(x86_enter_userspace) - - null_idt_descr: .word 0 .word 0,0 diff --git a/src/system/kernel/arch/x86/asm_offsets.cpp b/src/system/kernel/arch/x86/asm_offsets.cpp index 490fab9dd2..b38f803ae4 100644 --- a/src/system/kernel/arch/x86/asm_offsets.cpp +++ b/src/system/kernel/arch/x86/asm_offsets.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -34,6 +35,7 @@ dummy() DEFINE_OFFSET_MACRO(CPU_ENT, cpu_ent, fault_handler_stack_pointer); // struct Thread + DEFINE_OFFSET_MACRO(THREAD, Thread, time_lock); DEFINE_OFFSET_MACRO(THREAD, Thread, kernel_time); DEFINE_OFFSET_MACRO(THREAD, Thread, user_time); DEFINE_OFFSET_MACRO(THREAD, Thread, last_time); @@ -43,6 +45,7 @@ dummy() DEFINE_OFFSET_MACRO(THREAD, Thread, fault_handler); // struct iframe + DEFINE_SIZEOF_MACRO(IFRAME, iframe); DEFINE_OFFSET_MACRO(IFRAME, iframe, cs); DEFINE_OFFSET_MACRO(IFRAME, iframe, eax); DEFINE_OFFSET_MACRO(IFRAME, iframe, edx); @@ -66,4 +69,20 @@ dummy() memcpy); DEFINE_OFFSET_MACRO(X86_OPTIMIZED_FUNCTIONS, x86_optimized_functions, memset); + + // struct signal_frame_data + DEFINE_SIZEOF_MACRO(SIGNAL_FRAME_DATA, signal_frame_data); + DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, info); + DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, context); + DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, user_data); + DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, handler); + + // struct ucontext_t + DEFINE_OFFSET_MACRO(UCONTEXT_T, __ucontext_t, uc_mcontext); + + // struct vregs + DEFINE_SIZEOF_MACRO(VREGS, vregs); + + // struct siginfo_t + DEFINE_OFFSET_MACRO(SIGINFO_T, __siginfo_t, si_signo); } diff --git a/src/system/kernel/arch/x86/x86_signals.cpp b/src/system/kernel/arch/x86/x86_signals.cpp new file mode 100644 index 0000000000..80977a331c --- /dev/null +++ b/src/system/kernel/arch/x86/x86_signals.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "x86_signals.h" + +#include + +#include + +#include +#include +#include +#include + +#include "syscall_numbers.h" + + +// implemented in assembly +extern "C" void x86_signal_frame_function_beos(signal_frame_data* frameData); + + +extern "C" void +x86_signal_frame_function(signal_frame_data* frameData) +{ + // Note: This function is copied to the commpage. Hence it needs to be + // position independent. We don't build this source file with the respective + // flags, but the code the compiler generates for this function is position + // independent anyway. It simply doesn't contain constructs that could + // result in position dependent code. The potentially problematic jumps + // needed due to the "if" statement are all harmless relative jumps. + + if (frameData->siginfo_handler) { + // SA_SIGINFO style handler function -- we additionally pass the user + // data pointer + void (*handler)(int, siginfo_t*, void*, void*) + = (void (*)(int, siginfo_t*, void*, void*))frameData->handler; + handler(frameData->info.si_signo, &frameData->info, + &frameData->context, frameData->user_data); + } else { + // Simple handler function -- we call it with additional user data + // pointer and vregs parameters. Note that unlike in BeOS the last + // parameter is a pointer to a vregs structure, while in BeOS the + // structure was passed be value. For setting up a BeOS binary + // compatible signal handler call x86_signal_frame_function_beos() is + // used instead. + void (*handler)(int, void*, vregs*) + = (void (*)(int, void*, vregs*))frameData->handler; + handler(frameData->info.si_signo, frameData->user_data, + &frameData->context.uc_mcontext); + } + + #define TO_STRING_LITERAL_HELPER(number) #number + #define TO_STRING_LITERAL(number) TO_STRING_LITERAL_HELPER(number) + + // call the restore_signal_frame() syscall -- does not return (here) + asm volatile( + // push frameData -- the parameter to restore_signal_frame() + "pushl %0;" + // push a dummy return value + "pushl $0;" + // syscall number to eax + "movl $" TO_STRING_LITERAL(SYSCALL_RESTORE_SIGNAL_FRAME) ", %%eax;" + // syscall + "int $99;" + :: "r"(frameData) + ); + + #undef TO_STRING_LITERAL_HELPER + #undef TO_STRING_LITERAL +} + + +static void +register_signal_handler_function(const char* functionName, int32 commpageIndex, + const char* commpageSymbolName, addr_t expectedAddress) +{ + // look up the x86_signal_frame_function() symbol -- we have its address, + // but also need its size + elf_symbol_info symbolInfo; + if (elf_lookup_kernel_symbol(functionName, &symbolInfo) + != B_OK) { + panic("x86_initialize_commpage_signal_handler(): Failed to find " + "signal frame function \"%s\"!", functionName); + } + + ASSERT(expectedAddress == symbolInfo.address); + + // fill in the commpage table entry + fill_commpage_entry(commpageIndex, (void*)symbolInfo.address, + symbolInfo.size); + + // add symbol to the commpage image + image_id image = get_commpage_image(); + elf_add_memory_image_symbol(image, commpageSymbolName, + ((addr_t*)USER_COMMPAGE_ADDR)[commpageIndex], symbolInfo.size, + B_SYMBOL_TYPE_TEXT); +} + + +void +x86_initialize_commpage_signal_handler() +{ + // standard handler + register_signal_handler_function("x86_signal_frame_function", + COMMPAGE_ENTRY_X86_SIGNAL_HANDLER, "commpage_signal_handler", + (addr_t)&x86_signal_frame_function); + + // handler for BeOS backwards compatibility + register_signal_handler_function("x86_signal_frame_function_beos", + COMMPAGE_ENTRY_X86_SIGNAL_HANDLER_BEOS, "commpage_signal_handler_beos", + (addr_t)&x86_signal_frame_function_beos); +} + + +addr_t +x86_get_user_signal_handler_wrapper(bool beosHandler) +{ + int32 index = beosHandler + ? COMMPAGE_ENTRY_X86_SIGNAL_HANDLER_BEOS + : COMMPAGE_ENTRY_X86_SIGNAL_HANDLER; + return ((addr_t*)USER_COMMPAGE_ADDR)[index]; +} diff --git a/src/system/kernel/arch/x86/x86_signals.h b/src/system/kernel/arch/x86/x86_signals.h new file mode 100644 index 0000000000..282d4afec1 --- /dev/null +++ b/src/system/kernel/arch/x86/x86_signals.h @@ -0,0 +1,16 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_X86_SIGNALS_H +#define _KERNEL_ARCH_X86_SIGNALS_H + + +#include + + +void x86_initialize_commpage_signal_handler(); +addr_t x86_get_user_signal_handler_wrapper(bool beosHandler); + + +#endif // _KERNEL_ARCH_X86_SIGNALS_H diff --git a/src/system/kernel/arch/x86/x86_signals_asm.S b/src/system/kernel/arch/x86/x86_signals_asm.S new file mode 100644 index 0000000000..38c32e9d54 --- /dev/null +++ b/src/system/kernel/arch/x86/x86_signals_asm.S @@ -0,0 +1,71 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include +#include + +#include "asm_offsets.h" +#include "syscall_numbers.h" + + +/*! \fn void x86_signal_frame_function_beos(signal_frame_data* frameData) + \brief Wrapper function for BeOS-style signal handler functions. + \param frameData The signal frame data. +*/ +FUNCTION(x86_signal_frame_function_beos): + // set up a stack frame + push %ebp + mov %esp, %ebp + + // Move our parameter to %esi, so we can conveniently work with it. Note + // that we're free to use non-scratch registers without saving them, since + // we don't have any caller to save them for. The caller will restore the + // interrupted environment anyway. + mov 8(%ebp), %esi + + // push the parameters for the handler function + + // make space for the vregs parameter + lea -VREGS_sizeof(%esp), %esp + mov %esp, %edi + + // copy the vregs via memcpy() + pushl $VREGS_sizeof + lea SIGNAL_FRAME_DATA_context + UCONTEXT_T_uc_mcontext(%esi), %eax + push %eax + push %edi + movl USER_COMMPAGE_ADDR + 4 * COMMPAGE_ENTRY_X86_MEMCPY, %eax + call *%eax + addl $12, %esp + + // the vregs are on the stack -- push user data and signal number + movl SIGNAL_FRAME_DATA_user_data(%esi), %eax + push %eax + movl SIGNAL_FRAME_DATA_info+SIGINFO_T_si_signo(%esi), %eax + push %eax + + // call the signal handler + movl SIGNAL_FRAME_DATA_handler(%esi), %eax + call *%eax + addl $8, %esp // pop only signal number and user data arguments + + // copy the vregs back to the frameData structure + pushl $VREGS_sizeof + push %edi + lea SIGNAL_FRAME_DATA_context + UCONTEXT_T_uc_mcontext(%esi), %eax + push %eax + movl USER_COMMPAGE_ADDR + 4 * COMMPAGE_ENTRY_X86_MEMCPY, %eax + call *%eax + addl $12 + VREGS_sizeof, %esp + + // call the _kern_restore_signal_frame() syscall -- does not return (here) + pushl %esi + pushl $0 // dummy return value + movl $SYSCALL_RESTORE_SIGNAL_FRAME, %eax + int $99 + + // never gets here +FUNCTION_END(x86_signal_frame_function_beos) diff --git a/src/system/kernel/arch/x86/x86_syscalls.cpp b/src/system/kernel/arch/x86/x86_syscalls.cpp index d0ba8d0473..bd14bb0faf 100644 --- a/src/system/kernel/arch/x86/x86_syscalls.cpp +++ b/src/system/kernel/arch/x86/x86_syscalls.cpp @@ -75,7 +75,7 @@ init_amd_syscall_registers(void* dummy, int cpuNum) // #pragma mark - -status_t +void x86_initialize_commpage_syscall(void) { void* syscallCode = (void *)&_user_syscall_int; @@ -113,6 +113,4 @@ x86_initialize_commpage_syscall(void) elf_add_memory_image_symbol(image, "commpage_syscall", ((addr_t*)USER_COMMPAGE_ADDR)[COMMPAGE_ENTRY_X86_SYSCALL], len, B_SYMBOL_TYPE_TEXT); - - return B_OK; } diff --git a/src/system/kernel/arch/x86/x86_syscalls.h b/src/system/kernel/arch/x86/x86_syscalls.h index f6434ac694..db0dd75f33 100644 --- a/src/system/kernel/arch/x86/x86_syscalls.h +++ b/src/system/kernel/arch/x86/x86_syscalls.h @@ -12,7 +12,7 @@ extern void (*gX86SetSyscallStack)(addr_t stackTop); -status_t x86_initialize_commpage_syscall(); +void x86_initialize_commpage_syscall(); static inline void diff --git a/src/system/kernel/condition_variable.cpp b/src/system/kernel/condition_variable.cpp index 6fb71c5d0d..ef0dcd28d0 100644 --- a/src/system/kernel/condition_variable.cpp +++ b/src/system/kernel/condition_variable.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Copyright 2007-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -134,14 +134,15 @@ ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout) conditionLocker.Unlock(); - SpinLocker threadLocker(gThreadSpinlock); + SpinLocker schedulerLocker(gSchedulerLock); status_t error; if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) != 0) error = thread_block_with_timeout_locked(flags, timeout); else error = thread_block_locked(thread_get_current_thread()); - threadLocker.Unlock(); + + schedulerLocker.Unlock(); conditionLocker.Lock(); @@ -220,12 +221,12 @@ ConditionVariable::Publish(const void* object, const char* objectType) void -ConditionVariable::Unpublish(bool threadsLocked) +ConditionVariable::Unpublish(bool schedulerLocked) { ASSERT(fObject != NULL); InterruptsLocker _; - SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock); + SpinLocker schedulerLocker(schedulerLocked ? NULL : &gSchedulerLock); SpinLocker locker(sConditionVariablesLock); #if KDEBUG @@ -262,7 +263,7 @@ ConditionVariable::Wait(uint32 flags, bigtime_t timeout) /*static*/ void -ConditionVariable::NotifyOne(const void* object, bool threadsLocked, +ConditionVariable::NotifyOne(const void* object, bool schedulerLocked, status_t result) { InterruptsSpinLocker locker(sConditionVariablesLock); @@ -271,13 +272,13 @@ ConditionVariable::NotifyOne(const void* object, bool threadsLocked, if (variable == NULL) return; - variable->NotifyOne(threadsLocked, result); + variable->NotifyOne(schedulerLocked, result); } /*static*/ void -ConditionVariable::NotifyAll(const void* object, - bool threadsLocked, status_t result) +ConditionVariable::NotifyAll(const void* object, bool schedulerLocked, + status_t result) { InterruptsSpinLocker locker(sConditionVariablesLock); ConditionVariable* variable = sConditionVariableHash.Lookup(object); @@ -285,7 +286,7 @@ ConditionVariable::NotifyAll(const void* object, if (variable == NULL) return; - variable->NotifyAll(threadsLocked, result); + variable->NotifyAll(schedulerLocked, result); } @@ -321,10 +322,10 @@ ConditionVariable::Dump() const void -ConditionVariable::_Notify(bool all, bool threadsLocked, status_t result) +ConditionVariable::_Notify(bool all, bool schedulerLocked, status_t result) { InterruptsLocker _; - SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock); + SpinLocker schedulerLocker(schedulerLocked ? NULL : &gSchedulerLock); SpinLocker locker(sConditionVariablesLock); if (!fEntries.IsEmpty()) { @@ -339,7 +340,7 @@ ConditionVariable::_Notify(bool all, bool threadsLocked, status_t result) /*! Called with interrupts disabled and the condition variable spinlock and - thread lock held. + scheduler lock held. */ void ConditionVariable::_NotifyLocked(bool all, status_t result) diff --git a/src/system/kernel/cpu.cpp b/src/system/kernel/cpu.cpp index 46bb56b663..44bff7e25d 100644 --- a/src/system/kernel/cpu.cpp +++ b/src/system/kernel/cpu.cpp @@ -10,12 +10,14 @@ #include -#include #include -#include #include +#include +#include +#include + /* global per-cpu structure */ cpu_ent gCPU[MAX_BOOT_CPUS]; @@ -30,7 +32,7 @@ cpu_init(kernel_args *args) } -status_t +status_t cpu_init_percpu(kernel_args *args, int curr_cpu) { return arch_cpu_init_percpu(args, curr_cpu); @@ -66,24 +68,15 @@ cpu_preboot_init_percpu(kernel_args *args, int curr_cpu) bigtime_t cpu_get_active_time(int32 cpu) { - bigtime_t activeTime; - cpu_status state; - if (cpu < 0 || cpu > smp_get_num_cpus()) return 0; - // We need to grab the thread lock here, because the thread activity - // time is not maintained atomically (because there is no need to) + // We need to grab the scheduler lock here, because the thread activity + // time is not maintained atomically (because there is no need to). - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); - activeTime = gCPU[cpu].active_time; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - return activeTime; + return gCPU[cpu].active_time; } diff --git a/src/system/kernel/debug/debug_heap.cpp b/src/system/kernel/debug/debug_heap.cpp index 633b89ef64..069e97d6f1 100644 --- a/src/system/kernel/debug/debug_heap.cpp +++ b/src/system/kernel/debug/debug_heap.cpp @@ -12,11 +12,11 @@ #include -#define INITIAL_HEAP_SIZE B_PAGE_SIZE +#define INITIAL_DEBUG_HEAP_SIZE B_PAGE_SIZE -static char sInitialHeap[INITIAL_HEAP_SIZE] __attribute__ ((aligned (8))); +static char sInitialHeap[INITIAL_DEBUG_HEAP_SIZE] __attribute__ ((aligned (8))); static void* sHeapBase = sInitialHeap; -static size_t sHeapSize = INITIAL_HEAP_SIZE; +static size_t sHeapSize = INITIAL_DEBUG_HEAP_SIZE; const kdebug_alloc_t kdebug_alloc = {}; diff --git a/src/system/kernel/debug/system_profiler.cpp b/src/system/kernel/debug/system_profiler.cpp index 6c034503af..c2d75492cc 100644 --- a/src/system/kernel/debug/system_profiler.cpp +++ b/src/system/kernel/debug/system_profiler.cpp @@ -108,10 +108,6 @@ private: inline void _MaybeNotifyProfilerThreadLocked(); inline void _MaybeNotifyProfilerThread(); - static bool _InitialTeamIterator(Team* team, - void* cookie); - static bool _InitialThreadIterator(Thread* thread, - void* cookie); static bool _InitialImageIterator(struct image* image, void* cookie); @@ -189,6 +185,8 @@ private: size_t fBufferStart; size_t fBufferSize; uint64 fDroppedEvents; + int64 fLastTeamAddedSerialNumber; + int64 fLastThreadAddedSerialNumber; bool fTeamNotificationsRequested; bool fTeamNotificationsEnabled; bool fThreadNotificationsRequested; @@ -203,7 +201,6 @@ private: bool fProfilingActive; bool fReentered[B_MAX_CPU_COUNT]; CPUProfileData fCPUData[B_MAX_CPU_COUNT]; - Thread** fRunningThreads; WaitObject* fWaitObjectBuffer; int32 fWaitObjectCount; WaitObjectList fUsedWaitObjects; @@ -212,6 +209,9 @@ private: }; +/*! Notifies the profiler thread when the profiling buffer is full enough. + The caller must hold the scheduler lock and fLock. +*/ inline void SystemProfiler::_MaybeNotifyProfilerThreadLocked() { @@ -219,7 +219,9 @@ SystemProfiler::_MaybeNotifyProfilerThreadLocked() if (fWaitingProfilerThread != NULL && fBufferSize > fBufferCapacity / 2) { int cpu = smp_get_current_cpu(); fReentered[cpu] = true; + thread_unblock_locked(fWaitingProfilerThread, B_OK); + fWaitingProfilerThread = NULL; fReentered[cpu] = false; } @@ -232,7 +234,7 @@ SystemProfiler::_MaybeNotifyProfilerThread() if (fWaitingProfilerThread == NULL) return; - InterruptsSpinLocker threadsLocker(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); SpinLocker locker(fLock); _MaybeNotifyProfilerThreadLocked(); @@ -255,6 +257,8 @@ SystemProfiler::SystemProfiler(team_id team, const area_info& userAreaInfo, fBufferStart(0), fBufferSize(0), fDroppedEvents(0), + fLastTeamAddedSerialNumber(0), + fLastThreadAddedSerialNumber(0), fTeamNotificationsRequested(false), fTeamNotificationsEnabled(false), fThreadNotificationsRequested(false), @@ -294,6 +298,7 @@ SystemProfiler::~SystemProfiler() // inactive. InterruptsSpinLocker locker(fLock); if (fWaitingProfilerThread != NULL) { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(fWaitingProfilerThread, B_OK); fWaitingProfilerThread = NULL; } @@ -302,7 +307,7 @@ SystemProfiler::~SystemProfiler() // stop scheduler listening if (fSchedulerNotificationsRequested) { - InterruptsSpinLocker threadsLocker(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); scheduler_remove_listener(this); } @@ -446,11 +451,24 @@ SystemProfiler::Init() // teams if ((fFlags & B_SYSTEM_PROFILER_TEAM_EVENTS) != 0) { - InterruptsSpinLocker teamsLocker(gTeamSpinlock); - if (team_iterate_through_teams(&_InitialTeamIterator, this) != NULL) - return B_BUFFER_OVERFLOW; + InterruptsSpinLocker locker(fLock); + + TeamListIterator iterator; + while (Team* team = iterator.Next()) { + locker.Unlock(); + + bool added = _TeamAdded(team); + + // release the reference returned by the iterator + team->ReleaseReference(); + + if (!added) + return B_BUFFER_OVERFLOW; + + locker.Lock(); + } + fTeamNotificationsEnabled = true; - teamsLocker.Unlock(); } // images @@ -460,21 +478,29 @@ SystemProfiler::Init() } // threads - Thread* runningThreads[B_MAX_CPU_COUNT]; - memset(runningThreads, 0, sizeof(runningThreads)); - fRunningThreads = runningThreads; + if ((fFlags & B_SYSTEM_PROFILER_THREAD_EVENTS) != 0) { + InterruptsSpinLocker locker(fLock); - InterruptsSpinLocker threadsLocker(gThreadSpinlock); - if ((fFlags & B_SYSTEM_PROFILER_THREAD_EVENTS) != 0 - || (fFlags & B_SYSTEM_PROFILER_SCHEDULING_EVENTS) != 0) { - if (thread_iterate_through_threads(&_InitialThreadIterator, this) - != NULL) { - return B_BUFFER_OVERFLOW; + ThreadListIterator iterator; + while (Thread* thread = iterator.Next()) { + locker.Unlock(); + + bool added = _ThreadAdded(thread); + + // release the reference returned by the iterator + thread->ReleaseReference(); + + if (!added) + return B_BUFFER_OVERFLOW; + + locker.Lock(); } - fThreadNotificationsEnabled - = (fFlags & B_SYSTEM_PROFILER_THREAD_EVENTS) != 0; + + fThreadNotificationsEnabled = true; } + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + fProfilingActive = true; // start scheduler and wait object listening @@ -490,12 +516,13 @@ SystemProfiler::Init() // fake schedule events for the initially running threads int32 cpuCount = smp_get_num_cpus(); for (int32 i = 0; i < cpuCount; i++) { - if (runningThreads[i] != NULL) - ThreadScheduled(runningThreads[i], runningThreads[i]); + Thread* thread = gCPU[i].running_thread; + if (thread != NULL) + ThreadScheduled(thread, thread); } } - threadsLocker.Unlock(); + schedulerLocker.Unlock(); // I/O scheduling if ((fFlags & B_SYSTEM_PROFILER_IO_SCHEDULING_EVENTS) != 0) { @@ -545,9 +572,12 @@ SystemProfiler::NextBuffer(size_t bytesRead, uint64* _droppedEvents) Thread* thread = thread_get_current_thread(); fWaitingProfilerThread = thread; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + thread_prepare_to_block(thread, B_CAN_INTERRUPT, THREAD_BLOCK_TYPE_OTHER, "system profiler buffer"); + schedulerLocker.Unlock(); locker.Unlock(); status_t error = thread_block_with_timeout(B_RELATIVE_TIMEOUT, 1000000); @@ -587,16 +617,14 @@ SystemProfiler::EventOccurred(NotificationService& service, return; if (strcmp(service.Name(), "teams") == 0) { - if (!fTeamNotificationsEnabled) - return; - Team* team = (Team*)event->GetPointer("teamStruct", NULL); if (team == NULL) return; switch (eventCode) { case TEAM_ADDED: - _TeamAdded(team); + if (fTeamNotificationsEnabled) + _TeamAdded(team); break; case TEAM_REMOVED: @@ -613,28 +641,39 @@ SystemProfiler::EventOccurred(NotificationService& service, return; } - _TeamRemoved(team); + // When we're still doing the initial team list scan, we are + // also interested in removals that happened to teams we have + // already seen. + if (fTeamNotificationsEnabled + || team->serial_number <= fLastTeamAddedSerialNumber) { + _TeamRemoved(team); + } break; case TEAM_EXEC: - _TeamExec(team); + if (fTeamNotificationsEnabled) + _TeamExec(team); break; } } else if (strcmp(service.Name(), "threads") == 0) { - if (!fThreadNotificationsEnabled) - return; - Thread* thread = (Thread*)event->GetPointer("threadStruct", NULL); if (thread == NULL) return; switch (eventCode) { case THREAD_ADDED: - _ThreadAdded(thread); + if (fThreadNotificationsEnabled) + _ThreadAdded(thread); break; case THREAD_REMOVED: - _ThreadRemoved(thread); + // When we're still doing the initial thread list scan, we are + // also interested in removals that happened to threads we have + // already seen. + if (fThreadNotificationsEnabled + || thread->serial_number <= fLastThreadAddedSerialNumber) { + _ThreadRemoved(thread); + } break; } } else if (strcmp(service.Name(), "images") == 0) { @@ -820,11 +859,22 @@ SystemProfiler::RWLockInitialized(rw_lock* lock) bool SystemProfiler::_TeamAdded(Team* team) { - size_t nameLen = strlen(team->name); - size_t argsLen = strlen(team->args); + TeamLocker teamLocker(team); + + size_t nameLen = strlen(team->Name()); + size_t argsLen = strlen(team->Args()); InterruptsSpinLocker locker(fLock); + // During the initial scan check whether the team is already gone again. + // Later this cannot happen, since the team creator notifies us before + // actually starting the team. + if (!fTeamNotificationsEnabled && team->state >= TEAM_STATE_DEATH) + return true; + + if (team->serial_number > fLastTeamAddedSerialNumber) + fLastTeamAddedSerialNumber = team->serial_number; + system_profiler_team_added* event = (system_profiler_team_added*) _AllocateBuffer( sizeof(system_profiler_team_added) + nameLen + 1 + argsLen, @@ -833,9 +883,9 @@ SystemProfiler::_TeamAdded(Team* team) return false; event->team = team->id; - strcpy(event->name, team->name); + strcpy(event->name, team->Name()); event->args_offset = nameLen + 1; - strcpy(event->name + nameLen + 1, team->args); + strcpy(event->name + nameLen + 1, team->Args()); fHeader->size = fBufferSize; @@ -846,6 +896,12 @@ SystemProfiler::_TeamAdded(Team* team) bool SystemProfiler::_TeamRemoved(Team* team) { + // TODO: It is possible that we get remove notifications for teams that + // had already been removed from the global team list when we did the + // initial scan, but were still in the process of dying. ATM it is not + // really possible to identify such a case. + + TeamLocker teamLocker(team); InterruptsSpinLocker locker(fLock); system_profiler_team_removed* event = (system_profiler_team_removed*) @@ -865,7 +921,9 @@ SystemProfiler::_TeamRemoved(Team* team) bool SystemProfiler::_TeamExec(Team* team) { - size_t argsLen = strlen(team->args); + TeamLocker teamLocker(team); + + size_t argsLen = strlen(team->Args()); InterruptsSpinLocker locker(fLock); @@ -878,7 +936,7 @@ SystemProfiler::_TeamExec(Team* team) event->team = team->id; strlcpy(event->thread_name, team->main_thread->name, sizeof(event->thread_name)); - strcpy(event->args, team->args); + strcpy(event->args, team->Args()); fHeader->size = fBufferSize; @@ -889,8 +947,18 @@ SystemProfiler::_TeamExec(Team* team) bool SystemProfiler::_ThreadAdded(Thread* thread) { + ThreadLocker threadLocker(thread); InterruptsSpinLocker locker(fLock); + // During the initial scan check whether the team is already gone again. + // Later this cannot happen, since the team creator notifies us before + // actually starting the thread. + if (!fThreadNotificationsEnabled && !thread->IsAlive()) + return true; + + if (thread->serial_number > fLastThreadAddedSerialNumber) + fLastThreadAddedSerialNumber = thread->serial_number; + system_profiler_thread_added* event = (system_profiler_thread_added*) _AllocateBuffer(sizeof(system_profiler_thread_added), B_SYSTEM_PROFILER_THREAD_ADDED, 0, 0); @@ -910,6 +978,12 @@ SystemProfiler::_ThreadAdded(Thread* thread) bool SystemProfiler::_ThreadRemoved(Thread* thread) { + // TODO: It is possible that we get remove notifications for threads that + // had already been removed from the global thread list when we did the + // initial scan, but were still in the process of dying. ATM it is not + // really possible to identify such a case. + + ThreadLocker threadLocker(thread); InterruptsSpinLocker locker(fLock); system_profiler_thread_removed* event @@ -1236,28 +1310,6 @@ SystemProfiler::_WaitObjectUsed(addr_t object, uint32 type) } -/*static*/ bool -SystemProfiler::_InitialTeamIterator(Team* team, void* cookie) -{ - SystemProfiler* self = (SystemProfiler*)cookie; - return !self->_TeamAdded(team); -} - - -/*static*/ bool -SystemProfiler::_InitialThreadIterator(Thread* thread, void* cookie) -{ - SystemProfiler* self = (SystemProfiler*)cookie; - - if ((self->fFlags & B_SYSTEM_PROFILER_SCHEDULING_EVENTS) != 0 - && thread->state == B_THREAD_RUNNING && thread->cpu != NULL) { - self->fRunningThreads[thread->cpu->cpu_num] = thread; - } - - return !self->_ThreadAdded(thread); -} - - /*static*/ bool SystemProfiler::_InitialImageIterator(struct image* image, void* cookie) { diff --git a/src/system/kernel/debug/user_debugger.cpp b/src/system/kernel/debug/user_debugger.cpp index bde389b349..ebb173673e 100644 --- a/src/system/kernel/debug/user_debugger.cpp +++ b/src/system/kernel/debug/user_debugger.cpp @@ -64,12 +64,12 @@ static status_t ensure_debugger_installed(); static void get_team_debug_info(team_debug_info &teamDebugInfo); -static status_t +static inline status_t kill_interruptable_write_port(port_id port, int32 code, const void *buffer, size_t bufferSize) { - return write_port_etc(port, code, buffer, bufferSize, - B_KILL_CAN_INTERRUPT, 0); + return write_port_etc(port, code, buffer, bufferSize, B_KILL_CAN_INTERRUPT, + 0); } @@ -127,7 +127,7 @@ debugger_write(port_id port, int32 code, const void *buffer, size_t bufferSize, /*! Updates the thread::flags field according to what user debugger flags are set for the thread. - Interrupts must be disabled and the thread lock must be held. + Interrupts must be disabled and the thread's debug info lock must be held. */ static void update_thread_user_debug_flag(Thread* thread) @@ -141,7 +141,7 @@ update_thread_user_debug_flag(Thread* thread) /*! Updates the thread::flags THREAD_FLAGS_BREAKPOINTS_DEFINED bit of the given thread. - Interrupts must be disabled and the team lock must be held. + Interrupts must be disabled and the thread debug info lock must be held. */ static void update_thread_breakpoints_flag(Thread* thread) @@ -155,15 +155,16 @@ update_thread_breakpoints_flag(Thread* thread) } -/*! Updates the thread::flags THREAD_FLAGS_BREAKPOINTS_DEFINED bit of all +/*! Updates the Thread::flags THREAD_FLAGS_BREAKPOINTS_DEFINED bit of all threads of the current team. */ static void update_threads_breakpoints_flag() { - InterruptsSpinLocker _(gTeamSpinlock); - Team* team = thread_get_current_thread()->team; + + TeamLocker teamLocker(team); + Thread* thread = team->thread_list; if (arch_has_breakpoints(&team->debug_info.arch_info)) { @@ -177,8 +178,7 @@ update_threads_breakpoints_flag() /*! Updates the thread::flags B_TEAM_DEBUG_DEBUGGER_INSTALLED bit of the - given thread. - Interrupts must be disabled and the team lock must be held. + given thread, which must be the current thread. */ static void update_thread_debugger_installed_flag(Thread* thread) @@ -194,7 +194,7 @@ update_thread_debugger_installed_flag(Thread* thread) /*! Updates the thread::flags THREAD_FLAGS_DEBUGGER_INSTALLED bit of all threads of the given team. - Interrupts must be disabled and the team lock must be held. + The team's lock must be held. */ static void update_threads_debugger_installed_flag(Team* team) @@ -296,6 +296,7 @@ void init_thread_debug_info(struct thread_debug_info *info) { if (info) { + B_INITIALIZE_SPINLOCK(&info->lock); arch_clear_thread_debug_info(&info->arch_info); info->flags = B_THREAD_DEBUG_DEFAULT_FLAGS; info->debug_port = -1; @@ -309,7 +310,8 @@ init_thread_debug_info(struct thread_debug_info *info) } -/*! Invoked with thread lock being held. +/*! Clears the debug info for the current thread. + Invoked with thread debug info lock being held. */ void clear_thread_debug_info(struct thread_debug_info *info, bool dying) @@ -373,19 +375,18 @@ prepare_debugger_change(team_id teamID, ConditionVariable& condition, while (true) { // get the team - InterruptsSpinLocker teamLocker(gTeamSpinlock); - - team = team_get_team_struct_locked(teamID); - if (team == NULL || team->death_entry != NULL) + team = Team::GetAndLock(teamID); + if (team == NULL) return B_BAD_TEAM_ID; + BReference teamReference(team, true); + TeamLocker teamLocker(team, true); // don't allow messing with the kernel team if (team == team_get_kernel_team()) return B_NOT_ALLOWED; // check whether the condition is already set - SpinLocker threadLocker(gThreadSpinlock); - SpinLocker debugInfoLocker(team->debug_info.lock); + InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); if (team->debug_info.debugger_changed_condition == NULL) { // nobody there yet -- set our condition variable and be done @@ -398,7 +399,6 @@ prepare_debugger_change(team_id teamID, ConditionVariable& condition, team->debug_info.debugger_changed_condition->Add(&entry); debugInfoLocker.Unlock(); - threadLocker.Unlock(); teamLocker.Unlock(); entry.Wait(); @@ -411,8 +411,7 @@ prepare_debugger_change(Team* team, ConditionVariable& condition) { while (true) { // check whether the condition is already set - InterruptsSpinLocker threadLocker(gThreadSpinlock); - SpinLocker debugInfoLocker(team->debug_info.lock); + InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); if (team->debug_info.debugger_changed_condition == NULL) { // nobody there yet -- set our condition variable and be done @@ -425,7 +424,6 @@ prepare_debugger_change(Team* team, ConditionVariable& condition) team->debug_info.debugger_changed_condition->Add(&entry); debugInfoLocker.Unlock(); - threadLocker.Unlock(); entry.Wait(); } @@ -436,13 +434,12 @@ static void finish_debugger_change(Team* team) { // unset our condition variable and notify all threads waiting on it - InterruptsSpinLocker threadLocker(gThreadSpinlock); - SpinLocker debugInfoLocker(team->debug_info.lock); + InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); ConditionVariable* condition = team->debug_info.debugger_changed_condition; team->debug_info.debugger_changed_condition = NULL; - condition->NotifyAll(true); + condition->NotifyAll(false); } @@ -460,14 +457,12 @@ user_debug_prepare_for_exec() // get the port port_id debugPort = -1; - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); - if (thread->debug_info.flags & B_THREAD_DEBUG_INITIALIZED) + if ((thread->debug_info.flags & B_THREAD_DEBUG_INITIALIZED) != 0) debugPort = thread->debug_info.debug_port; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + threadDebugInfoLocker.Unlock(); // set the new port ownership if (debugPort >= 0) @@ -489,14 +484,12 @@ user_debug_finish_after_exec() // get the port port_id debugPort = -1; - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); if (thread->debug_info.flags & B_THREAD_DEBUG_INITIALIZED) debugPort = thread->debug_info.debug_port; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + threadDebugInfoLocker.Unlock(); // set the new port ownership if (debugPort >= 0) @@ -565,8 +558,8 @@ thread_hit_debug_event_internal(debug_debugger_message event, port_id nubPort = -1; status_t error = B_OK; cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info); + SpinLocker threadDebugInfoLocker(thread->debug_info.lock); uint32 threadFlags = thread->debug_info.flags; threadFlags &= ~B_THREAD_DEBUG_STOP; @@ -578,7 +571,6 @@ thread_hit_debug_event_internal(debug_debugger_message event, thread->id)); error = B_ERROR; - } else if (debuggerInstalled || !requireDebugger) { if (debuggerInstalled) { debuggerPort = thread->team->debug_info.debugger_port; @@ -613,8 +605,8 @@ thread_hit_debug_event_internal(debug_debugger_message event, update_thread_user_debug_flag(thread); + threadDebugInfoLocker.Unlock(); RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info); - RELEASE_THREAD_LOCK(); restore_interrupts(state); // delete the superfluous port @@ -738,7 +730,7 @@ thread_hit_debug_event_internal(debug_debugger_message event, thread_debug_info threadDebugInfo; state = disable_interrupts(); - GRAB_THREAD_LOCK(); + threadDebugInfoLocker.Lock(); // check, if the team is still being debugged int32 teamDebugFlags = atomic_get(&thread->team->debug_info.flags); @@ -765,7 +757,7 @@ thread_hit_debug_event_internal(debug_debugger_message event, destroyThreadInfo = true; } - RELEASE_THREAD_LOCK(); + threadDebugInfoLocker.Unlock(); restore_interrupts(state); // enable/disable single stepping @@ -790,7 +782,7 @@ thread_hit_debug_event(debug_debugger_message event, const void *message, requireDebugger, restart); } while (result >= 0 && restart); - // Prepare to continue -- we install a debugger change condition, so no-one + // Prepare to continue -- we install a debugger change condition, so no one // will change the debugger while we're playing with the breakpoint manager. // TODO: Maybe better use ref-counting and a flag in the breakpoint manager. Team* team = thread_get_current_thread()->team; @@ -954,8 +946,9 @@ void user_debug_stop_thread() { // check whether this is actually an emulated single-step notification - InterruptsSpinLocker threadsLocker(gThreadSpinlock); Thread* thread = thread_get_current_thread(); + InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); + bool singleStepped = false; if ((atomic_and(&thread->debug_info.flags, ~B_THREAD_DEBUG_NOTIFY_SINGLE_STEP) @@ -963,7 +956,7 @@ user_debug_stop_thread() singleStepped = true; } - threadsLocker.Unlock(); + threadDebugInfoLocker.Unlock(); if (singleStepped) { user_debug_single_stepped(); @@ -1035,19 +1028,15 @@ user_debug_team_exec() } +/*! Called by a new userland thread to update the debugging related flags of + \c Thread::flags before the thread first enters userland. + \param thread The calling thread. +*/ void -user_debug_update_new_thread_flags(thread_id threadID) +user_debug_update_new_thread_flags(Thread* thread) { - // Update thread::flags of the thread. - - InterruptsLocker interruptsLocker; - - SpinLocker teamLocker(gTeamSpinlock); - SpinLocker threadLocker(gThreadSpinlock); - - Thread *thread = thread_get_thread_struct_locked(threadID); - if (!thread) - return; + // lock it and update it's flags + InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); update_thread_user_debug_flag(thread); update_thread_breakpoints_flag(thread); @@ -1082,20 +1071,18 @@ user_debug_thread_deleted(team_id teamID, thread_id threadID) // the debugged team (but to the kernel). So we can't use debugger_write(). // get the team debug flags and debugger port - InterruptsSpinLocker teamLocker(gTeamSpinlock); - - Team *team = team_get_team_struct_locked(teamID); + Team* team = Team::Get(teamID); if (team == NULL) return; + BReference teamReference(team, true); - SpinLocker debugInfoLocker(team->debug_info.lock); + InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); int32 teamDebugFlags = atomic_get(&team->debug_info.flags); port_id debuggerPort = team->debug_info.debugger_port; sem_id writeLock = team->debug_info.debugger_write_lock; debugInfoLocker.Unlock(); - teamLocker.Unlock(); // check, if a debugger is installed and is interested in thread events if (~teamDebugFlags @@ -1109,19 +1096,12 @@ user_debug_thread_deleted(team_id teamID, thread_id threadID) return; // re-get the team debug info -- we need to check whether anything changed - teamLocker.Lock(); - - team = team_get_team_struct_locked(teamID); - if (team == NULL) - return; - debugInfoLocker.Lock(); teamDebugFlags = atomic_get(&team->debug_info.flags); port_id newDebuggerPort = team->debug_info.debugger_port; debugInfoLocker.Unlock(); - teamLocker.Unlock(); // Send the message only if the debugger hasn't changed in the meantime or // the team is about to be handed over. @@ -1141,14 +1121,18 @@ user_debug_thread_deleted(team_id teamID, thread_id threadID) } +/*! Called for a thread that is about to die, cleaning up all user debug + facilities installed for the thread. + \param thread The current thread, the one that is going to die. +*/ void user_debug_thread_exiting(Thread* thread) { - InterruptsLocker interruptsLocker; - SpinLocker teamLocker(gTeamSpinlock); - + // thread is the current thread, so using team is safe Team* team = thread->team; + InterruptsLocker interruptsLocker; + GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); int32 teamDebugFlags = atomic_get(&team->debug_info.flags); @@ -1156,8 +1140,6 @@ user_debug_thread_exiting(Thread* thread) RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); - teamLocker.Unlock(); - // check, if a debugger is installed if ((teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) == 0 || debuggerPort < 0) { @@ -1165,7 +1147,7 @@ user_debug_thread_exiting(Thread* thread) } // detach the profile info and mark the thread dying - SpinLocker threadLocker(gThreadSpinlock); + SpinLocker threadDebugInfoLocker(thread->debug_info.lock); thread_debug_info& threadDebugInfo = thread->debug_info; if (threadDebugInfo.profile.samples == NULL) @@ -1183,7 +1165,7 @@ user_debug_thread_exiting(Thread* thread) atomic_or(&threadDebugInfo.flags, B_THREAD_DEBUG_DYING); - threadLocker.Unlock(); + threadDebugInfoLocker.Unlock(); interruptsLocker.Unlock(); // notify the debugger @@ -1294,17 +1276,26 @@ user_debug_single_stepped() } +/*! Schedules the profiling timer for the current thread. + The caller must hold the thread's debug info lock. + \param thread The current thread. + \param interval The time after which the timer should fire. +*/ static void schedule_profiling_timer(Thread* thread, bigtime_t interval) { struct timer* timer = &sProfilingTimers[thread->cpu->cpu_num]; thread->debug_info.profile.installed_timer = timer; thread->debug_info.profile.timer_end = system_time() + interval; - add_timer(timer, &profiling_event, interval, - B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_THREAD_LOCK); + add_timer(timer, &profiling_event, interval, B_ONE_SHOT_RELATIVE_TIMER); } +/*! Samples the current thread's instruction pointer/stack trace. + The caller must hold the current thread's debug info lock. + \param flushBuffer Return parameter: Set to \c true when the sampling + buffer must be flushed. +*/ static bool profiling_do_sample(bool& flushBuffer) { @@ -1385,10 +1376,15 @@ profiling_do_sample(bool& flushBuffer) static void profiling_buffer_full(void*) { + // It is undefined whether the function is called with interrupts enabled + // or disabled. We are allowed to enable interrupts, though. First make + // sure interrupts are disabled. + disable_interrupts(); + Thread* thread = thread_get_current_thread(); thread_debug_info& debugInfo = thread->debug_info; - GRAB_THREAD_LOCK(); + SpinLocker threadDebugInfoLocker(debugInfo.lock); if (debugInfo.profile.samples != NULL && debugInfo.profile.buffer_full) { int32 sampleCount = debugInfo.profile.sample_count; @@ -1401,7 +1397,7 @@ profiling_buffer_full(void*) debugInfo.profile.sample_count = 0; debugInfo.profile.dropped_ticks = 0; - RELEASE_THREAD_LOCK(); + threadDebugInfoLocker.Unlock(); enable_interrupts(); // prepare the message @@ -1417,7 +1413,7 @@ profiling_buffer_full(void*) sizeof(message), false); disable_interrupts(); - GRAB_THREAD_LOCK(); + threadDebugInfoLocker.Lock(); // do the sampling and reschedule timer, if still profiling this thread bool flushBuffer; @@ -1427,11 +1423,13 @@ profiling_buffer_full(void*) } } - RELEASE_THREAD_LOCK(); + threadDebugInfoLocker.Unlock(); + enable_interrupts(); } -/*! The thread spinlock is being held. +/*! Profiling timer event callback. + Called with interrupts disabled. */ static int32 profiling_event(timer* /*unused*/) @@ -1439,6 +1437,8 @@ profiling_event(timer* /*unused*/) Thread* thread = thread_get_current_thread(); thread_debug_info& debugInfo = thread->debug_info; + SpinLocker threadDebugInfoLocker(debugInfo.lock); + bool flushBuffer = false; if (profiling_do_sample(flushBuffer)) { if (flushBuffer) { @@ -1458,9 +1458,14 @@ profiling_event(timer* /*unused*/) } +/*! Called by the scheduler when a debugged thread has been unscheduled. + The scheduler lock is being held. +*/ void user_debug_thread_unscheduled(Thread* thread) { + SpinLocker threadDebugInfoLocker(thread->debug_info.lock); + // if running, cancel the profiling timer struct timer* timer = thread->debug_info.profile.installed_timer; if (timer != NULL) { @@ -1470,14 +1475,23 @@ user_debug_thread_unscheduled(Thread* thread) thread->debug_info.profile.installed_timer = NULL; // cancel timer + threadDebugInfoLocker.Unlock(); + // not necessary, but doesn't harm and reduces contention cancel_timer(timer); + // since invoked on the same CPU, this will not possibly wait for + // an already called timer hook } } +/*! Called by the scheduler when a debugged thread has been scheduled. + The scheduler lock is being held. +*/ void user_debug_thread_scheduled(Thread* thread) { + SpinLocker threadDebugInfoLocker(thread->debug_info.lock); + if (thread->debug_info.profile.samples != NULL && !thread->debug_info.profile.buffer_full) { // install profiling timer @@ -1500,22 +1514,26 @@ broadcast_debugged_thread_message(Thread *nubThread, int32 code, int32 cookie = 0; while (get_next_thread_info(nubThread->team->id, &cookie, &threadInfo) == B_OK) { - // find the thread and get its debug port - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // get the thread and lock it + Thread* thread = Thread::GetAndLock(threadInfo.thread); + if (thread == NULL) + continue; + + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); + + // get the thread's debug port + InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); port_id threadDebugPort = -1; - thread_id threadID = -1; - Thread *thread = thread_get_thread_struct_locked(threadInfo.thread); if (thread && thread != nubThread && thread->team == nubThread->team && (thread->debug_info.flags & B_THREAD_DEBUG_INITIALIZED) != 0 && (thread->debug_info.flags & B_THREAD_DEBUG_STOPPED) != 0) { threadDebugPort = thread->debug_info.debug_port; - threadID = thread->id; } - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + threadDebugInfoLocker.Unlock(); + threadLocker.Unlock(); // send the message to the thread if (threadDebugPort >= 0) { @@ -1523,7 +1541,7 @@ broadcast_debugged_thread_message(Thread *nubThread, int32 code, code, message, size); if (error != B_OK) { TRACE(("broadcast_debugged_thread_message(): Failed to send " - "message to thread %ld: %lx\n", threadID, error)); + "message to thread %ld: %lx\n", thread->id, error)); } } } @@ -1542,6 +1560,9 @@ nub_thread_cleanup(Thread *nubThread) team_debug_info teamDebugInfo; bool destroyDebugInfo = false; + TeamLocker teamLocker(nubThread->team); + // required by update_threads_debugger_installed_flag() + cpu_status state = disable_interrupts(); GRAB_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info); @@ -1559,6 +1580,8 @@ nub_thread_cleanup(Thread *nubThread) RELEASE_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info); restore_interrupts(state); + teamLocker.Unlock(); + if (destroyDebugInfo) teamDebugInfo.breakpoint_manager->RemoveAllBreakpoints(); @@ -1580,30 +1603,31 @@ static status_t debug_nub_thread_get_thread_debug_port(Thread *nubThread, thread_id threadID, port_id &threadDebugPort) { - status_t result = B_OK; threadDebugPort = -1; - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // get the thread + Thread* thread = Thread::GetAndLock(threadID); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - Thread *thread = thread_get_thread_struct_locked(threadID); - if (thread) { - if (thread->team != nubThread->team) - result = B_BAD_VALUE; - else if (thread->debug_info.flags & B_THREAD_DEBUG_STOPPED) - threadDebugPort = thread->debug_info.debug_port; - else - result = B_BAD_THREAD_STATE; - } else - result = B_BAD_THREAD_ID; + // get the debug port + InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + if (thread->team != nubThread->team) + return B_BAD_VALUE; + if ((thread->debug_info.flags & B_THREAD_DEBUG_STOPPED) == 0) + return B_BAD_THREAD_STATE; - if (result == B_OK && threadDebugPort < 0) - result = B_ERROR; + threadDebugPort = thread->debug_info.debug_port; - return result; + threadDebugInfoLocker.Unlock(); + + if (threadDebugPort < 0) + return B_ERROR; + + return B_OK; } @@ -1614,7 +1638,6 @@ debug_nub_thread(void *) // check, if we're still the current nub thread and get our port cpu_status state = disable_interrupts(); - GRAB_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info); if (nubThread->team->debug_info.nub_thread != nubThread->id) { @@ -1779,20 +1802,21 @@ debug_nub_thread(void *) flags)); // set the flags - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + Thread* thread = Thread::GetAndLock(threadID); + if (thread == NULL) + break; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - Thread *thread = thread_get_thread_struct_locked(threadID); - if (thread - && thread->team == thread_get_current_thread()->team) { + InterruptsSpinLocker threadDebugInfoLocker( + thread->debug_info.lock); + + if (thread->team == thread_get_current_thread()->team) { flags |= thread->debug_info.flags & B_THREAD_DEBUG_KERNEL_FLAG_MASK; atomic_set(&thread->debug_info.flags, flags); } - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - break; } @@ -2025,12 +2049,16 @@ debug_nub_thread(void *) ignoreOp, ignoreOnce, ignoreOnceOp)); // set the masks - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + Thread* thread = Thread::GetAndLock(threadID); + if (thread == NULL) + break; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - Thread *thread = thread_get_thread_struct_locked(threadID); - if (thread - && thread->team == thread_get_current_thread()->team) { + InterruptsSpinLocker threadDebugInfoLocker( + thread->debug_info.lock); + + if (thread->team == thread_get_current_thread()->team) { thread_debug_info &threadDebugInfo = thread->debug_info; // set ignore mask switch (ignoreOp) { @@ -2059,9 +2087,6 @@ debug_nub_thread(void *) } } - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - break; } @@ -2076,19 +2101,19 @@ debug_nub_thread(void *) uint64 ignore = 0; uint64 ignoreOnce = 0; - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + Thread* thread = Thread::GetAndLock(threadID); + if (thread != NULL) { + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); + + InterruptsSpinLocker threadDebugInfoLocker( + thread->debug_info.lock); - Thread *thread = thread_get_thread_struct_locked(threadID); - if (thread) { ignore = thread->debug_info.ignore_signals; ignoreOnce = thread->debug_info.ignore_signals_once; } else result = B_BAD_THREAD_ID; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - TRACE(("nub thread %ld: B_DEBUG_MESSAGE_GET_SIGNAL_MASKS: " "reply port: %ld, thread: %ld, ignore: %llx, " "ignore once: %llx, result: %lx\n", nubThread->id, @@ -2106,30 +2131,15 @@ debug_nub_thread(void *) case B_DEBUG_MESSAGE_SET_SIGNAL_HANDLER: { // get the parameters - thread_id threadID = message.set_signal_handler.thread; int signal = message.set_signal_handler.signal; struct sigaction &handler = message.set_signal_handler.handler; TRACE(("nub thread %ld: B_DEBUG_MESSAGE_SET_SIGNAL_HANDLER: " - "thread: %ld, signal: %d, handler: %p\n", nubThread->id, - threadID, signal, handler.sa_handler)); - - // check, if the thread exists and is ours - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - Thread *thread = thread_get_thread_struct_locked(threadID); - if (thread - && thread->team != thread_get_current_thread()->team) { - thread = NULL; - } - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + "signal: %d, handler: %p\n", nubThread->id, + signal, handler.sa_handler)); // set the handler - if (thread) - sigaction_etc(threadID, signal, &handler, NULL); + sigaction(signal, &handler, NULL); break; } @@ -2138,35 +2148,18 @@ debug_nub_thread(void *) { // get the parameters replyPort = message.get_signal_handler.reply_port; - thread_id threadID = message.get_signal_handler.thread; int signal = message.get_signal_handler.signal; status_t result = B_OK; - // check, if the thread exists and is ours - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - Thread *thread = thread_get_thread_struct_locked(threadID); - if (thread) { - if (thread->team != thread_get_current_thread()->team) - result = B_BAD_VALUE; - } else - result = B_BAD_THREAD_ID; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - // get the handler - if (result == B_OK - && sigaction_etc(threadID, signal, NULL, - &reply.get_signal_handler.handler) != 0) { + if (sigaction(signal, NULL, &reply.get_signal_handler.handler) + != 0) { result = errno; } TRACE(("nub thread %ld: B_DEBUG_MESSAGE_GET_SIGNAL_HANDLER: " - "reply port: %ld, thread: %ld, signal: %d, " - "handler: %p\n", nubThread->id, replyPort, - threadID, signal, + "reply port: %ld, signal: %d, handler: %p\n", nubThread->id, + replyPort, signal, reply.get_signal_handler.handler.sa_handler)); // prepare the message @@ -2275,12 +2268,16 @@ debug_nub_thread(void *) // get the thread and set the profile info int32 imageEvent = nubThread->team->debug_info.image_event; if (result == B_OK) { - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + Thread* thread = Thread::GetAndLock(threadID); + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - Thread *thread = thread_get_thread_struct_locked(threadID); - if (thread && thread->team == nubThread->team) { + if (thread != NULL && thread->team == nubThread->team) { thread_debug_info &threadDebugInfo = thread->debug_info; + + InterruptsSpinLocker threadDebugInfoLocker( + threadDebugInfo.lock); + if (threadDebugInfo.profile.samples == NULL) { threadDebugInfo.profile.interval = interval; threadDebugInfo.profile.sample_area @@ -2307,9 +2304,6 @@ debug_nub_thread(void *) result = B_BAD_VALUE; } else result = B_BAD_THREAD_ID; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); } // on error unlock and delete the sample area @@ -2349,12 +2343,16 @@ debug_nub_thread(void *) int32 droppedTicks = 0; // get the thread and detach the profile info - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + Thread* thread = Thread::GetAndLock(threadID); + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - Thread *thread = thread_get_thread_struct_locked(threadID); if (thread && thread->team == nubThread->team) { thread_debug_info &threadDebugInfo = thread->debug_info; + + InterruptsSpinLocker threadDebugInfoLocker( + threadDebugInfo.lock); + if (threadDebugInfo.profile.samples != NULL) { sampleArea = threadDebugInfo.profile.sample_area; samples = threadDebugInfo.profile.samples; @@ -2373,8 +2371,7 @@ debug_nub_thread(void *) } else result = B_BAD_THREAD_ID; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + threadLocker.Unlock(); // prepare the reply if (result == B_OK) { @@ -2424,9 +2421,7 @@ debug_nub_thread(void *) /** \brief Helper function for install_team_debugger(), that sets up the team and thread debug infos. - Interrupts must be disabled and the team debug info lock of the team to be - debugged must be held. The function will release the lock, but leave - interrupts disabled. + The caller must hold the team's lock as well as the team debug info lock. The function also clears the arch specific team and thread debug infos (including among other things formerly set break/watchpoints). @@ -2447,14 +2442,11 @@ install_team_debugger_init_debug_infos(Team *team, team_id debuggerTeam, arch_clear_team_debug_info(&team->debug_info.arch_info); - RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); - // set the user debug flags and signal masks of all threads to the default - GRAB_THREAD_LOCK(); + for (Thread *thread = team->thread_list; thread; + thread = thread->team_next) { + SpinLocker threadDebugInfoLocker(thread->debug_info.lock); - for (Thread *thread = team->thread_list; - thread; - thread = thread->team_next) { if (thread->id == nubThread) { atomic_set(&thread->debug_info.flags, B_THREAD_DEBUG_NUB_THREAD); } else { @@ -2469,8 +2461,6 @@ install_team_debugger_init_debug_infos(Team *team, team_id debuggerTeam, } } - RELEASE_THREAD_LOCK(); - // update the thread::flags fields update_threads_debugger_installed_flag(team); } @@ -2519,10 +2509,10 @@ install_team_debugger(team_id teamID, port_id debuggerPort, bool done = false; port_id result = B_ERROR; bool handOver = false; - bool releaseDebugInfoLock = true; port_id oldDebuggerPort = -1; port_id nubPort = -1; + TeamLocker teamLocker(team); cpu_status state = disable_interrupts(); GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); @@ -2553,7 +2543,6 @@ install_team_debugger(team_id teamID, port_id debuggerPort, debuggerPort, nubPort, team->debug_info.nub_thread, team->debug_info.debugger_write_lock, causingThread); - releaseDebugInfoLock = false; handOver = true; done = true; } @@ -2570,11 +2559,9 @@ install_team_debugger(team_id teamID, port_id debuggerPort, error = B_BAD_VALUE; } - // in case of a handover the lock has already been released - if (releaseDebugInfoLock) - RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); - + RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); restore_interrupts(state); + teamLocker.Unlock(); if (handOver && set_port_owner(nubPort, debuggerTeam) != B_OK) { // The old debugger must just have died. Just proceed as @@ -2681,13 +2668,14 @@ install_team_debugger(team_id teamID, port_id debuggerPort, if (error == B_OK) { snprintf(nameBuffer, sizeof(nameBuffer), "team %ld debug task", teamID); nubThread = spawn_kernel_thread_etc(debug_nub_thread, nameBuffer, - B_NORMAL_PRIORITY, NULL, teamID, -1); + B_NORMAL_PRIORITY, NULL, teamID); if (nubThread < 0) error = nubThread; } // now adjust the debug info accordingly if (error == B_OK) { + TeamLocker teamLocker(team); state = disable_interrupts(); GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); @@ -2696,6 +2684,7 @@ install_team_debugger(team_id teamID, port_id debuggerPort, debuggerPort, nubPort, nubThread, debuggerWriteLock, causingThread); + RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); restore_interrupts(state); } @@ -2865,52 +2854,59 @@ _user_debug_thread(thread_id threadID) { TRACE(("[%ld] _user_debug_thread(%ld)\n", find_thread(NULL), threadID)); - // tell the thread to stop as soon as possible - status_t error = B_OK; - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // get the thread + Thread* thread = Thread::GetAndLock(threadID); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - Thread *thread = thread_get_thread_struct_locked(threadID); - if (!thread) { - // thread doesn't exist any longer - error = B_BAD_THREAD_ID; - } else if (thread->team == team_get_kernel_team()) { - // we can't debug the kernel team - error = B_NOT_ALLOWED; - } else if (thread->debug_info.flags & B_THREAD_DEBUG_DYING) { - // the thread is already dying -- too late to debug it - error = B_BAD_THREAD_ID; - } else if (thread->debug_info.flags & B_THREAD_DEBUG_NUB_THREAD) { - // don't debug the nub thread - error = B_NOT_ALLOWED; - } else if (!(thread->debug_info.flags & B_THREAD_DEBUG_STOPPED)) { - // set the flag that tells the thread to stop as soon as possible - atomic_or(&thread->debug_info.flags, B_THREAD_DEBUG_STOP); + // we can't debug the kernel team + if (thread->team == team_get_kernel_team()) + return B_NOT_ALLOWED; - update_thread_user_debug_flag(thread); + InterruptsLocker interruptsLocker; + SpinLocker threadDebugInfoLocker(thread->debug_info.lock); - switch (thread->state) { - case B_THREAD_SUSPENDED: - // thread suspended: wake it up - scheduler_enqueue_in_run_queue(thread); - break; + // If the thread is already dying, it's too late to debug it. + if ((thread->debug_info.flags & B_THREAD_DEBUG_DYING) != 0) + return B_BAD_THREAD_ID; - default: - // thread may be waiting: interrupt it - thread_interrupt(thread, false); - // TODO: If the thread is already in the kernel and e.g. - // about to acquire a semaphore (before - // thread_prepare_to_block()), we won't interrupt it. - // Maybe we should rather send a signal (SIGTRAP). - scheduler_reschedule_if_necessary_locked(); - break; - } + // don't debug the nub thread + if ((thread->debug_info.flags & B_THREAD_DEBUG_NUB_THREAD) != 0) + return B_NOT_ALLOWED; + + // already marked stopped? + if ((thread->debug_info.flags & B_THREAD_DEBUG_STOPPED) != 0) + return B_OK; + + // set the flag that tells the thread to stop as soon as possible + atomic_or(&thread->debug_info.flags, B_THREAD_DEBUG_STOP); + + update_thread_user_debug_flag(thread); + + // resume/interrupt the thread, if necessary + threadDebugInfoLocker.Unlock(); + SpinLocker schedulerLocker(gSchedulerLock); + + switch (thread->state) { + case B_THREAD_SUSPENDED: + // thread suspended: wake it up + scheduler_enqueue_in_run_queue(thread); + break; + + default: + // thread may be waiting: interrupt it + thread_interrupt(thread, false); + // TODO: If the thread is already in the kernel and e.g. + // about to acquire a semaphore (before + // thread_prepare_to_block()), we won't interrupt it. + // Maybe we should rather send a signal (SIGTRAP). + scheduler_reschedule_if_necessary_locked(); + break; } - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - return error; + return B_OK; } diff --git a/src/system/kernel/device_manager/IOSchedulerSimple.cpp b/src/system/kernel/device_manager/IOSchedulerSimple.cpp index f2acf90133..07bd88a06f 100644 --- a/src/system/kernel/device_manager/IOSchedulerSimple.cpp +++ b/src/system/kernel/device_manager/IOSchedulerSimple.cpp @@ -810,8 +810,7 @@ IOSchedulerSimple::_GetRequestOwner(team_id team, thread_id thread, RequestOwnerList existingOwners; while ((owner = fUnusedRequestOwners.RemoveHead()) != NULL) { - if (owner->thread < 0 - || thread_get_thread_struct(owner->thread) == NULL) { + if (owner->thread < 0 || !Thread::IsAlive(owner->thread)) { if (owner->thread >= 0) fRequestOwners->RemoveUnchecked(owner); owner->team = team; diff --git a/src/system/kernel/elf.cpp b/src/system/kernel/elf.cpp index 5874697798..ffc90ccc55 100644 --- a/src/system/kernel/elf.cpp +++ b/src/system/kernel/elf.cpp @@ -1772,6 +1772,20 @@ elf_debug_lookup_symbol(const char* searchName) } +status_t +elf_lookup_kernel_symbol(const char* name, elf_symbol_info* info) +{ + // find the symbol + Elf32_Sym* foundSymbol = elf_find_symbol(sKernelImage, name, NULL, false); + if (foundSymbol == NULL) + return B_MISSING_SYMBOL; + + info->address = foundSymbol->st_value + sKernelImage->text_region.delta; + info->size = foundSymbol->st_size; + return B_OK; +} + + status_t elf_load_user_image(const char *path, Team *team, int flags, addr_t *entry) { diff --git a/src/system/kernel/fs/Vnode.cpp b/src/system/kernel/fs/Vnode.cpp index 723829094e..890e29bf9f 100644 --- a/src/system/kernel/fs/Vnode.cpp +++ b/src/system/kernel/fs/Vnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -88,6 +88,6 @@ vnode::_WakeUpLocker() atomic_and(&fFlags, ~kFlagsWaitingLocker); // and wake it up - InterruptsSpinLocker threadLocker(gThreadSpinlock); + InterruptsSpinLocker threadLocker(gSchedulerLock); thread_unblock_locked(waiter->thread, B_OK); } diff --git a/src/system/kernel/fs/fd.cpp b/src/system/kernel/fs/fd.cpp index 5c85e8ac28..d1e89b61d7 100644 --- a/src/system/kernel/fs/fd.cpp +++ b/src/system/kernel/fs/fd.cpp @@ -476,17 +476,12 @@ int dup_foreign_fd(team_id fromTeam, int fd, bool kernel) { // get the I/O context for the team in question - InterruptsSpinLocker teamsLocker(gTeamSpinlock); - Team* team = team_get_team_struct_locked(fromTeam); + Team* team = Team::Get(fromTeam); if (team == NULL) return B_BAD_TEAM_ID; + BReference teamReference(team, true); io_context* fromContext = team->io_context; - vfs_get_io_context(fromContext); - - teamsLocker.Unlock(); - - CObjectDeleter _(fromContext, vfs_put_io_context); // get the file descriptor file_descriptor* descriptor = get_fd(fromContext, fd); diff --git a/src/system/kernel/fs/fifo.cpp b/src/system/kernel/fs/fifo.cpp index 905e716045..9c771ff816 100644 --- a/src/system/kernel/fs/fifo.cpp +++ b/src/system/kernel/fs/fifo.cpp @@ -100,7 +100,7 @@ public: TRACE("ReadRequest %p::Notify(), fNotified %d\n", this, fNotified); if (!fNotified) { - SpinLocker threadLocker(gThreadSpinlock); + SpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(fThread, status); fNotified = true; } diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 85be4e8c5a..e1e1d0609c 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -1764,62 +1764,18 @@ replace_vnode_if_disconnected(struct fs_mount* mount, This is not a cheap function and should be used with care and rarely. TODO: there is currently no means to stop a blocking read/write! */ -void +static void disconnect_mount_or_vnode_fds(struct fs_mount* mount, struct vnode* vnodeToDisconnect) { // iterate over all teams and peek into their file descriptors - int32 nextTeamID = 0; + TeamListIterator teamIterator; + while (Team* team = teamIterator.Next()) { + BReference teamReference(team, true); - while (true) { - struct io_context* context = NULL; - bool contextLocked = false; - Team* team = NULL; - team_id lastTeamID; - - cpu_status state = disable_interrupts(); - SpinLocker teamsLock(gTeamSpinlock); - - lastTeamID = peek_next_thread_id(); - if (nextTeamID < lastTeamID) { - // get next valid team - while (nextTeamID < lastTeamID - && !(team = team_get_team_struct_locked(nextTeamID))) { - nextTeamID++; - } - - if (team) { - context = (io_context*)team->io_context; - - // Some acrobatics to lock the context in a safe way - // (cf. _kern_get_next_fd_info() for details). - GRAB_THREAD_LOCK(); - teamsLock.Unlock(); - contextLocked = mutex_lock_threads_locked(&context->io_mutex) - == B_OK; - RELEASE_THREAD_LOCK(); - - nextTeamID++; - } - } - - teamsLock.Unlock(); - restore_interrupts(state); - - if (context == NULL) - break; - - // we now have a context - since we couldn't lock it while having - // safe access to the team structure, we now need to lock the mutex - // manually - - if (!contextLocked) { - // team seems to be gone, go over to the next team - continue; - } - - // the team cannot be deleted completely while we're owning its - // io_context mutex, so we can safely play with it now + // lock the I/O context + io_context* context = team->io_context; + MutexLocker contextLocker(context->io_mutex); replace_vnode_if_disconnected(mount, vnodeToDisconnect, context->root, sRoot, true); @@ -1843,8 +1799,6 @@ disconnect_mount_or_vnode_fds(struct fs_mount* mount, put_fd(descriptor); } } - - mutex_unlock(&context->io_mutex); } } @@ -7828,38 +7782,15 @@ _kern_get_next_fd_info(team_id teamID, uint32* _cookie, fd_info* info, if (infoSize != sizeof(fd_info)) return B_BAD_VALUE; - struct io_context* context = NULL; - Team* team = NULL; - - cpu_status state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - bool contextLocked = false; - team = team_get_team_struct_locked(teamID); - if (team) { - // We cannot lock the IO context while holding the team lock, nor can - // we just drop the team lock, since it might be deleted in the - // meantime. team_remove_team() acquires the thread lock when removing - // the team from the team hash table, though. Hence we switch to the - // thread lock and use mutex_lock_threads_locked(). - context = (io_context*)team->io_context; - - GRAB_THREAD_LOCK(); - RELEASE_TEAM_LOCK(); - contextLocked = mutex_lock_threads_locked(&context->io_mutex) == B_OK; - RELEASE_THREAD_LOCK(); - } else - RELEASE_TEAM_LOCK(); - - restore_interrupts(state); - - if (!contextLocked) { - // team doesn't exit or seems to be gone + // get the team + Team* team = Team::Get(teamID); + if (team == NULL) return B_BAD_TEAM_ID; - } + BReference teamReference(team, true); - // the team cannot be deleted completely while we're owning its - // io_context mutex, so we can safely play with it now + // now that we have a team reference, its I/O context won't go away + io_context* context = team->io_context; + MutexLocker contextLocker(context->io_mutex); uint32 slot = *_cookie; @@ -7869,10 +7800,8 @@ _kern_get_next_fd_info(team_id teamID, uint32* _cookie, fd_info* info, slot++; } - if (slot >= context->table_size) { - mutex_unlock(&context->io_mutex); + if (slot >= context->table_size) return B_ENTRY_NOT_FOUND; - } info->number = slot; info->open_mode = descriptor->open_mode; @@ -7886,8 +7815,6 @@ _kern_get_next_fd_info(team_id teamID, uint32* _cookie, fd_info* info, info->node = -1; } - mutex_unlock(&context->io_mutex); - *_cookie = slot + 1; return B_OK; } diff --git a/src/system/kernel/image.cpp b/src/system/kernel/image.cpp index 0643e00b14..05bd513f06 100644 --- a/src/system/kernel/image.cpp +++ b/src/system/kernel/image.cpp @@ -145,7 +145,7 @@ unregister_image(Team *team, image_id id) /*! Counts the registered images from the specified team. - The team lock must be held when you call this function. + Interrupts must be enabled. */ int32 count_images(Team *team) @@ -153,6 +153,8 @@ count_images(Team *team) struct image *image = NULL; int32 count = 0; + MutexLocker locker(sImageMutex); + while ((image = (struct image*)list_get_next_item(&team->image_list, image)) != NULL) { count++; @@ -215,45 +217,29 @@ _get_next_image_info(team_id teamID, int32 *cookie, image_info *info, if (size > sizeof(image_info)) return B_BAD_VALUE; - status_t status = B_ENTRY_NOT_FOUND; - Team *team; - cpu_status state; + // get the team + Team* team = Team::Get(teamID); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); - mutex_lock(&sImageMutex); + // iterate through the team's images + MutexLocker imageLocker(sImageMutex); - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + struct image* image = NULL; + int32 count = 0; - if (teamID == B_CURRENT_TEAM) - team = thread_get_current_thread()->team; - else if (teamID == B_SYSTEM_TEAM) - team = team_get_kernel_team(); - else - team = team_get_team_struct_locked(teamID); - - if (team) { - struct image *image = NULL; - int32 count = 0; - - while ((image = (struct image*)list_get_next_item(&team->image_list, - image)) != NULL) { - if (count == *cookie) { - memcpy(info, &image->info, size); - status = B_OK; - (*cookie)++; - break; - } - count++; + while ((image = (struct image*)list_get_next_item(&team->image_list, + image)) != NULL) { + if (count == *cookie) { + memcpy(info, &image->info, size); + (*cookie)++; + return B_OK; } - } else - status = B_BAD_TEAM_ID; + count++; + } - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - mutex_unlock(&sImageMutex); - - return status; + return B_ENTRY_NOT_FOUND; } @@ -366,24 +352,21 @@ image_init(void) static void notify_loading_app(status_t result, bool suspend) { - cpu_status state; - Team *team; + Team* team = thread_get_current_thread()->team; - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + TeamLocker teamLocker(team); - team = thread_get_current_thread()->team; if (team->loading_info) { // there's indeed someone waiting - struct team_loading_info *loadingInfo = team->loading_info; + struct team_loading_info* loadingInfo = team->loading_info; team->loading_info = NULL; loadingInfo->result = result; loadingInfo->done = true; - // we're done with the team stuff, get the thread lock instead - RELEASE_TEAM_LOCK(); - GRAB_THREAD_LOCK(); + // we're done with the team stuff, get the scheduler lock instead + teamLocker.Unlock(); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); // wake up the waiting thread if (loadingInfo->thread->state == B_THREAD_SUSPENDED) @@ -394,14 +377,7 @@ notify_loading_app(status_t result, bool suspend) thread_get_current_thread()->next_state = B_THREAD_SUSPENDED; scheduler_reschedule(); } - - RELEASE_THREAD_LOCK(); - } else { - // no-one is waiting - RELEASE_TEAM_LOCK(); } - - restore_interrupts(state); } diff --git a/src/system/kernel/kernel_versions b/src/system/kernel/kernel_versions index 895ff9c540..9b4daf51e5 100644 --- a/src/system/kernel/kernel_versions +++ b/src/system/kernel/kernel_versions @@ -8,3 +8,6 @@ KERNEL_1_ALPHA1 { KERNEL_1_ALPHA3 { } KERNEL_1_ALPHA1; + +KERNEL_1_ALPHA4 { +} KERNEL_1_ALPHA3; diff --git a/src/system/kernel/lib/Jamfile b/src/system/kernel/lib/Jamfile index f6cf003a9a..77e0958c8b 100644 --- a/src/system/kernel/lib/Jamfile +++ b/src/system/kernel/lib/Jamfile @@ -3,6 +3,12 @@ SubDir HAIKU_TOP src system kernel lib ; UsePrivateHeaders shared ; UsePrivateHeaders [ FDirName libroot locale ] ; +# force inclusion of kernel_lib.h +local kernelLibHeader = [ FDirName $(SUBDIR) kernel_lib.h ] ; +CCFLAGS += -include $(kernelLibHeader) ; +C++FLAGS += -include $(kernelLibHeader) ; + + # kernel libroot os files KernelMergeObject kernel_os_main.o : diff --git a/src/system/kernel/lib/kernel_lib.h b/src/system/kernel/lib/kernel_lib.h new file mode 100644 index 0000000000..e34b5b5df0 --- /dev/null +++ b/src/system/kernel/lib/kernel_lib.h @@ -0,0 +1,15 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_KERNEL_LIB_H +#define _KERNEL_KERNEL_LIB_H + + +#include + + +#define pthread_testcancel() do {} while (false) + + +#endif // _KERNEL_KERNEL_LIB_H diff --git a/src/system/kernel/locks/lock.cpp b/src/system/kernel/locks/lock.cpp index 9f69313cb9..1cc5c358cf 100644 --- a/src/system/kernel/locks/lock.cpp +++ b/src/system/kernel/locks/lock.cpp @@ -260,7 +260,7 @@ rw_lock_destroy(rw_lock* lock) ? (char*)lock->name : NULL; // unblock all waiters - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if KDEBUG if (lock->waiters != NULL && thread_get_current_thread_id() @@ -296,7 +296,7 @@ rw_lock_destroy(rw_lock* lock) status_t _rw_lock_read_lock(rw_lock* lock) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // We might be the writer ourselves. if (lock->holder == thread_get_current_thread_id()) { @@ -328,7 +328,7 @@ status_t _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags, bigtime_t timeout) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // We might be the writer ourselves. if (lock->holder == thread_get_current_thread_id()) { @@ -407,9 +407,9 @@ _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags, void -_rw_lock_read_unlock(rw_lock* lock, bool threadsLocked) +_rw_lock_read_unlock(rw_lock* lock, bool schedulerLocked) { - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); // If we're still holding the write lock or if there are other readers, // no-one can be woken up. @@ -437,7 +437,7 @@ _rw_lock_read_unlock(rw_lock* lock, bool threadsLocked) status_t rw_lock_write_lock(rw_lock* lock) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // If we're already the lock holder, we just need to increment the owner // count. @@ -473,9 +473,9 @@ rw_lock_write_lock(rw_lock* lock) void -_rw_lock_write_unlock(rw_lock* lock, bool threadsLocked) +_rw_lock_write_unlock(rw_lock* lock, bool schedulerLocked) { - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); if (thread_get_current_thread_id() != lock->holder) { panic("rw_lock_write_unlock(): lock %p not write-locked by this thread", @@ -600,7 +600,7 @@ mutex_destroy(mutex* lock) ? (char*)lock->name : NULL; // unblock all waiters - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if KDEBUG if (lock->waiters != NULL && thread_get_current_thread_id() @@ -631,7 +631,7 @@ mutex_destroy(mutex* lock) status_t mutex_switch_lock(mutex* from, mutex* to) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if !KDEBUG if (atomic_add(&from->count, 1) < -1) @@ -645,7 +645,7 @@ mutex_switch_lock(mutex* from, mutex* to) status_t mutex_switch_from_read_lock(rw_lock* from, mutex* to) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if KDEBUG_RW_LOCK_DEBUG _rw_lock_write_unlock(from, true); @@ -660,17 +660,17 @@ mutex_switch_from_read_lock(rw_lock* from, mutex* to) status_t -_mutex_lock(mutex* lock, bool threadsLocked) +_mutex_lock(mutex* lock, bool schedulerLocked) { #if KDEBUG - if (!gKernelStartup && !threadsLocked && !are_interrupts_enabled()) { + if (!gKernelStartup && !schedulerLocked && !are_interrupts_enabled()) { panic("_mutex_lock(): called with interrupts disabled for lock %p", lock); } #endif // lock only, if !threadsLocked - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); // Might have been released after we decremented the count, but before // we acquired the spinlock. @@ -716,10 +716,10 @@ _mutex_lock(mutex* lock, bool threadsLocked) void -_mutex_unlock(mutex* lock, bool threadsLocked) +_mutex_unlock(mutex* lock, bool schedulerLocked) { // lock only, if !threadsLocked - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); #if KDEBUG if (thread_get_current_thread_id() != lock->holder) { @@ -768,7 +768,7 @@ status_t _mutex_trylock(mutex* lock) { #if KDEBUG - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); if (lock->holder <= 0) { lock->holder = thread_get_current_thread_id(); @@ -789,7 +789,7 @@ _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout) } #endif - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // Might have been released after we decremented the count, but before // we acquired the spinlock. diff --git a/src/system/kernel/main.cpp b/src/system/kernel/main.cpp index 34fd5ee5a5..a45a947c83 100644 --- a/src/system/kernel/main.cpp +++ b/src/system/kernel/main.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU) timer_init(&sKernelArgs); TRACE("init real time clock\n"); rtc_init(&sKernelArgs); + timer_init_post_rtc(); TRACE("init condition variables\n"); condition_variable_init(); @@ -180,6 +182,8 @@ _start(kernel_args *bootKernelArgs, int currentCPU) TRACE("init VM threads\n"); vm_init_post_thread(&sKernelArgs); low_resource_manager_init_post_thread(); + TRACE("init DPC\n"); + dpc_init(); TRACE("init VFS\n"); vfs_init(&sKernelArgs); #if ENABLE_SWAP_SUPPORT diff --git a/src/system/kernel/port.cpp b/src/system/kernel/port.cpp index bb19d609e0..3c076bd918 100644 --- a/src/system/kernel/port.cpp +++ b/src/system/kernel/port.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. * @@ -19,6 +20,8 @@ #include +#include + #include #include #include @@ -41,6 +44,23 @@ #endif +// Locking: +// * sPortsLock: Protects the sPorts hash table, Team::port_list, and +// Port::owner. +// * Port::lock: Protects all Port members save team_link, hash_link, and lock. +// id is immutable. +// +// The locking order is sPortsLock -> Port::lock. A port must be looked up +// in sPorts and locked with sPortsLock held. Afterwards sPortsLock can be +// dropped, unless any field guarded by sPortsLock is accessed. + + +struct port_message; + + +static void put_port_message(port_message* message); + + struct port_message : DoublyLinkedListLinkImpl { int32 code; size_t size; @@ -52,8 +72,10 @@ struct port_message : DoublyLinkedListLinkImpl { typedef DoublyLinkedList MessageList; -struct port_entry { + +struct Port { struct list_link team_link; + Port* hash_link; port_id id; team_id owner; int32 capacity; @@ -66,8 +88,62 @@ struct port_entry { // messages read from port since creation select_info* select_infos; MessageList messages; + + Port(team_id owner, int32 queueLength, char* name) + : + owner(owner), + capacity(queueLength), + read_count(0), + write_count(queueLength), + total_count(0), + select_infos(NULL) + { + // id is initialized when the caller adds the port to the hash table + + mutex_init(&lock, name); + read_condition.Init(this, "port read"); + write_condition.Init(this, "port write"); + } + + ~Port() + { + while (port_message* message = messages.RemoveHead()) + put_port_message(message); + + free((char*)lock.name); + lock.name = NULL; + } }; + +struct PortHashDefinition { + typedef port_id KeyType; + typedef Port ValueType; + + size_t HashKey(port_id key) const + { + return key; + } + + size_t Hash(Port* value) const + { + return HashKey(value->id); + } + + bool Compare(port_id key, Port* value) const + { + return value->id == key; + } + + Port*& GetLink(Port* value) const + { + return value->hash_link; + } +}; + +typedef BOpenHashTable PortHashTable; + + class PortNotificationService : public DefaultNotificationService { public: PortNotificationService(); @@ -81,13 +157,13 @@ namespace PortTracing { class Create : public AbstractTraceEntry { public: - Create(port_entry& port) + Create(Port* port) : - fID(port.id), - fOwner(port.owner), - fCapacity(port.capacity) + fID(port->id), + fOwner(port->owner), + fCapacity(port->capacity) { - fName = alloc_tracing_buffer_strcpy(port.lock.name, B_OS_NAME_LENGTH, + fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH, false); Initialized(); @@ -109,9 +185,9 @@ private: class Delete : public AbstractTraceEntry { public: - Delete(port_entry& port) + Delete(Port* port) : - fID(port.id) + fID(port->id) { Initialized(); } @@ -128,11 +204,12 @@ private: class Read : public AbstractTraceEntry { public: - Read(port_entry& port, int32 code, ssize_t result) + Read(port_id id, int32 readCount, int32 writeCount, int32 code, + ssize_t result) : - fID(port.id), - fReadCount(port.read_count), - fWriteCount(port.write_count), + fID(id), + fReadCount(readCount), + fWriteCount(writeCount), fCode(code), fResult(result) { @@ -156,11 +233,12 @@ private: class Write : public AbstractTraceEntry { public: - Write(port_entry& port, int32 code, size_t bufferSize, ssize_t result) + Write(port_id id, int32 readCount, int32 writeCount, int32 code, + size_t bufferSize, ssize_t result) : - fID(port.id), - fReadCount(port.read_count), - fWriteCount(port.write_count), + fID(id), + fReadCount(readCount), + fWriteCount(writeCount), fCode(code), fBufferSize(bufferSize), fResult(result) @@ -186,11 +264,12 @@ private: class Info : public AbstractTraceEntry { public: - Info(port_entry& port, int32 code, ssize_t result) + Info(port_id id, int32 readCount, int32 writeCount, int32 code, + ssize_t result) : - fID(port.id), - fReadCount(port.read_count), - fWriteCount(port.write_count), + fID(id), + fReadCount(readCount), + fWriteCount(writeCount), fCode(code), fResult(result) { @@ -214,10 +293,10 @@ private: class OwnerChange : public AbstractTraceEntry { public: - OwnerChange(port_entry& port, team_id newOwner, status_t status) + OwnerChange(Port* port, team_id newOwner, status_t status) : - fID(port.id), - fOldOwner(port.owner), + fID(port->id), + fOldOwner(port->owner), fNewOwner(newOwner), fStatus(status) { @@ -253,20 +332,17 @@ static const size_t kBufferGrowRate = kInitialPortBufferSize; #define MAX_QUEUE_LENGTH 4096 #define PORT_MAX_MESSAGE_SIZE (256 * 1024) -// sMaxPorts must be power of 2 static int32 sMaxPorts = 4096; static int32 sUsedPorts = 0; -static struct port_entry* sPorts; -static area_id sPortArea; +static PortHashTable sPorts; static heap_allocator* sPortAllocator; static ConditionVariable sNoSpaceCondition; static vint32 sTotalSpaceInUse; static vint32 sAreaChangeCounter; static vint32 sAllocatingArea; +static port_id sNextPortID = 1; static bool sPortsActive = false; -static port_id sNextPort = 1; -static int32 sFirstFreeSlot = 1; static mutex sPortsLock = MUTEX_INITIALIZER("ports list"); static PortNotificationService sNotificationService; @@ -303,7 +379,6 @@ dump_port_list(int argc, char** argv) { const char* name = NULL; team_id owner = -1; - int32 i; if (argc > 2) { if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner")) @@ -316,10 +391,9 @@ dump_port_list(int argc, char** argv) kprintf("port id cap read-cnt write-cnt total team " "name\n"); - for (i = 0; i < sMaxPorts; i++) { - struct port_entry* port = &sPorts[i]; - if (port->id < 0 - || (owner != -1 && port->owner != owner) + for (PortHashTable::Iterator it = sPorts.GetIterator(); + Port* port = it.Next();) { + if ((owner != -1 && port->owner != owner) || (name != NULL && strstr(port->lock.name, name) == NULL)) continue; @@ -333,7 +407,7 @@ dump_port_list(int argc, char** argv) static void -_dump_port_info(struct port_entry* port) +_dump_port_info(Port* port) { kprintf("PORT: %p\n", port); kprintf(" id: %ld\n", port->id); @@ -372,7 +446,7 @@ dump_port_info(int argc, char** argv) if (argc > 2) { if (!strcmp(argv[1], "address")) { - _dump_port_info((struct port_entry*)parse_expression(argv[2])); + _dump_port_info((Port*)parse_expression(argv[2])); return 0; } else if (!strcmp(argv[1], "condition")) condition = (ConditionVariable*)parse_expression(argv[2]); @@ -381,23 +455,24 @@ dump_port_info(int argc, char** argv) } else if (parse_expression(argv[1]) > 0) { // if the argument looks like a number, treat it as such int32 num = parse_expression(argv[1]); - int32 slot = num % sMaxPorts; - if (sPorts[slot].id != num) { + Port* port = sPorts.Lookup(num); + if (port == NULL) { kprintf("port %ld (%#lx) doesn't exist!\n", num, num); return 0; } - _dump_port_info(&sPorts[slot]); + _dump_port_info(port); return 0; } else name = argv[1]; // walk through the ports list, trying to match name - for (int32 i = 0; i < sMaxPorts; i++) { - if ((name != NULL && sPorts[i].lock.name != NULL - && !strcmp(name, sPorts[i].lock.name)) - || (condition != NULL && (&sPorts[i].read_condition == condition - || &sPorts[i].write_condition == condition))) { - _dump_port_info(&sPorts[i]); + for (PortHashTable::Iterator it = sPorts.GetIterator(); + Port* port = it.Next();) { + if ((name != NULL && port->lock.name != NULL + && !strcmp(name, port->lock.name)) + || (condition != NULL && (&port->read_condition == condition + || &port->write_condition == condition))) { + _dump_port_info(port); return 0; } } @@ -406,11 +481,14 @@ dump_port_info(int argc, char** argv) } +/*! Notifies the port's select events. + The port must be locked. +*/ static void -notify_port_select_events(int slot, uint16 events) +notify_port_select_events(Port* port, uint16 events) { - if (sPorts[slot].select_infos) - notify_select_events_list(sPorts[slot].select_infos, events); + if (port->select_infos) + notify_select_events_list(port->select_infos, events); } @@ -512,19 +590,19 @@ get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout, /*! You need to own the port's lock when calling this function */ -static bool -is_port_closed(int32 slot) +static inline bool +is_port_closed(Port* port) { - return sPorts[slot].capacity == 0; + return port->capacity == 0; } /*! Fills the port_info structure with information from the specified port. - The port lock must be held when called. + The port's lock must be held when called. */ static void -fill_port_info(struct port_entry* port, port_info* info, size_t size) +fill_port_info(Port* port, port_info* info, size_t size) { info->port = port->id; info->team = port->owner; @@ -562,66 +640,64 @@ copy_port_message(port_message* message, int32* _code, void* buffer, static void -uninit_port_locked(struct port_entry& port) +uninit_port_locked(Port* port) { - int32 id = port.id; - - // mark port as invalid - port.id = -1; - free((char*)port.lock.name); - port.lock.name = NULL; - - while (port_message* message = port.messages.RemoveHead()) { - put_port_message(message); - } - - notify_port_select_events(id % sMaxPorts, B_EVENT_INVALID); - port.select_infos = NULL; + notify_port_select_events(port, B_EVENT_INVALID); + port->select_infos = NULL; // Release the threads that were blocking on this port. // read_port() will see the B_BAD_PORT_ID return value, and act accordingly - port.read_condition.NotifyAll(false, B_BAD_PORT_ID); - port.write_condition.NotifyAll(false, B_BAD_PORT_ID); - sNotificationService.Notify(PORT_REMOVED, id); + port->read_condition.NotifyAll(false, B_BAD_PORT_ID); + port->write_condition.NotifyAll(false, B_BAD_PORT_ID); + sNotificationService.Notify(PORT_REMOVED, port->id); +} + + +static Port* +get_locked_port(port_id id) +{ + MutexLocker portsLocker(sPortsLock); + + Port* port = sPorts.Lookup(id); + if (port != NULL) + mutex_lock(&port->lock); + return port; } // #pragma mark - private kernel API -/*! This function delets all the ports that are owned by the passed team. +/*! This function deletes all the ports that are owned by the passed team. */ void delete_owned_ports(Team* team) { TRACE(("delete_owned_ports(owner = %ld)\n", team->id)); + MutexLocker portsLocker(sPortsLock); + + // move the ports from the team's port list to a local list struct list queue; + list_move_to_list(&team->port_list, &queue); - { - InterruptsSpinLocker locker(gTeamSpinlock); - list_move_to_list(&team->port_list, &queue); - } - - int32 firstSlot = sMaxPorts; - int32 count = 0; - - while (port_entry* port = (port_entry*)list_remove_head_item(&queue)) { - if (firstSlot > port->id % sMaxPorts) - firstSlot = port->id % sMaxPorts; - count++; - + // iterate through the list or ports, remove them from the hash table and + // uninitialize them + Port* port = (Port*)list_get_first_item(&queue); + while (port != NULL) { MutexLocker locker(port->lock); - uninit_port_locked(*port); + sPorts.Remove(port); + uninit_port_locked(port); + sUsedPorts--; + + port = (Port*)list_get_next_item(&queue, port); } - MutexLocker _(sPortsLock); + portsLocker.Unlock(); - // update the first free slot hint in the array - if (firstSlot < sFirstFreeSlot) - sFirstFreeSlot = firstSlot; - - sUsedPorts -= count; + // delete the ports + while (Port* port = (Port*)list_remove_head_item(&queue)) + delete port; } @@ -642,26 +718,11 @@ port_used_ports(void) status_t port_init(kernel_args *args) { - size_t size = sizeof(struct port_entry) * sMaxPorts; - - // create and initialize ports table - virtual_address_restrictions virtualRestrictions = {}; - virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS; - physical_address_restrictions physicalRestrictions = {}; - sPortArea = create_area_etc(B_SYSTEM_TEAM, "port_table", size, B_FULL_LOCK, - B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, CREATE_AREA_DONT_WAIT, - &virtualRestrictions, &physicalRestrictions, (void**)&sPorts); - if (sPortArea < 0) { - panic("unable to allocate kernel port table!\n"); - return sPortArea; - } - - memset(sPorts, 0, size); - for (int32 i = 0; i < sMaxPorts; i++) { - mutex_init(&sPorts[i].lock, NULL); - sPorts[i].id = -1; - sPorts[i].read_condition.Init(&sPorts[i], "port read"); - sPorts[i].write_condition.Init(&sPorts[i], "port write"); + // initialize ports table + new(&sPorts) PortHashTable; + if (sPorts.Init() != B_OK) { + panic("Failed to init port hash table!"); + return B_NO_MEMORY; } addr_t base; @@ -686,7 +747,7 @@ port_init(kernel_args *args) return B_NO_MEMORY; } - sNoSpaceCondition.Init(sPorts, "port space"); + sNoSpaceCondition.Init(&sPorts, "port space"); // add debugger commands add_debugger_command_etc("ports", &dump_port_list, @@ -731,64 +792,53 @@ create_port(int32 queueLength, const char* name) if (team == NULL) return B_BAD_TEAM_ID; - MutexLocker locker(sPortsLock); - - // check early on if there are any free port slots to use - if (sUsedPorts >= sMaxPorts) - return B_NO_MORE_PORTS; - // check & dup name char* nameBuffer = strdup(name != NULL ? name : "unnamed port"); if (nameBuffer == NULL) return B_NO_MEMORY; + // create a port + Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength, + nameBuffer); + if (port == NULL) { + free(nameBuffer); + return B_NO_MEMORY; + } + ObjectDeleter portDeleter(port); + + MutexLocker locker(sPortsLock); + + // check the ports limit + if (sUsedPorts >= sMaxPorts) + return B_NO_MORE_PORTS; + sUsedPorts++; - // find the first empty spot - for (int32 slot = 0; slot < sMaxPorts; slot++) { - int32 i = (slot + sFirstFreeSlot) % sMaxPorts; + // allocate a port ID + do { + port->id = sNextPortID++; - if (sPorts[i].id == -1) { - // make the port_id be a multiple of the slot it's in - if (i >= sNextPort % sMaxPorts) - sNextPort += i - sNextPort % sMaxPorts; - else - sNextPort += sMaxPorts - (sNextPort % sMaxPorts - i); - sFirstFreeSlot = slot + 1; + // handle integer overflow + if (sNextPortID < 0) + sNextPortID = 1; + } while (sPorts.Lookup(port->id) != NULL); - MutexLocker portLocker(sPorts[i].lock); - sPorts[i].id = sNextPort++; - locker.Unlock(); + // insert port in table and team list + sPorts.Insert(port); + list_add_item(&team->port_list, &port->team_link); + portDeleter.Detach(); - sPorts[i].capacity = queueLength; - sPorts[i].owner = team_get_current_team_id(); - sPorts[i].lock.name = nameBuffer; - sPorts[i].read_count = 0; - sPorts[i].write_count = queueLength; - sPorts[i].total_count = 0; - sPorts[i].select_infos = NULL; + // tracing, notifications, etc. + T(Create(port)); - { - InterruptsSpinLocker teamLocker(gTeamSpinlock); - list_add_item(&team->port_list, &sPorts[i].team_link); - } + port_id id = port->id; - port_id id = sPorts[i].id; + locker.Unlock(); - T(Create(sPorts[i])); - portLocker.Unlock(); + TRACE(("create_port() done: port created %ld\n", id)); - TRACE(("create_port() done: port created %ld\n", id)); - - sNotificationService.Notify(PORT_ADDED, id); - return id; - } - } - - // Still not enough ports... - due to sUsedPorts, this cannot really - // happen anymore. - panic("out of ports, but sUsedPorts is broken"); - return B_NO_MORE_PORTS; + sNotificationService.Notify(PORT_ADDED, id); + return id; } @@ -800,25 +850,23 @@ close_port(port_id id) if (!sPortsActive || id < 0) return B_BAD_PORT_ID; - int32 slot = id % sMaxPorts; - - // walk through the sem list, trying to match name - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id) { + // get the port + Port* port = get_locked_port(id); + if (port == NULL) { TRACE(("close_port: invalid port_id %ld\n", id)); return B_BAD_PORT_ID; } + MutexLocker lock(&port->lock, true); // mark port to disable writing - deleting the semaphores will // wake up waiting read/writes - sPorts[slot].capacity = 0; + port->capacity = 0; - notify_port_select_events(slot, B_EVENT_INVALID); - sPorts[slot].select_infos = NULL; + notify_port_select_events(port, B_EVENT_INVALID); + port->select_infos = NULL; - sPorts[slot].read_condition.NotifyAll(false, B_BAD_PORT_ID); - sPorts[slot].write_condition.NotifyAll(false, B_BAD_PORT_ID); + port->read_condition.NotifyAll(false, B_BAD_PORT_ID); + port->write_condition.NotifyAll(false, B_BAD_PORT_ID); return B_OK; } @@ -832,33 +880,34 @@ delete_port(port_id id) if (!sPortsActive || id < 0) return B_BAD_PORT_ID; - int32 slot = id % sMaxPorts; - - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id) { - TRACE(("delete_port: invalid port_id %ld\n", id)); - return B_BAD_PORT_ID; - } - - T(Delete(sPorts[slot])); - + // get the port and remove it from the hash table and the team + Port* port; + MutexLocker locker; { - InterruptsSpinLocker teamLocker(gTeamSpinlock); - list_remove_link(&sPorts[slot].team_link); + MutexLocker portsLocker(sPortsLock); + + port = sPorts.Lookup(id); + if (port == NULL) { + TRACE(("delete_port: invalid port_id %ld\n", id)); + return B_BAD_PORT_ID; + } + + sPorts.Remove(port); + list_remove_link(&port->team_link); + + sUsedPorts--; + + locker.SetTo(port->lock, false); + + uninit_port_locked(port); } - uninit_port_locked(sPorts[slot]); + T(Delete(port)); locker.Unlock(); - MutexLocker _(sPortsLock); + delete port; - // update the first free slot hint in the array - if (slot < sFirstFreeSlot) - sFirstFreeSlot = slot; - - sUsedPorts--; return B_OK; } @@ -869,13 +918,17 @@ select_port(int32 id, struct select_info* info, bool kernel) if (id < 0) return B_BAD_PORT_ID; - int32 slot = id % sMaxPorts; - - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id || is_port_closed(slot)) + // get the port + Port* port = get_locked_port(id); + if (port == NULL) return B_BAD_PORT_ID; - if (!kernel && sPorts[slot].owner == team_get_kernel_team_id()) { + MutexLocker locker(port->lock, true); + + // port must not yet be closed + if (is_port_closed(port)) + return B_BAD_PORT_ID; + + if (!kernel && port->owner == team_get_kernel_team_id()) { // kernel port, but call from userland return B_NOT_ALLOWED; } @@ -885,16 +938,16 @@ select_port(int32 id, struct select_info* info, bool kernel) if (info->selected_events != 0) { uint16 events = 0; - info->next = sPorts[slot].select_infos; - sPorts[slot].select_infos = info; + info->next = port->select_infos; + port->select_infos = info; // check for events if ((info->selected_events & B_EVENT_READ) != 0 - && !sPorts[slot].messages.IsEmpty()) { + && !port->messages.IsEmpty()) { events |= B_EVENT_READ; } - if (sPorts[slot].write_count > 0) + if (port->write_count > 0) events |= B_EVENT_WRITE; if (events != 0) @@ -913,18 +966,19 @@ deselect_port(int32 id, struct select_info* info, bool kernel) if (info->selected_events == 0) return B_OK; - int32 slot = id % sMaxPorts; + // get the port + Port* port = get_locked_port(id); + if (port == NULL) + return B_BAD_PORT_ID; + MutexLocker locker(port->lock, true); - MutexLocker locker(sPorts[slot].lock); + // find and remove the infos + select_info** infoLocation = &port->select_infos; + while (*infoLocation != NULL && *infoLocation != info) + infoLocation = &(*infoLocation)->next; - if (sPorts[slot].id == id) { - select_info** infoLocation = &sPorts[slot].select_infos; - while (*infoLocation != NULL && *infoLocation != info) - infoLocation = &(*infoLocation)->next; - - if (*infoLocation == info) - *infoLocation = info->next; - } + if (*infoLocation == info) + *infoLocation = info->next; return B_OK; } @@ -942,17 +996,12 @@ find_port(const char* name) if (name == NULL) return B_BAD_VALUE; - // Since we have to check every single port, and we don't - // care if it goes away at any point, we're only grabbing - // the port lock in question, not the port list lock + MutexLocker portsLocker(sPortsLock); - // loop over list - for (int32 i = 0; i < sMaxPorts; i++) { - // lock every individual port before comparing - MutexLocker _(sPorts[i].lock); - - if (sPorts[i].id >= 0 && !strcmp(name, sPorts[i].lock.name)) - return sPorts[i].id; + for (PortHashTable::Iterator it = sPorts.GetIterator(); + Port* port = it.Next();) { + if (!strcmp(name, port->lock.name)) + return port->id; } return B_NAME_NOT_FOUND; @@ -969,60 +1018,64 @@ _get_port_info(port_id id, port_info* info, size_t size) if (!sPortsActive || id < 0) return B_BAD_PORT_ID; - int32 slot = id % sMaxPorts; - - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id || sPorts[slot].capacity == 0) { + // get the port + Port* port = get_locked_port(id); + if (port == NULL) { TRACE(("get_port_info: invalid port_id %ld\n", id)); return B_BAD_PORT_ID; } + MutexLocker locker(port->lock, true); // fill a port_info struct with info - fill_port_info(&sPorts[slot], info, size); + fill_port_info(port, info, size); return B_OK; } status_t -_get_next_port_info(team_id team, int32* _cookie, struct port_info* info, +_get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info, size_t size) { - TRACE(("get_next_port_info(team = %ld)\n", team)); + TRACE(("get_next_port_info(team = %ld)\n", teamID)); if (info == NULL || size != sizeof(port_info) || _cookie == NULL - || team < B_OK) + || teamID < 0) { return B_BAD_VALUE; + } if (!sPortsActive) return B_BAD_PORT_ID; - int32 slot = *_cookie; - if (slot >= sMaxPorts) - return B_BAD_PORT_ID; + Team* team = Team::Get(teamID); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); - if (team == B_CURRENT_TEAM) - team = team_get_current_team_id(); + // iterate through the team's port list + MutexLocker portsLocker(sPortsLock); - info->port = -1; // used as found flag + int32 stopIndex = *_cookie; + int32 index = 0; - while (slot < sMaxPorts) { - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != -1 && !is_port_closed(slot) - && sPorts[slot].owner == team) { - // found one! - fill_port_info(&sPorts[slot], info, size); - slot++; - break; + Port* port = (Port*)list_get_first_item(&team->port_list); + while (port != NULL) { + if (!is_port_closed(port)) { + if (index == stopIndex) + break; + index++; } - slot++; + port = (Port*)list_get_next_item(&team->port_list, port); } - if (info->port == -1) + if (port == NULL) return B_BAD_PORT_ID; - *_cookie = slot; + // fill in the port info + MutexLocker locker(port->lock); + portsLocker.Unlock(); + fill_port_info(port, info, size); + + *_cookie = stopIndex + 1; return B_OK; } @@ -1054,25 +1107,26 @@ _get_port_message_info_etc(port_id id, port_message_info* info, flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT; - int32 slot = id % sMaxPorts; - MutexLocker locker(sPorts[slot].lock); + // get the port + Port* port = get_locked_port(id); + if (port == NULL) + return B_BAD_PORT_ID; + MutexLocker locker(port->lock, true); - if (sPorts[slot].id != id - || (is_port_closed(slot) && sPorts[slot].messages.IsEmpty())) { - T(Info(sPorts[slot], 0, B_BAD_PORT_ID)); - TRACE(("_get_port_message_info_etc(): %s port %ld\n", - sPorts[slot].id == id ? "closed" : "invalid", id)); + if (is_port_closed(port) && port->messages.IsEmpty()) { + T(Info(port, 0, B_BAD_PORT_ID)); + TRACE(("_get_port_message_info_etc(): closed port %ld\n", id)); return B_BAD_PORT_ID; } - while (sPorts[slot].read_count == 0) { + while (port->read_count == 0) { // We need to wait for a message to appear if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) return B_WOULD_BLOCK; ConditionVariableEntry entry; - sPorts[slot].read_condition.Add(&entry); + port->read_condition.Add(&entry); locker.Unlock(); @@ -1080,24 +1134,30 @@ _get_port_message_info_etc(port_id id, port_message_info* info, status_t status = entry.Wait(flags, timeout); if (status != B_OK) { - T(Info(sPorts[slot], 0, status)); + T(Info(port, 0, status)); return status; } - locker.Lock(); + // re-lock + Port* newPort = get_locked_port(id); + if (newPort == NULL) { + T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); + return B_BAD_PORT_ID; + } + locker.SetTo(newPort->lock, true); - if (sPorts[slot].id != id - || (is_port_closed(slot) && sPorts[slot].messages.IsEmpty())) { + if (newPort != port + || (is_port_closed(port) && port->messages.IsEmpty())) { // the port is no longer there - T(Info(sPorts[slot], 0, B_BAD_PORT_ID)); + T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); return B_BAD_PORT_ID; } } // determine tail & get the length of the message - port_message* message = sPorts[slot].messages.Head(); + port_message* message = port->messages.Head(); if (message == NULL) { - panic("port %ld: no messages found\n", sPorts[slot].id); + panic("port %ld: no messages found\n", port->id); return B_ERROR; } @@ -1106,10 +1166,10 @@ _get_port_message_info_etc(port_id id, port_message_info* info, info->sender_group = message->sender_group; info->sender_team = message->sender_team; - T(Info(sPorts[slot], message->code, B_OK)); + T(Info(id, id->read_count, id->write_count, message->code, B_OK)); // notify next one, as we haven't read from the port - sPorts[slot].read_condition.NotifyOne(); + port->read_condition.NotifyOne(); return B_OK; } @@ -1121,17 +1181,16 @@ port_count(port_id id) if (!sPortsActive || id < 0) return B_BAD_PORT_ID; - int32 slot = id % sMaxPorts; - - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id) { + // get the port + Port* port = get_locked_port(id); + if (port == NULL) { TRACE(("port_count: invalid port_id %ld\n", id)); return B_BAD_PORT_ID; } + MutexLocker locker(port->lock, true); // return count of messages - return sPorts[slot].read_count; + return port->read_count; } @@ -1158,50 +1217,56 @@ read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize, flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT; - int32 slot = id % sMaxPorts; + // get the port + Port* port = get_locked_port(id); + if (port == NULL) + return B_BAD_PORT_ID; + MutexLocker locker(port->lock, true); - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id - || (is_port_closed(slot) && sPorts[slot].messages.IsEmpty())) { - T(Read(sPorts[slot], 0, B_BAD_PORT_ID)); - TRACE(("read_port_etc(): %s port %ld\n", - sPorts[slot].id == id ? "closed" : "invalid", id)); + if (is_port_closed(port) && port->messages.IsEmpty()) { + T(Read(port, 0, B_BAD_PORT_ID)); + TRACE(("read_port_etc(): closed port %ld\n", id)); return B_BAD_PORT_ID; } - while (sPorts[slot].read_count == 0) { + while (port->read_count == 0) { if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) return B_WOULD_BLOCK; // We need to wait for a message to appear ConditionVariableEntry entry; - sPorts[slot].read_condition.Add(&entry); + port->read_condition.Add(&entry); locker.Unlock(); // block if no message, or, if B_TIMEOUT flag set, block with timeout status_t status = entry.Wait(flags, timeout); - locker.Lock(); + // re-lock + Port* newPort = get_locked_port(id); + if (newPort == NULL) { + T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); + return B_BAD_PORT_ID; + } + locker.SetTo(newPort->lock, true); - if (sPorts[slot].id != id - || (is_port_closed(slot) && sPorts[slot].messages.IsEmpty())) { + if (newPort != port + || (is_port_closed(port) && port->messages.IsEmpty())) { // the port is no longer there - T(Read(sPorts[slot], 0, B_BAD_PORT_ID)); + T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); return B_BAD_PORT_ID; } if (status != B_OK) { - T(Read(sPorts[slot], 0, status)); + T(Read(port, 0, status)); return status; } } // determine tail & get the length of the message - port_message* message = sPorts[slot].messages.Head(); + port_message* message = port->messages.Head(); if (message == NULL) { - panic("port %ld: no messages found\n", sPorts[slot].id); + panic("port %ld: no messages found\n", port->id); return B_ERROR; } @@ -1209,27 +1274,29 @@ read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize, size_t size = copy_port_message(message, _code, buffer, bufferSize, userCopy); - T(Read(sPorts[slot], message->code, size)); + T(Read(port, message->code, size)); - sPorts[slot].read_condition.NotifyOne(); + port->read_condition.NotifyOne(); // we only peeked, but didn't grab the message return size; } - sPorts[slot].messages.RemoveHead(); - sPorts[slot].total_count++; - sPorts[slot].write_count++; - sPorts[slot].read_count--; + port->messages.RemoveHead(); + port->total_count++; + port->write_count++; + port->read_count--; - notify_port_select_events(slot, B_EVENT_WRITE); - sPorts[slot].write_condition.NotifyOne(); + notify_port_select_events(port, B_EVENT_WRITE); + port->write_condition.NotifyOne(); // make one spot in queue available again for write + T(Read(id, port->read_count, port->write_count, message->code, + min_c(bufferSize, message->size))); + locker.Unlock(); size_t size = copy_port_message(message, _code, buffer, bufferSize, userCopy); - T(Read(sPorts[slot], message->code, size)); put_port_message(message); return size; @@ -1277,47 +1344,54 @@ writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs, bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0; - int32 slot = id % sMaxPorts; status_t status; port_message* message = NULL; - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id) { + // get the port + Port* port = get_locked_port(id); + if (port == NULL) { TRACE(("write_port_etc: invalid port_id %ld\n", id)); return B_BAD_PORT_ID; } - if (is_port_closed(slot)) { + MutexLocker locker(port->lock, true); + + if (is_port_closed(port)) { TRACE(("write_port_etc: port %ld closed\n", id)); return B_BAD_PORT_ID; } - if (sPorts[slot].write_count <= 0) { + if (port->write_count <= 0) { if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) return B_WOULD_BLOCK; - sPorts[slot].write_count--; + port->write_count--; // We need to block in order to wait for a free message slot ConditionVariableEntry entry; - sPorts[slot].write_condition.Add(&entry); + port->write_condition.Add(&entry); locker.Unlock(); status = entry.Wait(flags, timeout); - locker.Lock(); + // re-lock + Port* newPort = get_locked_port(id); + if (newPort == NULL) { + T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); + return B_BAD_PORT_ID; + } + locker.SetTo(newPort->lock, true); - if (sPorts[slot].id != id || is_port_closed(slot)) { + if (newPort != port || is_port_closed(port)) { // the port is no longer there - T(Write(sPorts[slot], 0, 0, B_BAD_PORT_ID)); + T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); return B_BAD_PORT_ID; } if (status != B_OK) goto error; } else - sPorts[slot].write_count--; + port->write_count--; status = get_port_message(msgCode, bufferSize, flags, timeout, &message); @@ -1365,22 +1439,23 @@ writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs, } } - sPorts[slot].messages.Add(message); - sPorts[slot].read_count++; + port->messages.Add(message); + port->read_count++; - T(Write(sPorts[slot], message->code, message->size, B_OK)); + T(Write(id, port->read_count, port->write_count, message->code, + message->size, B_OK)); - notify_port_select_events(slot, B_EVENT_READ); - sPorts[slot].read_condition.NotifyOne(); + notify_port_select_events(port, B_EVENT_READ); + port->read_condition.NotifyOne(); return B_OK; error: // Give up our slot in the queue again, and let someone else // try and fail - T(Write(sPorts[slot], 0, 0, status)); - sPorts[slot].write_count++; - notify_port_select_events(slot, B_EVENT_WRITE); - sPorts[slot].write_condition.NotifyOne(); + T(Write(id, port->read_count, port->write_count, 0, 0, status)); + port->write_count++; + notify_port_select_events(port, B_EVENT_WRITE); + port->write_condition.NotifyOne(); return status; } @@ -1394,29 +1469,29 @@ set_port_owner(port_id id, team_id newTeamID) if (id < 0) return B_BAD_PORT_ID; - int32 slot = id % sMaxPorts; + // get the new team + Team* team = Team::Get(newTeamID); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); - MutexLocker locker(sPorts[slot].lock); - - if (sPorts[slot].id != id) { + // get the port + MutexLocker portsLocker(sPortsLock); + Port* port = sPorts.Lookup(id); + if (port == NULL) { TRACE(("set_port_owner: invalid port_id %ld\n", id)); return B_BAD_PORT_ID; } - - InterruptsSpinLocker teamLocker(gTeamSpinlock); - - Team* team = team_get_team_struct_locked(newTeamID); - if (team == NULL) { - T(OwnerChange(sPorts[slot], newTeamID, B_BAD_TEAM_ID)); - return B_BAD_TEAM_ID; - } + MutexLocker locker(port->lock); // transfer ownership to other team - list_remove_link(&sPorts[slot].team_link); - list_add_item(&team->port_list, &sPorts[slot].team_link); - sPorts[slot].owner = newTeamID; + if (team->id != port->owner) { + list_remove_link(&port->team_link); + list_add_item(&team->port_list, &port->team_link); + port->owner = team->id; + } - T(OwnerChange(sPorts[slot], newTeamID, B_OK)); + T(OwnerChange(port, team->id, B_OK)); return B_OK; } diff --git a/src/system/kernel/posix/xsi_message_queue.cpp b/src/system/kernel/posix/xsi_message_queue.cpp index cab9239420..85aa4d24b5 100644 --- a/src/system/kernel/posix/xsi_message_queue.cpp +++ b/src/system/kernel/posix/xsi_message_queue.cpp @@ -126,7 +126,7 @@ public: // Unlock the queue before blocking queueLocker->Unlock(); - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to // interruption, we will still be queued. A WakeUpThread() at this point will // call thread_unblock() and might thus screw with our trying to re-lock the @@ -246,7 +246,8 @@ public: void WakeUpThread(bool waitForMessage) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + if (waitForMessage) { // Wake up all waiting thread for a message // TODO: this can cause starvation for any @@ -399,7 +400,8 @@ XsiMessageQueue::~XsiMessageQueue() // Wake up any threads still waiting if (fThreadsWaitingToSend || fThreadsWaitingToReceive) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + while (queued_thread *entry = fWaitingToReceive.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, EIDRM); diff --git a/src/system/kernel/posix/xsi_semaphore.cpp b/src/system/kernel/posix/xsi_semaphore.cpp index 941ceca127..161479c85a 100644 --- a/src/system/kernel/posix/xsi_semaphore.cpp +++ b/src/system/kernel/posix/xsi_semaphore.cpp @@ -101,7 +101,8 @@ public: { // For some reason the semaphore is getting destroyed. // Wake up any remaing awaiting threads - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, EIDRM); @@ -143,7 +144,7 @@ public: // Unlock the set before blocking setLocker->Unlock(); - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to // interruption, we will still be queued. A WakeUpThread() at this point will // call thread_unblock() and might thus screw with our trying to re-lock the @@ -217,7 +218,7 @@ public: void WakeUpThread(bool waitingForZero) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); if (waitingForZero) { // Wake up all threads waiting on zero while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) { diff --git a/src/system/kernel/real_time_clock.cpp b/src/system/kernel/real_time_clock.cpp index 2ef8569453..6f99492af8 100644 --- a/src/system/kernel/real_time_clock.cpp +++ b/src/system/kernel/real_time_clock.cpp @@ -35,6 +35,14 @@ static bigtime_t sTimezoneOffset = 0; static char sTimezoneName[B_FILE_NAME_LENGTH] = "GMT"; +static void +real_time_clock_changed() +{ + timer_real_time_clock_changed(); + user_timer_real_time_clock_changed(); +} + + /*! Write the system time to CMOS. */ static void rtc_system_to_hw(void) @@ -105,12 +113,19 @@ rtc_init(kernel_args *args) // #pragma mark - public kernel API +void +set_real_time_clock_usecs(bigtime_t currentTime) +{ + arch_rtc_set_system_time_offset(sRealTimeData, currentTime - system_time()); + rtc_system_to_hw(); + real_time_clock_changed(); +} + + void set_real_time_clock(uint32 currentTime) { - arch_rtc_set_system_time_offset(sRealTimeData, - currentTime * 1000000LL - system_time()); - rtc_system_to_hw(); + set_real_time_clock_usecs((bigtime_t)currentTime * 1000000); } @@ -207,12 +222,12 @@ _user_system_time(void) status_t -_user_set_real_time_clock(uint32 time) +_user_set_real_time_clock(bigtime_t time) { if (geteuid() != 0) return B_NOT_ALLOWED; - set_real_time_clock(time); + set_real_time_clock_usecs(time); return B_OK; } @@ -242,6 +257,7 @@ _user_set_timezone(time_t timezoneOffset, const char *name, size_t nameLength) arch_rtc_set_system_time_offset(sRealTimeData, arch_rtc_get_system_time_offset(sRealTimeData) + sTimezoneOffset - offset); + real_time_clock_changed(); } sTimezoneOffset = offset; @@ -286,6 +302,7 @@ _user_set_real_time_clock_is_gmt(bool isGMT) arch_rtc_set_system_time_offset(sRealTimeData, arch_rtc_get_system_time_offset(sRealTimeData) + (sIsGMT ? 1 : -1) * sTimezoneOffset); + real_time_clock_changed(); } return B_OK; diff --git a/src/system/kernel/scheduler/scheduler.cpp b/src/system/kernel/scheduler/scheduler.cpp index fc1a5d41a6..0d571d67dd 100644 --- a/src/system/kernel/scheduler/scheduler.cpp +++ b/src/system/kernel/scheduler/scheduler.cpp @@ -16,6 +16,7 @@ struct scheduler_ops* gScheduler; +spinlock gSchedulerLock = B_SPINLOCK_INITIALIZER; SchedulerListenerList gSchedulerListeners; static void (*sRescheduleFunction)(void); @@ -30,7 +31,7 @@ scheduler_reschedule_no_op(void) } -// #pragma mark - +// #pragma mark - SchedulerListener SchedulerListener::~SchedulerListener() @@ -38,6 +39,9 @@ SchedulerListener::~SchedulerListener() } +// #pragma mark - kernel private + + /*! Add the given scheduler listener. Thread lock must be held. */ void @@ -106,12 +110,19 @@ _user_estimate_max_scheduling_latency(thread_id id) { syscall_64_bit_return_value(); - InterruptsSpinLocker locker(gThreadSpinlock); - - Thread* thread = id < 0 - ? thread_get_current_thread() : thread_get_thread_struct_locked(id); - if (thread == NULL) - return 0; + // get the thread + Thread* thread; + if (id < 0) { + thread = thread_get_current_thread(); + thread->AcquireReference(); + } else { + thread = Thread::Get(id); + if (thread == NULL) + return 0; + } + BReference threadReference(thread, true); + // ask the scheduler for the thread's latency + InterruptsSpinLocker locker(gSchedulerLock); return gScheduler->estimate_max_scheduling_latency(thread); } diff --git a/src/system/kernel/scheduler/scheduler_affine.cpp b/src/system/kernel/scheduler/scheduler_affine.cpp index 508808f39b..721b9af0dc 100644 --- a/src/system/kernel/scheduler/scheduler_affine.cpp +++ b/src/system/kernel/scheduler/scheduler_affine.cpp @@ -25,8 +25,8 @@ #include #include #include -#include +#include "scheduler_common.h" #include "scheduler_tracing.h" @@ -339,28 +339,6 @@ affine_estimate_max_scheduling_latency(Thread* thread) } -static void -context_switch(Thread *fromThread, Thread *toThread) -{ - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_unscheduled(fromThread); - - cpu_ent* cpu = fromThread->cpu; - toThread->previous_cpu = toThread->cpu = cpu; - fromThread->cpu = NULL; - cpu->running_thread = toThread; - - arch_thread_set_current_thread(toThread); - arch_thread_context_switch(fromThread, toThread); - - // Looks weird, but is correct. fromThread had been unscheduled earlier, - // but is back now. The notification for a thread scheduled the first time - // happens in thread.cpp:thread_kthread_entry(). - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_scheduled(fromThread); -} - - static int32 reschedule_event(timer *unused) { @@ -486,9 +464,7 @@ affine_reschedule(void) oldThread->was_yielded = false; // track kernel time (user time is tracked in thread_at_kernel_entry()) - bigtime_t now = system_time(); - oldThread->kernel_time += now - oldThread->last_time; - nextThread->last_time = now; + scheduler_update_thread_times(oldThread, nextThread); // track CPU activity if (!thread_is_idle_thread(oldThread)) { @@ -525,20 +501,27 @@ affine_reschedule(void) quantum = kMaxThreadQuantum; add_timer(quantumTimer, &reschedule_event, quantum, - B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_THREAD_LOCK); + B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_SCHEDULER_LOCK); if (nextThread != oldThread) - context_switch(oldThread, nextThread); + scheduler_switch_thread(oldThread, nextThread); } } -static void -affine_on_thread_create(Thread* thread) +static status_t +affine_on_thread_create(Thread* thread, bool idleThread) { + // we don't need a data structure for the idle threads + if (idleThread) { + thread->scheduler_data = NULL; + return B_OK; + } + thread->scheduler_data = new(std::nothrow) scheduler_thread_data(); if (thread->scheduler_data == NULL) - panic("affine_scheduler: Unable to allocate scheduling data structure for thread %ld\n", thread->id); + return B_NO_MEMORY; + return B_OK; } @@ -562,11 +545,9 @@ affine_on_thread_destroy(Thread* thread) static void affine_start(void) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); affine_reschedule(); - - RELEASE_THREAD_LOCK(); } diff --git a/src/system/kernel/scheduler/scheduler_common.h b/src/system/kernel/scheduler/scheduler_common.h new file mode 100644 index 0000000000..0de57db2b6 --- /dev/null +++ b/src/system/kernel/scheduler/scheduler_common.h @@ -0,0 +1,86 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef KERNEL_SCHEDULER_COMMON_H +#define KERNEL_SCHEDULER_COMMON_H + + +#include +#include +#include + + +/*! Switches the currently running thread. + This is a service function for scheduler implementations. + + \param fromThread The currently running thread. + \param toThread The thread to switch to. Must be different from + \a fromThread. +*/ +static inline void +scheduler_switch_thread(Thread* fromThread, Thread* toThread) +{ + // notify the user debugger code + if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) + user_debug_thread_unscheduled(fromThread); + + // stop CPU time based user timers + if (fromThread->HasActiveCPUTimeUserTimers() + || fromThread->team->HasActiveCPUTimeUserTimers()) { + user_timer_stop_cpu_timers(fromThread, toThread); + } + + // update CPU and Thread structures and perform the context switch + cpu_ent* cpu = fromThread->cpu; + toThread->previous_cpu = toThread->cpu = cpu; + fromThread->cpu = NULL; + cpu->running_thread = toThread; + cpu->previous_thread = fromThread; + + arch_thread_set_current_thread(toThread); + arch_thread_context_switch(fromThread, toThread); + + // The use of fromThread below looks weird, but is correct. fromThread had + // been unscheduled earlier, but is back now. For a thread scheduled the + // first time the same is done in thread.cpp:common_thread_entry(). + + // continue CPU time based user timers + if (fromThread->HasActiveCPUTimeUserTimers() + || fromThread->team->HasActiveCPUTimeUserTimers()) { + user_timer_continue_cpu_timers(fromThread, cpu->previous_thread); + } + + // notify the user debugger code + if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) + user_debug_thread_scheduled(fromThread); +} + + +static inline void +scheduler_update_thread_times(Thread* oldThread, Thread* nextThread) +{ + bigtime_t now = system_time(); + if (oldThread == nextThread) { + acquire_spinlock(&oldThread->time_lock); + oldThread->kernel_time += now - oldThread->last_time; + oldThread->last_time = now; + release_spinlock(&oldThread->time_lock); + } else { + acquire_spinlock(&oldThread->time_lock); + oldThread->kernel_time += now - oldThread->last_time; + release_spinlock(&oldThread->time_lock); + + acquire_spinlock(&nextThread->time_lock); + nextThread->last_time = now; + release_spinlock(&nextThread->time_lock); + } + + // If the old thread's team has user time timers, check them now. + Team* team = oldThread->team; + if (team->HasActiveUserTimeUserTimers()) + user_timer_check_team_user_timers(team); +} + + +#endif // KERNEL_SCHEDULER_COMMON_H diff --git a/src/system/kernel/scheduler/scheduler_simple.cpp b/src/system/kernel/scheduler/scheduler_simple.cpp index b7a72d4e7f..2b1acc99c3 100644 --- a/src/system/kernel/scheduler/scheduler_simple.cpp +++ b/src/system/kernel/scheduler/scheduler_simple.cpp @@ -23,8 +23,8 @@ #include #include #include -#include +#include "scheduler_common.h" #include "scheduler_tracing.h" @@ -179,28 +179,6 @@ simple_estimate_max_scheduling_latency(Thread* thread) } -static void -context_switch(Thread *fromThread, Thread *toThread) -{ - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_unscheduled(fromThread); - - cpu_ent* cpu = fromThread->cpu; - toThread->previous_cpu = toThread->cpu = cpu; - fromThread->cpu = NULL; - cpu->running_thread = toThread; - - arch_thread_set_current_thread(toThread); - arch_thread_context_switch(fromThread, toThread); - - // Looks weird, but is correct. fromThread had been unscheduled earlier, - // but is back now. The notification for a thread scheduled the first time - // happens in thread.cpp:thread_kthread_entry(). - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_scheduled(fromThread); -} - - static int32 reschedule_event(timer *unused) { @@ -324,9 +302,7 @@ simple_reschedule(void) oldThread->was_yielded = false; // track kernel time (user time is tracked in thread_at_kernel_entry()) - bigtime_t now = system_time(); - oldThread->kernel_time += now - oldThread->last_time; - nextThread->last_time = now; + scheduler_update_thread_times(oldThread, nextThread); // track CPU activity if (!thread_is_idle_thread(oldThread)) { @@ -349,18 +325,19 @@ simple_reschedule(void) oldThread->cpu->preempted = 0; add_timer(quantumTimer, &reschedule_event, quantum, - B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_THREAD_LOCK); + B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_SCHEDULER_LOCK); if (nextThread != oldThread) - context_switch(oldThread, nextThread); + scheduler_switch_thread(oldThread, nextThread); } } -static void -simple_on_thread_create(Thread* thread) +static status_t +simple_on_thread_create(Thread* thread, bool idleThread) { // do nothing + return B_OK; } @@ -384,11 +361,9 @@ simple_on_thread_destroy(Thread* thread) static void simple_start(void) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); simple_reschedule(); - - RELEASE_THREAD_LOCK(); } diff --git a/src/system/kernel/scheduler/scheduler_simple_smp.cpp b/src/system/kernel/scheduler/scheduler_simple_smp.cpp index 49b434e14b..721860afac 100644 --- a/src/system/kernel/scheduler/scheduler_simple_smp.cpp +++ b/src/system/kernel/scheduler/scheduler_simple_smp.cpp @@ -24,8 +24,8 @@ #include #include #include -#include +#include "scheduler_common.h" #include "scheduler_tracing.h" @@ -244,28 +244,6 @@ estimate_max_scheduling_latency(Thread* thread) } -static void -context_switch(Thread *fromThread, Thread *toThread) -{ - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_unscheduled(fromThread); - - cpu_ent* cpu = fromThread->cpu; - toThread->previous_cpu = toThread->cpu = cpu; - fromThread->cpu = NULL; - cpu->running_thread = toThread; - - arch_thread_set_current_thread(toThread); - arch_thread_context_switch(fromThread, toThread); - - // Looks weird, but is correct. fromThread had been unscheduled earlier, - // but is back now. The notification for a thread scheduled the first time - // happens in thread.cpp:thread_kthread_entry(). - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_scheduled(fromThread); -} - - static int32 reschedule_event(timer *unused) { @@ -423,9 +401,7 @@ reschedule(void) oldThread->was_yielded = false; // track kernel time (user time is tracked in thread_at_kernel_entry()) - bigtime_t now = system_time(); - oldThread->kernel_time += now - oldThread->last_time; - nextThread->last_time = now; + scheduler_update_thread_times(oldThread, nextThread); // track CPU activity if (!thread_is_idle_thread(oldThread)) { @@ -448,18 +424,19 @@ reschedule(void) oldThread->cpu->preempted = 0; add_timer(quantumTimer, &reschedule_event, quantum, - B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_THREAD_LOCK); + B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_SCHEDULER_LOCK); if (nextThread != oldThread) - context_switch(oldThread, nextThread); + scheduler_switch_thread(oldThread, nextThread); } } -static void -on_thread_create(Thread* thread) +static status_t +on_thread_create(Thread* thread, bool idleThread) { // do nothing + return B_OK; } @@ -483,11 +460,9 @@ on_thread_destroy(Thread* thread) static void start(void) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); reschedule(); - - RELEASE_THREAD_LOCK(); } diff --git a/src/system/kernel/sem.cpp b/src/system/kernel/sem.cpp index cf47fb2e9f..2c460b9e16 100644 --- a/src/system/kernel/sem.cpp +++ b/src/system/kernel/sem.cpp @@ -56,6 +56,20 @@ #endif +// Locking: +// * sSemsSpinlock: Protects the semaphore free list (sFreeSemsHead, +// sFreeSemsTail), Team::sem_list, and together with sem_entry::lock +// write access to sem_entry::owner/team_link. +// * sem_entry::lock: Protects all sem_entry members. owner, team_link +// additional need sSemsSpinlock for write access. +// lock itself doesn't need protection -- sem_entry objects are never deleted. +// +// The locking order is sSemsSpinlock -> sem_entry::lock -> scheduler lock. All +// semaphores are in the sSems array (sem_entry[]). Access by sem_id requires +// computing the object index (id % sMaxSems), locking the respective +// sem_entry::lock and verifying that sem_entry::id matches afterwards. + + struct queued_thread : DoublyLinkedListLinkImpl { queued_thread(Thread *thread, int32 count) : @@ -82,7 +96,7 @@ struct sem_entry { // count + acquisition count of all blocked // threads char* name; - Team* owner; + team_id owner; select_info* select_infos; thread_id last_acquirer; #if DEBUG_SEM_LAST_ACQUIRER @@ -146,15 +160,13 @@ dump_sem_list(int argc, char** argv) if (sem->id < 0 || (last != -1 && sem->u.used.last_acquirer != last) || (name != NULL && strstr(sem->u.used.name, name) == NULL) - || (owner != -1 - && (sem->u.used.owner == NULL - || sem->u.used.owner->id != owner))) + || (owner != -1 && sem->u.used.owner != owner)) continue; kprintf("%p %6ld %5ld %6ld " "%6ld " " %s\n", sem, sem->id, sem->u.used.count, - sem->u.used.owner != NULL ? sem->u.used.owner->id : -1, + sem->u.used.owner, sem->u.used.last_acquirer > 0 ? sem->u.used.last_acquirer : 0, sem->u.used.name); } @@ -170,8 +182,7 @@ dump_sem(struct sem_entry* sem) kprintf("id: %ld (%#lx)\n", sem->id, sem->id); if (sem->id >= 0) { kprintf("name: '%s'\n", sem->u.used.name); - kprintf("owner: %ld\n", - sem->u.used.owner != NULL ? sem->u.used.owner->id : -1); + kprintf("owner: %ld\n", sem->u.used.owner); kprintf("count: %ld\n", sem->u.used.count); kprintf("queue: "); if (!sem->queue.IsEmpty()) { @@ -184,8 +195,7 @@ dump_sem(struct sem_entry* sem) set_debug_variable("_sem", (addr_t)sem); set_debug_variable("_semID", sem->id); - set_debug_variable("_owner", - sem->u.used.owner != NULL ? sem->u.used.owner->id : -1); + set_debug_variable("_owner", sem->u.used.owner); #if DEBUG_SEM_LAST_ACQUIRER kprintf("last acquired by: %ld, count: %ld\n", @@ -291,15 +301,14 @@ notify_sem_select_events(struct sem_entry* sem, uint16 events) } -/*! Fills the thread_info structure with information from the specified - thread. - The thread lock must be held when called. +/*! Fills the sem_info structure with information from the given semaphore. + The semaphore's lock must be held when called. */ static void fill_sem_info(struct sem_entry* sem, sem_info* info, size_t size) { info->sem = sem->id; - info->team = sem->u.used.owner != NULL ? sem->u.used.owner->id : -1; + info->team = sem->u.used.owner; strlcpy(info->name, sem->u.used.name, sizeof(info->name)); info->count = sem->u.used.count; info->latest_holder = sem->u.used.last_acquirer; @@ -320,12 +329,12 @@ uninit_sem_locked(struct sem_entry& sem, char** _name) sem.u.used.select_infos = NULL; // free any threads waiting for this semaphore - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); while (queued_thread* entry = sem.queue.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, B_BAD_SEM_ID); } - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); int32 id = sem.id; sem.id = -1; @@ -353,41 +362,41 @@ delete_sem_internal(sem_id id, bool checkPermission) int32 slot = id % sMaxSems; cpu_status state = disable_interrupts(); - GRAB_TEAM_LOCK(); + GRAB_SEM_LIST_LOCK(); GRAB_SEM_LOCK(sSems[slot]); if (sSems[slot].id != id) { RELEASE_SEM_LOCK(sSems[slot]); - RELEASE_TEAM_LOCK(); + RELEASE_SEM_LIST_LOCK(); restore_interrupts(state); TRACE(("delete_sem: invalid sem_id %ld\n", id)); return B_BAD_SEM_ID; } if (checkPermission - && sSems[slot].u.used.owner == team_get_kernel_team()) { + && sSems[slot].u.used.owner == team_get_kernel_team_id()) { RELEASE_SEM_LOCK(sSems[slot]); - RELEASE_TEAM_LOCK(); + RELEASE_SEM_LIST_LOCK(); restore_interrupts(state); dprintf("thread %ld tried to delete kernel semaphore %ld.\n", thread_get_current_thread_id(), id); return B_NOT_ALLOWED; } - if (sSems[slot].u.used.owner != NULL) { + if (sSems[slot].u.used.owner >= 0) { list_remove_link(&sSems[slot].u.used.team_link); - sSems[slot].u.used.owner = NULL; + sSems[slot].u.used.owner = -1; } else panic("sem %ld has no owner", id); - RELEASE_TEAM_LOCK(); + RELEASE_SEM_LIST_LOCK(); char* name; uninit_sem_locked(sSems[slot], &name); - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); scheduler_reschedule_if_necessary_locked(); - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); restore_interrupts(state); @@ -480,6 +489,13 @@ create_sem_etc(int32 count, const char* name, team_id owner) if (name == NULL) name = "unnamed semaphore"; + // get the owning team + Team* team = Team::Get(owner); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); + + // clone the name nameLength = strlen(name) + 1; nameLength = min_c(nameLength, B_OS_NAME_LENGTH); tempName = (char*)malloc(nameLength); @@ -488,30 +504,7 @@ create_sem_etc(int32 count, const char* name, team_id owner) strlcpy(tempName, name, nameLength); - Team* team = NULL; - if (owner == team_get_kernel_team_id()) - team = team_get_kernel_team(); - else if (owner == team_get_current_team_id()) - team = thread_get_current_thread()->team; - - bool teamsLocked = false; state = disable_interrupts(); - - if (team == NULL) { - // We need to hold the team lock to make sure this one exists (and - // won't go away. - GRAB_TEAM_LOCK(); - - team = team_get_team_struct_locked(owner); - if (team == NULL) { - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - free(tempName); - return B_BAD_TEAM_ID; - } - - teamsLocked = true; - } GRAB_SEM_LIST_LOCK(); // get the first slot from the free list @@ -529,14 +522,11 @@ create_sem_etc(int32 count, const char* name, team_id owner) sem->u.used.net_count = count; new(&sem->queue) ThreadQueue; sem->u.used.name = tempName; - sem->u.used.owner = team; + sem->u.used.owner = team->id; sem->u.used.select_infos = NULL; id = sem->id; - if (teamsLocked) { - // insert now - list_add_item(&team->sem_list, &sem->u.used.team_link); - } + list_add_item(&team->sem_list, &sem->u.used.team_link); RELEASE_SEM_LOCK(*sem); @@ -551,20 +541,6 @@ create_sem_etc(int32 count, const char* name, team_id owner) } RELEASE_SEM_LIST_LOCK(); - - int32 slot = id % sMaxSems; - if (sem != NULL && !teamsLocked) { - GRAB_TEAM_LOCK(); - GRAB_SEM_LOCK(sSems[slot]); - - list_add_item(&team->sem_list, &sem->u.used.team_link); - - RELEASE_SEM_LOCK(sSems[slot]); - teamsLocked = true; - } - - if (teamsLocked) - RELEASE_TEAM_LOCK(); restore_interrupts(state); if (sem == NULL) @@ -593,7 +569,7 @@ select_sem(int32 id, struct select_info* info, bool kernel) // bad sem ID error = B_BAD_SEM_ID; } else if (!kernel - && sSems[slot].u.used.owner == team_get_kernel_team()) { + && sSems[slot].u.used.owner == team_get_kernel_team_id()) { // kernel semaphore, but call from userland error = B_NOT_ALLOWED; } else { @@ -665,10 +641,11 @@ remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem) // We're done with this entry. We only have to check, if other threads // need unblocking, too. - // Now see if more threads need to be woken up. We get the thread lock for - // that time, so the blocking state of threads won't change. We need that - // lock anyway when unblocking a thread. - GRAB_THREAD_LOCK(); + // Now see if more threads need to be woken up. We get the scheduler lock + // for that time, so the blocking state of threads won't change (due to + // interruption or timeout). We need that lock anyway when unblocking a + // thread. + SpinLocker schedulerLocker(gSchedulerLock); while ((entry = sem->queue.Head()) != NULL) { if (thread_is_blocked(entry->thread)) { @@ -689,7 +666,7 @@ remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem) entry->queued = false; } - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); // select notification, if the semaphore is now acquirable if (sem->u.used.count > 0) @@ -702,19 +679,20 @@ remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem) void sem_delete_owned_sems(Team* team) { - struct list queue; - - { - InterruptsSpinLocker locker(gTeamSpinlock); - list_move_to_list(&team->sem_list, &queue); - } - - while (sem_entry* sem = (sem_entry*)list_remove_head_item(&queue)) { + while (true) { char* name; { + // get the next semaphore from the team's sem list InterruptsLocker locker; + SpinLocker semListLocker(sSemsSpinlock); + sem_entry* sem = (sem_entry*)list_remove_head_item(&team->sem_list); + if (sem == NULL) + break; + + // delete the semaphore GRAB_SEM_LOCK(*sem); + semListLocker.Unlock(); uninit_sem_locked(*sem, &name); } @@ -814,7 +792,7 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, // TODO: the B_CHECK_PERMISSION flag should be made private, as it // doesn't have any use outside the kernel if ((flags & B_CHECK_PERMISSION) != 0 - && sSems[slot].u.used.owner == team_get_kernel_team()) { + && sSems[slot].u.used.owner == team_get_kernel_team_id()) { dprintf("thread %ld tried to acquire kernel semaphore %ld.\n", thread_get_current_thread_id(), id); status = B_NOT_ALLOWED; @@ -846,7 +824,9 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, // do a quick check to see if the thread has any pending signals // this should catch most of the cases where the thread had a signal + SpinLocker schedulerLocker(gSchedulerLock); if (thread_is_interrupted(thread, flags)) { + schedulerLocker.Unlock(); sSems[slot].u.used.count += count; status = B_INTERRUPTED; // the other semaphore will be released later @@ -872,13 +852,13 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, semToBeReleased = -1; } - GRAB_THREAD_LOCK(); + schedulerLocker.Lock(); status_t acquireStatus = timeout == B_INFINITE_TIMEOUT ? thread_block_locked(thread) : thread_block_with_timeout_locked(flags, timeout); - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); GRAB_SEM_LOCK(sSems[slot]); // If we're still queued, this means the acquiration failed, and we @@ -963,7 +943,7 @@ release_sem_etc(sem_id id, int32 count, uint32 flags) // ToDo: the B_CHECK_PERMISSION flag should be made private, as it // doesn't have any use outside the kernel if ((flags & B_CHECK_PERMISSION) != 0 - && sSems[slot].u.used.owner == team_get_kernel_team()) { + && sSems[slot].u.used.owner == team_get_kernel_team_id()) { dprintf("thread %ld tried to release kernel semaphore.\n", thread_get_current_thread_id()); return B_NOT_ALLOWED; @@ -990,7 +970,9 @@ release_sem_etc(sem_id id, int32 count, uint32 flags) flags |= B_RELEASE_IF_WAITING_ONLY; } - SpinLocker threadLocker(gThreadSpinlock); + // Grab the scheduler lock, so thread_is_blocked() is reliable (due to + // possible interruptions or timeouts, it wouldn't be otherwise). + SpinLocker schedulerLocker(gSchedulerLock); while (count > 0) { queued_thread* entry = sSems[slot].queue.Head(); @@ -1027,7 +1009,7 @@ release_sem_etc(sem_id id, int32 count, uint32 flags) entry->queued = false; } - threadLocker.Unlock(); + schedulerLocker.Unlock(); if (sSems[slot].u.used.count > 0) notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE); @@ -1036,7 +1018,7 @@ release_sem_etc(sem_id id, int32 count, uint32 flags) // been told not to. if ((flags & B_DO_NOT_RESCHEDULE) == 0) { semLocker.Unlock(); - threadLocker.Lock(); + schedulerLocker.Lock(); scheduler_reschedule_if_necessary_locked(); } @@ -1123,16 +1105,12 @@ _get_next_sem_info(team_id teamID, int32 *_cookie, struct sem_info *info, if (teamID < 0) return B_BAD_TEAM_ID; - InterruptsSpinLocker locker(gTeamSpinlock); - - Team* team; - if (teamID == B_CURRENT_TEAM) - team = thread_get_current_thread()->team; - else - team = team_get_team_struct_locked(teamID); - + Team* team = Team::Get(teamID); if (team == NULL) return B_BAD_TEAM_ID; + BReference teamReference(team, true); + + InterruptsSpinLocker semListLocker(sSemsSpinlock); // TODO: find a way to iterate the list that is more reliable sem_entry* sem = (sem_entry*)list_get_first_item(&team->sem_list); @@ -1152,7 +1130,7 @@ _get_next_sem_info(team_id teamID, int32 *_cookie, struct sem_info *info, GRAB_SEM_LOCK(*sem); - if (sem->id != -1 && sem->u.used.owner == team) { + if (sem->id != -1 && sem->u.used.owner == team->id) { // found one! fill_sem_info(sem, info, size); newIndex = index + 1; @@ -1183,12 +1161,13 @@ set_sem_owner(sem_id id, team_id newTeamID) int32 slot = id % sMaxSems; - InterruptsSpinLocker teamLocker(gTeamSpinlock); - - Team* newTeam = team_get_team_struct_locked(newTeamID); + // get the new team + Team* newTeam = Team::Get(newTeamID); if (newTeam == NULL) return B_BAD_TEAM_ID; + BReference newTeamReference(newTeam, true); + InterruptsSpinLocker semListLocker(sSemsSpinlock); SpinLocker semLocker(sSems[slot].lock); if (sSems[slot].id != id) { @@ -1199,7 +1178,7 @@ set_sem_owner(sem_id id, team_id newTeamID) list_remove_link(&sSems[slot].u.used.team_link); list_add_item(&newTeam->sem_list, &sSems[slot].u.used.team_link); - sSems[slot].u.used.owner = newTeam; + sSems[slot].u.used.owner = newTeam->id; return B_OK; } diff --git a/src/system/kernel/signal.cpp b/src/system/kernel/signal.cpp index bd2520cc24..21542da474 100644 --- a/src/system/kernel/signal.cpp +++ b/src/system/kernel/signal.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. * Copyright 2002, Angelo Mottola, a.mottola@libero.it. * @@ -41,13 +42,19 @@ #endif -#define BLOCKABLE_SIGNALS (~(KILL_SIGNALS | SIGNAL_TO_MASK(SIGSTOP))) +#define BLOCKABLE_SIGNALS \ + (~(KILL_SIGNALS | SIGNAL_TO_MASK(SIGSTOP) \ + | SIGNAL_TO_MASK(SIGNAL_CONTINUE_THREAD) \ + | SIGNAL_TO_MASK(SIGNAL_CANCEL_THREAD))) #define STOP_SIGNALS \ (SIGNAL_TO_MASK(SIGSTOP) | SIGNAL_TO_MASK(SIGTSTP) \ | SIGNAL_TO_MASK(SIGTTIN) | SIGNAL_TO_MASK(SIGTTOU)) +#define CONTINUE_SIGNALS \ + (SIGNAL_TO_MASK(SIGCONT) | SIGNAL_TO_MASK(SIGNAL_CONTINUE_THREAD)) #define DEFAULT_IGNORE_SIGNALS \ (SIGNAL_TO_MASK(SIGCHLD) | SIGNAL_TO_MASK(SIGWINCH) \ - | SIGNAL_TO_MASK(SIGCONT)) + | SIGNAL_TO_MASK(SIGCONT) \ + | SIGNAL_RANGE_TO_MASK(SIGNAL_REALTIME_MIN, SIGNAL_REALTIME_MAX)) #define NON_DEFERRABLE_SIGNALS \ (KILL_SIGNALS \ | SIGNAL_TO_MASK(SIGILL) \ @@ -55,17 +62,501 @@ | SIGNAL_TO_MASK(SIGSEGV)) -const char * const sigstr[NSIG] = { - "NONE", "HUP", "INT", "QUIT", "ILL", "CHLD", "ABRT", "PIPE", - "FPE", "KILL", "STOP", "SEGV", "CONT", "TSTP", "ALRM", "TERM", - "TTIN", "TTOU", "USR1", "USR2", "WINCH", "KILLTHR", "TRAP", - "POLL", "PROF", "SYS", "URG", "VTALRM", "XCPU", "XFSZ" +static const struct { + const char* name; + int32 priority; +} kSignalInfos[__MAX_SIGNO + 1] = { + {"NONE", -1}, + {"HUP", 0}, + {"INT", 0}, + {"QUIT", 0}, + {"ILL", 0}, + {"CHLD", 0}, + {"ABRT", 0}, + {"PIPE", 0}, + {"FPE", 0}, + {"KILL", 100}, + {"STOP", 0}, + {"SEGV", 0}, + {"CONT", 0}, + {"TSTP", 0}, + {"ALRM", 0}, + {"TERM", 0}, + {"TTIN", 0}, + {"TTOU", 0}, + {"USR1", 0}, + {"USR2", 0}, + {"WINCH", 0}, + {"KILLTHR", 100}, + {"TRAP", 0}, + {"POLL", 0}, + {"PROF", 0}, + {"SYS", 0}, + {"URG", 0}, + {"VTALRM", 0}, + {"XCPU", 0}, + {"XFSZ", 0}, + {"SIGBUS", 0}, + {"SIGRESERVED1", 0}, + {"SIGRESERVED2", 0}, + {"SIGRT1", 8}, + {"SIGRT2", 7}, + {"SIGRT3", 6}, + {"SIGRT4", 5}, + {"SIGRT5", 4}, + {"SIGRT6", 3}, + {"SIGRT7", 2}, + {"SIGRT8", 1}, + {"invalid 41", 0}, + {"invalid 42", 0}, + {"invalid 43", 0}, + {"invalid 44", 0}, + {"invalid 45", 0}, + {"invalid 46", 0}, + {"invalid 47", 0}, + {"invalid 48", 0}, + {"invalid 49", 0}, + {"invalid 50", 0}, + {"invalid 51", 0}, + {"invalid 52", 0}, + {"invalid 53", 0}, + {"invalid 54", 0}, + {"invalid 55", 0}, + {"invalid 56", 0}, + {"invalid 57", 0}, + {"invalid 58", 0}, + {"invalid 59", 0}, + {"invalid 60", 0}, + {"invalid 61", 0}, + {"invalid 62", 0}, + {"CANCEL_THREAD", 0}, + {"CONTINUE_THREAD", 0} // priority must be <= that of SIGSTOP }; -static status_t deliver_signal(Thread *thread, uint signal, uint32 flags); +static inline const char* +signal_name(uint32 number) +{ + return number <= __MAX_SIGNO ? kSignalInfos[number].name : "invalid"; +} +// #pragma mark - SignalHandledCaller + + +struct SignalHandledCaller { + SignalHandledCaller(Signal* signal) + : + fSignal(signal) + { + } + + ~SignalHandledCaller() + { + Done(); + } + + void Done() + { + if (fSignal != NULL) { + fSignal->Handled(); + fSignal = NULL; + } + } + +private: + Signal* fSignal; +}; + + +// #pragma mark - QueuedSignalsCounter + + +/*! Creates a counter with the given limit. + The limit defines the maximum the counter may reach. Since the + BReferenceable's reference count is used, it is assumed that the owning + team holds a reference and the reference count is one greater than the + counter value. + \param limit The maximum allowed value the counter may have. When + \code < 0 \endcode, the value is not limited. +*/ +QueuedSignalsCounter::QueuedSignalsCounter(int32 limit) + : + fLimit(limit) +{ +} + + +/*! Increments the counter, if the limit allows that. + \return \c true, if incrementing the counter succeeded, \c false otherwise. +*/ +bool +QueuedSignalsCounter::Increment() +{ + // no limit => no problem + if (fLimit < 0) { + AcquireReference(); + return true; + } + + // Increment the reference count manually, so we can check atomically. We + // compare the old value > fLimit, assuming that our (primary) owner has a + // reference, we don't want to count. + if (atomic_add(&fReferenceCount, 1) > fLimit) { + ReleaseReference(); + return false; + } + + return true; +} + + +// #pragma mark - Signal + + +Signal::Signal() + : + fCounter(NULL), + fPending(false) +{ +} + + +Signal::Signal(const Signal& other) + : + fCounter(NULL), + fNumber(other.fNumber), + fSignalCode(other.fSignalCode), + fErrorCode(other.fErrorCode), + fSendingProcess(other.fSendingProcess), + fSendingUser(other.fSendingUser), + fStatus(other.fStatus), + fPollBand(other.fPollBand), + fAddress(other.fAddress), + fUserValue(other.fUserValue), + fPending(false) +{ +} + + +Signal::Signal(uint32 number, int32 signalCode, int32 errorCode, + pid_t sendingProcess) + : + fCounter(NULL), + fNumber(number), + fSignalCode(signalCode), + fErrorCode(errorCode), + fSendingProcess(sendingProcess), + fSendingUser(getuid()), + fStatus(0), + fPollBand(0), + fAddress(NULL), + fPending(false) +{ + fUserValue.sival_ptr = NULL; +} + + +Signal::~Signal() +{ + if (fCounter != NULL) + fCounter->ReleaseReference(); +} + + +/*! Creates a queuable clone of the given signal. + Also enforces the current team's signal queuing limit. + + \param signal The signal to clone. + \param queuingRequired If \c true, the function will return an error code + when creating the clone fails for any reason. Otherwise, the function + will set \a _signalToQueue to \c NULL, but still return \c B_OK. + \param _signalToQueue Return parameter. Set to the clone of the signal. + \return When \c queuingRequired is \c false, always \c B_OK. Otherwise + \c B_OK, when creating the signal clone succeeds, another error code, + when it fails. +*/ +/*static*/ status_t +Signal::CreateQueuable(const Signal& signal, bool queuingRequired, + Signal*& _signalToQueue) +{ + _signalToQueue = NULL; + + // If interrupts are disabled, we can't allocate a signal. + if (!are_interrupts_enabled()) + return queuingRequired ? B_BAD_VALUE : B_OK; + + // increment the queued signals counter + QueuedSignalsCounter* counter + = thread_get_current_thread()->team->QueuedSignalsCounter(); + if (!counter->Increment()) + return queuingRequired ? EAGAIN : B_OK; + + // allocate the signal + Signal* signalToQueue = new(std::nothrow) Signal(signal); + if (signalToQueue == NULL) { + counter->Decrement(); + return queuingRequired ? B_NO_MEMORY : B_OK; + } + + signalToQueue->fCounter = counter; + + _signalToQueue = signalToQueue; + return B_OK; +} + +void +Signal::SetTo(uint32 number) +{ + Team* team = thread_get_current_thread()->team; + + fNumber = number; + fSignalCode = SI_USER; + fErrorCode = 0; + fSendingProcess = team->id; + fSendingUser = team->effective_uid; + // assuming scheduler lock is being held + fStatus = 0; + fPollBand = 0; + fAddress = NULL; + fUserValue.sival_ptr = NULL; +} + + +int32 +Signal::Priority() const +{ + return kSignalInfos[fNumber].priority; +} + + +void +Signal::Handled() +{ + ReleaseReference(); +} + + +void +Signal::LastReferenceReleased() +{ + if (are_interrupts_enabled()) + delete this; + else + deferred_delete(this); +} + + +// #pragma mark - PendingSignals + + +PendingSignals::PendingSignals() + : + fQueuedSignalsMask(0), + fUnqueuedSignalsMask(0) +{ +} + + +PendingSignals::~PendingSignals() +{ + Clear(); +} + + +/*! Of the signals in \a nonBlocked returns the priority of that with the + highest priority. + \param nonBlocked The mask with the non-blocked signals. + \return The priority of the highest priority non-blocked signal, or, if all + signals are blocked, \c -1. +*/ +int32 +PendingSignals::HighestSignalPriority(sigset_t nonBlocked) const +{ + Signal* queuedSignal; + int32 unqueuedSignal; + return _GetHighestPrioritySignal(nonBlocked, queuedSignal, unqueuedSignal); +} + + +void +PendingSignals::Clear() +{ + // release references of all queued signals + while (Signal* signal = fQueuedSignals.RemoveHead()) + signal->Handled(); + + fQueuedSignalsMask = 0; + fUnqueuedSignalsMask = 0; +} + + +/*! Adds a signal. + Takes over the reference to the signal from the caller. +*/ +void +PendingSignals::AddSignal(Signal* signal) +{ + // queue according to priority + int32 priority = signal->Priority(); + Signal* otherSignal = NULL; + for (SignalList::Iterator it = fQueuedSignals.GetIterator(); + (otherSignal = it.Next()) != NULL;) { + if (priority > otherSignal->Priority()) + break; + } + + fQueuedSignals.InsertBefore(otherSignal, signal); + signal->SetPending(true); + + fQueuedSignalsMask |= SIGNAL_TO_MASK(signal->Number()); +} + + +void +PendingSignals::RemoveSignal(Signal* signal) +{ + signal->SetPending(false); + fQueuedSignals.Remove(signal); + _UpdateQueuedSignalMask(); +} + + +void +PendingSignals::RemoveSignals(sigset_t mask) +{ + // remove from queued signals + if ((fQueuedSignalsMask & mask) != 0) { + for (SignalList::Iterator it = fQueuedSignals.GetIterator(); + Signal* signal = it.Next();) { + // remove signal, if in mask + if ((SIGNAL_TO_MASK(signal->Number()) & mask) != 0) { + it.Remove(); + signal->SetPending(false); + signal->Handled(); + } + } + + fQueuedSignalsMask &= ~mask; + } + + // remove from unqueued signals + fUnqueuedSignalsMask &= ~mask; +} + + +/*! Removes and returns a signal in \a nonBlocked that has the highest priority. + The caller gets a reference to the returned signal, if any. + \param nonBlocked The mask of non-blocked signals. + \param buffer If the signal is not queued this buffer is returned. In this + case the method acquires a reference to \a buffer, so that the caller + gets a reference also in this case. + \return The removed signal or \c NULL, if all signals are blocked. +*/ +Signal* +PendingSignals::DequeueSignal(sigset_t nonBlocked, Signal& buffer) +{ + // find the signal with the highest priority + Signal* queuedSignal; + int32 unqueuedSignal; + if (_GetHighestPrioritySignal(nonBlocked, queuedSignal, unqueuedSignal) < 0) + return NULL; + + // if it is a queued signal, dequeue it + if (queuedSignal != NULL) { + fQueuedSignals.Remove(queuedSignal); + queuedSignal->SetPending(false); + _UpdateQueuedSignalMask(); + return queuedSignal; + } + + // it is unqueued -- remove from mask + fUnqueuedSignalsMask &= ~SIGNAL_TO_MASK(unqueuedSignal); + + // init buffer + buffer.SetTo(unqueuedSignal); + buffer.AcquireReference(); + return &buffer; +} + + +/*! Of the signals not it \a blocked returns the priority of that with the + highest priority. + \param blocked The mask with the non-blocked signals. + \param _queuedSignal If the found signal is a queued signal, the variable + will be set to that signal, otherwise to \c NULL. + \param _unqueuedSignal If the found signal is an unqueued signal, the + variable is set to that signal's number, otherwise to \c -1. + \return The priority of the highest priority non-blocked signal, or, if all + signals are blocked, \c -1. +*/ +int32 +PendingSignals::_GetHighestPrioritySignal(sigset_t nonBlocked, + Signal*& _queuedSignal, int32& _unqueuedSignal) const +{ + // check queued signals + Signal* queuedSignal = NULL; + int32 queuedPriority = -1; + + if ((fQueuedSignalsMask & nonBlocked) != 0) { + for (SignalList::ConstIterator it = fQueuedSignals.GetIterator(); + Signal* signal = it.Next();) { + if ((SIGNAL_TO_MASK(signal->Number()) & nonBlocked) != 0) { + queuedPriority = signal->Priority(); + queuedSignal = signal; + break; + } + } + } + + // check unqueued signals + int32 unqueuedSignal = -1; + int32 unqueuedPriority = -1; + + sigset_t unqueuedSignals = fUnqueuedSignalsMask & nonBlocked; + if (unqueuedSignals != 0) { + int32 signal = 1; + while (unqueuedSignals != 0) { + sigset_t mask = SIGNAL_TO_MASK(signal); + if ((unqueuedSignals & mask) != 0) { + int32 priority = kSignalInfos[signal].priority; + if (priority > unqueuedPriority) { + unqueuedSignal = signal; + unqueuedPriority = priority; + } + unqueuedSignals &= ~mask; + } + + signal++; + } + } + + // Return found queued or unqueued signal, whichever has the higher + // priority. + if (queuedPriority >= unqueuedPriority) { + _queuedSignal = queuedSignal; + _unqueuedSignal = -1; + return queuedPriority; + } + + _queuedSignal = NULL; + _unqueuedSignal = unqueuedSignal; + return unqueuedPriority; +} + + +void +PendingSignals::_UpdateQueuedSignalMask() +{ + sigset_t mask = 0; + for (SignalList::Iterator it = fQueuedSignals.GetIterator(); + Signal* signal = it.Next();) { + mask |= SIGNAL_TO_MASK(signal->Number()); + } + + fQueuedSignalsMask = mask; +} + // #pragma mark - signal tracing @@ -75,28 +566,29 @@ static status_t deliver_signal(Thread *thread, uint signal, uint32 flags); namespace SignalTracing { -class HandleSignals : public AbstractTraceEntry { +class HandleSignal : public AbstractTraceEntry { public: - HandleSignals(uint32 signals) + HandleSignal(uint32 signal) : - fSignals(signals) + fSignal(signal) { Initialized(); } virtual void AddDump(TraceOutput& out) { - out.Print("signal handle: 0x%lx", fSignals); + out.Print("signal handle: %" B_PRIu32 " (%s)" , fSignal, + signal_name(fSignal)); } private: - uint32 fSignals; + uint32 fSignal; }; class ExecuteSignalHandler : public AbstractTraceEntry { public: - ExecuteSignalHandler(int signal, struct sigaction* handler) + ExecuteSignalHandler(uint32 signal, struct sigaction* handler) : fSignal(signal), fHandler((void*)handler->sa_handler) @@ -106,12 +598,12 @@ class ExecuteSignalHandler : public AbstractTraceEntry { virtual void AddDump(TraceOutput& out) { - out.Print("signal exec handler: signal: %d, handler: %p", - fSignal, fHandler); + out.Print("signal exec handler: signal: %" B_PRIu32 " (%s), " + "handler: %p", fSignal, signal_name(fSignal), fHandler); } private: - int fSignal; + uint32 fSignal; void* fHandler; }; @@ -130,8 +622,7 @@ class SendSignal : public AbstractTraceEntry { virtual void AddDump(TraceOutput& out) { out.Print("signal send: target: %ld, signal: %lu (%s), " - "flags: 0x%lx", fTarget, fSignal, - (fSignal < NSIG ? sigstr[fSignal] : "invalid"), fFlags); + "flags: 0x%lx", fTarget, fSignal, signal_name(fSignal), fFlags); } private: @@ -143,9 +634,8 @@ class SendSignal : public AbstractTraceEntry { class SigAction : public AbstractTraceEntry { public: - SigAction(Thread* thread, uint32 signal, const struct sigaction* act) + SigAction(uint32 signal, const struct sigaction* act) : - fThread(thread->id), fSignal(signal), fAction(*act) { @@ -154,15 +644,13 @@ class SigAction : public AbstractTraceEntry { virtual void AddDump(TraceOutput& out) { - out.Print("signal action: thread: %ld, signal: %lu (%s), " - "action: {handler: %p, flags: 0x%x, mask: 0x%lx}", - fThread, fSignal, - (fSignal < NSIG ? sigstr[fSignal] : "invalid"), - fAction.sa_handler, fAction.sa_flags, fAction.sa_mask); + out.Print("signal action: signal: %lu (%s), " + "action: {handler: %p, flags: 0x%x, mask: 0x%llx}", fSignal, + signal_name(fSignal), fAction.sa_handler, fAction.sa_flags, + (long long)fAction.sa_mask); } private: - thread_id fThread; uint32 fSignal; struct sigaction fAction; }; @@ -194,8 +682,8 @@ class SigProcMask : public AbstractTraceEntry { break; } - out.Print("signal proc mask: %s 0x%lx, old mask: 0x%lx", how, fMask, - fOldMask); + out.Print("signal proc mask: %s 0x%llx, old mask: 0x%llx", how, + (long long)fMask, (long long)fOldMask); } private: @@ -217,8 +705,8 @@ class SigSuspend : public AbstractTraceEntry { virtual void AddDump(TraceOutput& out) { - out.Print("signal suspend: %#" B_PRIx32 ", old mask: %#" B_PRIx32, - fMask, fOldMask); + out.Print("signal suspend: %#llx, old mask: %#llx", + (long long)fMask, (long long)fOldMask); } private: @@ -231,7 +719,7 @@ class SigSuspendDone : public AbstractTraceEntry { public: SigSuspendDone() : - fSignals(thread_get_current_thread()->sig_pending) + fSignals(thread_get_current_thread()->ThreadPendingSignals()) { Initialized(); } @@ -257,80 +745,191 @@ class SigSuspendDone : public AbstractTraceEntry { // #pragma mark - -/*! Updates the thread::flags field according to what signals are pending. - Interrupts must be disabled and the thread lock must be held. +/*! Updates the given thread's Thread::flags field according to what signals are + pending. + The caller must hold the scheduler lock. */ static void update_thread_signals_flag(Thread* thread) { - sigset_t mask = ~atomic_get(&thread->sig_block_mask) - | thread->sig_temp_enabled; - if (atomic_get(&thread->sig_pending) & mask) + sigset_t mask = ~thread->sig_block_mask; + if ((thread->AllPendingSignals() & mask) != 0) atomic_or(&thread->flags, THREAD_FLAGS_SIGNALS_PENDING); else atomic_and(&thread->flags, ~THREAD_FLAGS_SIGNALS_PENDING); } -void +/*! Updates the current thread's Thread::flags field according to what signals + are pending. + The caller must hold the scheduler lock. +*/ +static void update_current_thread_signals_flag() { - InterruptsSpinLocker locker(gThreadSpinlock); - update_thread_signals_flag(thread_get_current_thread()); } -static bool -notify_debugger(Thread *thread, int signal, struct sigaction *handler, - bool deadly) +/*! Updates all of the given team's threads' Thread::flags fields according to + what signals are pending. + The caller must hold the scheduler lock. +*/ +static void +update_team_threads_signal_flag(Team* team) { - uint64 signalMask = SIGNAL_TO_MASK(signal); - - // first check the ignore signal masks the debugger specified for the thread - - if (atomic_get(&thread->debug_info.ignore_signals_once) & signalMask) { - atomic_and(&thread->debug_info.ignore_signals_once, ~signalMask); - return true; + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + update_thread_signals_flag(thread); } - - if (atomic_get(&thread->debug_info.ignore_signals) & signalMask) - return true; - - // deliver the event - return user_debug_handle_signal(signal, handler, deadly); } -/*! Actually handles the signal - ie. the thread will exit, a custom signal - handler is prepared, or whatever the signal demands. -*/ -bool -handle_signals(Thread *thread) -{ - uint32 signalMask = atomic_get(&thread->sig_pending) - & (~atomic_get(&thread->sig_block_mask) | thread->sig_temp_enabled); - thread->sig_temp_enabled = 0; +/*! Notifies the user debugger about a signal to be handled. - // If SIGKILL[THR] are pending, we ignore other signals. - // Otherwise check, if the thread shall stop for debugging. - if (signalMask & KILL_SIGNALS) { - signalMask &= KILL_SIGNALS; - } else if (thread->debug_info.flags & B_THREAD_DEBUG_STOP) { - user_debug_stop_thread(); + The caller must not hold any locks. + + \param thread The current thread. + \param signal The signal to be handled. + \param handler The installed signal handler for the signal. + \param deadly Indicates whether the signal is deadly. + \return \c true, if the signal shall be handled, \c false, if it shall be + ignored. +*/ +static bool +notify_debugger(Thread* thread, Signal* signal, struct sigaction& handler, + bool deadly) +{ + uint64 signalMask = SIGNAL_TO_MASK(signal->Number()); + + // first check the ignore signal masks the debugger specified for the thread + InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); + + if ((thread->debug_info.ignore_signals_once & signalMask) != 0) { + thread->debug_info.ignore_signals_once &= ~signalMask; + return true; } - if (signalMask == 0) - return 0; + if ((thread->debug_info.ignore_signals & signalMask) != 0) + return true; + + threadDebugInfoLocker.Unlock(); + + // deliver the event + return user_debug_handle_signal(signal->Number(), &handler, deadly); +} + + +/*! Removes and returns a signal with the highest priority in \a nonBlocked that + is pending in the given thread or its team. + After dequeuing the signal the Thread::flags field of the affected threads + are updated. + The caller gets a reference to the returned signal, if any. + The caller must hold the scheduler lock. + \param thread The thread. + \param nonBlocked The mask of non-blocked signals. + \param buffer If the signal is not queued this buffer is returned. In this + case the method acquires a reference to \a buffer, so that the caller + gets a reference also in this case. + \return The removed signal or \c NULL, if all signals are blocked. +*/ +static Signal* +dequeue_thread_or_team_signal(Thread* thread, sigset_t nonBlocked, + Signal& buffer) +{ + Team* team = thread->team; + Signal* signal; + if (team->HighestPendingSignalPriority(nonBlocked) + > thread->HighestPendingSignalPriority(nonBlocked)) { + signal = team->DequeuePendingSignal(nonBlocked, buffer); + update_team_threads_signal_flag(team); + } else { + signal = thread->DequeuePendingSignal(nonBlocked, buffer); + update_thread_signals_flag(thread); + } + + return signal; +} + + +static status_t +setup_signal_frame(Thread* thread, struct sigaction* action, Signal* signal, + sigset_t signalMask) +{ + // prepare the data, we need to copy onto the user stack + signal_frame_data frameData; + + // signal info + frameData.info.si_signo = signal->Number(); + frameData.info.si_code = signal->SignalCode(); + frameData.info.si_errno = signal->ErrorCode(); + frameData.info.si_pid = signal->SendingProcess(); + frameData.info.si_uid = signal->SendingUser(); + frameData.info.si_addr = signal->Address(); + frameData.info.si_status = signal->Status(); + frameData.info.si_band = signal->PollBand(); + frameData.info.si_value = signal->UserValue(); + + // context + frameData.context.uc_link = thread->user_signal_context; + frameData.context.uc_sigmask = signalMask; + // uc_stack and uc_mcontext are filled in by the architecture specific code. + + // user data + frameData.user_data = action->sa_userdata; + + // handler function + frameData.siginfo_handler = (action->sa_flags & SA_SIGINFO) != 0; + frameData.handler = frameData.siginfo_handler + ? (void*)action->sa_sigaction : (void*)action->sa_handler; + + // thread flags -- save the and clear the thread's syscall restart related + // flags + frameData.thread_flags = atomic_and(&thread->flags, + ~(THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_64_BIT_SYSCALL_RETURN)); + + // syscall restart related fields + memcpy(frameData.syscall_restart_parameters, + thread->syscall_restart.parameters, + sizeof(frameData.syscall_restart_parameters)); + // syscall_restart_return_value is filled in by the architecture specific + // code. + + return arch_setup_signal_frame(thread, action, &frameData); +} + + +/*! Actually handles pending signals -- i.e. the thread will exit, a custom + signal handler is prepared, or whatever the signal demands. + The function will not return, when a deadly signal is encountered. The + function will suspend the thread indefinitely, when a stop signal is + encountered. + Interrupts must be enabled. + \param thread The current thread. +*/ +void +handle_signals(Thread* thread) +{ + Team* team = thread->team; + + TeamLocker teamLocker(team); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + // If userland requested to defer signals, we check now, if this is + // possible. + sigset_t nonBlockedMask = ~thread->sig_block_mask; + sigset_t signalMask = thread->AllPendingSignals() & nonBlockedMask; if (thread->user_thread->defer_signals > 0 - && (signalMask & NON_DEFERRABLE_SIGNALS) == 0) { + && (signalMask & NON_DEFERRABLE_SIGNALS) == 0 + && thread->sigsuspend_original_unblocked_mask == 0) { thread->user_thread->pending_signals = signalMask; - return 0; + return; } thread->user_thread->pending_signals = 0; + // determine syscall restart behavior uint32 restartFlags = atomic_and(&thread->flags, ~THREAD_FLAGS_DONT_RESTART_SYSCALL); bool alwaysRestart @@ -338,45 +937,94 @@ handle_signals(Thread *thread) bool restart = alwaysRestart || (restartFlags & THREAD_FLAGS_DONT_RESTART_SYSCALL) == 0; - T(HandleSignals(signalMask)); + // Loop until we've handled all signals. + bool initialIteration = true; + while (true) { + if (initialIteration) { + initialIteration = false; + } else { + teamLocker.Lock(); + schedulerLocker.Lock(); - for (int32 i = 0; i < NSIG; i++) { - bool debugSignal; - int32 signal = i + 1; + signalMask = thread->AllPendingSignals() & nonBlockedMask; + } - if ((signalMask & SIGNAL_TO_MASK(signal)) == 0) + // Unless SIGKILL[THR] are pending, check, if the thread shall stop for + // debugging. + if ((signalMask & KILL_SIGNALS) == 0 + && (atomic_get(&thread->debug_info.flags) & B_THREAD_DEBUG_STOP) + != 0) { + schedulerLocker.Unlock(); + teamLocker.Unlock(); + + user_debug_stop_thread(); continue; + } - // clear the signal that we will handle - atomic_and(&thread->sig_pending, ~SIGNAL_TO_MASK(signal)); + // We're done, if there aren't any pending signals anymore. + if ((signalMask & nonBlockedMask) == 0) + break; - debugSignal = !(~atomic_get(&thread->team->debug_info.flags) - & (B_TEAM_DEBUG_SIGNALS | B_TEAM_DEBUG_DEBUGGER_INSTALLED)); + // get pending non-blocked thread or team signal with the highest + // priority + Signal stackSignal; + Signal* signal = dequeue_thread_or_team_signal(thread, nonBlockedMask, + stackSignal); + ASSERT(signal != NULL); + SignalHandledCaller signalHandledCaller(signal); - // TODO: since sigaction_etc() could clobber the fields at any time, - // we should actually copy the relevant fields atomically before - // accessing them (only the debugger is calling sigaction_etc() - // right now). - // Update: sigaction_etc() is only used by the userland debugger - // support. We can just as well restrict getting/setting signal - // handlers to work only when the respective thread is stopped. - // Then sigaction() could be used instead and we could get rid of - // sigaction_etc(). - struct sigaction* handler = &thread->sig_action[i]; + schedulerLocker.Unlock(); - TRACE(("Thread 0x%lx received signal %s\n", thread->id, sigstr[signal])); + // get the action for the signal + struct sigaction handler; + if (signal->Number() <= MAX_SIGNAL_NUMBER) { + handler = team->SignalActionFor(signal->Number()); + } else { + handler.sa_handler = SIG_DFL; + handler.sa_flags = 0; + } - if (handler->sa_handler == SIG_IGN) { + if ((handler.sa_flags & SA_ONESHOT) != 0 + && handler.sa_handler != SIG_IGN && handler.sa_handler != SIG_DFL) { + team->SignalActionFor(signal->Number()).sa_handler = SIG_DFL; + } + + T(HandleSignal(signal->Number())); + + teamLocker.Unlock(); + + // debug the signal, if a debugger is installed and the signal debugging + // flag is set + bool debugSignal = (~atomic_get(&team->debug_info.flags) + & (B_TEAM_DEBUG_DEBUGGER_INSTALLED | B_TEAM_DEBUG_SIGNALS)) + == 0; + + // handle the signal + TRACE(("Thread %" B_PRId32 " received signal %s\n", thread->id, + kSignalInfos[signal->Number()].name)); + + if (handler.sa_handler == SIG_IGN) { // signal is to be ignored - // ToDo: apply zombie cleaning on SIGCHLD + // TODO: apply zombie cleaning on SIGCHLD // notify the debugger if (debugSignal) notify_debugger(thread, signal, handler, false); continue; - } else if (handler->sa_handler == SIG_DFL) { + } else if (handler.sa_handler == SIG_DFL) { // default signal behaviour - switch (signal) { + + // realtime signals are ignored by default + if (signal->Number() >= SIGNAL_REALTIME_MIN + && signal->Number() <= SIGNAL_REALTIME_MAX) { + // notify the debugger + if (debugSignal) + notify_debugger(thread, signal, handler, false); + continue; + } + + bool killTeam = false; + switch (signal->Number()) { case SIGCHLD: case SIGWINCH: case SIGURG: @@ -385,6 +1033,23 @@ handle_signals(Thread *thread) notify_debugger(thread, signal, handler, false); continue; + case SIGNAL_CANCEL_THREAD: + // set up the signal handler + handler.sa_handler = thread->cancel_function; + handler.sa_flags = 0; + handler.sa_mask = 0; + handler.sa_userdata = NULL; + + restart = false; + // we always want to interrupt + break; + + case SIGNAL_CONTINUE_THREAD: + // prevent syscall restart, but otherwise ignore + restart = false; + atomic_and(&thread->flags, ~THREAD_FLAGS_RESTART_SYSCALL); + continue; + case SIGCONT: // notify the debugger if (debugSignal @@ -392,11 +1057,14 @@ handle_signals(Thread *thread) continue; // notify threads waiting for team state changes - if (thread == thread->team->main_thread) { - InterruptsSpinLocker locker(gTeamSpinlock); - team_set_job_control_state(thread->team, + if (thread == team->main_thread) { + team->LockTeamAndParent(false); + + team_set_job_control_state(team, JOB_CONTROL_STATE_CONTINUED, signal, false); + team->UnlockTeamAndParent(); + // The standard states that the system *may* send a // SIGCHLD when a child is continued. I haven't found // a good reason why we would want to, though. @@ -407,43 +1075,68 @@ handle_signals(Thread *thread) case SIGTSTP: case SIGTTIN: case SIGTTOU: + { // notify the debugger if (debugSignal && !notify_debugger(thread, signal, handler, false)) continue; - thread->next_state = B_THREAD_SUSPENDED; + // The terminal-sent stop signals are allowed to stop the + // process only, if it doesn't belong to an orphaned process + // group. Otherwise the signal must be discarded. + team->LockProcessGroup(); + AutoLocker groupLocker(team->group, true); + if (signal->Number() != SIGSTOP + && team->group->IsOrphaned()) { + continue; + } // notify threads waiting for team state changes - if (thread == thread->team->main_thread) { - InterruptsSpinLocker locker(gTeamSpinlock); - team_set_job_control_state(thread->team, + if (thread == team->main_thread) { + team->LockTeamAndParent(false); + + team_set_job_control_state(team, JOB_CONTROL_STATE_STOPPED, signal, false); // send a SIGCHLD to the parent (if it does have // SA_NOCLDSTOP defined) - SpinLocker _(gThreadSpinlock); - Thread* parentThread - = thread->team->parent->main_thread; + Team* parentTeam = team->parent; + struct sigaction& parentHandler - = parentThread->sig_action[SIGCHLD - 1]; - if ((parentHandler.sa_flags & SA_NOCLDSTOP) == 0) - deliver_signal(parentThread, SIGCHLD, 0); + = parentTeam->SignalActionFor(SIGCHLD); + if ((parentHandler.sa_flags & SA_NOCLDSTOP) == 0) { + Signal childSignal(SIGCHLD, CLD_STOPPED, B_OK, + team->id); + childSignal.SetStatus(signal->Number()); + childSignal.SetSendingUser(signal->SendingUser()); + send_signal_to_team(parentTeam, childSignal, 0); + } + + team->UnlockTeamAndParent(); } - return true; + groupLocker.Unlock(); + + // Suspend the thread, unless there's already a signal to + // continue or kill pending. + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + if ((thread->AllPendingSignals() + & (CONTINUE_SIGNALS | KILL_SIGNALS)) == 0) { + thread->next_state = B_THREAD_SUSPENDED; + scheduler_reschedule(); + } + schedulerLocker.Unlock(); + + continue; + } case SIGSEGV: + case SIGBUS: case SIGFPE: case SIGILL: case SIGTRAP: case SIGABRT: - // If this is the main thread, we just fall through and let - // this signal kill the team. Otherwise we send a SIGKILL to - // the main thread first, since the signal will kill this - // thread only. - if (thread != thread->team->main_thread) - send_signal(thread->team->main_thread->id, SIGKILL); + case SIGKILL: case SIGQUIT: case SIGPOLL: case SIGPROF: @@ -451,21 +1144,52 @@ handle_signals(Thread *thread) case SIGVTALRM: case SIGXCPU: case SIGXFSZ: - TRACE(("Shutting down thread 0x%lx due to signal #%ld\n", - thread->id, signal)); - case SIGKILL: - case SIGKILLTHR: default: - // if the thread exited normally, the exit reason is already set - if (thread->exit.reason != THREAD_RETURN_EXIT) { - thread->exit.reason = THREAD_RETURN_INTERRUPTED; - thread->exit.signal = (uint16)signal; + TRACE(("Shutting down team %" B_PRId32 " due to signal %" + B_PRIu32 " received in thread %" B_PRIu32 " \n", + team->id, signal->Number(), thread->id)); + + // This signal kills the team regardless which thread + // received it. + killTeam = true; + + // fall through + case SIGKILLTHR: + // notify the debugger + if (debugSignal && signal->Number() != SIGKILL + && signal->Number() != SIGKILLTHR + && !notify_debugger(thread, signal, handler, true)) { + continue; } - // notify the debugger - if (debugSignal && signal != SIGKILL && signal != SIGKILLTHR - && !notify_debugger(thread, signal, handler, true)) - continue; + if (killTeam || thread == team->main_thread) { + // The signal is terminal for the team or the thread is + // the main thread. In either case the team is going + // down. Set its exit status, if that didn't happen yet. + teamLocker.Lock(); + + if (!team->exit.initialized) { + team->exit.reason = CLD_KILLED; + team->exit.signal = signal->Number(); + team->exit.signaling_user = signal->SendingUser(); + team->exit.status = 0; + team->exit.initialized = true; + } + + teamLocker.Unlock(); + + // If this is not the main thread, send it a SIGKILLTHR + // so that the team terminates. + if (thread != team->main_thread) { + Signal childSignal(SIGKILLTHR, SI_USER, B_OK, + team->id); + send_signal_to_thread_id(team->id, childSignal, 0); + } + } + + // explicitly get rid of the signal reference, since + // thread_exit() won't return + signalHandledCaller.Done(); thread_exit(); // won't return @@ -479,70 +1203,176 @@ handle_signals(Thread *thread) continue; if (!restart - || ((!alwaysRestart && handler->sa_flags & SA_RESTART) == 0)) { + || (!alwaysRestart && (handler.sa_flags & SA_RESTART) == 0)) { atomic_and(&thread->flags, ~THREAD_FLAGS_RESTART_SYSCALL); } - T(ExecuteSignalHandler(signal, handler)); + T(ExecuteSignalHandler(signal->Number(), &handler)); TRACE(("### Setting up custom signal handler frame...\n")); - arch_setup_signal_frame(thread, handler, signal, - atomic_get(&thread->sig_block_mask)); - if (handler->sa_flags & SA_ONESHOT) - handler->sa_handler = SIG_DFL; - if ((handler->sa_flags & SA_NOMASK) == 0) { - // Update the block mask while the signal handler is running - it - // will be automatically restored when the signal frame is left. - atomic_or(&thread->sig_block_mask, - (handler->sa_mask | SIGNAL_TO_MASK(signal)) & BLOCKABLE_SIGNALS); + // save the old block mask -- we may need to adjust it for the handler + schedulerLocker.Lock(); + + sigset_t oldBlockMask = thread->sigsuspend_original_unblocked_mask != 0 + ? ~thread->sigsuspend_original_unblocked_mask + : thread->sig_block_mask; + + // Update the block mask while the signal handler is running -- it + // will be automatically restored when the signal frame is left. + thread->sig_block_mask |= handler.sa_mask & BLOCKABLE_SIGNALS; + + if ((handler.sa_flags & SA_NOMASK) == 0) { + thread->sig_block_mask + |= SIGNAL_TO_MASK(signal->Number()) & BLOCKABLE_SIGNALS; } update_current_thread_signals_flag(); - return false; + schedulerLocker.Unlock(); + + setup_signal_frame(thread, &handler, signal, oldBlockMask); + + // Reset sigsuspend_original_unblocked_mask. It would have been set by + // sigsuspend_internal(). In that case, above we set oldBlockMask + // accordingly so that after the handler returns the thread's signal + // mask is reset. + thread->sigsuspend_original_unblocked_mask = 0; + + return; } - // clear syscall restart thread flag, if we're not supposed to restart the - // syscall - if (!restart) + // We have not handled any signal (respectively only ignored ones). + + // If sigsuspend_original_unblocked_mask is non-null, we came from a + // sigsuspend_internal(). Not having handled any signal, we should restart + // the syscall. + if (thread->sigsuspend_original_unblocked_mask != 0) { + restart = true; + atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL); + } else if (!restart) { + // clear syscall restart thread flag, if we're not supposed to restart + // the syscall atomic_and(&thread->flags, ~THREAD_FLAGS_RESTART_SYSCALL); - - update_current_thread_signals_flag(); - - return false; + } } -bool -is_kill_signal_pending(void) -{ - return (atomic_get(&thread_get_current_thread()->sig_pending) - & KILL_SIGNALS) != 0; -} - - -bool -is_signal_blocked(int signal) -{ - return (atomic_get(&thread_get_current_thread()->sig_block_mask) - & SIGNAL_TO_MASK(signal)) != 0; -} - - -/*! Delivers the \a signal to the \a thread, but doesn't handle the signal - - it just makes sure the thread gets the signal, ie. unblocks it if needed. - This function must be called with interrupts disabled and the - thread lock held. +/*! Checks whether the given signal is blocked for the given team (i.e. all of + its threads). + The caller must hold the team's lock and the scheduler lock. */ -static status_t -deliver_signal(Thread *thread, uint signal, uint32 flags) +bool +is_team_signal_blocked(Team* team, int signal) { - if (flags & B_CHECK_PERMISSION) { - // ToDo: introduce euid & uid fields to the team and check permission + sigset_t mask = SIGNAL_TO_MASK(signal); + + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + if ((thread->sig_block_mask & mask) == 0) + return false; } - if (signal == 0) + return true; +} + + +/*! Gets (guesses) the current thread's currently used stack from the given + stack pointer. + Fills in \a stack with either the signal stack or the thread's user stack. + \param address A stack pointer address to be used to determine the used + stack. + \param stack Filled in by the function. +*/ +void +signal_get_user_stack(addr_t address, stack_t* stack) +{ + // If a signal stack is enabled for the stack and the address is within it, + // return the signal stack. In all other cases return the thread's user + // stack, even if the address doesn't lie within it. + Thread* thread = thread_get_current_thread(); + if (thread->signal_stack_enabled && address >= thread->signal_stack_base + && address < thread->signal_stack_base + thread->signal_stack_size) { + stack->ss_sp = (void*)thread->signal_stack_base; + stack->ss_size = thread->signal_stack_size; + } else { + stack->ss_sp = (void*)thread->user_stack_base; + stack->ss_size = thread->user_stack_size; + } + + stack->ss_flags = 0; +} + + +/*! Checks whether any non-blocked signal is pending for the current thread. + The caller must hold the scheduler lock. + \param thread The current thread. +*/ +static bool +has_signals_pending(Thread* thread) +{ + return (thread->AllPendingSignals() & ~thread->sig_block_mask) != 0; +} + + +/*! Checks whether the current user has permission to send a signal to the given + target team. + + The caller must hold the scheduler lock or \a team's lock. + + \param team The target team. + \param schedulerLocked \c true, if the caller holds the scheduler lock, + \c false otherwise. +*/ +static bool +has_permission_to_signal(Team* team, bool schedulerLocked) +{ + // get the current user + uid_t currentUser = schedulerLocked + ? thread_get_current_thread()->team->effective_uid + : geteuid(); + + // root is omnipotent -- in the other cases the current user must match the + // target team's + return currentUser == 0 || currentUser == team->effective_uid; +} + + +/*! Delivers a signal to the \a thread, but doesn't handle the signal -- it just + makes sure the thread gets the signal, i.e. unblocks it if needed. + + The caller must hold the scheduler lock. + + \param thread The thread the signal shall be delivered to. + \param signalNumber The number of the signal to be delivered. If \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + \param signal If non-NULL the signal to be queued (has number + \a signalNumber in this case). The caller transfers an object reference + to this function. If \c NULL an unqueued signal will be delivered to the + thread. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_thread_locked(Thread* thread, uint32 signalNumber, + Signal* signal, uint32 flags) +{ + ASSERT(signal == NULL || signalNumber == signal->Number()); + + T(SendSignal(thread->id, signalNumber, flags)); + + // The caller transferred a reference to the signal to us. + BReference signalReference(signal, true); + + if ((flags & B_CHECK_PERMISSION) != 0) { + if (!has_permission_to_signal(thread->team, true)) + return EPERM; + } + + if (signalNumber == 0) return B_OK; if (thread->team == team_get_kernel_team()) { @@ -552,24 +1382,33 @@ deliver_signal(Thread *thread, uint signal, uint32 flags) return B_OK; } - atomic_or(&thread->sig_pending, SIGNAL_TO_MASK(signal)); + if (signal != NULL) + thread->AddPendingSignal(signal); + else + thread->AddPendingSignal(signalNumber); - switch (signal) { + // the thread has the signal reference, now + signalReference.Detach(); + + switch (signalNumber) { case SIGKILL: { - // Forward KILLTHR to the main thread of the team - Thread *mainThread = thread->team->main_thread; - atomic_or(&mainThread->sig_pending, SIGNAL_TO_MASK(SIGKILLTHR)); + // If sent to a thread other than the team's main thread, also send + // a SIGKILLTHR to the main thread to kill the team. + Thread* mainThread = thread->team->main_thread; + if (mainThread != NULL && mainThread != thread) { + mainThread->AddPendingSignal(SIGKILLTHR); - // Wake up main thread - if (mainThread->state == B_THREAD_SUSPENDED) - scheduler_enqueue_in_run_queue(mainThread); - else - thread_interrupt(mainThread, true); + // wake up main thread + if (mainThread->state == B_THREAD_SUSPENDED) + scheduler_enqueue_in_run_queue(mainThread); + else + thread_interrupt(mainThread, true); - update_thread_signals_flag(mainThread); + update_thread_signals_flag(mainThread); + } - // Supposed to fall through + // supposed to fall through } case SIGKILLTHR: // Wake up suspended threads and interrupt waiting ones @@ -579,21 +1418,32 @@ deliver_signal(Thread *thread, uint signal, uint32 flags) thread_interrupt(thread, true); break; - case SIGCONT: - // Wake up thread if it was suspended + case SIGNAL_CONTINUE_THREAD: + // wake up thread, and interrupt its current syscall if (thread->state == B_THREAD_SUSPENDED) scheduler_enqueue_in_run_queue(thread); - if ((flags & SIGNAL_FLAG_DONT_RESTART_SYSCALL) != 0) - atomic_or(&thread->flags, THREAD_FLAGS_DONT_RESTART_SYSCALL); + atomic_or(&thread->flags, THREAD_FLAGS_DONT_RESTART_SYSCALL); + break; - atomic_and(&thread->sig_pending, ~STOP_SIGNALS); - // remove any pending stop signals + case SIGCONT: + // Wake up thread if it was suspended, otherwise interrupt it, if + // the signal isn't blocked. + if (thread->state == B_THREAD_SUSPENDED) + scheduler_enqueue_in_run_queue(thread); + else if ((SIGNAL_TO_MASK(SIGCONT) & ~thread->sig_block_mask) != 0) + thread_interrupt(thread, false); + + // remove any pending stop signals + thread->RemovePendingSignals(STOP_SIGNALS); break; default: - if (thread->sig_pending - & (~thread->sig_block_mask | SIGNAL_TO_MASK(SIGCHLD))) { + // If the signal is not masked, interrupt the thread, if it is + // currently waiting (interruptibly). + if ((thread->AllPendingSignals() + & (~thread->sig_block_mask | SIGNAL_TO_MASK(SIGCHLD))) + != 0) { // Interrupt thread if it was waiting thread_interrupt(thread, false); } @@ -606,83 +1456,412 @@ deliver_signal(Thread *thread, uint signal, uint32 flags) } -int -send_signal_etc(pid_t id, uint signal, uint32 flags) +/*! Sends the given signal to the given thread. + + The caller must not hold the scheduler lock. + + \param thread The thread the signal shall be sent to. + \param signal The signal to be delivered. If the signal's number is \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + The given object will be copied. The caller retains ownership. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + - \c B_DO_NOT_RESCHEDULE: If clear and a higher level thread has been + woken up, the scheduler will be invoked. If set that will not be + done explicitly, but rescheduling can still happen, e.g. when the + current thread's time slice runs out. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_thread(Thread* thread, const Signal& signal, uint32 flags) { - status_t status = B_BAD_THREAD_ID; - Thread *thread; - cpu_status state = 0; + // Clone the signal -- the clone will be queued. If something fails and the + // caller doesn't require queuing, we will add an unqueued signal. + Signal* signalToQueue = NULL; + status_t error = Signal::CreateQueuable(signal, + (flags & SIGNAL_FLAG_QUEUING_REQUIRED) != 0, signalToQueue); + if (error != B_OK) + return error; - if (signal < 0 || signal > MAX_SIGNO) - return B_BAD_VALUE; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); - T(SendSignal(id, signal, flags)); + error = send_signal_to_thread_locked(thread, signal.Number(), signalToQueue, + flags); + if (error != B_OK) + return error; - if ((flags & SIGNAL_FLAG_TEAMS_LOCKED) == 0) - state = disable_interrupts(); - - if (id > 0) { - // send a signal to the specified thread - - GRAB_THREAD_LOCK(); - - thread = thread_get_thread_struct_locked(id); - if (thread != NULL) - status = deliver_signal(thread, signal, flags); - } else { - // send a signal to the specified process group - // (the absolute value of the id) - - struct process_group *group; - - // TODO: handle -1 correctly - if (id == 0 || id == -1) { - // send a signal to the current team - id = thread_get_current_thread()->team->id; - } else - id = -id; - - if ((flags & SIGNAL_FLAG_TEAMS_LOCKED) == 0) - GRAB_TEAM_LOCK(); - - group = team_get_process_group_locked(NULL, id); - if (group != NULL) { - Team *team, *next; - - // Send a signal to all teams in this process group - - for (team = group->teams; team != NULL; team = next) { - next = team->group_next; - id = team->id; - - GRAB_THREAD_LOCK(); - - thread = thread_get_thread_struct_locked(id); - if (thread != NULL) { - // we don't stop because of an error sending the signal; we - // rather want to send as much signals as possible - status = deliver_signal(thread, signal, flags); - } - - RELEASE_THREAD_LOCK(); - } - } - - if ((flags & SIGNAL_FLAG_TEAMS_LOCKED) == 0) - RELEASE_TEAM_LOCK(); - - GRAB_THREAD_LOCK(); - } - - if ((flags & (B_DO_NOT_RESCHEDULE | SIGNAL_FLAG_TEAMS_LOCKED)) == 0) + if ((flags & B_DO_NOT_RESCHEDULE) == 0) scheduler_reschedule_if_necessary_locked(); - RELEASE_THREAD_LOCK(); + return B_OK; +} - if ((flags & SIGNAL_FLAG_TEAMS_LOCKED) == 0) - restore_interrupts(state); - return status; +/*! Sends the given signal to the thread with the given ID. + + The caller must not hold the scheduler lock. + + \param threadID The ID of the thread the signal shall be sent to. + \param signal The signal to be delivered. If the signal's number is \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + The given object will be copied. The caller retains ownership. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + - \c B_DO_NOT_RESCHEDULE: If clear and a higher level thread has been + woken up, the scheduler will be invoked. If set that will not be + done explicitly, but rescheduling can still happen, e.g. when the + current thread's time slice runs out. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_thread_id(thread_id threadID, const Signal& signal, uint32 flags) +{ + Thread* thread = Thread::Get(threadID); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + + return send_signal_to_thread(thread, signal, flags); +} + + +/*! Sends the given signal to the given team. + + The caller must hold the scheduler lock. + + \param team The team the signal shall be sent to. + \param signalNumber The number of the signal to be delivered. If \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + \param signal If non-NULL the signal to be queued (has number + \a signalNumber in this case). The caller transfers an object reference + to this function. If \c NULL an unqueued signal will be delivered to the + thread. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + - \c B_DO_NOT_RESCHEDULE: If clear and a higher level thread has been + woken up, the scheduler will be invoked. If set that will not be + done explicitly, but rescheduling can still happen, e.g. when the + current thread's time slice runs out. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_team_locked(Team* team, uint32 signalNumber, Signal* signal, + uint32 flags) +{ + ASSERT(signal == NULL || signalNumber == signal->Number()); + + T(SendSignal(team->id, signalNumber, flags)); + + // The caller transferred a reference to the signal to us. + BReference signalReference(signal, true); + + if ((flags & B_CHECK_PERMISSION) != 0) { + if (!has_permission_to_signal(team, true)) + return EPERM; + } + + if (signalNumber == 0) + return B_OK; + + if (team == team_get_kernel_team()) { + // signals to the kernel team are not allowed + return EPERM; + } + + if (signal != NULL) + team->AddPendingSignal(signal); + else + team->AddPendingSignal(signalNumber); + + // the team has the signal reference, now + signalReference.Detach(); + + switch (signalNumber) { + case SIGKILL: + case SIGKILLTHR: + { + // Also add a SIGKILLTHR to the main thread's signals and wake it + // up/interrupt it, so we get this over with as soon as possible + // (only the main thread shuts down the team). + Thread* mainThread = team->main_thread; + if (mainThread != NULL) { + mainThread->AddPendingSignal(SIGKILLTHR); + + // wake up main thread + if (mainThread->state == B_THREAD_SUSPENDED) + scheduler_enqueue_in_run_queue(mainThread); + else + thread_interrupt(mainThread, true); + } + break; + } + + case SIGCONT: + // Wake up any suspended threads, interrupt the others, if they + // don't block the signal. + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + if (thread->state == B_THREAD_SUSPENDED) { + scheduler_enqueue_in_run_queue(thread); + } else if ((SIGNAL_TO_MASK(SIGCONT) & ~thread->sig_block_mask) + != 0) { + thread_interrupt(thread, false); + } + + // remove any pending stop signals + thread->RemovePendingSignals(STOP_SIGNALS); + } + + // remove any pending team stop signals + team->RemovePendingSignals(STOP_SIGNALS); + break; + + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + // send the stop signal to all threads + // TODO: Is that correct or should we only target the main thread? + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + thread->AddPendingSignal(signalNumber); + } + + // remove the stop signal from the team again + if (signal != NULL) { + team->RemovePendingSignal(signal); + signalReference.SetTo(signal, true); + } else + team->RemovePendingSignal(signalNumber); + + // fall through to interrupt threads + default: + // Interrupt all interruptibly waiting threads, if the signal is + // not masked. + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + sigset_t nonBlocked = ~thread->sig_block_mask + | SIGNAL_TO_MASK(SIGCHLD); + if ((thread->AllPendingSignals() & nonBlocked) != 0) + thread_interrupt(thread, false); + } + break; + } + + update_team_threads_signal_flag(team); + + if ((flags & B_DO_NOT_RESCHEDULE) == 0) + scheduler_reschedule_if_necessary_locked(); + + return B_OK; +} + + +/*! Sends the given signal to the given team. + + \param team The team the signal shall be sent to. + \param signal The signal to be delivered. If the signal's number is \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + The given object will be copied. The caller retains ownership. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + - \c B_DO_NOT_RESCHEDULE: If clear and a higher level thread has been + woken up, the scheduler will be invoked. If set that will not be + done explicitly, but rescheduling can still happen, e.g. when the + current thread's time slice runs out. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_team(Team* team, const Signal& signal, uint32 flags) +{ + // Clone the signal -- the clone will be queued. If something fails and the + // caller doesn't require queuing, we will add an unqueued signal. + Signal* signalToQueue = NULL; + status_t error = Signal::CreateQueuable(signal, + (flags & SIGNAL_FLAG_QUEUING_REQUIRED) != 0, signalToQueue); + if (error != B_OK) + return error; + + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + return send_signal_to_team_locked(team, signal.Number(), signalToQueue, + flags); +} + + +/*! Sends the given signal to the team with the given ID. + + \param teamID The ID of the team the signal shall be sent to. + \param signal The signal to be delivered. If the signal's number is \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + The given object will be copied. The caller retains ownership. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + - \c B_DO_NOT_RESCHEDULE: If clear and a higher level thread has been + woken up, the scheduler will be invoked. If set that will not be + done explicitly, but rescheduling can still happen, e.g. when the + current thread's time slice runs out. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_team_id(team_id teamID, const Signal& signal, uint32 flags) +{ + // get the team + Team* team = Team::Get(teamID); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); + + return send_signal_to_team(team, signal, flags); +} + + +/*! Sends the given signal to the given process group. + + The caller must hold the process group's lock. Interrupts must be enabled. + + \param group The the process group the signal shall be sent to. + \param signal The signal to be delivered. If the signal's number is \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + The given object will be copied. The caller retains ownership. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + - \c B_DO_NOT_RESCHEDULE: If clear and a higher level thread has been + woken up, the scheduler will be invoked. If set that will not be + done explicitly, but rescheduling can still happen, e.g. when the + current thread's time slice runs out. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_process_group_locked(ProcessGroup* group, const Signal& signal, + uint32 flags) +{ + T(SendSignal(-group->id, signal.Number(), flags)); + + bool firstTeam = true; + + for (Team* team = group->teams; team != NULL; team = team->group_next) { + status_t error = send_signal_to_team(team, signal, + flags | B_DO_NOT_RESCHEDULE); + // If sending to the first team in the group failed, let the whole call + // fail. + if (firstTeam) { + if (error != B_OK) + return error; + firstTeam = false; + } + } + + if ((flags & B_DO_NOT_RESCHEDULE) == 0) + scheduler_reschedule_if_necessary(); + + return B_OK; +} + + +/*! Sends the given signal to the process group specified by the given ID. + + The caller must not hold any process group, team, or thread lock. Interrupts + must be enabled. + + \param groupID The ID of the process group the signal shall be sent to. + \param signal The signal to be delivered. If the signal's number is \c 0, no + actual signal will be delivered. Only delivery checks will be performed. + The given object will be copied. The caller retains ownership. + \param flags A bitwise combination of any number of the following: + - \c B_CHECK_PERMISSION: Check the caller's permission to send the + target thread the signal. + - \c B_DO_NOT_RESCHEDULE: If clear and a higher level thread has been + woken up, the scheduler will be invoked. If set that will not be + done explicitly, but rescheduling can still happen, e.g. when the + current thread's time slice runs out. + \return \c B_OK, when the signal was delivered successfully, another error + code otherwise. +*/ +status_t +send_signal_to_process_group(pid_t groupID, const Signal& signal, uint32 flags) +{ + ProcessGroup* group = ProcessGroup::Get(groupID); + if (group == NULL) + return B_BAD_TEAM_ID; + BReference groupReference(group); + + T(SendSignal(-group->id, signal.Number(), flags)); + + AutoLocker groupLocker(group); + + status_t error = send_signal_to_process_group_locked(group, signal, + flags | B_DO_NOT_RESCHEDULE); + if (error != B_OK) + return error; + + groupLocker.Unlock(); + + if ((flags & B_DO_NOT_RESCHEDULE) == 0) + scheduler_reschedule_if_necessary(); + + return B_OK; +} + + +static status_t +send_signal_internal(pid_t id, uint signalNumber, union sigval userValue, + uint32 flags) +{ + if (signalNumber > MAX_SIGNAL_NUMBER) + return B_BAD_VALUE; + + Thread* thread = thread_get_current_thread(); + + Signal signal(signalNumber, + (flags & SIGNAL_FLAG_QUEUING_REQUIRED) != 0 ? SI_QUEUE : SI_USER, + B_OK, thread->team->id); + // Note: SI_USER/SI_QUEUE is not correct, if called from within the + // kernel (or a driver), but we don't have any info here. + signal.SetUserValue(userValue); + + // If id is > 0, send the signal to the respective thread. + if (id > 0) + return send_signal_to_thread_id(id, signal, flags); + + // If id == 0, send the signal to the current thread. + if (id == 0) + return send_signal_to_thread(thread, signal, flags); + + // If id == -1, send the signal to all teams the calling team has permission + // to send signals to. + if (id == -1) { + // TODO: Implement correctly! + // currently only send to the current team + return send_signal_to_team_id(thread->team->id, signal, flags); + } + + // Send a signal to the specified process group (the absolute value of the + // id). + return send_signal_to_process_group(-id, signal, flags); +} + + +int +send_signal_etc(pid_t id, uint signalNumber, uint32 flags) +{ + // a dummy user value + union sigval userValue; + userValue.sival_ptr = NULL; + + return send_signal_internal(id, signalNumber, userValue, flags); } @@ -695,36 +1874,27 @@ send_signal(pid_t threadID, uint signal) } -int -has_signals_pending(void *_thread) -{ - Thread *thread = (Thread *)_thread; - if (thread == NULL) - thread = thread_get_current_thread(); - - return atomic_get(&thread->sig_pending) - & ~atomic_get(&thread->sig_block_mask); -} - - static int -sigprocmask_internal(int how, const sigset_t *set, sigset_t *oldSet) +sigprocmask_internal(int how, const sigset_t* set, sigset_t* oldSet) { - Thread *thread = thread_get_current_thread(); - sigset_t oldMask = atomic_get(&thread->sig_block_mask); + Thread* thread = thread_get_current_thread(); + + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + sigset_t oldMask = thread->sig_block_mask; if (set != NULL) { T(SigProcMask(how, *set)); switch (how) { case SIG_BLOCK: - atomic_or(&thread->sig_block_mask, *set & BLOCKABLE_SIGNALS); + thread->sig_block_mask |= *set & BLOCKABLE_SIGNALS; break; case SIG_UNBLOCK: - atomic_and(&thread->sig_block_mask, ~*set); + thread->sig_block_mask &= ~*set; break; case SIG_SETMASK: - atomic_set(&thread->sig_block_mask, *set & BLOCKABLE_SIGNALS); + thread->sig_block_mask = *set & BLOCKABLE_SIGNALS; break; default: return B_BAD_VALUE; @@ -741,161 +1911,126 @@ sigprocmask_internal(int how, const sigset_t *set, sigset_t *oldSet) int -sigprocmask(int how, const sigset_t *set, sigset_t *oldSet) +sigprocmask(int how, const sigset_t* set, sigset_t* oldSet) { RETURN_AND_SET_ERRNO(sigprocmask_internal(how, set, oldSet)); } -/*! \brief sigaction() for the specified thread. - A \a threadID is < 0 specifies the current thread. +/*! \brief Like sigaction(), but returning the error instead of setting errno. */ static status_t -sigaction_etc_internal(thread_id threadID, int signal, const struct sigaction *act, - struct sigaction *oldAction) +sigaction_internal(int signal, const struct sigaction* act, + struct sigaction* oldAction) { - Thread *thread; - cpu_status state; - status_t error = B_OK; - - if (signal < 1 || signal > MAX_SIGNO + if (signal < 1 || signal > MAX_SIGNAL_NUMBER || (SIGNAL_TO_MASK(signal) & ~BLOCKABLE_SIGNALS) != 0) return B_BAD_VALUE; - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // get and lock the team + Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); - thread = (threadID < 0 - ? thread_get_current_thread() - : thread_get_thread_struct_locked(threadID)); - - if (thread) { - if (oldAction) { - // save previous sigaction structure - memcpy(oldAction, &thread->sig_action[signal - 1], - sizeof(struct sigaction)); - } - - if (act) { - T(SigAction(thread, signal, act)); - - // set new sigaction structure - memcpy(&thread->sig_action[signal - 1], act, - sizeof(struct sigaction)); - thread->sig_action[signal - 1].sa_mask &= BLOCKABLE_SIGNALS; - } - - if (act && act->sa_handler == SIG_IGN) { - // remove pending signal if it should now be ignored - atomic_and(&thread->sig_pending, ~SIGNAL_TO_MASK(signal)); - } else if (act && act->sa_handler == SIG_DFL - && (SIGNAL_TO_MASK(signal) & DEFAULT_IGNORE_SIGNALS) != 0) { - // remove pending signal for those signals whose default - // action is to ignore them - atomic_and(&thread->sig_pending, ~SIGNAL_TO_MASK(signal)); - } - } else - error = B_BAD_THREAD_ID; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - return error; -} - - -int -sigaction_etc(thread_id threadID, int signal, const struct sigaction *act, - struct sigaction *oldAction) -{ - RETURN_AND_SET_ERRNO(sigaction_etc_internal(threadID, signal, act, - oldAction)); -} - - -int -sigaction(int signal, const struct sigaction *act, struct sigaction *oldAction) -{ - return sigaction_etc(-1, signal, act, oldAction); -} - - -/*! Triggers a SIGALRM to the thread that issued the timer and reschedules */ -static int32 -alarm_event(timer *t) -{ - // The hook can be called from any context, but we have to - // deliver the signal to the thread that originally called - // set_alarm(). - // Since thread->alarm is this timer structure, we can just - // cast it back - ugly but it works for now - Thread *thread = (Thread *)((uint8 *)t - offsetof(Thread, alarm)); - // ToDo: investigate adding one user parameter to the timer structure to fix this hack - - TRACE(("alarm_event: thread = %p\n", thread)); - send_signal_etc(thread->id, SIGALRM, B_DO_NOT_RESCHEDULE); - - return B_HANDLED_INTERRUPT; -} - - -/*! Sets the alarm timer for the current thread. The timer fires at the - specified time in the future, periodically or just once, as determined - by \a mode. - \return the time left until a previous set alarm would have fired. -*/ -bigtime_t -set_alarm(bigtime_t time, uint32 mode) -{ - Thread *thread = thread_get_current_thread(); - bigtime_t remainingTime = 0; - - ASSERT(B_ONE_SHOT_RELATIVE_ALARM == B_ONE_SHOT_RELATIVE_TIMER); - // just to be sure no one changes the headers some day - - TRACE(("set_alarm: thread = %p\n", thread)); - - if (thread->alarm.period) - remainingTime = (bigtime_t)thread->alarm.schedule_time - system_time(); - - cancel_timer(&thread->alarm); - - if (time != B_INFINITE_TIMEOUT) - add_timer(&thread->alarm, &alarm_event, time, mode); - else { - // this marks the alarm as canceled (for returning the remaining time) - thread->alarm.period = 0; + struct sigaction& teamHandler = team->SignalActionFor(signal); + if (oldAction) { + // save previous sigaction structure + *oldAction = teamHandler; } - return remainingTime; + if (act) { + T(SigAction(signal, act)); + + // set new sigaction structure + teamHandler = *act; + teamHandler.sa_mask &= BLOCKABLE_SIGNALS; + } + + // Remove pending signal if it should now be ignored and remove pending + // signal for those signals whose default action is to ignore them. + if ((act && act->sa_handler == SIG_IGN) + || (act && act->sa_handler == SIG_DFL + && (SIGNAL_TO_MASK(signal) & DEFAULT_IGNORE_SIGNALS) != 0)) { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + team->RemovePendingSignal(signal); + + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + thread->RemovePendingSignal(signal); + } + } + + return B_OK; } -/*! Wait for the specified signals, and return the signal retrieved in - \a _signal. +int +sigaction(int signal, const struct sigaction* act, struct sigaction* oldAction) +{ + RETURN_AND_SET_ERRNO(sigaction_internal(signal, act, oldAction)); +} + + +/*! Wait for the specified signals, and return the information for the retrieved + signal in \a info. + The \c flags and \c timeout combination must either define an infinite + timeout (no timeout flags set), an absolute timeout (\c B_ABSOLUTE_TIMEOUT + set), or a relative timeout \code <= 0 \endcode (\c B_RELATIVE_TIMEOUT set). */ static status_t -sigwait_internal(const sigset_t *set, int *_signal) +sigwait_internal(const sigset_t* set, siginfo_t* info, uint32 flags, + bigtime_t timeout) { + // restrict mask to blockable signals sigset_t requestedSignals = *set & BLOCKABLE_SIGNALS; + // make always interruptable + flags |= B_CAN_INTERRUPT; + + // check whether we are allowed to wait at all + bool canWait = (flags & B_RELATIVE_TIMEOUT) == 0 || timeout > 0; + Thread* thread = thread_get_current_thread(); - while (true) { - sigset_t pendingSignals = atomic_get(&thread->sig_pending); - sigset_t blockedSignals = atomic_get(&thread->sig_block_mask); - sigset_t pendingRequestedSignals = pendingSignals & requestedSignals; - if ((pendingRequestedSignals) != 0) { - // select the lowest pending signal to return in _signal - for (int signal = 1; signal < NSIG; signal++) { - if ((SIGNAL_TO_MASK(signal) & pendingSignals) != 0) { - atomic_and(&thread->sig_pending, ~SIGNAL_TO_MASK(signal)); - *_signal = signal; - return B_OK; - } - } + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + bool timedOut = false; + status_t error = B_OK; + + while (!timedOut) { + sigset_t pendingSignals = thread->AllPendingSignals(); + + // If a kill signal is pending, just bail out. + if ((pendingSignals & KILL_SIGNALS) != 0) + return B_INTERRUPTED; + + if ((pendingSignals & requestedSignals) != 0) { + // get signal with the highest priority + Signal stackSignal; + Signal* signal = dequeue_thread_or_team_signal(thread, + requestedSignals, stackSignal); + ASSERT(signal != NULL); + + SignalHandledCaller signalHandledCaller(signal); + schedulerLocker.Unlock(); + + info->si_signo = signal->Number(); + info->si_code = signal->SignalCode(); + info->si_errno = signal->ErrorCode(); + info->si_pid = signal->SendingProcess(); + info->si_uid = signal->SendingUser(); + info->si_addr = signal->Address(); + info->si_status = signal->Status(); + info->si_band = signal->PollBand(); + info->si_value = signal->UserValue(); + + return B_OK; } + if (!canWait) + return B_WOULD_BLOCK; + + sigset_t blockedSignals = thread->sig_block_mask; if ((pendingSignals & ~blockedSignals) != 0) { // Non-blocked signals are pending -- return to let them be handled. return B_INTERRUPTED; @@ -903,27 +2038,32 @@ sigwait_internal(const sigset_t *set, int *_signal) // No signals yet. Set the signal block mask to not include the // requested mask and wait until we're interrupted. - atomic_set(&thread->sig_block_mask, - blockedSignals & ~(requestedSignals & BLOCKABLE_SIGNALS)); + thread->sig_block_mask = blockedSignals & ~requestedSignals; while (!has_signals_pending(thread)) { - thread_prepare_to_block(thread, B_CAN_INTERRUPT, - THREAD_BLOCK_TYPE_SIGNAL, NULL); - thread_block(); + thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SIGNAL, + NULL); + + if ((flags & B_ABSOLUTE_TIMEOUT) != 0) { + error = thread_block_with_timeout_locked(flags, timeout); + if (error == B_WOULD_BLOCK || error == B_TIMED_OUT) { + error = B_WOULD_BLOCK; + // POSIX requires EAGAIN (B_WOULD_BLOCK) on timeout + timedOut = true; + break; + } + } else + thread_block_locked(thread); } // restore the original block mask - atomic_set(&thread->sig_block_mask, blockedSignals); + thread->sig_block_mask = blockedSignals; update_current_thread_signals_flag(); } -} - -int -sigwait(const sigset_t *set, int *_signal) -{ - RETURN_AND_SET_ERRNO(sigwait_internal(set, _signal)); + // we get here only when timed out + return error; } @@ -931,29 +2071,37 @@ sigwait(const sigset_t *set, int *_signal) Before returning, the original signal block mask is reinstantiated. */ static status_t -sigsuspend_internal(const sigset_t *mask) +sigsuspend_internal(const sigset_t* _mask) { - T(SigSuspend(*mask)); + sigset_t mask = *_mask & BLOCKABLE_SIGNALS; - Thread *thread = thread_get_current_thread(); - sigset_t oldMask = atomic_get(&thread->sig_block_mask); + T(SigSuspend(mask)); - // Set the new block mask and block until interrupted. + Thread* thread = thread_get_current_thread(); - atomic_set(&thread->sig_block_mask, *mask & BLOCKABLE_SIGNALS); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + // Set the new block mask and block until interrupted. We might be here + // after a syscall restart, in which case sigsuspend_original_unblocked_mask + // will still be set. + sigset_t oldMask = thread->sigsuspend_original_unblocked_mask != 0 + ? ~thread->sigsuspend_original_unblocked_mask : thread->sig_block_mask; + thread->sig_block_mask = mask & BLOCKABLE_SIGNALS; + + update_current_thread_signals_flag(); while (!has_signals_pending(thread)) { thread_prepare_to_block(thread, B_CAN_INTERRUPT, THREAD_BLOCK_TYPE_SIGNAL, NULL); - thread_block(); + thread_block_locked(thread); } - // restore the original block mask - atomic_set(&thread->sig_block_mask, oldMask); - - thread->sig_temp_enabled = ~*mask; - - update_current_thread_signals_flag(); + // Set sigsuspend_original_unblocked_mask (guaranteed to be non-0 due to + // BLOCKABLE_SIGNALS). This will indicate to handle_signals() that it is + // called after a _user_sigsuspend(). It will reset the field after invoking + // a signal handler, or restart the syscall, if there wasn't anything to + // handle anymore (e.g. because another thread was faster). + thread->sigsuspend_original_unblocked_mask = ~oldMask; T(SigSuspendDone()); @@ -962,54 +2110,91 @@ sigsuspend_internal(const sigset_t *mask) } -int -sigsuspend(const sigset_t *mask) -{ - RETURN_AND_SET_ERRNO(sigsuspend_internal(mask)); -} - - static status_t -sigpending_internal(sigset_t *set) +sigpending_internal(sigset_t* set) { - Thread *thread = thread_get_current_thread(); + Thread* thread = thread_get_current_thread(); if (set == NULL) return B_BAD_VALUE; - *set = atomic_get(&thread->sig_pending); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + *set = thread->AllPendingSignals() & thread->sig_block_mask; + return B_OK; } -int -sigpending(sigset_t *set) -{ - RETURN_AND_SET_ERRNO(sigpending_internal(set)); -} - - // #pragma mark - syscalls -bigtime_t -_user_set_alarm(bigtime_t time, uint32 mode) +/*! Sends a signal to a thread, process, or process group. + \param id Specifies the ID of the target: + - \code id > 0 \endcode: If \a toThread is \c true, the target is the + thread with ID \a id, otherwise the team with the ID \a id. + - \code id == 0 \endcode: If toThread is \c true, the target is the + current thread, otherwise the current team. + - \code id == -1 \endcode: The target are all teams the current team has + permission to send signals to. Currently not implemented correctly. + - \code id < -1 \endcode: The target are is the process group with ID + \c -id. + \param signalNumber The signal number. \c 0 to just perform checks, but not + actually send any signal. + \param userUserValue A user value to be associated with the signal. Might be + ignored unless signal queuing is forced. Can be \c NULL. + \param flags A bitwise or of any number of the following: + - \c SIGNAL_FLAG_QUEUING_REQUIRED: Signal queuing is required. Fail + instead of falling back to unqueued signals, when queuing isn't + possible. + - \c SIGNAL_FLAG_SEND_TO_THREAD: Interpret the the given ID as a + \c thread_id rather than a \c team_id. Ignored when the \a id is + \code < 0 \endcode -- then the target is a process group. + \return \c B_OK on success, another error code otherwise. +*/ +status_t +_user_send_signal(int32 id, uint32 signalNumber, + const union sigval* userUserValue, uint32 flags) { - syscall_64_bit_return_value(); + // restrict flags to the allowed ones and add B_CHECK_PERMISSION + flags &= SIGNAL_FLAG_QUEUING_REQUIRED | SIGNAL_FLAG_SEND_TO_THREAD; + flags |= B_CHECK_PERMISSION; - return set_alarm(time, mode); + // Copy the user value from userland. If not given, use a dummy value. + union sigval userValue; + if (userUserValue != NULL) { + if (!IS_USER_ADDRESS(userUserValue) + || user_memcpy(&userValue, userUserValue, sizeof(userValue)) + != B_OK) { + return B_BAD_ADDRESS; + } + } else + userValue.sival_ptr = NULL; + + // If to be sent to a thread, delegate to send_signal_internal(). Also do + // that when id < 0, since in this case the semantics is the same as well. + if ((flags & SIGNAL_FLAG_SEND_TO_THREAD) != 0 || id < 0) + return send_signal_internal(id, signalNumber, userValue, flags); + + // kill() semantics for id >= 0 + if (signalNumber > MAX_SIGNAL_NUMBER) + return B_BAD_VALUE; + + Thread* thread = thread_get_current_thread(); + + Signal signal(signalNumber, + (flags & SIGNAL_FLAG_QUEUING_REQUIRED) != 0 ? SI_QUEUE : SI_USER, + B_OK, thread->team->id); + signal.SetUserValue(userValue); + + // send to current team for id == 0, otherwise to the respective team + return send_signal_to_team_id(id == 0 ? team_get_current_team_id() : id, + signal, flags); } status_t -_user_send_signal(pid_t team, uint signal) -{ - return send_signal_etc(team, signal, B_CHECK_PERMISSION); -} - - -status_t -_user_sigprocmask(int how, const sigset_t *userSet, sigset_t *userOldSet) +_user_set_signal_mask(int how, const sigset_t *userSet, sigset_t *userOldSet) { sigset_t set, oldSet; status_t status; @@ -1044,7 +2229,7 @@ _user_sigaction(int signal, const struct sigaction *userAction, sizeof(struct sigaction)) < B_OK)) return B_BAD_ADDRESS; - status = sigaction(signal, userAction ? &act : NULL, + status = sigaction_internal(signal, userAction ? &act : NULL, userOldAction ? &oact : NULL); // only copy the old action if a pointer has been given @@ -1057,26 +2242,37 @@ _user_sigaction(int signal, const struct sigaction *userAction, status_t -_user_sigwait(const sigset_t *userSet, int *_userSignal) +_user_sigwait(const sigset_t *userSet, siginfo_t *userInfo, uint32 flags, + bigtime_t timeout) { - if (userSet == NULL || _userSignal == NULL) - return B_BAD_VALUE; - + // copy userSet to stack sigset_t set; - if (user_memcpy(&set, userSet, sizeof(sigset_t)) < B_OK) + if (userSet == NULL || !IS_USER_ADDRESS(userSet) + || user_memcpy(&set, userSet, sizeof(sigset_t)) != B_OK) { return B_BAD_ADDRESS; - - int signal; - status_t status = sigwait_internal(&set, &signal); - if (status == B_INTERRUPTED) { - // make sure we'll be restarted - Thread* thread = thread_get_current_thread(); - atomic_or(&thread->flags, - THREAD_FLAGS_ALWAYS_RESTART_SYSCALL | THREAD_FLAGS_RESTART_SYSCALL); - return status; } - return user_memcpy(_userSignal, &signal, sizeof(int)); + // userInfo is optional, but must be a user address when given + if (userInfo != NULL && !IS_USER_ADDRESS(userInfo)) + return B_BAD_ADDRESS; + + syscall_restart_handle_timeout_pre(flags, timeout); + + flags |= B_CAN_INTERRUPT; + + siginfo_t info; + status_t status = sigwait_internal(&set, &info, flags, timeout); + if (status == B_OK) { + // copy the info back to userland, if userSet is non-NULL + if (userInfo != NULL) + status = user_memcpy(userInfo, &info, sizeof(info)); + } else if (status == B_INTERRUPTED) { + // make sure we'll be restarted + Thread* thread = thread_get_current_thread(); + atomic_or(&thread->flags, THREAD_FLAGS_ALWAYS_RESTART_SYSCALL); + } + + return syscall_restart_handle_timeout_post(status, timeout); } @@ -1115,7 +2311,7 @@ _user_sigpending(sigset_t *userSet) status_t -_user_set_signal_stack(const stack_t *newUserStack, stack_t *oldUserStack) +_user_set_signal_stack(const stack_t* newUserStack, stack_t* oldUserStack) { Thread *thread = thread_get_current_thread(); struct stack_t newStack, oldStack; @@ -1169,3 +2365,76 @@ _user_set_signal_stack(const stack_t *newUserStack, stack_t *oldUserStack) return B_OK; } + +/*! Restores the environment of a function that was interrupted by a signal + handler call. + This syscall is invoked when a signal handler function returns. It + deconstructs the signal handler frame and restores the stack and register + state of the function that was interrupted by a signal. The syscall is + therefore somewhat unusual, since it does not return to the calling + function, but to someplace else. In case the signal interrupted a syscall, + it will appear as if the syscall just returned. That is also the reason, why + this syscall returns an int64, since it needs to return the value the + interrupted syscall returns, which is potentially 64 bits wide. + + \param userSignalFrameData The signal frame data created for the signal + handler. Potentially some data (e.g. registers) have been modified by + the signal handler. + \return In case the signal interrupted a syscall, the return value of that + syscall. Otherwise (i.e. in case of a (hardware) interrupt/exception) + the value might need to be tailored such that after a return to userland + the interrupted environment is identical to the interrupted one (unless + explicitly modified). E.g. for x86 to achieve that, the return value + must contain the eax|edx values of the interrupted environment. +*/ +int64 +_user_restore_signal_frame(struct signal_frame_data* userSignalFrameData) +{ + syscall_64_bit_return_value(); + + Thread *thread = thread_get_current_thread(); + + // copy the signal frame data from userland + signal_frame_data signalFrameData; + if (userSignalFrameData == NULL || !IS_USER_ADDRESS(userSignalFrameData) + || user_memcpy(&signalFrameData, userSignalFrameData, + sizeof(signalFrameData)) != B_OK) { + // We failed to copy the signal frame data from userland. This is a + // serious problem. Kill the thread. + dprintf("_user_restore_signal_frame(): thread %" B_PRId32 ": Failed to " + "copy signal frame data (%p) from userland. Killing thread...\n", + thread->id, userSignalFrameData); + kill_thread(thread->id); + return B_BAD_ADDRESS; + } + + // restore the signal block mask + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + thread->sig_block_mask + = signalFrameData.context.uc_sigmask & BLOCKABLE_SIGNALS; + update_current_thread_signals_flag(); + + schedulerLocker.Unlock(); + + // restore the syscall restart related thread flags and the syscall restart + // parameters + atomic_and(&thread->flags, + ~(THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_64_BIT_SYSCALL_RETURN)); + atomic_or(&thread->flags, signalFrameData.thread_flags + & (THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_64_BIT_SYSCALL_RETURN)); + + memcpy(thread->syscall_restart.parameters, + signalFrameData.syscall_restart_parameters, + sizeof(thread->syscall_restart.parameters)); + + // restore the previously stored Thread::user_signal_context + thread->user_signal_context = signalFrameData.context.uc_link; + if (thread->user_signal_context != NULL + && !IS_USER_ADDRESS(thread->user_signal_context)) { + thread->user_signal_context = NULL; + } + + // let the architecture specific code restore the registers + return arch_restore_signal_frame(&signalFrameData); +} diff --git a/src/system/kernel/syscalls.cpp b/src/system/kernel/syscalls.cpp index ac9da08b18..2085db4ee3 100644 --- a/src/system/kernel/syscalls.cpp +++ b/src/system/kernel/syscalls.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -181,16 +182,6 @@ _user_is_computer_on(void) } -//! Map to the arch specific call -static inline int64 -_user_restore_signal_frame() -{ - syscall_64_bit_return_value(); - - return arch_restore_signal_frame(); -} - - // #pragma mark - diff --git a/src/system/kernel/team.cpp b/src/system/kernel/team.cpp index 4f8360f701..e2a1f29d39 100644 --- a/src/system/kernel/team.cpp +++ b/src/system/kernel/team.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -52,7 +53,9 @@ #include #include #include -#include + +#include "TeamThreadTables.h" + //#define TRACE_TEAM #ifdef TRACE_TEAM @@ -77,21 +80,9 @@ struct team_arg { uint32 error_token; }; -struct fork_arg { - area_id user_stack_area; - addr_t user_stack_base; - size_t user_stack_size; - addr_t user_local_storage; - sigset_t sig_block_mask; - struct sigaction sig_action[32]; - addr_t signal_stack_base; - size_t signal_stack_size; - bool signal_stack_enabled; - struct user_thread* user_thread; +namespace { - struct arch_fork_arg arch_info; -}; class TeamNotificationService : public DefaultNotificationService { public: @@ -101,46 +92,103 @@ public: }; -struct TeamHashDefinition { - typedef team_id KeyType; - typedef Team ValueType; +// #pragma mark - TeamTable - size_t HashKey(team_id key) const + +typedef BKernel::TeamThreadTable TeamTable; + + +// #pragma mark - ProcessGroupHashDefinition + + +struct ProcessGroupHashDefinition { + typedef pid_t KeyType; + typedef ProcessGroup ValueType; + + size_t HashKey(pid_t key) const { return key; } - size_t Hash(Team* value) const + size_t Hash(ProcessGroup* value) const { return HashKey(value->id); } - bool Compare(team_id key, Team* value) const + bool Compare(pid_t key, ProcessGroup* value) const { return value->id == key; } - Team*& GetLink(Team* value) const + ProcessGroup*& GetLink(ProcessGroup* value) const { return value->next; } }; -typedef BOpenHashTable TeamHashTable; +typedef BOpenHashTable ProcessGroupHashTable; -static TeamHashTable sTeamHash; -static hash_table* sGroupHash = NULL; +} // unnamed namespace + + +// #pragma mark - + + +// the team_id -> Team hash table and the lock protecting it +static TeamTable sTeamHash; +static spinlock sTeamHashLock = B_SPINLOCK_INITIALIZER; + +// the pid_t -> ProcessGroup hash table and the lock protecting it +static ProcessGroupHashTable sGroupHash; +static spinlock sGroupHashLock = B_SPINLOCK_INITIALIZER; + static Team* sKernelTeam = NULL; -// some arbitrary chosen limits - should probably depend on the available +// A list of process groups of children of dying session leaders that need to +// be signalled, if they have become orphaned and contain stopped processes. +static ProcessGroupList sOrphanedCheckProcessGroups; +static mutex sOrphanedCheckLock + = MUTEX_INITIALIZER("orphaned process group check"); + +// some arbitrarily chosen limits -- should probably depend on the available // memory (the limit is not yet enforced) static int32 sMaxTeams = 2048; static int32 sUsedTeams = 1; static TeamNotificationService sNotificationService; -spinlock gTeamSpinlock = B_SPINLOCK_INITIALIZER; + +// #pragma mark - TeamListIterator + + +TeamListIterator::TeamListIterator() +{ + // queue the entry + InterruptsSpinLocker locker(sTeamHashLock); + sTeamHash.InsertIteratorEntry(&fEntry); +} + + +TeamListIterator::~TeamListIterator() +{ + // remove the entry + InterruptsSpinLocker locker(sTeamHashLock); + sTeamHash.RemoveIteratorEntry(&fEntry); +} + + +Team* +TeamListIterator::Next() +{ + // get the next team -- if there is one, get reference for it + InterruptsSpinLocker locker(sTeamHashLock); + Team* team = sTeamHash.NextElement(&fEntry); + if (team != NULL) + team->AcquireReference(); + + return team; +} // #pragma mark - Tracing @@ -243,11 +291,11 @@ job_control_state_name(job_control_state state) class SetJobControlState : public AbstractTraceEntry { public: - SetJobControlState(team_id team, job_control_state newState, int signal) + SetJobControlState(team_id team, job_control_state newState, Signal* signal) : fTeam(team), fNewState(newState), - fSignal(signal) + fSignal(signal != NULL ? signal->Number() : 0) { Initialized(); } @@ -360,7 +408,706 @@ TeamNotificationService::Notify(uint32 eventCode, Team* team) } -// #pragma mark - Private functions +// #pragma mark - Team + + +Team::Team(team_id id, bool kernel) +{ + // allocate an ID + this->id = id; + visible = true; + serial_number = -1; + + // init mutex + if (kernel) { + mutex_init(&fLock, "Team:kernel"); + } else { + char lockName[16]; + snprintf(lockName, sizeof(lockName), "Team:%" B_PRId32, id); + mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); + } + + hash_next = siblings_next = children = parent = NULL; + fName[0] = '\0'; + fArgs[0] = '\0'; + num_threads = 0; + io_context = NULL; + address_space = NULL; + realtime_sem_context = NULL; + xsi_sem_context = NULL; + thread_list = NULL; + main_thread = NULL; + loading_info = NULL; + state = TEAM_STATE_BIRTH; + flags = 0; + death_entry = NULL; + user_data_area = -1; + user_data = 0; + used_user_data = 0; + user_data_size = 0; + free_user_threads = NULL; + + supplementary_groups = NULL; + supplementary_group_count = 0; + + dead_threads_kernel_time = 0; + dead_threads_user_time = 0; + cpu_clock_offset = 0; + + // dead threads + list_init(&dead_threads); + dead_threads_count = 0; + + // dead children + dead_children.count = 0; + dead_children.kernel_time = 0; + dead_children.user_time = 0; + + // job control entry + job_control_entry = new(nothrow) ::job_control_entry; + if (job_control_entry != NULL) { + job_control_entry->state = JOB_CONTROL_STATE_NONE; + job_control_entry->thread = id; + job_control_entry->team = this; + } + + // exit status -- setting initialized to false suffices + exit.initialized = false; + + list_init(&sem_list); + list_init(&port_list); + list_init(&image_list); + list_init(&watcher_list); + + clear_team_debug_info(&debug_info, true); + + // init dead/stopped/continued children condition vars + dead_children.condition_variable.Init(&dead_children, "team children"); + + fQueuedSignalsCounter = new(std::nothrow) BKernel::QueuedSignalsCounter( + kernel ? -1 : MAX_QUEUED_SIGNALS); + memset(fSignalActions, 0, sizeof(fSignalActions)); + + fUserDefinedTimerCount = 0; +} + + +Team::~Team() +{ + // get rid of all associated data + PrepareForDeletion(); + + vfs_put_io_context(io_context); + delete_owned_ports(this); + sem_delete_owned_sems(this); + + DeleteUserTimers(false); + + fPendingSignals.Clear(); + + if (fQueuedSignalsCounter != NULL) + fQueuedSignalsCounter->ReleaseReference(); + + while (thread_death_entry* threadDeathEntry + = (thread_death_entry*)list_remove_head_item(&dead_threads)) { + free(threadDeathEntry); + } + + while (::job_control_entry* entry = dead_children.entries.RemoveHead()) + delete entry; + + while (free_user_thread* entry = free_user_threads) { + free_user_threads = entry->next; + free(entry); + } + + malloc_referenced_release(supplementary_groups); + + delete job_control_entry; + // usually already NULL and transferred to the parent + + mutex_destroy(&fLock); +} + + +/*static*/ Team* +Team::Create(team_id id, const char* name, bool kernel) +{ + // create the team object + Team* team = new(std::nothrow) Team(id, kernel); + if (team == NULL) + return NULL; + ObjectDeleter teamDeleter(team); + + if (name != NULL) + team->SetName(name); + + // check initialization + if (team->job_control_entry == NULL || team->fQueuedSignalsCounter == NULL) + return NULL; + + // finish initialization (arch specifics) + if (arch_team_init_team_struct(team, kernel) != B_OK) + return NULL; + + if (!kernel) { + status_t error = user_timer_create_team_timers(team); + if (error != B_OK) + return NULL; + } + + // everything went fine + return teamDeleter.Detach(); +} + + +/*! \brief Returns the team with the given ID. + Returns a reference to the team. + Team and thread spinlock must not be held. +*/ +/*static*/ Team* +Team::Get(team_id id) +{ + if (id == B_CURRENT_TEAM) { + Team* team = thread_get_current_thread()->team; + team->AcquireReference(); + return team; + } + + InterruptsSpinLocker locker(sTeamHashLock); + Team* team = sTeamHash.Lookup(id); + if (team != NULL) + team->AcquireReference(); + return team; +} + + +/*! \brief Returns the team with the given ID in a locked state. + Returns a reference to the team. + Team and thread spinlock must not be held. +*/ +/*static*/ Team* +Team::GetAndLock(team_id id) +{ + // get the team + Team* team = Get(id); + if (team == NULL) + return NULL; + + // lock it + team->Lock(); + + // only return the team, when it isn't already dying + if (team->state >= TEAM_STATE_SHUTDOWN) { + team->Unlock(); + team->ReleaseReference(); + return NULL; + } + + return team; +} + + +/*! Locks the team and its parent team (if any). + The caller must hold a reference to the team or otherwise make sure that + it won't be deleted. + If the team doesn't have a parent, only the team itself is locked. If the + team's parent is the kernel team and \a dontLockParentIfKernel is \c true, + only the team itself is locked. + + \param dontLockParentIfKernel If \c true, the team's parent team is only + locked, if it is not the kernel team. +*/ +void +Team::LockTeamAndParent(bool dontLockParentIfKernel) +{ + // The locking order is parent -> child. Since the parent can change as long + // as we don't lock the team, we need to do a trial and error loop. + Lock(); + + while (true) { + // If the team doesn't have a parent, we're done. Otherwise try to lock + // the parent.This will succeed in most cases, simplifying things. + Team* parent = this->parent; + if (parent == NULL || (dontLockParentIfKernel && parent == sKernelTeam) + || parent->TryLock()) { + return; + } + + // get a temporary reference to the parent, unlock this team, lock the + // parent, and re-lock this team + BReference parentReference(parent); + + Unlock(); + parent->Lock(); + Lock(); + + // If the parent hasn't changed in the meantime, we're done. + if (this->parent == parent) + return; + + // The parent has changed -- unlock and retry. + parent->Unlock(); + } +} + + +/*! Unlocks the team and its parent team (if any). +*/ +void +Team::UnlockTeamAndParent() +{ + if (parent != NULL) + parent->Unlock(); + + Unlock(); +} + + +/*! Locks the team, its parent team (if any), and the team's process group. + The caller must hold a reference to the team or otherwise make sure that + it won't be deleted. + If the team doesn't have a parent, only the team itself is locked. +*/ +void +Team::LockTeamParentAndProcessGroup() +{ + LockTeamAndProcessGroup(); + + // We hold the group's and the team's lock, but not the parent team's lock. + // If we have a parent, try to lock it. + if (this->parent == NULL || this->parent->TryLock()) + return; + + // No success -- unlock the team and let LockTeamAndParent() do the rest of + // the job. + Unlock(); + LockTeamAndParent(false); +} + + +/*! Unlocks the team, its parent team (if any), and the team's process group. +*/ +void +Team::UnlockTeamParentAndProcessGroup() +{ + group->Unlock(); + + if (parent != NULL) + parent->Unlock(); + + Unlock(); +} + + +void +Team::LockTeamAndProcessGroup() +{ + // The locking order is process group -> child. Since the process group can + // change as long as we don't lock the team, we need to do a trial and error + // loop. + Lock(); + + while (true) { + // Try to lock the group. This will succeed in most cases, simplifying + // things. + ProcessGroup* group = this->group; + if (group->TryLock()) + return; + + // get a temporary reference to the group, unlock this team, lock the + // group, and re-lock this team + BReference groupReference(group); + + Unlock(); + group->Lock(); + Lock(); + + // If the group hasn't changed in the meantime, we're done. + if (this->group == group) + return; + + // The group has changed -- unlock and retry. + group->Unlock(); + } +} + + +void +Team::UnlockTeamAndProcessGroup() +{ + group->Unlock(); + Unlock(); +} + + +void +Team::SetName(const char* name) +{ + if (const char* lastSlash = strrchr(name, '/')) + name = lastSlash + 1; + + strlcpy(fName, name, B_OS_NAME_LENGTH); +} + + +void +Team::SetArgs(const char* args) +{ + strlcpy(fArgs, args, sizeof(fArgs)); +} + + +void +Team::SetArgs(const char* path, const char* const* otherArgs, int otherArgCount) +{ + fArgs[0] = '\0'; + strlcpy(fArgs, path, sizeof(fArgs)); + for (int i = 0; i < otherArgCount; i++) { + strlcat(fArgs, " ", sizeof(fArgs)); + strlcat(fArgs, otherArgs[i], sizeof(fArgs)); + } +} + + +void +Team::ResetSignalsOnExec() +{ + // We are supposed to keep pending signals. Signal actions shall be reset + // partially: SIG_IGN and SIG_DFL dispositions shall be kept as they are + // (for SIGCHLD it's implementation-defined). Others shall be reset to + // SIG_DFL. SA_ONSTACK shall be cleared. There's no mention of the other + // flags, but since there aren't any handlers, they make little sense, so + // we clear them. + + for (uint32 i = 1; i <= MAX_SIGNAL_NUMBER; i++) { + struct sigaction& action = SignalActionFor(i); + if (action.sa_handler != SIG_IGN && action.sa_handler != SIG_DFL) + action.sa_handler = SIG_DFL; + + action.sa_mask = 0; + action.sa_flags = 0; + action.sa_userdata = NULL; + } +} + + +void +Team::InheritSignalActions(Team* parent) +{ + memcpy(fSignalActions, parent->fSignalActions, sizeof(fSignalActions)); +} + + +/*! Adds the given user timer to the team and, if user-defined, assigns it an + ID. + + The caller must hold the team's lock. + + \param timer The timer to be added. If it doesn't have an ID yet, it is + considered user-defined and will be assigned an ID. + \return \c B_OK, if the timer was added successfully, another error code + otherwise. +*/ +status_t +Team::AddUserTimer(UserTimer* timer) +{ + // don't allow addition of timers when already shutting the team down + if (state >= TEAM_STATE_SHUTDOWN) + return B_BAD_TEAM_ID; + + // If the timer is user-defined, check timer limit and increment + // user-defined count. + if (timer->ID() < 0 && !CheckAddUserDefinedTimer()) + return EAGAIN; + + fUserTimers.AddTimer(timer); + + return B_OK; +} + + +/*! Removes the given user timer from the team. + + The caller must hold the team's lock. + + \param timer The timer to be removed. + +*/ +void +Team::RemoveUserTimer(UserTimer* timer) +{ + fUserTimers.RemoveTimer(timer); + + if (timer->ID() >= USER_TIMER_FIRST_USER_DEFINED_ID) + UserDefinedTimersRemoved(1); +} + + +/*! Deletes all (or all user-defined) user timers of the team. + + Timer's belonging to the team's threads are not affected. + The caller must hold the team's lock. + + \param userDefinedOnly If \c true, only the user-defined timers are deleted, + otherwise all timers are deleted. +*/ +void +Team::DeleteUserTimers(bool userDefinedOnly) +{ + int32 count = fUserTimers.DeleteTimers(userDefinedOnly); + UserDefinedTimersRemoved(count); +} + + +/*! If not at the limit yet, increments the team's user-defined timer count. + \return \c true, if the limit wasn't reached yet, \c false otherwise. +*/ +bool +Team::CheckAddUserDefinedTimer() +{ + int32 oldCount = atomic_add(&fUserDefinedTimerCount, 1); + if (oldCount >= MAX_USER_TIMERS_PER_TEAM) { + atomic_add(&fUserDefinedTimerCount, -1); + return false; + } + + return true; +} + + +/*! Subtracts the given count for the team's user-defined timer count. + \param count The count to subtract. +*/ +void +Team::UserDefinedTimersRemoved(int32 count) +{ + atomic_add(&fUserDefinedTimerCount, -count); +} + + +void +Team::DeactivateCPUTimeUserTimers() +{ + while (TeamTimeUserTimer* timer = fCPUTimeUserTimers.Head()) + timer->Deactivate(); + + while (TeamUserTimeUserTimer* timer = fUserTimeUserTimers.Head()) + timer->Deactivate(); +} + + +/*! Returns the team's current total CPU time (kernel + user + offset). + + The caller must hold the scheduler lock. + + \param ignoreCurrentRun If \c true and the current thread is one team's + threads, don't add the time since the last time \c last_time was + updated. Should be used in "thread unscheduled" scheduler callbacks, + since although the thread is still running at that time, its time has + already been stopped. + \return The team's current total CPU time. +*/ +bigtime_t +Team::CPUTime(bool ignoreCurrentRun) const +{ + bigtime_t time = cpu_clock_offset + dead_threads_kernel_time + + dead_threads_user_time; + + Thread* currentThread = thread_get_current_thread(); + bigtime_t now = system_time(); + + for (Thread* thread = thread_list; thread != NULL; + thread = thread->team_next) { + SpinLocker threadTimeLocker(thread->time_lock); + time += thread->kernel_time + thread->user_time; + + if (thread->IsRunning()) { + if (!ignoreCurrentRun || thread != currentThread) + time += now - thread->last_time; + } + } + + return time; +} + + +/*! Returns the team's current user CPU time. + + The caller must hold the scheduler lock. + + \return The team's current user CPU time. +*/ +bigtime_t +Team::UserCPUTime() const +{ + bigtime_t time = dead_threads_user_time; + + bigtime_t now = system_time(); + + for (Thread* thread = thread_list; thread != NULL; + thread = thread->team_next) { + SpinLocker threadTimeLocker(thread->time_lock); + time += thread->user_time; + + if (thread->IsRunning() && !thread->in_kernel) + time += now - thread->last_time; + } + + return time; +} + + +// #pragma mark - ProcessGroup + + +ProcessGroup::ProcessGroup(pid_t id) + : + id(id), + teams(NULL), + fSession(NULL), + fInOrphanedCheckList(false) +{ + char lockName[32]; + snprintf(lockName, sizeof(lockName), "Group:%" B_PRId32, id); + mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); +} + + +ProcessGroup::~ProcessGroup() +{ + TRACE(("ProcessGroup::~ProcessGroup(): id = %ld\n", group->id)); + + // If the group is in the orphaned check list, remove it. + MutexLocker orphanedCheckLocker(sOrphanedCheckLock); + + if (fInOrphanedCheckList) + sOrphanedCheckProcessGroups.Remove(this); + + orphanedCheckLocker.Unlock(); + + // remove group from the hash table and from the session + if (fSession != NULL) { + InterruptsSpinLocker groupHashLocker(sGroupHashLock); + sGroupHash.RemoveUnchecked(this); + groupHashLocker.Unlock(); + + fSession->ReleaseReference(); + } + + mutex_destroy(&fLock); +} + + +/*static*/ ProcessGroup* +ProcessGroup::Get(pid_t id) +{ + InterruptsSpinLocker groupHashLocker(sGroupHashLock); + ProcessGroup* group = sGroupHash.Lookup(id); + if (group != NULL) + group->AcquireReference(); + return group; +} + + +/*! Adds the group the given session and makes it publicly accessible. + The caller must not hold the process group hash lock. +*/ +void +ProcessGroup::Publish(ProcessSession* session) +{ + InterruptsSpinLocker groupHashLocker(sGroupHashLock); + PublishLocked(session); +} + + +/*! Adds the group to the given session and makes it publicly accessible. + The caller must hold the process group hash lock. +*/ +void +ProcessGroup::PublishLocked(ProcessSession* session) +{ + ASSERT(sGroupHash.Lookup(this->id) == NULL); + + fSession = session; + fSession->AcquireReference(); + + sGroupHash.InsertUnchecked(this); +} + + +/*! Checks whether the process group is orphaned. + The caller must hold the group's lock. + \return \c true, if the group is orphaned, \c false otherwise. +*/ +bool +ProcessGroup::IsOrphaned() const +{ + // Orphaned Process Group: "A process group in which the parent of every + // member is either itself a member of the group or is not a member of the + // group's session." (Open Group Base Specs Issue 7) + bool orphaned = true; + + Team* team = teams; + while (orphaned && team != NULL) { + team->LockTeamAndParent(false); + + Team* parent = team->parent; + if (parent != NULL && parent->group_id != id + && parent->session_id == fSession->id) { + orphaned = false; + } + + team->UnlockTeamAndParent(); + + team = team->group_next; + } + + return orphaned; +} + + +void +ProcessGroup::ScheduleOrphanedCheck() +{ + MutexLocker orphanedCheckLocker(sOrphanedCheckLock); + + if (!fInOrphanedCheckList) { + sOrphanedCheckProcessGroups.Add(this); + fInOrphanedCheckList = true; + } +} + + +void +ProcessGroup::UnsetOrphanedCheck() +{ + fInOrphanedCheckList = false; +} + + +// #pragma mark - ProcessSession + + +ProcessSession::ProcessSession(pid_t id) + : + id(id), + controlling_tty(-1), + foreground_group(-1) +{ + char lockName[32]; + snprintf(lockName, sizeof(lockName), "Session:%" B_PRId32, id); + mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); +} + + +ProcessSession::~ProcessSession() +{ + mutex_destroy(&fLock); +} + + +// #pragma mark - KDL functions static void @@ -368,9 +1115,10 @@ _dump_team_info(Team* team) { kprintf("TEAM: %p\n", team); kprintf("id: %ld (%#lx)\n", team->id, team->id); - kprintf("name: '%s'\n", team->name); - kprintf("args: '%s'\n", team->args); - kprintf("next: %p\n", team->next); + kprintf("serial_number: %" B_PRId64 "\n", team->serial_number); + kprintf("name: '%s'\n", team->Name()); + kprintf("args: '%s'\n", team->Args()); + kprintf("hash_next: %p\n", team->hash_next); kprintf("parent: %p", team->parent); if (team->parent != NULL) { kprintf(" (id = %ld)\n", team->parent->id); @@ -417,9 +1165,9 @@ dump_team_info(int argc, char** argv) } // walk through the thread list, trying to match name or id - for (TeamHashTable::Iterator it = sTeamHash.GetIterator(); + for (TeamTable::Iterator it = sTeamHash.GetIterator(); Team* team = it.Next();) { - if ((team->name && strcmp(argv[1], team->name) == 0) + if ((team->Name() && strcmp(argv[1], team->Name()) == 0) || team->id == id) { _dump_team_info(team); found = true; @@ -438,41 +1186,25 @@ dump_teams(int argc, char** argv) { kprintf("team id parent name\n"); - for (TeamHashTable::Iterator it = sTeamHash.GetIterator(); + for (TeamTable::Iterator it = sTeamHash.GetIterator(); Team* team = it.Next();) { - kprintf("%p%7ld %p %s\n", team, team->id, team->parent, team->name); + kprintf("%p%7ld %p %s\n", team, team->id, team->parent, team->Name()); } return 0; } -static int -process_group_compare(void* _group, const void* _key) -{ - struct process_group* group = (struct process_group*)_group; - const struct team_key* key = (const struct team_key*)_key; - - if (group->id == key->id) - return 0; - - return 1; -} +// #pragma mark - Private functions -static uint32 -process_group_hash(void* _group, const void* _key, uint32 range) -{ - struct process_group* group = (struct process_group*)_group; - const struct team_key* key = (const struct team_key*)_key; - - if (group != NULL) - return group->id % range; - - return (uint32)key->id % range; -} +/*! Inserts team \a team into the child list of team \a parent. + The caller must hold the lock of both \a parent and \a team. + \param parent The parent team. + \param team The team to be inserted into \a parent's child list. +*/ static void insert_team_into_parent(Team* parent, Team* team) { @@ -484,7 +1216,13 @@ insert_team_into_parent(Team* parent, Team* team) } -/*! Note: must have team lock held */ +/*! Removes team \a team from the child list of team \a parent. + + The caller must hold the lock of both \a parent and \a team. + + \param parent The parent team. + \param team The team to be removed from \a parent's child list. +*/ static void remove_team_from_parent(Team* parent, Team* team) { @@ -507,31 +1245,9 @@ remove_team_from_parent(Team* parent, Team* team) } -/*! Reparent each of our children - Note: must have team lock held +/*! Returns whether the given team is a session leader. + The caller must hold the team's lock or its process group's lock. */ -static void -reparent_children(Team* team) -{ - Team* child; - - while ((child = team->children) != NULL) { - // remove the child from the current proc and add to the parent - remove_team_from_parent(team, child); - insert_team_into_parent(sKernelTeam, child); - } - - // move job control entries too - sKernelTeam->stopped_children.entries.MoveFrom( - &team->stopped_children.entries); - sKernelTeam->continued_children.entries.MoveFrom( - &team->continued_children.entries); - - // Note, we don't move the dead children entries. Those will be deleted - // when the team structure is deleted. -} - - static bool is_session_leader(Team* team) { @@ -539,6 +1255,9 @@ is_session_leader(Team* team) } +/*! Returns whether the given team is a process group leader. + The caller must hold the team's lock or its process group's lock. +*/ static bool is_process_group_leader(Team* team) { @@ -546,127 +1265,38 @@ is_process_group_leader(Team* team) } -static void -deferred_delete_process_group(struct process_group* group) -{ - if (group == NULL) - return; - - // remove_group_from_session() keeps this pointer around - // only if the session can be freed as well - if (group->session) { - TRACE(("deferred_delete_process_group(): frees session %ld\n", - group->session->id)); - deferred_free(group->session); - } - - deferred_free(group); -} - - -/*! Removes a group from a session, and puts the session object - back into the session cache, if it's not used anymore. - You must hold the team lock when calling this function. +/*! Inserts the given team into the given process group. + The caller must hold the process group's lock, the team's lock, and the + team's parent's lock. */ static void -remove_group_from_session(struct process_group* group) -{ - struct process_session* session = group->session; - - // the group must be in any session to let this function have any effect - if (session == NULL) - return; - - hash_remove(sGroupHash, group); - - // we cannot free the resource here, so we're keeping the group link - // around - this way it'll be freed by free_process_group() - if (--session->group_count > 0) - group->session = NULL; -} - - -/*! Team lock must be held. -*/ -static void -acquire_process_group_ref(pid_t groupID) -{ - process_group* group = team_get_process_group_locked(NULL, groupID); - if (group == NULL) { - panic("acquire_process_group_ref(): unknown group ID: %ld", groupID); - return; - } - - group->refs++; -} - - -/*! Team lock must be held. -*/ -static void -release_process_group_ref(pid_t groupID) -{ - process_group* group = team_get_process_group_locked(NULL, groupID); - if (group == NULL) { - panic("release_process_group_ref(): unknown group ID: %ld", groupID); - return; - } - - if (group->refs <= 0) { - panic("release_process_group_ref(%ld): ref count already 0", groupID); - return; - } - - if (--group->refs > 0) - return; - - // group is no longer used - - remove_group_from_session(group); - deferred_delete_process_group(group); -} - - -/*! You must hold the team lock when calling this function. */ -static void -insert_group_into_session(struct process_session* session, - struct process_group* group) -{ - if (group == NULL) - return; - - group->session = session; - hash_insert(sGroupHash, group); - session->group_count++; -} - - -/*! You must hold the team lock when calling this function. */ -static void -insert_team_into_group(struct process_group* group, Team* team) +insert_team_into_group(ProcessGroup* group, Team* team) { team->group = group; team->group_id = group->id; - team->session_id = group->session->id; + team->session_id = group->Session()->id; team->group_next = group->teams; group->teams = team; - acquire_process_group_ref(group->id); + group->AcquireReference(); } -/*! Removes the team from the group. +/*! Removes the given team from its process group. - \param team the team that'll be removed from it's group + The caller must hold the process group's lock, the team's lock, and the + team's parent's lock. Interrupts must be enabled. + + \param team The team that'll be removed from its process group. */ static void remove_team_from_group(Team* team) { - struct process_group* group = team->group; + ProcessGroup* group = team->group; Team* current; Team* last = NULL; - // the team must be in any team to let this function have any effect + // the team must be in a process group to let this function have any effect if (group == NULL) return; @@ -687,154 +1317,7 @@ remove_team_from_group(Team* team) team->group = NULL; team->group_next = NULL; - release_process_group_ref(group->id); -} - - -static struct process_group* -create_process_group(pid_t id) -{ - struct process_group* group - = (struct process_group*)malloc(sizeof(struct process_group)); - if (group == NULL) - return NULL; - - group->id = id; - group->refs = 0; - group->session = NULL; - group->teams = NULL; - group->orphaned = true; - return group; -} - - -static struct process_session* -create_process_session(pid_t id) -{ - struct process_session* session - = (struct process_session*)malloc(sizeof(struct process_session)); - if (session == NULL) - return NULL; - - session->id = id; - session->group_count = 0; - session->controlling_tty = -1; - session->foreground_group = -1; - - return session; -} - - -static void -set_team_name(Team* team, const char* name) -{ - if (const char* lastSlash = strrchr(name, '/')) - name = lastSlash + 1; - - strlcpy(team->name, name, B_OS_NAME_LENGTH); -} - - -static Team* -create_team_struct(const char* name, bool kernel) -{ - Team* team = new(std::nothrow) Team; - if (team == NULL) - return NULL; - ObjectDeleter teamDeleter(team); - - team->next = team->siblings_next = team->children = team->parent = NULL; - team->id = allocate_thread_id(); - set_team_name(team, name); - team->args[0] = '\0'; - team->num_threads = 0; - team->io_context = NULL; - team->address_space = NULL; - team->realtime_sem_context = NULL; - team->xsi_sem_context = NULL; - team->thread_list = NULL; - team->main_thread = NULL; - team->loading_info = NULL; - team->state = TEAM_STATE_BIRTH; - team->flags = 0; - team->death_entry = NULL; - team->user_data_area = -1; - team->user_data = 0; - team->used_user_data = 0; - team->user_data_size = 0; - team->free_user_threads = NULL; - - team->supplementary_groups = NULL; - team->supplementary_group_count = 0; - - team->dead_threads_kernel_time = 0; - team->dead_threads_user_time = 0; - - // dead threads - list_init(&team->dead_threads); - team->dead_threads_count = 0; - - // dead children - team->dead_children.count = 0; - team->dead_children.kernel_time = 0; - team->dead_children.user_time = 0; - - // job control entry - team->job_control_entry = new(nothrow) job_control_entry; - if (team->job_control_entry == NULL) - return NULL; - ObjectDeleter jobControlEntryDeleter( - team->job_control_entry); - team->job_control_entry->state = JOB_CONTROL_STATE_NONE; - team->job_control_entry->thread = team->id; - team->job_control_entry->team = team; - - list_init(&team->sem_list); - list_init(&team->port_list); - list_init(&team->image_list); - list_init(&team->watcher_list); - - clear_team_debug_info(&team->debug_info, true); - - if (arch_team_init_team_struct(team, kernel) < 0) - return NULL; - - // publish dead/stopped/continued children condition vars - team->dead_children.condition_variable.Init(&team->dead_children, - "team children"); - - // keep all allocated structures - jobControlEntryDeleter.Detach(); - teamDeleter.Detach(); - - return team; -} - - -static void -delete_team_struct(Team* team) -{ - // get rid of all associated data - team->PrepareForDeletion(); - - while (death_entry* threadDeathEntry = (death_entry*)list_remove_head_item( - &team->dead_threads)) { - free(threadDeathEntry); - } - - while (job_control_entry* entry = team->dead_children.entries.RemoveHead()) - delete entry; - - while (free_user_thread* entry = team->free_user_threads) { - team->free_user_threads = entry->next; - free(entry); - } - - malloc_referenced_release(team->supplementary_groups); - - delete team->job_control_entry; - // usually already NULL and transferred to the parent - delete team; + group->ReleaseReference(); } @@ -979,8 +1462,8 @@ create_team_arg(struct team_arg** _teamArg, const char* path, char** flatArgs, } -static int32 -team_create_thread_start(void* args) +static status_t +team_create_thread_start_internal(void* args) { status_t err; Thread* thread; @@ -988,12 +1471,10 @@ team_create_thread_start(void* args) struct team_arg* teamArgs = (struct team_arg*)args; const char* path; addr_t entry; - char userStackName[128]; - uint32 sizeLeft; char** userArgs; char** userEnv; struct user_space_program_args* programArgs; - uint32 argCount, envCount, i; + uint32 argCount, envCount; thread = thread_get_current_thread(); team = thread->team; @@ -1001,11 +1482,6 @@ team_create_thread_start(void* args) TRACE(("team_create_thread_start: entry thread %ld\n", thread->id)); - // get a user thread for the main thread - thread->user_thread = team_allocate_user_thread(team); - - // create an initial primary stack area - // Main stack area layout is currently as follows (starting from 0): // // size | usage @@ -1020,34 +1496,6 @@ team_create_thread_start(void* args) // the heap // TODO: we could reserve the whole USER_STACK_REGION upfront... - sizeLeft = PAGE_ALIGN(USER_MAIN_THREAD_STACK_SIZE - + USER_STACK_GUARD_PAGES * B_PAGE_SIZE + TLS_SIZE - + sizeof(struct user_space_program_args) + teamArgs->flat_args_size); - thread->user_stack_base - = USER_STACK_REGION + USER_STACK_REGION_SIZE - sizeLeft; - thread->user_stack_size = USER_MAIN_THREAD_STACK_SIZE - + USER_STACK_GUARD_PAGES * B_PAGE_SIZE; - // the exact location at the end of the user stack area - - sprintf(userStackName, "%s_main_stack", team->name); - virtual_address_restrictions virtualRestrictions = {}; - virtualRestrictions.address = (void*)thread->user_stack_base; - virtualRestrictions.address_specification = B_EXACT_ADDRESS; - physical_address_restrictions physicalRestrictions = {}; - thread->user_stack_area = create_area_etc(team->id, userStackName, sizeLeft, - B_NO_LOCK, B_READ_AREA | B_WRITE_AREA | B_STACK_AREA, 0, - &virtualRestrictions, &physicalRestrictions, NULL); - if (thread->user_stack_area < 0) { - dprintf("team_create_thread_start: could not create default user stack " - "region: %s\n", strerror(thread->user_stack_area)); - - free_team_arg(teamArgs); - return thread->user_stack_area; - } - - // now that the TLS area is allocated, initialize TLS - arch_thread_init_tls(thread); - argCount = teamArgs->arg_count; envCount = teamArgs->env_count; @@ -1078,13 +1526,11 @@ team_create_thread_start(void* args) TRACE(("team_create_thread_start: loading elf binary '%s'\n", path)); - // add args to info member - team->args[0] = 0; - strlcpy(team->args, path, sizeof(team->args)); - for (i = 1; i < argCount; i++) { - strlcat(team->args, " ", sizeof(team->args)); - strlcat(team->args, teamArgs->flat_args[i], sizeof(team->args)); - } + // set team args and update state + team->Lock(); + team->SetArgs(path, teamArgs->flat_args + 1, argCount - 1); + team->state = TEAM_STATE_NORMAL; + team->Unlock(); free_team_arg(teamArgs); // the arguments are already on the user stack, we no longer need @@ -1121,11 +1567,19 @@ team_create_thread_start(void* args) TRACE(("team_create_thread_start: loaded elf. entry = %#lx\n", entry)); - team->state = TEAM_STATE_NORMAL; + // enter userspace -- returns only in case of error + return thread_enter_userspace_new_team(thread, (addr_t)entry, + programArgs, NULL); +} - // jump to the entry point in user space - return arch_thread_enter_userspace(thread, entry, programArgs, NULL); - // only returns in case of error + +static status_t +team_create_thread_start(void* args) +{ + team_create_thread_start_internal(args); + thread_exit(); + // does not return + return B_OK; } @@ -1135,14 +1589,12 @@ load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount, port_id errorPort, uint32 errorToken) { char** flatArgs = _flatArgs; - Team* team; - const char* threadName; thread_id thread; status_t status; - cpu_status state; struct team_arg* teamArgs; struct team_loading_info loadingInfo; io_context* parentIOContext = NULL; + team_id teamID; if (flatArgs == NULL || argCount == 0) return B_BAD_VALUE; @@ -1152,9 +1604,25 @@ load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount, TRACE(("load_image_internal: name '%s', args = %p, argCount = %ld\n", path, flatArgs, argCount)); - team = create_team_struct(path, false); + // cut the path from the main thread name + const char* threadName = strrchr(path, '/'); + if (threadName != NULL) + threadName++; + else + threadName = path; + + // create the main thread object + Thread* mainThread; + status = Thread::Create(threadName, mainThread); + if (status != B_OK) + return status; + BReference mainThreadReference(mainThread, true); + + // create team object + Team* team = Team::Create(mainThread->id, path, false); if (team == NULL) return B_NO_MEMORY; + BReference teamReference(team, true); if (flags & B_WAIT_TILL_LOADED) { loadingInfo.thread = thread_get_current_thread(); @@ -1163,35 +1631,37 @@ load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount, team->loading_info = &loadingInfo; } - InterruptsSpinLocker teamLocker(gTeamSpinlock); - // get the parent team - Team* parent; + Team* parent = Team::Get(parentID); + if (parent == NULL) + return B_BAD_TEAM_ID; + BReference parentReference(parent, true); - if (parentID == B_CURRENT_TEAM) - parent = thread_get_current_thread()->team; - else - parent = team_get_team_struct_locked(parentID); - - if (parent == NULL) { - teamLocker.Unlock(); - status = B_BAD_TEAM_ID; - goto err0; - } + parent->LockTeamAndProcessGroup(); + team->Lock(); // inherit the parent's user/group - inherit_parent_user_and_group_locked(team, parent); + inherit_parent_user_and_group(team, parent); + + InterruptsSpinLocker teamsLocker(sTeamHashLock); + + sTeamHash.Insert(team); + sUsedTeams++; + + teamsLocker.Unlock(); - sTeamHash.InsertUnchecked(team); insert_team_into_parent(parent, team); insert_team_into_group(parent->group, team); - sUsedTeams++; // get a reference to the parent's I/O context -- we need it to create ours parentIOContext = parent->io_context; vfs_get_io_context(parentIOContext); - teamLocker.Unlock(); + team->Unlock(); + parent->UnlockTeamAndProcessGroup(); + + // notify team listeners + sNotificationService.Notify(TEAM_ADDED, team); // check the executable's set-user/group-id permission update_set_id_user_and_group(team, path); @@ -1224,71 +1694,60 @@ load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount, if (status != B_OK) goto err3; - // cut the path from the main thread name - threadName = strrchr(path, '/'); - if (threadName != NULL) - threadName++; - else - threadName = path; - // create the user data area status = create_team_user_data(team); if (status != B_OK) goto err4; - // notify team listeners - sNotificationService.Notify(TEAM_ADDED, team); + // In case we start the main thread, we shouldn't access the team object + // afterwards, so cache the team's ID. + teamID = team->id; // Create a kernel thread, but under the context of the new team - // The new thread will take over ownership of teamArgs - thread = spawn_kernel_thread_etc(team_create_thread_start, threadName, - B_NORMAL_PRIORITY, teamArgs, team->id, team->id); + // The new thread will take over ownership of teamArgs. + thread = thread_create_thread( + ThreadCreationAttributes(team_create_thread_start, threadName, + B_NORMAL_PRIORITY, teamArgs, teamID, mainThread), + false); if (thread < 0) { status = thread; goto err5; } + // The team has been created successfully, so we keep the reference. Or + // more precisely: It's owned by the team's main thread, now. + teamReference.Detach(); + // wait for the loader of the new team to finish its work if ((flags & B_WAIT_TILL_LOADED) != 0) { - Thread* mainThread; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // resume the team's main thread + if (mainThread != NULL && mainThread->state == B_THREAD_SUSPENDED) + scheduler_enqueue_in_run_queue(mainThread); - mainThread = thread_get_thread_struct_locked(thread); - if (mainThread) { - // resume the team's main thread - if (mainThread->state == B_THREAD_SUSPENDED) - scheduler_enqueue_in_run_queue(mainThread); - - // Now suspend ourselves until loading is finished. - // We will be woken either by the thread, when it finished or - // aborted loading, or when the team is going to die (e.g. is - // killed). In either case the one setting `loadingInfo.done' is - // responsible for removing the info from the team structure. - while (!loadingInfo.done) { - thread_get_current_thread()->next_state = B_THREAD_SUSPENDED; - scheduler_reschedule(); - } - } else { - // Impressive! Someone managed to kill the thread in this short - // time. + // Now suspend ourselves until loading is finished. We will be woken + // either by the thread, when it finished or aborted loading, or when + // the team is going to die (e.g. is killed). In either case the one + // setting `loadingInfo.done' is responsible for removing the info from + // the team structure. + while (!loadingInfo.done) { + thread_get_current_thread()->next_state = B_THREAD_SUSPENDED; + scheduler_reschedule(); } - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + schedulerLocker.Unlock(); if (loadingInfo.result < B_OK) return loadingInfo.result; } // notify the debugger - user_debug_team_created(team->id); + user_debug_team_created(teamID); return thread; err5: - sNotificationService.Notify(TEAM_REMOVED, team); delete_team_user_data(team); err4: team->address_space->Put(); @@ -1300,20 +1759,22 @@ err1: if (parentIOContext != NULL) vfs_put_io_context(parentIOContext); - // Remove the team structure from the team hash table and delete the team - // structure - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + // Remove the team structure from the process group, the parent team, and + // the team hash table and delete the team structure. + parent->LockTeamAndProcessGroup(); + team->Lock(); remove_team_from_group(team); remove_team_from_parent(team->parent, team); - sTeamHash.RemoveUnchecked(team); - RELEASE_TEAM_LOCK(); - restore_interrupts(state); + team->Unlock(); + parent->UnlockTeamAndProcessGroup(); -err0: - delete_team_struct(team); + teamsLocker.Lock(); + sTeamHash.Remove(team); + teamsLocker.Unlock(); + + sNotificationService.Notify(TEAM_REMOVED, team); return status; } @@ -1322,7 +1783,8 @@ err0: /*! Almost shuts down the current team and loads a new image into it. If successful, this function does not return and will takeover ownership of the arguments provided. - This function may only be called from user space. + This function may only be called in a userland team (caused by one of the + exec*() syscalls). */ static status_t exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize, @@ -1334,9 +1796,6 @@ exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize, Team* team = thread_get_current_thread()->team; struct team_arg* teamArgs; const char* threadName; - status_t status = B_OK; - cpu_status state; - Thread* thread; thread_id nubThreadID = -1; TRACE(("exec_team(path = \"%s\", argc = %ld, envCount = %ld): team %ld\n", @@ -1349,38 +1808,36 @@ exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize, return B_NOT_ALLOWED; // we currently need to be single threaded here - // ToDo: maybe we should just kill all other threads and + // TODO: maybe we should just kill all other threads and // make the current thread the team's main thread? - if (team->main_thread != thread_get_current_thread()) + Thread* currentThread = thread_get_current_thread(); + if (currentThread != team->main_thread) return B_NOT_ALLOWED; // The debug nub thread, a pure kernel thread, is allowed to survive. // We iterate through the thread list to make sure that there's no other // thread. - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); + TeamLocker teamLocker(team); + InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) nubThreadID = team->debug_info.nub_thread; - RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); + debugInfoLocker.Unlock(); - for (thread = team->thread_list; thread; thread = thread->team_next) { - if (thread != team->main_thread && thread->id != nubThreadID) { - status = B_NOT_ALLOWED; - break; - } + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + if (thread != team->main_thread && thread->id != nubThreadID) + return B_NOT_ALLOWED; } - RELEASE_TEAM_LOCK(); - restore_interrupts(state); + team->DeleteUserTimers(true); + team->ResetSignalsOnExec(); - if (status != B_OK) - return status; + teamLocker.Unlock(); - status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, argCount, - envCount, umask, -1, 0); + status_t status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, + argCount, envCount, umask, -1, 0); if (status != B_OK) return status; @@ -1417,7 +1874,9 @@ exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize, // rename the team - set_team_name(team, path); + team->Lock(); + team->SetName(path); + team->Unlock(); // cut the path from the team name and rename the main thread, too threadName = strrchr(path, '/'); @@ -1438,65 +1897,40 @@ exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize, // notify team listeners sNotificationService.Notify(TEAM_EXEC, team); - status = team_create_thread_start(teamArgs); - // this one usually doesn't return... + // get a user thread for the thread + user_thread* userThread = team_allocate_user_thread(team); + // cannot fail (the allocation for the team would have failed already) + ThreadLocker currentThreadLocker(currentThread); + currentThread->user_thread = userThread; + currentThreadLocker.Unlock(); - // sorry, we have to kill us, there is no way out anymore - // (without any areas left and all that) + // create the user stack for the thread + status = thread_create_user_stack(currentThread->team, currentThread, NULL, + 0, sizeof(user_space_program_args) + teamArgs->flat_args_size); + if (status == B_OK) { + // prepare the stack, load the runtime loader, and enter userspace + team_create_thread_start(teamArgs); + // does never return + } else + free_team_arg(teamArgs); + + // Sorry, we have to kill ourselves, there is no way out anymore + // (without any areas left and all that). exit_thread(status); - // we return a status here since the signal that is sent by the - // call above is not immediately handled + // We return a status here since the signal that is sent by the + // call above is not immediately handled. return B_ERROR; } -/*! This is the first function to be called from the newly created - main child thread. - It will fill in everything what's left to do from fork_arg, and - return from the parent's fork() syscall to the child. -*/ -static int32 -fork_team_thread_start(void* _args) -{ - Thread* thread = thread_get_current_thread(); - struct fork_arg* forkArgs = (struct fork_arg*)_args; - - struct arch_fork_arg archArgs = forkArgs->arch_info; - // we need a local copy of the arch dependent part - - thread->user_stack_area = forkArgs->user_stack_area; - thread->user_stack_base = forkArgs->user_stack_base; - thread->user_stack_size = forkArgs->user_stack_size; - thread->user_local_storage = forkArgs->user_local_storage; - thread->sig_block_mask = forkArgs->sig_block_mask; - thread->user_thread = forkArgs->user_thread; - memcpy(thread->sig_action, forkArgs->sig_action, - sizeof(forkArgs->sig_action)); - thread->signal_stack_base = forkArgs->signal_stack_base; - thread->signal_stack_size = forkArgs->signal_stack_size; - thread->signal_stack_enabled = forkArgs->signal_stack_enabled; - - arch_thread_init_tls(thread); - - free(forkArgs); - - // set frame of the parent thread to this one, too - - arch_restore_fork_frame(&archArgs); - // This one won't return here - - return 0; -} - - static thread_id fork_team(void) { Thread* parentThread = thread_get_current_thread(); Team* parentTeam = parentThread->team; Team* team; - struct fork_arg* forkArgs; + arch_fork_arg* forkArgs; struct area_info info; thread_id threadID; status_t status; @@ -1511,29 +1945,51 @@ fork_team(void) // TODO: this is very similar to load_image_internal() - maybe we can do // something about it :) - team = create_team_struct(parentTeam->name, false); + // create the main thread object + Thread* thread; + status = Thread::Create(parentThread->name, thread); + if (status != B_OK) + return status; + BReference threadReference(thread, true); + + // create the team object + team = Team::Create(thread->id, NULL, false); if (team == NULL) return B_NO_MEMORY; - strlcpy(team->args, parentTeam->args, sizeof(team->args)); + parentTeam->LockTeamAndProcessGroup(); + team->Lock(); - InterruptsSpinLocker teamLocker(gTeamSpinlock); + team->SetName(parentTeam->Name()); + team->SetArgs(parentTeam->Args()); // Inherit the parent's user/group. - inherit_parent_user_and_group_locked(team, parentTeam); + inherit_parent_user_and_group(team, parentTeam); - sTeamHash.InsertUnchecked(team); - insert_team_into_parent(parentTeam, team); - insert_team_into_group(parentTeam->group, team); + // inherit signal handlers + team->InheritSignalActions(parentTeam); + + InterruptsSpinLocker teamsLocker(sTeamHashLock); + + sTeamHash.Insert(team); sUsedTeams++; - teamLocker.Unlock(); + teamsLocker.Unlock(); + + insert_team_into_parent(parentTeam, team); + insert_team_into_group(parentTeam->group, team); + + team->Unlock(); + parentTeam->UnlockTeamAndProcessGroup(); + + // notify team listeners + sNotificationService.Notify(TEAM_ADDED, team); // inherit some team debug flags team->debug_info.flags |= atomic_get(&parentTeam->debug_info.flags) & B_TEAM_DEBUG_INHERITED_FLAGS; - forkArgs = (struct fork_arg*)malloc(sizeof(struct fork_arg)); + forkArgs = (arch_fork_arg*)malloc(sizeof(arch_fork_arg)); if (forkArgs == NULL) { status = B_NO_MEMORY; goto err1; @@ -1565,9 +2021,6 @@ fork_team(void) // copy all areas of the team // TODO: should be able to handle stack areas differently (ie. don't have // them copy-on-write) - // TODO: all stacks of other threads than the current one could be left out - - forkArgs->user_thread = NULL; cookie = 0; while (get_next_area_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) { @@ -1577,7 +2030,7 @@ fork_team(void) if (status != B_OK) break; - forkArgs->user_thread = team_allocate_user_thread(team); + thread->user_thread = team_allocate_user_thread(team); } else { void* address; area_id area = vm_copy_area(team->address_space->ID(), info.name, @@ -1588,14 +2041,14 @@ fork_team(void) } if (info.area == parentThread->user_stack_area) - forkArgs->user_stack_area = area; + thread->user_stack_area = area; } } if (status < B_OK) goto err4; - if (forkArgs->user_thread == NULL) { + if (thread->user_thread == NULL) { #if KDEBUG panic("user data area not found, parent area is %ld", parentTeam->user_data_area); @@ -1604,17 +2057,15 @@ fork_team(void) goto err4; } - forkArgs->user_stack_base = parentThread->user_stack_base; - forkArgs->user_stack_size = parentThread->user_stack_size; - forkArgs->user_local_storage = parentThread->user_local_storage; - forkArgs->sig_block_mask = parentThread->sig_block_mask; - memcpy(forkArgs->sig_action, parentThread->sig_action, - sizeof(forkArgs->sig_action)); - forkArgs->signal_stack_base = parentThread->signal_stack_base; - forkArgs->signal_stack_size = parentThread->signal_stack_size; - forkArgs->signal_stack_enabled = parentThread->signal_stack_enabled; + thread->user_stack_base = parentThread->user_stack_base; + thread->user_stack_size = parentThread->user_stack_size; + thread->user_local_storage = parentThread->user_local_storage; + thread->sig_block_mask = parentThread->sig_block_mask; + thread->signal_stack_base = parentThread->signal_stack_base; + thread->signal_stack_size = parentThread->signal_stack_size; + thread->signal_stack_enabled = parentThread->signal_stack_enabled; - arch_store_fork_frame(&forkArgs->arch_info); + arch_store_fork_frame(forkArgs); // copy image list image_info imageInfo; @@ -1625,16 +2076,16 @@ fork_team(void) goto err5; } - // notify team listeners - sNotificationService.Notify(TEAM_ADDED, team); - - // create a kernel thread under the context of the new team - threadID = spawn_kernel_thread_etc(fork_team_thread_start, - parentThread->name, parentThread->priority, forkArgs, - team->id, team->id); - if (threadID < 0) { - status = threadID; - goto err5; + // create the main thread + { + ThreadCreationAttributes threadCreationAttributes(NULL, + parentThread->name, parentThread->priority, NULL, team->id, thread); + threadCreationAttributes.forkArgs = forkArgs; + threadID = thread_create_thread(threadCreationAttributes, false); + if (threadID < 0) { + status = threadID; + goto err5; + } } // notify the debugger @@ -1646,7 +2097,6 @@ fork_team(void) return threadID; err5: - sNotificationService.Notify(TEAM_REMOVED, team); remove_images(team); err4: team->address_space->RemoveAndPut(); @@ -1657,38 +2107,40 @@ err25: err2: free(forkArgs); err1: - // remove the team structure from the team hash table and delete the team - // structure - teamLocker.Lock(); + // Remove the team structure from the process group, the parent team, and + // the team hash table and delete the team structure. + parentTeam->LockTeamAndProcessGroup(); + team->Lock(); remove_team_from_group(team); - remove_team_from_parent(parentTeam, team); - sTeamHash.RemoveUnchecked(team); + remove_team_from_parent(team->parent, team); - teamLocker.Unlock(); + team->Unlock(); + parentTeam->UnlockTeamAndProcessGroup(); - delete_team_struct(team); + teamsLocker.Lock(); + sTeamHash.Remove(team); + teamsLocker.Unlock(); + + sNotificationService.Notify(TEAM_REMOVED, team); + + team->ReleaseReference(); return status; } -/*! Returns if the specified \a team has any children belonging to the - specified \a group. - Must be called with the team lock held. +/*! Returns if the specified team \a parent has any children belonging to the + process group with the specified ID \a groupID. + The caller must hold \a parent's lock. */ static bool has_children_in_group(Team* parent, pid_t groupID) { - Team* team; - - struct process_group* group = team_get_process_group_locked( - parent->group->session, groupID); - if (group == NULL) - return false; - - for (team = group->teams; team; team = team->group_next) { - if (team->parent == parent) + for (Team* child = parent->children; child != NULL; + child = child->siblings_next) { + TeamLocker childLocker(child); + if (child->group_id == groupID) return true; } @@ -1696,6 +2148,19 @@ has_children_in_group(Team* parent, pid_t groupID) } +/*! Returns the first job control entry from \a children, which matches \a id. + \a id can be: + - \code > 0 \endcode: Matching an entry with that team ID. + - \code == -1 \endcode: Matching any entry. + - \code < -1 \endcode: Matching any entry with a process group ID of \c -id. + \c 0 is an invalid value for \a id. + + The caller must hold the lock of the team that \a children belongs to. + + \param children The job control entry list to check. + \param id The match criterion. + \return The first matching entry or \c NULL, if none matches. +*/ static job_control_entry* get_job_control_entry(team_job_control_children& children, pid_t id) { @@ -1719,6 +2184,25 @@ get_job_control_entry(team_job_control_children& children, pid_t id) } +/*! Returns the first job control entry from one of team's dead, continued, or + stopped children which matches \a id. + \a id can be: + - \code > 0 \endcode: Matching an entry with that team ID. + - \code == -1 \endcode: Matching any entry. + - \code < -1 \endcode: Matching any entry with a process group ID of \c -id. + \c 0 is an invalid value for \a id. + + The caller must hold \a team's lock. + + \param team The team whose dead, stopped, and continued child lists shall be + checked. + \param id The match criterion. + \param flags Specifies which children shall be considered. Dead children + always are. Stopped children are considered when \a flags is ORed + bitwise with \c WUNTRACED, continued children when \a flags is ORed + bitwise with \c WCONTINUED. + \return The first matching entry or \c NULL, if none matches. +*/ static job_control_entry* get_job_control_entry(Team* team, pid_t id, uint32 flags) { @@ -1744,27 +2228,44 @@ job_control_entry::job_control_entry() job_control_entry::~job_control_entry() { if (has_group_ref) { - InterruptsSpinLocker locker(gTeamSpinlock); - release_process_group_ref(group_id); + InterruptsSpinLocker groupHashLocker(sGroupHashLock); + + ProcessGroup* group = sGroupHash.Lookup(group_id); + if (group == NULL) { + panic("job_control_entry::~job_control_entry(): unknown group " + "ID: %ld", group_id); + return; + } + + groupHashLocker.Unlock(); + + group->ReleaseReference(); } } -/*! Team and thread lock must be held. +/*! Invoked when the owning team is dying, initializing the entry according to + the dead state. + + The caller must hold the owning team's lock and the scheduler lock. */ void job_control_entry::InitDeadState() { if (team != NULL) { - Thread* thread = team->main_thread; + ASSERT(team->exit.initialized); + group_id = team->group_id; - this->thread = thread->id; - status = thread->exit.status; - reason = thread->exit.reason; - signal = thread->exit.signal; - team = NULL; - acquire_process_group_ref(group_id); + team->group->AcquireReference(); has_group_ref = true; + + thread = team->id; + status = team->exit.status; + reason = team->exit.reason; + signal = team->exit.signal; + signaling_user = team->exit.signaling_user; + + team = NULL; } } @@ -1774,23 +2275,22 @@ job_control_entry::operator=(const job_control_entry& other) { state = other.state; thread = other.thread; + signal = other.signal; has_group_ref = false; + signaling_user = other.signaling_user; team = other.team; group_id = other.group_id; status = other.status; reason = other.reason; - signal = other.signal; return *this; } -/*! This is the kernel backend for waitpid(). It is a bit more powerful when it - comes to the reason why a thread has died than waitpid() can be. +/*! This is the kernel backend for waitid(). */ static thread_id -wait_for_child(pid_t child, uint32 flags, int32* _reason, - status_t* _returnCode) +wait_for_child(pid_t child, uint32 flags, siginfo_t& _info) { Thread* thread = thread_get_current_thread(); Team* team = thread->team; @@ -1802,16 +2302,18 @@ wait_for_child(pid_t child, uint32 flags, int32* _reason, T(WaitForChild(child, flags)); - if (child == 0) { - // wait for all children in the process group of the calling team - child = -team->group_id; - } + pid_t originalChild = child; bool ignoreFoundEntries = false; bool ignoreFoundEntriesChecked = false; while (true) { - InterruptsSpinLocker locker(gTeamSpinlock); + // lock the team + TeamLocker teamLocker(team); + + // A 0 child argument means to wait for all children in the process + // group of the calling team. + child = originalChild == 0 ? -team->group_id : originalChild; // check whether any condition holds job_control_entry* entry = get_job_control_entry(team, child, flags); @@ -1819,16 +2321,19 @@ wait_for_child(pid_t child, uint32 flags, int32* _reason, // If we don't have an entry yet, check whether there are any children // complying to the process group specification at all. if (entry == NULL) { - // No success yet -- check whether there are any children we could - // wait for. + // No success yet -- check whether there are any children complying + // to the process group specification at all. bool childrenExist = false; if (child == -1) { childrenExist = team->children != NULL; } else if (child < -1) { childrenExist = has_children_in_group(team, -child); } else { - if (Team* childTeam = team_get_team_struct_locked(child)) + if (Team* childTeam = Team::Get(child)) { + BReference childTeamReference(childTeam, true); + TeamLocker childTeamLocker(childTeam); childrenExist = childTeam->parent == team; + } } if (!childrenExist) { @@ -1841,15 +2346,19 @@ wait_for_child(pid_t child, uint32 flags, int32* _reason, } else { // got something foundEntry = *entry; - if (entry->state == JOB_CONTROL_STATE_DEAD) { - // The child is dead. Reap its death entry. - freeDeathEntry = entry; - team->dead_children.entries.Remove(entry); - team->dead_children.count--; - } else { - // The child is well. Reset its job control state. - team_set_job_control_state(entry->team, - JOB_CONTROL_STATE_NONE, 0, false); + + // unless WNOWAIT has been specified, "consume" the wait state + if ((flags & WNOWAIT) == 0 || ignoreFoundEntries) { + if (entry->state == JOB_CONTROL_STATE_DEAD) { + // The child is dead. Reap its death entry. + freeDeathEntry = entry; + team->dead_children.entries.Remove(entry); + team->dead_children.count--; + } else { + // The child is well. Reset its job control state. + team_set_job_control_state(entry->team, + JOB_CONTROL_STATE_NONE, NULL, false); + } } } @@ -1860,7 +2369,7 @@ wait_for_child(pid_t child, uint32 flags, int32* _reason, if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0) team->dead_children.condition_variable.Add(&deadWaitEntry); - locker.Unlock(); + teamLocker.Unlock(); // we got our entry and can return to our caller if (status == B_OK) { @@ -1889,48 +2398,62 @@ wait_for_child(pid_t child, uint32 flags, int32* _reason, // all our children are dead and fail with ECHILD. We check the // condition at this point. if (!ignoreFoundEntriesChecked) { - struct sigaction& handler = thread->sig_action[SIGCHLD - 1]; + teamLocker.Lock(); + + struct sigaction& handler = team->SignalActionFor(SIGCHLD); if ((handler.sa_flags & SA_NOCLDWAIT) != 0 || handler.sa_handler == SIG_IGN) { ignoreFoundEntries = true; } + teamLocker.Unlock(); + ignoreFoundEntriesChecked = true; } } delete freeDeathEntry; - // when we got here, we have a valid death entry, and - // already got unregistered from the team or group - int reason = 0; + // When we got here, we have a valid death entry, and already got + // unregistered from the team or group. Fill in the returned info. + memset(&_info, 0, sizeof(_info)); + _info.si_signo = SIGCHLD; + _info.si_pid = foundEntry.thread; + _info.si_uid = foundEntry.signaling_user; + // TODO: Fill in si_errno? + switch (foundEntry.state) { case JOB_CONTROL_STATE_DEAD: - reason = foundEntry.reason; + _info.si_code = foundEntry.reason; + _info.si_status = foundEntry.reason == CLD_EXITED + ? foundEntry.status : foundEntry.signal; break; case JOB_CONTROL_STATE_STOPPED: - reason = THREAD_STOPPED; + _info.si_code = CLD_STOPPED; + _info.si_status = foundEntry.signal; break; case JOB_CONTROL_STATE_CONTINUED: - reason = THREAD_CONTINUED; + _info.si_code = CLD_CONTINUED; + _info.si_status = 0; break; case JOB_CONTROL_STATE_NONE: // can't happen break; } - *_returnCode = foundEntry.status; - *_reason = (foundEntry.signal << 16) | reason; - // If SIGCHLD is blocked, we shall clear pending SIGCHLDs, if no other child // status is available. - if (is_signal_blocked(SIGCHLD)) { - InterruptsSpinLocker locker(gTeamSpinlock); + TeamLocker teamLocker(team); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + if (is_team_signal_blocked(team, SIGCHLD)) { if (get_job_control_entry(team, child, flags) == NULL) - atomic_and(&thread->sig_pending, ~SIGNAL_TO_MASK(SIGCHLD)); + team->RemovePendingSignals(SIGNAL_TO_MASK(SIGCHLD)); } + schedulerLocker.Unlock(); + teamLocker.Unlock(); + // When the team is dead, the main thread continues to live in the kernel // team for a very short time. To avoid surprises for the caller we rather // wait until the thread is really gone. @@ -1943,9 +2466,8 @@ wait_for_child(pid_t child, uint32 flags, int32* _reason, } -/*! Fills the team_info structure with information from the specified - team. - The team lock must be held when called. +/*! Fills the team_info structure with information from the specified team. + Interrupts must be enabled. The team must not be locked. */ static status_t fill_team_info(Team* team, team_info* info, size_t size) @@ -1953,69 +2475,49 @@ fill_team_info(Team* team, team_info* info, size_t size) if (size != sizeof(team_info)) return B_BAD_VALUE; - // ToDo: Set more informations for team_info + // TODO: Set more informations for team_info memset(info, 0, size); info->team = team->id; - info->thread_count = team->num_threads; + // immutable info->image_count = count_images(team); + // protected by sImageMutex + + TeamLocker teamLocker(team); + InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); + + info->thread_count = team->num_threads; //info->area_count = info->debugger_nub_thread = team->debug_info.nub_thread; info->debugger_nub_port = team->debug_info.nub_port; //info->uid = //info->gid = - strlcpy(info->args, team->args, sizeof(info->args)); + strlcpy(info->args, team->Args(), sizeof(info->args)); info->argc = 1; return B_OK; } -/*! Updates the \c orphaned field of a process_group and returns its new value. - Interrupts must be disabled and team lock be held. +/*! Returns whether the process group contains stopped processes. + The caller must hold the process group's lock. */ static bool -update_orphaned_process_group(process_group* group, pid_t dyingProcess) +process_group_has_stopped_processes(ProcessGroup* group) { - // Orphaned Process Group: "A process group in which the parent of every - // member is either itself a member of the group or is not a member of the - // group's session." (Open Group Base Specs Issue 6) - - // once orphaned, things won't change (exception: cf. setpgid()) - if (group->orphaned) - return true; - Team* team = group->teams; while (team != NULL) { - Team* parent = team->parent; - if (team->id != dyingProcess && parent != NULL - && parent->id != dyingProcess - && parent->group_id != group->id - && parent->session_id == group->session->id) { - return false; + // the parent team's lock guards the job control entry -- acquire it + team->LockTeamAndParent(false); + + if (team->job_control_entry != NULL + && team->job_control_entry->state == JOB_CONTROL_STATE_STOPPED) { + team->UnlockTeamAndParent(); + return true; } - team = team->group_next; - } - - group->orphaned = true; - return true; -} - - -/*! Returns whether the process group contains stopped processes. - Interrupts must be disabled and team lock be held. -*/ -static bool -process_group_has_stopped_processes(process_group* group) -{ - SpinLocker _(gThreadSpinlock); - - Team* team = group->teams; - while (team != NULL) { - if (team->main_thread->state == B_THREAD_SUSPENDED) - return true; + team->UnlockTeamAndParent(); team = team->group_next; } @@ -2024,40 +2526,148 @@ process_group_has_stopped_processes(process_group* group) } +/*! Iterates through all process groups queued in team_remove_team() and signals + those that are orphaned and have stopped processes. + The caller must not hold any team or process group locks. +*/ +static void +orphaned_process_group_check() +{ + // process as long as there are groups in the list + while (true) { + // remove the head from the list + MutexLocker orphanedCheckLocker(sOrphanedCheckLock); + + ProcessGroup* group = sOrphanedCheckProcessGroups.RemoveHead(); + if (group == NULL) + return; + + group->UnsetOrphanedCheck(); + BReference groupReference(group); + + orphanedCheckLocker.Unlock(); + + AutoLocker groupLocker(group); + + // If the group is orphaned and contains stopped processes, we're + // supposed to send SIGHUP + SIGCONT. + if (group->IsOrphaned() && process_group_has_stopped_processes(group)) { + Thread* currentThread = thread_get_current_thread(); + + Signal signal(SIGHUP, SI_USER, B_OK, currentThread->team->id); + send_signal_to_process_group_locked(group, signal, 0); + + signal.SetNumber(SIGCONT); + send_signal_to_process_group_locked(group, signal, 0); + } + } +} + + +static status_t +common_get_team_usage_info(team_id id, int32 who, team_usage_info* info, + uint32 flags) +{ + if (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN) + return B_BAD_VALUE; + + // get the team + Team* team = Team::GetAndLock(id); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); + TeamLocker teamLocker(team, true); + + if ((flags & B_CHECK_PERMISSION) != 0) { + uid_t uid = geteuid(); + if (uid != 0 && uid != team->effective_uid) + return B_NOT_ALLOWED; + } + + bigtime_t kernelTime = 0; + bigtime_t userTime = 0; + + switch (who) { + case B_TEAM_USAGE_SELF: + { + Thread* thread = team->thread_list; + + for (; thread != NULL; thread = thread->team_next) { + InterruptsSpinLocker threadTimeLocker(thread->time_lock); + kernelTime += thread->kernel_time; + userTime += thread->user_time; + } + + kernelTime += team->dead_threads_kernel_time; + userTime += team->dead_threads_user_time; + break; + } + + case B_TEAM_USAGE_CHILDREN: + { + Team* child = team->children; + for (; child != NULL; child = child->siblings_next) { + TeamLocker childLocker(child); + + Thread* thread = team->thread_list; + + for (; thread != NULL; thread = thread->team_next) { + InterruptsSpinLocker threadTimeLocker(thread->time_lock); + kernelTime += thread->kernel_time; + userTime += thread->user_time; + } + + kernelTime += child->dead_threads_kernel_time; + userTime += child->dead_threads_user_time; + } + + kernelTime += team->dead_children.kernel_time; + userTime += team->dead_children.user_time; + break; + } + } + + info->kernel_time = kernelTime; + info->user_time = userTime; + + return B_OK; +} + + // #pragma mark - Private kernel API status_t team_init(kernel_args* args) { - struct process_session* session; - struct process_group* group; - // create the team hash table - new(&sTeamHash) TeamHashTable; - if (sTeamHash.Init(32) != B_OK) + new(&sTeamHash) TeamTable; + if (sTeamHash.Init(64) != B_OK) panic("Failed to init team hash table!"); - sGroupHash = hash_init(16, offsetof(struct process_group, next), - &process_group_compare, &process_group_hash); + new(&sGroupHash) ProcessGroupHashTable; + if (sGroupHash.Init() != B_OK) + panic("Failed to init process group hash table!"); // create initial session and process groups - session = create_process_session(1); + ProcessSession* session = new(std::nothrow) ProcessSession(1); if (session == NULL) panic("Could not create initial session.\n"); + BReference sessionReference(session, true); - group = create_process_group(1); + ProcessGroup* group = new(std::nothrow) ProcessGroup(1); if (group == NULL) panic("Could not create initial process group.\n"); + BReference groupReference(group, true); - insert_group_into_session(session, group); + group->Publish(session); // create the kernel team - sKernelTeam = create_team_struct("kernel_team", true); + sKernelTeam = Team::Create(1, "kernel_team", true); if (sKernelTeam == NULL) panic("could not create kernel team!\n"); - strcpy(sKernelTeam->args, sKernelTeam->name); + sKernelTeam->SetArgs(sKernelTeam->Name()); sKernelTeam->state = TEAM_STATE_NORMAL; sKernelTeam->saved_set_uid = 0; @@ -2076,7 +2686,7 @@ team_init(kernel_args* args) panic("could not create io_context for kernel team!\n"); // stick it in the team hash - sTeamHash.InsertUnchecked(sKernelTeam); + sTeamHash.Insert(sKernelTeam); add_debugger_command_etc("team", &dump_team_info, "Dump info about a particular team", @@ -2106,27 +2716,20 @@ team_max_teams(void) int32 team_used_teams(void) { + InterruptsSpinLocker teamsLocker(sTeamHashLock); return sUsedTeams; } -/*! Iterates through the list of teams. The team spinlock must be held. -*/ -Team* -team_iterate_through_teams(team_iterator_callback callback, void* cookie) -{ - for (TeamHashTable::Iterator it = sTeamHash.GetIterator(); - Team* team = it.Next();) { - if (callback(team, cookie)) - return team; - } +/*! Returns a death entry of a child team specified by ID (if any). + The caller must hold the team's lock. - return NULL; -} - - -/*! Fills the provided death entry if it's in the team. - You need to have the team lock held when calling this function. + \param team The team whose dead children list to check. + \param child The ID of the child for whose death entry to lock. Must be > 0. + \param _deleteEntry Return variable, indicating whether the caller needs to + delete the returned entry. + \return The death entry of the matching team, or \c NULL, if no death entry + for the team was found. */ job_control_entry* team_get_death_entry(Team* team, thread_id child, bool* _deleteEntry) @@ -2155,21 +2758,12 @@ team_get_death_entry(Team* team, thread_id child, bool* _deleteEntry) bool team_is_valid(team_id id) { - Team* team; - cpu_status state; - if (id <= 0) return false; - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + InterruptsSpinLocker teamsLocker(sTeamHashLock); - team = team_get_team_struct_locked(id); - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - return team != NULL; + return team_get_team_struct_locked(id) != NULL; } @@ -2180,111 +2774,101 @@ team_get_team_struct_locked(team_id id) } -/*! This searches the session of the team for the specified group ID. - You must hold the team lock when you call this function. -*/ -struct process_group* -team_get_process_group_locked(struct process_session* session, pid_t id) -{ - struct process_group* group; - struct team_key key; - key.id = id; - - group = (struct process_group*)hash_lookup(sGroupHash, &key); - if (group != NULL && (session == NULL || session == group->session)) - return group; - - return NULL; -} - - -void -team_delete_process_group(struct process_group* group) -{ - if (group == NULL) - return; - - TRACE(("team_delete_process_group(id = %ld)\n", group->id)); - - // remove_group_from_session() keeps this pointer around - // only if the session can be freed as well - if (group->session) { - TRACE(("team_delete_process_group(): frees session %ld\n", - group->session->id)); - free(group->session); - } - - free(group); -} - - void team_set_controlling_tty(int32 ttyIndex) { + // lock the team, so its session won't change while we're playing with it Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); - InterruptsSpinLocker _(gTeamSpinlock); + // get and lock the session + ProcessSession* session = team->group->Session(); + AutoLocker sessionLocker(session); - team->group->session->controlling_tty = ttyIndex; - team->group->session->foreground_group = -1; + // set the session's fields + session->controlling_tty = ttyIndex; + session->foreground_group = -1; } int32 team_get_controlling_tty() { + // lock the team, so its session won't change while we're playing with it Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); - InterruptsSpinLocker _(gTeamSpinlock); + // get and lock the session + ProcessSession* session = team->group->Session(); + AutoLocker sessionLocker(session); - return team->group->session->controlling_tty; + // get the session's field + return session->controlling_tty; } status_t team_set_foreground_process_group(int32 ttyIndex, pid_t processGroupID) { + // lock the team, so its session won't change while we're playing with it Thread* thread = thread_get_current_thread(); Team* team = thread->team; + TeamLocker teamLocker(team); - InterruptsSpinLocker locker(gTeamSpinlock); + // get and lock the session + ProcessSession* session = team->group->Session(); + AutoLocker sessionLocker(session); - process_session* session = team->group->session; - - // must be the controlling tty of the calling process + // check given TTY -- must be the controlling tty of the calling process if (session->controlling_tty != ttyIndex) return ENOTTY; - // check process group -- must belong to our session - process_group* group = team_get_process_group_locked(session, - processGroupID); - if (group == NULL) - return B_BAD_VALUE; + // check given process group -- must belong to our session + { + InterruptsSpinLocker groupHashLocker(sGroupHashLock); + ProcessGroup* group = sGroupHash.Lookup(processGroupID); + if (group == NULL || group->Session() != session) + return B_BAD_VALUE; + } - // If we are a background group, we can't do that unharmed, only if we + // If we are a background group, we can do that unharmed only when we // ignore or block SIGTTOU. Otherwise the group gets a SIGTTOU. if (session->foreground_group != -1 && session->foreground_group != team->group_id - && thread->sig_action[SIGTTOU - 1].sa_handler != SIG_IGN - && !is_signal_blocked(SIGTTOU)) { - pid_t groupID = team->group->id; - locker.Unlock(); - send_signal(-groupID, SIGTTOU); - return B_INTERRUPTED; + && team->SignalActionFor(SIGTTOU).sa_handler != SIG_IGN) { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + if (!is_team_signal_blocked(team, SIGTTOU)) { + pid_t groupID = team->group_id; + + schedulerLocker.Unlock(); + sessionLocker.Unlock(); + teamLocker.Unlock(); + + Signal signal(SIGTTOU, SI_USER, B_OK, team->id); + send_signal_to_process_group(groupID, signal, 0); + return B_INTERRUPTED; + } } - team->group->session->foreground_group = processGroupID; + session->foreground_group = processGroupID; return B_OK; } -/*! Removes the specified team from the global team hash, and from its parent. - It also moves all of its children up to the parent. - You must hold the team lock when you call this function. +/*! Removes the specified team from the global team hash, from its process + group, and from its parent. + It also moves all of its children to the kernel team. + + The caller must hold the following locks: + - \a team's process group's lock, + - the kernel team's lock, + - \a team's parent team's lock (might be the kernel team), and + - \a team's lock. */ void -team_remove_team(Team* team) +team_remove_team(Team* team, pid_t& _signalGroup) { Team* parent = team->parent; @@ -2294,89 +2878,90 @@ team_remove_team(Team* team) parent->dead_children.user_time += team->dead_threads_user_time + team->dead_children.user_time; - // Also grab the thread spinlock while removing the team from the hash. - // This makes the following sequence safe: grab teams lock, lookup team, - // grab threads lock, unlock teams lock, - // mutex_lock_threads_lock(), as used in the VFS code to - // lock another team's IO context. - GRAB_THREAD_LOCK(); - sTeamHash.RemoveUnchecked(team); - RELEASE_THREAD_LOCK(); + // remove the team from the hash table + InterruptsSpinLocker teamsLocker(sTeamHashLock); + sTeamHash.Remove(team); sUsedTeams--; + teamsLocker.Unlock(); + // The team can no longer be accessed by ID. Navigation to it is still + // possible from its process group and its parent and children, but that + // will be rectified shortly. team->state = TEAM_STATE_DEATH; // If we're a controlling process (i.e. a session leader with controlling - // terminal), there's a bit of signalling we have to do. + // terminal), there's a bit of signalling we have to do. We can't do any of + // the signaling here due to the bunch of locks we're holding, but we need + // to determine, whom to signal. + _signalGroup = -1; + bool isSessionLeader = false; if (team->session_id == team->id - && team->group->session->controlling_tty >= 0) { - process_session* session = team->group->session; + && team->group->Session()->controlling_tty >= 0) { + isSessionLeader = true; + + ProcessSession* session = team->group->Session(); + + AutoLocker sessionLocker(session); session->controlling_tty = -1; - - // send SIGHUP to the foreground - if (session->foreground_group >= 0) { - send_signal_etc(-session->foreground_group, SIGHUP, - SIGNAL_FLAG_TEAMS_LOCKED); - } - - // send SIGHUP + SIGCONT to all newly-orphaned process groups with - // stopped processes - Team* child = team->children; - while (child != NULL) { - process_group* childGroup = child->group; - if (!childGroup->orphaned - && update_orphaned_process_group(childGroup, team->id) - && process_group_has_stopped_processes(childGroup)) { - send_signal_etc(-childGroup->id, SIGHUP, - SIGNAL_FLAG_TEAMS_LOCKED); - send_signal_etc(-childGroup->id, SIGCONT, - SIGNAL_FLAG_TEAMS_LOCKED); - } - - child = child->siblings_next; - } - } else { - // update "orphaned" flags of all children's process groups - Team* child = team->children; - while (child != NULL) { - process_group* childGroup = child->group; - if (!childGroup->orphaned) - update_orphaned_process_group(childGroup, team->id); - - child = child->siblings_next; - } - - // update "orphaned" flag of this team's process group - update_orphaned_process_group(team->group, team->id); + _signalGroup = session->foreground_group; } - // reparent each of the team's children - reparent_children(team); - // remove us from our process group remove_team_from_group(team); + // move the team's children to the kernel team + while (Team* child = team->children) { + // remove the child from the current team and add it to the kernel team + TeamLocker childLocker(child); + + remove_team_from_parent(team, child); + insert_team_into_parent(sKernelTeam, child); + + // move job control entries too + sKernelTeam->stopped_children.entries.MoveFrom( + &team->stopped_children.entries); + sKernelTeam->continued_children.entries.MoveFrom( + &team->continued_children.entries); + + // If the team was a session leader with controlling terminal, + // we need to send SIGHUP + SIGCONT to all newly-orphaned process + // groups with stopped processes. Due to locking complications we can't + // do that here, so we only check whether we were a reason for the + // child's process group not being an orphan and, if so, schedule a + // later check (cf. orphaned_process_group_check()). + if (isSessionLeader) { + ProcessGroup* childGroup = child->group; + if (childGroup->Session()->id == team->session_id + && childGroup->id != team->group_id) { + childGroup->ScheduleOrphanedCheck(); + } + } + + // Note, we don't move the dead children entries. Those will be deleted + // when the team structure is deleted. + } + // remove us from our parent remove_team_from_parent(parent, team); } -/*! Kills all threads but the main thread of the team. - To be called on exit of the team's main thread. The teams spinlock must be - held. The function may temporarily drop the spinlock, but will reacquire it - before it returns. +/*! Kills all threads but the main thread of the team and shuts down user + debugging for it. + To be called on exit of the team's main thread. No locks must be held. + \param team The team in question. - \param state The CPU state as returned by disable_interrupts(). Will be - adjusted, if the function needs to unlock and relock. \return The port of the debugger for the team, -1 if none. To be passed to team_delete_team(). */ port_id -team_shutdown_team(Team* team, cpu_status& state) +team_shutdown_team(Team* team) { ASSERT(thread_get_current_thread() == team->main_thread); + TeamLocker teamLocker(team); + // Make sure debugging changes won't happen anymore. port_id debuggerPort = -1; while (true) { @@ -2385,7 +2970,7 @@ team_shutdown_team(Team* team, cpu_status& state) ConditionVariableEntry waitForDebuggerEntry; bool waitForDebugger = false; - GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); + InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); if (team->debug_info.debugger_changed_condition != NULL) { team->debug_info.debugger_changed_condition->Add( @@ -2393,28 +2978,40 @@ team_shutdown_team(Team* team, cpu_status& state) waitForDebugger = true; } else if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) { // The team is being debugged. That will stop with the termination - // of the nub thread. Since we won't let go of the team lock, unless - // we set team::death_entry or until we have removed the tem from - // the team hash, no-one can install a debugger anymore. We fetch - // the debugger's port to send it a message at the bitter end. + // of the nub thread. Since we set the team state to death, no one + // can install a debugger anymore. We fetch the debugger's port to + // send it a message at the bitter end. debuggerPort = team->debug_info.debugger_port; } - RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); + debugInfoLocker.Unlock(); if (!waitForDebugger) break; // wait for the debugger change to be finished - RELEASE_TEAM_LOCK(); - restore_interrupts(state); + teamLocker.Unlock(); waitForDebuggerEntry.Wait(); - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + teamLocker.Lock(); } + // Mark the team as shutting down. That will prevent new threads from being + // created and debugger changes from taking place. + team->state = TEAM_STATE_SHUTDOWN; + + // delete all timers + team->DeleteUserTimers(false); + + // deactivate CPU time user timers for the team + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + if (team->HasActiveCPUTimeUserTimers()) + team->DeactivateCPUTimeUserTimers(); + + schedulerLocker.Unlock(); + // kill all threads but the main thread team_death_entry deathEntry; deathEntry.condition.Init(team, "team death"); @@ -2426,8 +3023,8 @@ team_shutdown_team(Team* team, cpu_status& state) Thread* thread = team->thread_list; while (thread != NULL) { if (thread != team->main_thread) { - send_signal_etc(thread->id, SIGKILLTHR, - B_DO_NOT_RESCHEDULE | SIGNAL_FLAG_TEAMS_LOCKED); + Signal signal(SIGKILLTHR, SI_USER, B_OK, team->id); + send_signal_to_thread(thread, signal, B_DO_NOT_RESCHEDULE); deathEntry.remaining_threads++; } @@ -2441,27 +3038,31 @@ team_shutdown_team(Team* team, cpu_status& state) ConditionVariableEntry entry; deathEntry.condition.Add(&entry); - RELEASE_TEAM_LOCK(); - restore_interrupts(state); + teamLocker.Unlock(); entry.Wait(); - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + teamLocker.Lock(); } team->death_entry = NULL; - // That makes the team "undead" again, but we have the teams spinlock - // and our caller won't drop it until after removing the team from the - // teams hash table. return debuggerPort; } +/*! Called on team exit to notify threads waiting on the team and free most + resources associated with it. + The caller shouldn't hold any locks. +*/ void team_delete_team(Team* team, port_id debuggerPort) { + // Not quite in our job description, but work that has been left by + // team_remove_team() and that can be done now that we're not holding any + // locks. + orphaned_process_group_check(); + team_id teamID = team->id; ASSERT(team->num_threads == 0); @@ -2470,8 +3071,7 @@ team_delete_team(Team* team, port_id debuggerPort) // unexpectedly before being done, we need to notify the waiting // thread now. - cpu_status state = disable_interrupts(); - GRAB_TEAM_LOCK(); + TeamLocker teamLocker(team); if (team->loading_info) { // there's indeed someone waiting @@ -2481,18 +3081,13 @@ team_delete_team(Team* team, port_id debuggerPort) loadingInfo->result = B_ERROR; loadingInfo->done = true; - GRAB_THREAD_LOCK(); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); // wake up the waiting thread if (loadingInfo->thread->state == B_THREAD_SUSPENDED) scheduler_enqueue_in_run_queue(loadingInfo->thread); - - RELEASE_THREAD_LOCK(); } - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - // notify team watchers { @@ -2506,19 +3101,18 @@ team_delete_team(Team* team, port_id debuggerPort) } } + teamLocker.Unlock(); + sNotificationService.Notify(TEAM_REMOVED, team); // free team resources - vfs_put_io_context(team->io_context); delete_realtime_sem_context(team->realtime_sem_context); xsi_sem_undo(team); - delete_owned_ports(team); - sem_delete_owned_sems(team); remove_images(team); team->address_space->RemoveAndPut(); - delete_team_struct(team); + team->ReleaseReference(); // notify the debugger, that the team is gone user_debug_team_deleted(teamID, debuggerPort); @@ -2552,43 +3146,42 @@ team_get_current_team_id(void) status_t team_get_address_space(team_id id, VMAddressSpace** _addressSpace) { - cpu_status state; - Team* team; - status_t status; - - // ToDo: we need to do something about B_SYSTEM_TEAM vs. its real ID (1) - if (id == 1) { + if (id == sKernelTeam->id) { // we're the kernel team, so we don't have to go through all // the hassle (locking and hash lookup) *_addressSpace = VMAddressSpace::GetKernel(); return B_OK; } - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + InterruptsSpinLocker teamsLocker(sTeamHashLock); - team = team_get_team_struct_locked(id); - if (team != NULL) { - team->address_space->Get(); - *_addressSpace = team->address_space; - status = B_OK; - } else - status = B_BAD_VALUE; + Team* team = team_get_team_struct_locked(id); + if (team == NULL) + return B_BAD_VALUE; - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - return status; + team->address_space->Get(); + *_addressSpace = team->address_space; + return B_OK; } /*! Sets the team's job control state. - Interrupts must be disabled and the team lock be held. - \a threadsLocked indicates whether the thread lock is being held, too. + The caller must hold the parent team's lock. Interrupts are allowed to be + enabled or disabled. In the latter case the scheduler lock may be held as + well. + \a team The team whose job control state shall be set. + \a newState The new state to be set. + \a signal The signal the new state was caused by. Can \c NULL, if none. Then + the caller is responsible for filling in the following fields of the + entry before releasing the parent team's lock, unless the new state is + \c JOB_CONTROL_STATE_NONE: + - \c signal: The number of the signal causing the state change. + - \c signaling_user: The real UID of the user sending the signal. + \a schedulerLocked indicates whether the scheduler lock is being held, too. */ void team_set_job_control_state(Team* team, job_control_state newState, - int signal, bool threadsLocked) + Signal* signal, bool schedulerLocked) { if (team == NULL || team->job_control_entry == NULL) return; @@ -2618,7 +3211,11 @@ team_set_job_control_state(Team* team, job_control_state newState, } entry->state = newState; - entry->signal = signal; + + if (signal != NULL) { + entry->signal = signal->Number(); + entry->signaling_user = signal->SendingUser(); + } // add to new list team_job_control_children* childList = NULL; @@ -2641,49 +3238,40 @@ team_set_job_control_state(Team* team, job_control_state newState, if (childList != NULL) { childList->entries.Add(entry); team->parent->dead_children.condition_variable.NotifyAll( - threadsLocked); + schedulerLocked); } } -/*! Adds a hook to the team that is called as soon as this - team goes away. +/*! Adds a hook to the team that is called as soon as this team goes away. This call might get public in the future. */ status_t start_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) { - struct team_watcher* watcher; - Team* team; - cpu_status state; - if (hook == NULL || teamID < B_OK) return B_BAD_VALUE; - watcher = (struct team_watcher*)malloc(sizeof(struct team_watcher)); + // create the watcher object + team_watcher* watcher = (team_watcher*)malloc(sizeof(team_watcher)); if (watcher == NULL) return B_NO_MEMORY; watcher->hook = hook; watcher->data = data; - // find team and add watcher - - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - team = team_get_team_struct_locked(teamID); - if (team != NULL) - list_add_item(&team->watcher_list, watcher); - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - + // add watcher, if the team isn't already dying + // get the team + Team* team = Team::GetAndLock(teamID); if (team == NULL) { free(watcher); return B_BAD_TEAM_ID; } + list_add_item(&team->watcher_list, watcher); + + team->UnlockAndReleaseReference(); + return B_OK; } @@ -2691,33 +3279,26 @@ start_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) status_t stop_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) { - struct team_watcher* watcher = NULL; - Team* team; - cpu_status state; - - if (hook == NULL || teamID < B_OK) + if (hook == NULL || teamID < 0) return B_BAD_VALUE; - // find team and remove watcher (if present) + // get team and remove watcher (if present) + Team* team = Team::GetAndLock(teamID); + if (team == NULL) + return B_BAD_TEAM_ID; - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - team = team_get_team_struct_locked(teamID); - if (team != NULL) { - // search for watcher - while ((watcher = (struct team_watcher*)list_get_next_item( - &team->watcher_list, watcher)) != NULL) { - if (watcher->hook == hook && watcher->data == data) { - // got it! - list_remove_item(&team->watcher_list, watcher); - break; - } + // search for watcher + team_watcher* watcher = NULL; + while ((watcher = (team_watcher*)list_get_next_item( + &team->watcher_list, watcher)) != NULL) { + if (watcher->hook == hook && watcher->data == data) { + // got it! + list_remove_item(&team->watcher_list, watcher); + break; } } - RELEASE_TEAM_LOCK(); - restore_interrupts(state); + team->UnlockAndReleaseReference(); if (watcher == NULL) return B_ENTRY_NOT_FOUND; @@ -2727,7 +3308,9 @@ stop_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) } -/*! The team lock must be held or the team must still be single threaded. +/*! Allocates a user_thread structure from the team. + The team lock must be held, unless the function is called for the team's + main thread. Interrupts must be enabled. */ struct user_thread* team_allocate_user_thread(Team* team) @@ -2735,41 +3318,48 @@ team_allocate_user_thread(Team* team) if (team->user_data == 0) return NULL; - user_thread* thread = NULL; - // take an entry from the free list, if any if (struct free_user_thread* entry = team->free_user_threads) { - thread = entry->thread; + user_thread* thread = entry->thread; team->free_user_threads = entry->next; - deferred_free(entry); + free(entry); return thread; - } else { - // enough space left? - size_t needed = _ALIGN(sizeof(user_thread)); - if (team->user_data_size - team->used_user_data < needed) - return NULL; - // TODO: This imposes a per team thread limit! We should resize the - // area, if necessary. That's problematic at this point, though, since - // we've got the team lock. - - thread = (user_thread*)(team->user_data + team->used_user_data); - team->used_user_data += needed; } - thread->defer_signals = 0; - thread->pending_signals = 0; - thread->wait_status = B_OK; + while (true) { + // enough space left? + size_t needed = ROUNDUP(sizeof(user_thread), 8); + if (team->user_data_size - team->used_user_data < needed) { + // try to resize the area + if (resize_area(team->user_data_area, + team->user_data_size + B_PAGE_SIZE) != B_OK) { + return NULL; + } - return thread; + // resized user area successfully -- try to allocate the user_thread + // again + team->user_data_size += B_PAGE_SIZE; + continue; + } + + // allocate the user_thread + user_thread* thread + = (user_thread*)(team->user_data + team->used_user_data); + team->used_user_data += needed; + + return thread; + } } -/*! The team lock must not be held. \a thread must be the current thread. +/*! Frees the given user_thread structure. + The team's lock must not be held. Interrupts must be enabled. + \param team The team the user thread was allocated from. + \param userThread The user thread to free. */ void -team_free_user_thread(Thread* thread) +team_free_user_thread(Team* team, struct user_thread* userThread) { - user_thread* userThread = thread->user_thread; if (userThread == NULL) return; @@ -2781,16 +3371,12 @@ team_free_user_thread(Thread* thread) return; } - InterruptsSpinLocker _(gTeamSpinlock); - - // detach from thread - SpinLocker threadLocker(gThreadSpinlock); - thread->user_thread = NULL; - threadLocker.Unlock(); + // add to free list + TeamLocker teamLocker(team); entry->thread = userThread; - entry->next = thread->team->free_user_threads; - thread->team->free_user_threads = entry; + entry->next = team->free_user_threads; + team->free_user_threads = entry; } @@ -2989,201 +3575,95 @@ load_image_etc(int32 argCount, const char* const* args, status_t wait_for_team(team_id id, status_t* _returnCode) { - Team* team; - thread_id thread; - cpu_status state; + // check whether the team exists + InterruptsSpinLocker teamsLocker(sTeamHashLock); - // find main thread and wait for that + Team* team = team_get_team_struct_locked(id); + if (team == NULL) + return B_BAD_TEAM_ID; - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + id = team->id; - team = team_get_team_struct_locked(id); - if (team != NULL && team->main_thread != NULL) - thread = team->main_thread->id; - else - thread = B_BAD_THREAD_ID; + teamsLocker.Unlock(); - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - if (thread < 0) - return thread; - - return wait_for_thread(thread, _returnCode); + // wait for the main thread (it has the same ID as the team) + return wait_for_thread(id, _returnCode); } status_t kill_team(team_id id) { - status_t status = B_OK; - thread_id threadID = -1; - Team* team; - cpu_status state; + InterruptsSpinLocker teamsLocker(sTeamHashLock); - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + Team* team = team_get_team_struct_locked(id); + if (team == NULL) + return B_BAD_TEAM_ID; - team = team_get_team_struct_locked(id); - if (team != NULL) { - if (team != sKernelTeam) { - threadID = team->id; - // the team ID is the same as the ID of its main thread - } else - status = B_NOT_ALLOWED; - } else - status = B_BAD_THREAD_ID; + id = team->id; - RELEASE_TEAM_LOCK(); - restore_interrupts(state); + teamsLocker.Unlock(); - if (status < B_OK) - return status; + if (team == sKernelTeam) + return B_NOT_ALLOWED; - // just kill the main thread in the team. The cleanup code there will - // take care of the team - return kill_thread(threadID); + // Just kill the team's main thread (it has same ID as the team). The + // cleanup code there will take care of the team. + return kill_thread(id); } status_t _get_team_info(team_id id, team_info* info, size_t size) { - cpu_status state; - status_t status = B_OK; - Team* team; + // get the team + Team* team = Team::Get(id); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - if (id == B_CURRENT_TEAM) - team = thread_get_current_thread()->team; - else - team = team_get_team_struct_locked(id); - - if (team == NULL) { - status = B_BAD_TEAM_ID; - goto err; - } - - status = fill_team_info(team, info, size); - -err: - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - return status; + // fill in the info + return fill_team_info(team, info, size); } status_t _get_next_team_info(int32* cookie, team_info* info, size_t size) { - status_t status = B_BAD_TEAM_ID; - Team* team = NULL; int32 slot = *cookie; - team_id lastTeamID; - cpu_status state; - if (slot < 1) slot = 1; - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + InterruptsSpinLocker locker(sTeamHashLock); - lastTeamID = peek_next_thread_id(); - if (slot >= lastTeamID) - goto err; + team_id lastTeamID = peek_next_thread_id(); + // TODO: This is broken, since the id can wrap around! // get next valid team + Team* team = NULL; while (slot < lastTeamID && !(team = team_get_team_struct_locked(slot))) slot++; - if (team) { - status = fill_team_info(team, info, size); - *cookie = ++slot; - } + if (team == NULL) + return B_BAD_TEAM_ID; -err: - RELEASE_TEAM_LOCK(); - restore_interrupts(state); + // get a reference to the team and unlock + BReference teamReference(team); + locker.Unlock(); - return status; + // fill in the info + *cookie = ++slot; + return fill_team_info(team, info, size); } status_t _get_team_usage_info(team_id id, int32 who, team_usage_info* info, size_t size) { - bigtime_t kernelTime = 0, userTime = 0; - status_t status = B_OK; - Team* team; - cpu_status state; - - if (size != sizeof(team_usage_info) - || (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN)) + if (size != sizeof(team_usage_info)) return B_BAD_VALUE; - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - if (id == B_CURRENT_TEAM) - team = thread_get_current_thread()->team; - else - team = team_get_team_struct_locked(id); - - if (team == NULL) { - status = B_BAD_TEAM_ID; - goto out; - } - - switch (who) { - case B_TEAM_USAGE_SELF: - { - Thread* thread = team->thread_list; - - for (; thread != NULL; thread = thread->team_next) { - kernelTime += thread->kernel_time; - userTime += thread->user_time; - } - - kernelTime += team->dead_threads_kernel_time; - userTime += team->dead_threads_user_time; - break; - } - - case B_TEAM_USAGE_CHILDREN: - { - Team* child = team->children; - for (; child != NULL; child = child->siblings_next) { - Thread* thread = team->thread_list; - - for (; thread != NULL; thread = thread->team_next) { - kernelTime += thread->kernel_time; - userTime += thread->user_time; - } - - kernelTime += child->dead_threads_kernel_time; - userTime += child->dead_threads_user_time; - } - - kernelTime += team->dead_children.kernel_time; - userTime += team->dead_children.user_time; - break; - } - } - -out: - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - if (status == B_OK) { - info->kernel_time = kernelTime; - info->user_time = userTime; - } - - return status; + return common_get_team_usage_info(id, who, info, 0); } @@ -3198,66 +3678,72 @@ pid_t getppid(void) { Team* team = thread_get_current_thread()->team; - cpu_status state; - pid_t parent; - state = disable_interrupts(); - GRAB_TEAM_LOCK(); + TeamLocker teamLocker(team); - parent = team->parent->id; - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - return parent; + return team->parent->id; } pid_t -getpgid(pid_t process) +getpgid(pid_t id) { - Thread* thread; - pid_t result = -1; - cpu_status state; + if (id < 0) { + errno = EINVAL; + return -1; + } - if (process == 0) - process = thread_get_current_thread()->team->id; + if (id == 0) { + // get process group of the calling process + Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); + return team->group_id; + } - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // get the team + Team* team = Team::GetAndLock(id); + if (team == NULL) { + errno = ESRCH; + return -1; + } - thread = thread_get_thread_struct_locked(process); - if (thread != NULL) - result = thread->team->group_id; + // get the team's process group ID + pid_t groupID = team->group_id; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + team->UnlockAndReleaseReference(); - return thread != NULL ? result : B_BAD_VALUE; + return groupID; } pid_t -getsid(pid_t process) +getsid(pid_t id) { - Thread* thread; - pid_t result = -1; - cpu_status state; + if (id < 0) { + errno = EINVAL; + return -1; + } - if (process == 0) - process = thread_get_current_thread()->team->id; + if (id == 0) { + // get session of the calling process + Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); + return team->session_id; + } - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // get the team + Team* team = Team::GetAndLock(id); + if (team == NULL) { + errno = ESRCH; + return -1; + } - thread = thread_get_thread_struct_locked(process); - if (thread != NULL) - result = thread->team->session_id; + // get the team's session ID + pid_t sessionID = team->session_id; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + team->UnlockAndReleaseReference(); - return thread != NULL ? result : B_BAD_VALUE; + return sessionID; } @@ -3299,34 +3785,22 @@ _user_fork(void) } -thread_id -_user_wait_for_child(thread_id child, uint32 flags, int32* _userReason, - status_t* _userReturnCode) +pid_t +_user_wait_for_child(thread_id child, uint32 flags, siginfo_t* userInfo) { - status_t returnCode; - int32 reason; - thread_id deadChild; - - if ((_userReason != NULL && !IS_USER_ADDRESS(_userReason)) - || (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode))) + if (userInfo != NULL && !IS_USER_ADDRESS(userInfo)) return B_BAD_ADDRESS; - deadChild = wait_for_child(child, flags, &reason, &returnCode); + siginfo_t info; + pid_t foundChild = wait_for_child(child, flags, info); + if (foundChild < 0) + return syscall_restart_handle_post(foundChild); - if (deadChild >= B_OK) { - // copy result data on successful completion - if ((_userReason != NULL - && user_memcpy(_userReason, &reason, sizeof(int32)) < B_OK) - || (_userReturnCode != NULL - && user_memcpy(_userReturnCode, &returnCode, sizeof(status_t)) - < B_OK)) { - return B_BAD_ADDRESS; - } + // copy info back to userland + if (userInfo != NULL && user_memcpy(userInfo, &info, sizeof(info)) != B_OK) + return B_BAD_ADDRESS; - return deadChild; - } - - return syscall_restart_handle_post(deadChild); + return foundChild; } @@ -3338,29 +3812,39 @@ _user_process_info(pid_t process, int32 which) && process != 0 && process != thread_get_current_thread()->team->id) return B_BAD_VALUE; + pid_t result; switch (which) { case SESSION_ID: - return getsid(process); + result = getsid(process); + break; case GROUP_ID: - return getpgid(process); + result = getpgid(process); + break; case PARENT_ID: - return getppid(); + result = getppid(); + break; + default: + return B_BAD_VALUE; } - return B_BAD_VALUE; + return result >= 0 ? result : errno; } pid_t _user_setpgid(pid_t processID, pid_t groupID) { - Thread* thread = thread_get_current_thread(); - Team* currentTeam = thread->team; - Team* team; + // setpgid() can be called either by the parent of the target process or + // by the process itself to do one of two things: + // * Create a new process group with the target process' ID and the target + // process as group leader. + // * Set the target process' process group to an already existing one in the + // same session. if (groupID < 0) return B_BAD_VALUE; + Team* currentTeam = thread_get_current_thread()->team; if (processID == 0) processID = currentTeam->id; @@ -3368,131 +3852,135 @@ _user_setpgid(pid_t processID, pid_t groupID) if (groupID == 0) groupID = processID; - if (processID == currentTeam->id) { - // we set our own group + // We loop when running into the following race condition: We create a new + // process group, because there isn't one with that ID yet, but later when + // trying to publish it, we find that someone else created and published + // a group with that ID in the meantime. In that case we just restart the + // whole action. + while (true) { + // Look up the process group by ID. If it doesn't exist yet and we are + // allowed to create a new one, do that. + ProcessGroup* group = ProcessGroup::Get(groupID); + bool newGroup = false; + if (group == NULL) { + if (groupID != processID) + return B_NOT_ALLOWED; - // we must not change our process group ID if we're a session leader - if (is_session_leader(currentTeam)) - return B_NOT_ALLOWED; - } else { - // another team is the target of the call -- check it out - InterruptsSpinLocker _(gTeamSpinlock); + group = new(std::nothrow) ProcessGroup(groupID); + if (group == NULL) + return B_NO_MEMORY; - team = team_get_team_struct_locked(processID); + newGroup = true; + } + BReference groupReference(group, true); + + // get the target team + Team* team = Team::Get(processID); if (team == NULL) return ESRCH; + BReference teamReference(team, true); - // The team must be a child of the calling team and in the same session. - // (If that's the case it isn't a session leader either.) - if (team->parent != currentTeam - || team->session_id != currentTeam->session_id) { + // lock the new process group and the team's current process group + while (true) { + // lock the team's current process group + team->LockProcessGroup(); + + ProcessGroup* oldGroup = team->group; + if (oldGroup == group) { + // it's the same as the target group, so just bail out + oldGroup->Unlock(); + return group->id; + } + + oldGroup->AcquireReference(); + + // lock the target process group, if locking order allows it + if (newGroup || group->id > oldGroup->id) { + group->Lock(); + break; + } + + // try to lock + if (group->TryLock()) + break; + + // no dice -- unlock the team's current process group and relock in + // the correct order + oldGroup->Unlock(); + + group->Lock(); + oldGroup->Lock(); + + // check whether things are still the same + TeamLocker teamLocker(team); + if (team->group == oldGroup) + break; + + // something changed -- unlock everything and retry + teamLocker.Unlock(); + oldGroup->Unlock(); + group->Unlock(); + oldGroup->ReleaseReference(); + } + + // we now have references and locks of both new and old process group + BReference oldGroupReference(team->group, true); + AutoLocker oldGroupLocker(team->group, true); + AutoLocker groupLocker(group, true); + + // also lock the target team and its parent + team->LockTeamAndParent(false); + TeamLocker parentLocker(team->parent, true); + TeamLocker teamLocker(team, true); + + // perform the checks + if (team == currentTeam) { + // we set our own group + + // we must not change our process group ID if we're a session leader + if (is_session_leader(currentTeam)) + return B_NOT_ALLOWED; + } else { + // Calling team != target team. The target team must be a child of + // the calling team and in the same session. (If that's the case it + // isn't a session leader either.) + if (team->parent != currentTeam + || team->session_id != currentTeam->session_id) { + return B_NOT_ALLOWED; + } + + // The call is also supposed to fail on a child, when the child has + // already executed exec*() [EACCES]. + if ((team->flags & TEAM_FLAG_EXEC_DONE) != 0) + return EACCES; + } + + // If we created a new process group, publish it now. + if (newGroup) { + InterruptsSpinLocker groupHashLocker(sGroupHashLock); + if (sGroupHash.Lookup(groupID)) { + // A group with the group ID appeared since we first checked. + // Back to square one. + continue; + } + + group->PublishLocked(team->group->Session()); + } else if (group->Session()->id != team->session_id) { + // The existing target process group belongs to a different session. + // That's not allowed. return B_NOT_ALLOWED; } - if (team->group_id == groupID) - return groupID; + // Everything is ready -- set the group. + remove_team_from_group(team); + insert_team_into_group(group, team); - // The call is also supposed to fail on a child, when the child already - // has executed exec*() [EACCES]. - if ((team->flags & TEAM_FLAG_EXEC_DONE) != 0) - return EACCES; - } - - struct process_group* group = NULL; - if (groupID == processID) { - // A new process group might be needed. - group = create_process_group(groupID); - if (group == NULL) - return B_NO_MEMORY; - - // Assume orphaned. We consider the situation of the team's parent - // below. - group->orphaned = true; - } - - status_t status = B_OK; - struct process_group* freeGroup = NULL; - - InterruptsSpinLocker locker(gTeamSpinlock); - - team = team_get_team_struct_locked(processID); - if (team != NULL) { - // check the conditions again -- they might have changed in the meantime - if (is_session_leader(team) - || team->session_id != currentTeam->session_id) { - status = B_NOT_ALLOWED; - } else if (team != currentTeam - && (team->flags & TEAM_FLAG_EXEC_DONE) != 0) { - status = EACCES; - } else if (team->group_id == groupID) { - // the team is already in the desired process group - freeGroup = group; - } else { - // Check if a process group with the requested ID already exists. - struct process_group* targetGroup - = team_get_process_group_locked(team->group->session, groupID); - if (targetGroup != NULL) { - // In case of processID == groupID we have to free the - // allocated group. - freeGroup = group; - } else if (processID == groupID) { - // We created a new process group, let us insert it into the - // team's session. - insert_group_into_session(team->group->session, group); - targetGroup = group; - } - - if (targetGroup != NULL) { - // we got a group, let's move the team there - process_group* oldGroup = team->group; - - remove_team_from_group(team); - insert_team_into_group(targetGroup, team); - - // Update the "orphaned" flag of all potentially affected - // groups. - - // the team's old group - if (oldGroup->teams != NULL) { - oldGroup->orphaned = false; - update_orphaned_process_group(oldGroup, -1); - } - - // the team's new group - Team* parent = team->parent; - targetGroup->orphaned &= parent == NULL - || parent->group == targetGroup - || team->parent->session_id != team->session_id; - - // children's groups - Team* child = team->children; - while (child != NULL) { - child->group->orphaned = false; - update_orphaned_process_group(child->group, -1); - - child = child->siblings_next; - } - } else - status = B_NOT_ALLOWED; - } - } else - status = B_NOT_ALLOWED; - - // Changing the process group might have changed the situation for a parent - // waiting in wait_for_child(). Hence we notify it. - if (status == B_OK) + // Changing the process group might have changed the situation for a + // parent waiting in wait_for_child(). Hence we notify it. team->parent->dead_children.condition_variable.NotifyAll(false); - locker.Unlock(); - - if (status != B_OK) { - // in case of error, the group hasn't been added into the hash - team_delete_process_group(group); + return group->id; } - - team_delete_process_group(freeGroup); - - return status == B_OK ? groupID : status; } @@ -3500,47 +3988,39 @@ pid_t _user_setsid(void) { Team* team = thread_get_current_thread()->team; - struct process_session* session; - struct process_group* group; - cpu_status state; - bool failed = false; + + // create a new process group and session + ProcessGroup* group = new(std::nothrow) ProcessGroup(team->id); + if (group == NULL) + return B_NO_MEMORY; + BReference groupReference(group, true); + AutoLocker groupLocker(group); + + ProcessSession* session = new(std::nothrow) ProcessSession(group->id); + if (session == NULL) + return B_NO_MEMORY; + BReference sessionReference(session, true); + + // lock the team's current process group, parent, and the team itself + team->LockTeamParentAndProcessGroup(); + AutoLocker oldGroupLocker(team->group, true); + TeamLocker parentLocker(team->parent, true); + TeamLocker teamLocker(team, true); // the team must not already be a process group leader if (is_process_group_leader(team)) return B_NOT_ALLOWED; - group = create_process_group(team->id); - if (group == NULL) - return B_NO_MEMORY; + // remove the team from the old and add it to the new process group + remove_team_from_group(team); + group->Publish(session); + insert_team_into_group(group, team); - session = create_process_session(group->id); - if (session == NULL) { - team_delete_process_group(group); - return B_NO_MEMORY; - } + // Changing the process group might have changed the situation for a + // parent waiting in wait_for_child(). Hence we notify it. + team->parent->dead_children.condition_variable.NotifyAll(false); - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - // this may have changed since the check above - if (!is_process_group_leader(team)) { - remove_team_from_group(team); - - insert_group_into_session(session, group); - insert_team_into_group(group, team); - } else - failed = true; - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - if (failed) { - team_delete_process_group(group); - free(session); - return B_NOT_ALLOWED; - } - - return team->group_id; + return group->id; } @@ -3598,25 +4078,33 @@ _user_exit_team(status_t returnValue) { Thread* thread = thread_get_current_thread(); Team* team = thread->team; - Thread* mainThread = team->main_thread; - mainThread->exit.status = returnValue; - mainThread->exit.reason = THREAD_RETURN_EXIT; + // set this thread's exit status + thread->exit.status = returnValue; - // Also set the exit code in the current thread for the sake of it - if (thread != mainThread) { - thread->exit.status = returnValue; - thread->exit.reason = THREAD_RETURN_EXIT; + // set the team exit status + TeamLocker teamLocker(team); + + if (!team->exit.initialized) { + team->exit.reason = CLD_EXITED; + team->exit.signal = 0; + team->exit.signaling_user = 0; + team->exit.status = returnValue; + team->exit.initialized = true; } - if ((atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_PREVENT_EXIT) - != 0) { - // This team is currently being debugged, and requested that teams - // should not be exited. + teamLocker.Unlock(); + + // Stop the thread, if the team is being debugged and that has been + // requested. + if ((atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_PREVENT_EXIT) != 0) user_debug_stop_thread(); - } - send_signal(thread->id, SIGKILL); + // Send this thread a SIGKILL. This makes sure the thread will not return to + // userland. The signal handling code forwards the signal to the main + // thread (if that's not already this one), which will take the team down. + Signal signal(SIGKILL, SI_USER, B_OK, team->id); + send_signal_to_thread(thread, signal, 0); } @@ -3681,18 +4169,17 @@ status_t _user_get_team_usage_info(team_id team, int32 who, team_usage_info* userInfo, size_t size) { + if (size != sizeof(team_usage_info)) + return B_BAD_VALUE; + team_usage_info info; - status_t status; + status_t status = common_get_team_usage_info(team, who, &info, + B_CHECK_PERMISSION); - if (!IS_USER_ADDRESS(userInfo)) - return B_BAD_ADDRESS; - - status = _get_team_usage_info(team, who, &info, size); - if (status != B_OK) - return status; - - if (user_memcpy(userInfo, &info, size) < B_OK) + if (userInfo == NULL || !IS_USER_ADDRESS(userInfo) + || user_memcpy(userInfo, &info, size) != B_OK) { return B_BAD_ADDRESS; + } return status; } @@ -3712,24 +4199,45 @@ _user_get_extended_team_info(team_id teamID, uint32 flags, void* buffer, KMessage info; if ((flags & B_TEAM_INFO_BASIC) != 0) { - // allocate memory for a copy of the team struct - Team* teamClone = new(std::nothrow) Team; + // allocate memory for a copy of the needed team data + struct ExtendedTeamData { + team_id id; + pid_t group_id; + pid_t session_id; + uid_t real_uid; + gid_t real_gid; + uid_t effective_uid; + gid_t effective_gid; + char name[B_OS_NAME_LENGTH]; + }; + + ExtendedTeamData* teamClone + = (ExtendedTeamData*)malloc(sizeof(ExtendedTeamData)); + // It would be nicer to use new, but then we'd have to use + // ObjectDeleter and declare the structure outside of the function + // due to template parameter restrictions. if (teamClone == NULL) return B_NO_MEMORY; - ObjectDeleter teamCloneDeleter(teamClone); + MemoryDeleter teamCloneDeleter(teamClone); io_context* ioContext; { // get the team structure - InterruptsSpinLocker _(gTeamSpinlock); - Team* team = teamID == B_CURRENT_TEAM - ? thread_get_current_thread()->team - : team_get_team_struct_locked(teamID); + Team* team = Team::GetAndLock(teamID); if (team == NULL) return B_BAD_TEAM_ID; + BReference teamReference(team, true); + TeamLocker teamLocker(team, true); - // copy it - memcpy(teamClone, team, sizeof(*team)); + // copy the data + teamClone->id = team->id; + strlcpy(teamClone->name, team->Name(), sizeof(teamClone->name)); + teamClone->group_id = team->group_id; + teamClone->session_id = team->session_id; + teamClone->real_uid = team->real_uid; + teamClone->real_gid = team->real_gid; + teamClone->effective_uid = team->effective_uid; + teamClone->effective_gid = team->effective_gid; // also fetch a reference to the I/O context ioContext = team->io_context; diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index f5dd86c88c..382c600c4b 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -20,10 +20,11 @@ #include #include +#include + #include #include -#include #include #include @@ -48,6 +49,8 @@ #include #include +#include "TeamThreadTables.h" + //#define TRACE_THREAD #ifdef TRACE_THREAD @@ -60,23 +63,25 @@ #define THREAD_MAX_MESSAGE_SIZE 65536 -struct thread_key { - thread_id id; -}; +// #pragma mark - ThreadHashTable + + +typedef BKernel::TeamThreadTable ThreadHashTable; -// global -spinlock gThreadSpinlock = B_SPINLOCK_INITIALIZER; // thread list static Thread sIdleThreads[B_MAX_CPU_COUNT]; -static hash_table *sThreadHash = NULL; -static thread_id sNextThreadID = 1; +static ThreadHashTable sThreadHash; +static spinlock sThreadHashLock = B_SPINLOCK_INITIALIZER; +static thread_id sNextThreadID = 2; + // ID 1 is allocated for the kernel by Team::Team() behind our back -// some arbitrary chosen limits - should probably depend on the available +// some arbitrarily chosen limits -- should probably depend on the available // memory (the limit is not yet enforced) static int32 sMaxThreads = 4096; static int32 sUsedThreads = 0; + struct UndertakerEntry : DoublyLinkedListLinkImpl { Thread* thread; team_id teamID; @@ -90,6 +95,22 @@ struct UndertakerEntry : DoublyLinkedListLinkImpl { }; +struct ThreadEntryArguments { + status_t (*kernelFunction)(void* argument); + void* argument; + bool enterUserland; +}; + +struct UserThreadEntryArguments : ThreadEntryArguments { + addr_t userlandEntry; + void* userlandArgument1; + void* userlandArgument2; + pthread_t pthread; + arch_fork_arg* forkArgs; + uint32 flags; +}; + + class ThreadNotificationService : public DefaultNotificationService { public: ThreadNotificationService() @@ -127,12 +148,448 @@ static ThreadNotificationService sNotificationService; // object cache to allocate thread structures from static object_cache* sThreadCache; -static void thread_kthread_entry(void); -static void thread_kthread_exit(void); + +// #pragma mark - Thread + + +/*! Constructs a thread. + + \param name The thread's name. + \param threadID The ID to be assigned to the new thread. If + \code < 0 \endcode a fresh one is allocated. + \param cpu The CPU the thread shall be assigned. +*/ +Thread::Thread(const char* name, thread_id threadID, struct cpu_ent* cpu) + : + flags(0), + serial_number(-1), + hash_next(NULL), + team_next(NULL), + queue_next(NULL), + priority(-1), + next_priority(-1), + io_priority(-1), + cpu(cpu), + previous_cpu(NULL), + pinned_to_cpu(0), + sig_block_mask(0), + sigsuspend_original_unblocked_mask(0), + user_signal_context(NULL), + signal_stack_base(0), + signal_stack_size(0), + signal_stack_enabled(false), + in_kernel(true), + was_yielded(false), + user_thread(NULL), + fault_handler(0), + page_faults_allowed(1), + team(NULL), + select_infos(NULL), + kernel_stack_area(-1), + kernel_stack_base(0), + user_stack_area(-1), + user_stack_base(0), + user_local_storage(0), + kernel_errno(0), + user_time(0), + kernel_time(0), + last_time(0), + cpu_clock_offset(0), + post_interrupt_callback(NULL), + post_interrupt_data(NULL) +{ + id = threadID >= 0 ? threadID : allocate_thread_id(); + visible = false; + + // init locks + char lockName[32]; + snprintf(lockName, sizeof(lockName), "Thread:%" B_PRId32, id); + mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); + + B_INITIALIZE_SPINLOCK(&time_lock); + + // init name + if (name != NULL) + strlcpy(this->name, name, B_OS_NAME_LENGTH); + else + strcpy(this->name, "unnamed thread"); + + alarm.period = 0; + + exit.status = 0; + + list_init(&exit.waiters); + + exit.sem = -1; + msg.write_sem = -1; + msg.read_sem = -1; + + // add to thread table -- yet invisible + InterruptsSpinLocker threadHashLocker(sThreadHashLock); + sThreadHash.Insert(this); +} + + +Thread::~Thread() +{ + // Delete resources that should actually be deleted by the thread itself, + // when it exited, but that might still exist, if the thread was never run. + + if (user_stack_area >= 0) + delete_area(user_stack_area); + + DeleteUserTimers(false); + + // delete the resources, that may remain in either case + + if (kernel_stack_area >= 0) + delete_area(kernel_stack_area); + + fPendingSignals.Clear(); + + if (exit.sem >= 0) + delete_sem(exit.sem); + if (msg.write_sem >= 0) + delete_sem(msg.write_sem); + if (msg.read_sem >= 0) + delete_sem(msg.read_sem); + + scheduler_on_thread_destroy(this); + + mutex_destroy(&fLock); + + // remove from thread table + InterruptsSpinLocker threadHashLocker(sThreadHashLock); + sThreadHash.Remove(this); +} + + +/*static*/ status_t +Thread::Create(const char* name, Thread*& _thread) +{ + Thread* thread = new Thread(name, -1, NULL); + if (thread == NULL) + return B_NO_MEMORY; + + status_t error = thread->Init(false); + if (error != B_OK) { + delete thread; + return error; + } + + _thread = thread; + return B_OK; +} + + +/*static*/ Thread* +Thread::Get(thread_id id) +{ + InterruptsSpinLocker threadHashLocker(sThreadHashLock); + Thread* thread = sThreadHash.Lookup(id); + if (thread != NULL) + thread->AcquireReference(); + return thread; +} + + +/*static*/ Thread* +Thread::GetAndLock(thread_id id) +{ + // look it up and acquire a reference + InterruptsSpinLocker threadHashLocker(sThreadHashLock); + Thread* thread = sThreadHash.Lookup(id); + if (thread == NULL) + return NULL; + + thread->AcquireReference(); + threadHashLocker.Unlock(); + + // lock and check, if it is still in the hash table + thread->Lock(); + threadHashLocker.Lock(); + + if (sThreadHash.Lookup(id) == thread) + return thread; + + threadHashLocker.Unlock(); + + // nope, the thread is no longer in the hash table + thread->UnlockAndReleaseReference(); + + return NULL; +} + + +/*static*/ Thread* +Thread::GetDebug(thread_id id) +{ + return sThreadHash.Lookup(id, false); +} + + +/*static*/ bool +Thread::IsAlive(thread_id id) +{ + InterruptsSpinLocker threadHashLocker(sThreadHashLock); + return sThreadHash.Lookup(id) != NULL; +} + + +void* +Thread::operator new(size_t size) +{ + return object_cache_alloc(sThreadCache, 0); +} + + +void* +Thread::operator new(size_t, void* pointer) +{ + return pointer; +} + + +void +Thread::operator delete(void* pointer, size_t size) +{ + object_cache_free(sThreadCache, pointer, 0); +} + + +status_t +Thread::Init(bool idleThread) +{ + status_t error = scheduler_on_thread_create(this, idleThread); + if (error != B_OK) + return error; + + char temp[64]; + sprintf(temp, "thread_%ld_retcode_sem", id); + exit.sem = create_sem(0, temp); + if (exit.sem < 0) + return exit.sem; + + sprintf(temp, "%s send", name); + msg.write_sem = create_sem(1, temp); + if (msg.write_sem < 0) + return msg.write_sem; + + sprintf(temp, "%s receive", name); + msg.read_sem = create_sem(0, temp); + if (msg.read_sem < 0) + return msg.read_sem; + + error = arch_thread_init_thread_struct(this); + if (error != B_OK) + return error; + + return B_OK; +} + + +/*! Checks whether the thread is still in the thread hash table. +*/ +bool +Thread::IsAlive() const +{ + InterruptsSpinLocker threadHashLocker(sThreadHashLock); + + return sThreadHash.Lookup(id) != NULL; +} + + +void +Thread::ResetSignalsOnExec() +{ + // We are supposed keep the pending signals and the signal mask. Only the + // signal stack, if set, shall be unset. + + sigsuspend_original_unblocked_mask = 0; + user_signal_context = NULL; + signal_stack_base = 0; + signal_stack_size = 0; + signal_stack_enabled = false; +} + + +/*! Adds the given user timer to the thread and, if user-defined, assigns it an + ID. + + The caller must hold the thread's lock. + + \param timer The timer to be added. If it doesn't have an ID yet, it is + considered user-defined and will be assigned an ID. + \return \c B_OK, if the timer was added successfully, another error code + otherwise. +*/ +status_t +Thread::AddUserTimer(UserTimer* timer) +{ + // If the timer is user-defined, check timer limit and increment + // user-defined count. + if (timer->ID() < 0 && !team->CheckAddUserDefinedTimer()) + return EAGAIN; + + fUserTimers.AddTimer(timer); + + return B_OK; +} + + +/*! Removes the given user timer from the thread. + + The caller must hold the thread's lock. + + \param timer The timer to be removed. + +*/ +void +Thread::RemoveUserTimer(UserTimer* timer) +{ + fUserTimers.RemoveTimer(timer); + + if (timer->ID() >= USER_TIMER_FIRST_USER_DEFINED_ID) + team->UserDefinedTimersRemoved(1); +} + + +/*! Deletes all (or all user-defined) user timers of the thread. + + The caller must hold the thread's lock. + + \param userDefinedOnly If \c true, only the user-defined timers are deleted, + otherwise all timers are deleted. +*/ +void +Thread::DeleteUserTimers(bool userDefinedOnly) +{ + int32 count = fUserTimers.DeleteTimers(userDefinedOnly); + team->UserDefinedTimersRemoved(count); +} + + +void +Thread::DeactivateCPUTimeUserTimers() +{ + while (ThreadTimeUserTimer* timer = fCPUTimeUserTimers.Head()) + timer->Deactivate(); +} + + +// #pragma mark - ThreadListIterator + + +ThreadListIterator::ThreadListIterator() +{ + // queue the entry + InterruptsSpinLocker locker(sThreadHashLock); + sThreadHash.InsertIteratorEntry(&fEntry); +} + + +ThreadListIterator::~ThreadListIterator() +{ + // remove the entry + InterruptsSpinLocker locker(sThreadHashLock); + sThreadHash.RemoveIteratorEntry(&fEntry); +} + + +Thread* +ThreadListIterator::Next() +{ + // get the next team -- if there is one, get reference for it + InterruptsSpinLocker locker(sThreadHashLock); + Thread* thread = sThreadHash.NextElement(&fEntry); + if (thread != NULL) + thread->AcquireReference(); + + return thread; +} + + +// #pragma mark - ThreadCreationAttributes + + +ThreadCreationAttributes::ThreadCreationAttributes(thread_func function, + const char* name, int32 priority, void* arg, team_id team, + Thread* thread) +{ + this->entry = NULL; + this->name = name; + this->priority = priority; + this->args1 = NULL; + this->args2 = NULL; + this->stack_address = NULL; + this->stack_size = 0; + this->pthread = NULL; + this->flags = 0; + this->team = team >= 0 ? team : team_get_kernel_team()->id; + this->thread = thread; + this->signal_mask = 0; + this->additional_stack_size = 0; + this->kernelEntry = function; + this->kernelArgument = arg; + this->forkArgs = NULL; +} + + +/*! Initializes the structure from a userland structure. + \param userAttributes The userland structure (must be a userland address). + \param nameBuffer A character array of at least size B_OS_NAME_LENGTH, + which will be used for the \c name field, if the userland structure has + a name. The buffer must remain valid as long as this structure is in + use afterwards (or until it is reinitialized). + \return \c B_OK, if the initialization went fine, another error code + otherwise. +*/ +status_t +ThreadCreationAttributes::InitFromUserAttributes( + const thread_creation_attributes* userAttributes, char* nameBuffer) +{ + if (userAttributes == NULL || !IS_USER_ADDRESS(userAttributes) + || user_memcpy((thread_creation_attributes*)this, userAttributes, + sizeof(thread_creation_attributes)) != B_OK) { + return B_BAD_ADDRESS; + } + + if (stack_size != 0 + && (stack_size < MIN_USER_STACK_SIZE + || stack_size > MAX_USER_STACK_SIZE)) { + return B_BAD_VALUE; + } + + if (entry == NULL || !IS_USER_ADDRESS(entry) + || (stack_address != NULL && !IS_USER_ADDRESS(stack_address)) + || (name != NULL && (!IS_USER_ADDRESS(name) + || user_strlcpy(nameBuffer, name, B_OS_NAME_LENGTH) < 0))) { + return B_BAD_ADDRESS; + } + + name = name != NULL ? nameBuffer : "user thread"; + + // kernel only attributes (not in thread_creation_attributes): + Thread* currentThread = thread_get_current_thread(); + team = currentThread->team->id; + thread = NULL; + signal_mask = currentThread->sig_block_mask; + // inherit the current thread's signal mask + additional_stack_size = 0; + kernelEntry = NULL; + kernelArgument = NULL; + forkArgs = NULL; + + return B_OK; +} + + +// #pragma mark - private functions /*! Inserts a thread into a team. - You must hold the team lock when you call this function. + The caller must hold the team's lock, the thread's lock, and the scheduler + lock. */ static void insert_thread_into_team(Team *team, Thread *thread) @@ -150,7 +607,8 @@ insert_thread_into_team(Team *team, Thread *thread) /*! Removes a thread from a team. - You must hold the team lock when you call this function. + The caller must hold the team's lock, the thread's lock, and the scheduler + lock. */ static void remove_thread_from_team(Team *team, Thread *thread) @@ -172,257 +630,289 @@ remove_thread_from_team(Team *team, Thread *thread) } -static int -thread_struct_compare(void *_t, const void *_key) +static status_t +enter_userspace(Thread* thread, UserThreadEntryArguments* args) { - Thread *thread = (Thread*)_t; - const struct thread_key *key = (const struct thread_key*)_key; - - if (thread->id == key->id) - return 0; - - return 1; -} - - -static uint32 -thread_struct_hash(void *_t, const void *_key, uint32 range) -{ - Thread *thread = (Thread*)_t; - const struct thread_key *key = (const struct thread_key*)_key; - - if (thread != NULL) - return thread->id % range; - - return (uint32)key->id % range; -} - - -static void -reset_signals(Thread *thread) -{ - thread->sig_pending = 0; - thread->sig_block_mask = 0; - thread->sig_temp_enabled = 0; - memset(thread->sig_action, 0, 32 * sizeof(struct sigaction)); - thread->signal_stack_base = 0; - thread->signal_stack_size = 0; - thread->signal_stack_enabled = false; -} - - -/*! Allocates and fills in thread structure. - - \param threadID The ID to be assigned to the new thread. If - \code < 0 \endcode a fresh one is allocated. - \param thread initialize this thread struct if nonnull -*/ -static Thread * -create_thread_struct(Thread *inthread, const char *name, - thread_id threadID, struct cpu_ent *cpu) -{ - Thread *thread; - char temp[64]; - - if (inthread == NULL) { - thread = (Thread*)object_cache_alloc(sThreadCache, 0); - if (thread == NULL) - return NULL; - - scheduler_on_thread_create(thread); - // TODO: We could use the object cache object - // constructor/destructor! - } else - thread = inthread; - - if (name != NULL) - strlcpy(thread->name, name, B_OS_NAME_LENGTH); - else - strcpy(thread->name, "unnamed thread"); - - thread->flags = 0; - thread->id = threadID >= 0 ? threadID : allocate_thread_id(); - thread->team = NULL; - thread->cpu = cpu; - thread->previous_cpu = NULL; - thread->pinned_to_cpu = 0; - thread->fault_handler = 0; - thread->page_faults_allowed = 1; - thread->kernel_stack_area = -1; - thread->kernel_stack_base = 0; - thread->user_stack_area = -1; - thread->user_stack_base = 0; - thread->user_local_storage = 0; - thread->kernel_errno = 0; - thread->team_next = NULL; - thread->queue_next = NULL; - thread->priority = thread->next_priority = -1; - thread->io_priority = -1; - thread->args1 = NULL; thread->args2 = NULL; - thread->alarm.period = 0; - reset_signals(thread); - thread->in_kernel = true; - thread->was_yielded = false; - thread->user_time = 0; - thread->kernel_time = 0; - thread->last_time = 0; - thread->exit.status = 0; - thread->exit.reason = 0; - thread->exit.signal = 0; - list_init(&thread->exit.waiters); - thread->select_infos = NULL; - thread->post_interrupt_callback = NULL; - thread->post_interrupt_data = NULL; - thread->user_thread = NULL; - - sprintf(temp, "thread_%ld_retcode_sem", thread->id); - thread->exit.sem = create_sem(0, temp); - if (thread->exit.sem < B_OK) - goto err1; - - sprintf(temp, "%s send", thread->name); - thread->msg.write_sem = create_sem(1, temp); - if (thread->msg.write_sem < B_OK) - goto err2; - - sprintf(temp, "%s receive", thread->name); - thread->msg.read_sem = create_sem(0, temp); - if (thread->msg.read_sem < B_OK) - goto err3; - - if (arch_thread_init_thread_struct(thread) < B_OK) - goto err4; - - return thread; - -err4: - delete_sem(thread->msg.read_sem); -err3: - delete_sem(thread->msg.write_sem); -err2: - delete_sem(thread->exit.sem); -err1: - // ToDo: put them in the dead queue instead? - if (inthread == NULL) { - scheduler_on_thread_destroy(thread); - object_cache_free(sThreadCache, thread, 0); + status_t error = arch_thread_init_tls(thread); + if (error != B_OK) { + dprintf("Failed to init TLS for new userland thread \"%s\" (%" B_PRId32 + ")\n", thread->name, thread->id); + free(args->forkArgs); + return error; } - return NULL; + user_debug_update_new_thread_flags(thread); + + // init the thread's user_thread + user_thread* userThread = thread->user_thread; + userThread->pthread = args->pthread; + userThread->flags = 0; + userThread->wait_status = B_OK; + userThread->defer_signals + = (args->flags & THREAD_CREATION_FLAG_DEFER_SIGNALS) != 0 ? 1 : 0; + userThread->pending_signals = 0; + + if (args->forkArgs != NULL) { + // This is a fork()ed thread. Copy the fork args onto the stack and + // free them. + arch_fork_arg archArgs = *args->forkArgs; + free(args->forkArgs); + + arch_restore_fork_frame(&archArgs); + // this one won't return here + return B_ERROR; + } + + // Jump to the entry point in user space. Only returns, if something fails. + return arch_thread_enter_userspace(thread, args->userlandEntry, + args->userlandArgument1, args->userlandArgument2); +} + + +status_t +thread_enter_userspace_new_team(Thread* thread, addr_t entryFunction, + void* argument1, void* argument2) +{ + UserThreadEntryArguments entryArgs; + entryArgs.kernelFunction = NULL; + entryArgs.argument = NULL; + entryArgs.enterUserland = true; + entryArgs.userlandEntry = (addr_t)entryFunction; + entryArgs.userlandArgument1 = argument1; + entryArgs.userlandArgument2 = argument2; + entryArgs.pthread = NULL; + entryArgs.forkArgs = NULL; + entryArgs.flags = 0; + + return enter_userspace(thread, &entryArgs); } static void -delete_thread_struct(Thread *thread) +common_thread_entry(void* _args) { - delete_sem(thread->exit.sem); - delete_sem(thread->msg.write_sem); - delete_sem(thread->msg.read_sem); + Thread* thread = thread_get_current_thread(); - scheduler_on_thread_destroy(thread); - object_cache_free(sThreadCache, thread, 0); -} + // The thread is new and has been scheduled the first time. + // start CPU time based user timers + if (thread->HasActiveCPUTimeUserTimers() + || thread->team->HasActiveCPUTimeUserTimers()) { + user_timer_continue_cpu_timers(thread, thread->cpu->previous_thread); + } -/*! This function gets run by a new thread before anything else */ -static void -thread_kthread_entry(void) -{ - Thread *thread = thread_get_current_thread(); - - // The thread is new and has been scheduled the first time. Notify the user - // debugger code. + // notify the user debugger code if ((thread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) user_debug_thread_scheduled(thread); - // simulates the thread spinlock release that would occur if the thread had been - // rescheded from. The resched didn't happen because the thread is new. - RELEASE_THREAD_LOCK(); - // start tracking time thread->last_time = system_time(); - enable_interrupts(); // this essentially simulates a return-from-interrupt -} + // unlock the scheduler lock and enable interrupts + release_spinlock(&gSchedulerLock); + enable_interrupts(); + // call the kernel function, if any + ThreadEntryArguments* args = (ThreadEntryArguments*)_args; + if (args->kernelFunction != NULL) + args->kernelFunction(args->argument); -static void -thread_kthread_exit(void) -{ - Thread *thread = thread_get_current_thread(); + // If requested, enter userland, now. + if (args->enterUserland) { + enter_userspace(thread, (UserThreadEntryArguments*)args); + // only returns or error + } - thread->exit.reason = THREAD_RETURN_EXIT; + // we're done thread_exit(); } -/*! Initializes the thread and jumps to its userspace entry point. - This function is called at creation time of every user thread, - but not for a team's main thread. -*/ -static int -_create_user_thread_kentry(void) +/*! Prepares the given thread's kernel stack for executing its entry function. + + The data pointed to by \a data of size \a dataSize are copied to the + thread's kernel stack. A pointer to the copy's data is passed to the entry + function. The entry function is common_thread_entry(). + + \param thread The thread. + \param data Pointer to data to be copied to the thread's stack and passed + to the entry function. + \param dataSize The size of \a data. + */ +static void +init_thread_kernel_stack(Thread* thread, const void* data, size_t dataSize) { - Thread *thread = thread_get_current_thread(); + uint8* stack = (uint8*)thread->kernel_stack_base; + uint8* stackTop = (uint8*)thread->kernel_stack_top; - // jump to the entry point in user space - arch_thread_enter_userspace(thread, (addr_t)thread->entry, - thread->args1, thread->args2); + // clear (or rather invalidate) the kernel stack contents, if compiled with + // debugging +#if KDEBUG > 0 +# if defined(DEBUG_KERNEL_STACKS) && defined(STACK_GROWS_DOWNWARDS) + memset((void*)(stack + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE), 0xcc, + KERNEL_STACK_SIZE); +# else + memset(stack, 0xcc, KERNEL_STACK_SIZE); +# endif +#endif - // only get here if the above call fails - return 0; + // copy the data onto the stack, with 16-byte alignment to be on the safe + // side + void* clonedData; +#ifdef STACK_GROWS_DOWNWARDS + clonedData = (void*)ROUNDDOWN((addr_t)stackTop - dataSize, 16); + stackTop = (uint8*)clonedData; +#else + clonedData = (void*)ROUNDUP((addr_t)stack, 16); + stack = (uint8*)clonedData + ROUNDUP(dataSize, 16); +#endif + + memcpy(clonedData, data, dataSize); + + arch_thread_init_kthread_stack(thread, stack, stackTop, + &common_thread_entry, clonedData); } -/*! Initializes the thread and calls it kernel space entry point. */ -static int -_create_kernel_thread_kentry(void) +static status_t +create_thread_user_stack(Team* team, Thread* thread, void* _stackBase, + size_t stackSize, size_t additionalSize, char* nameBuffer) { - Thread *thread = thread_get_current_thread(); - int (*func)(void *args) = (int (*)(void *))thread->entry; + area_id stackArea = -1; + uint8* stackBase = (uint8*)_stackBase; - // call the entry function with the appropriate args - return func(thread->args1); + if (stackBase != NULL) { + // A stack has been specified. It must be large enough to hold the + // TLS space at least. + STATIC_ASSERT(TLS_SIZE < MIN_USER_STACK_SIZE); + if (stackSize < MIN_USER_STACK_SIZE) + return B_BAD_VALUE; + + stackBase -= TLS_SIZE; + } + + if (stackBase == NULL) { + // No user-defined stack -- allocate one. For non-main threads the stack + // will be between USER_STACK_REGION and the main thread stack area. For + // a main thread the position is fixed. + + if (stackSize == 0) { + // Use the default size (a different one for a main thread). + stackSize = thread->id == team->id + ? USER_MAIN_THREAD_STACK_SIZE : USER_STACK_SIZE; + } else { + // Verify that the given stack size is large enough. + if (stackSize < MIN_USER_STACK_SIZE - TLS_SIZE) + return B_BAD_VALUE; + + stackSize = PAGE_ALIGN(stackSize); + } + stackSize += USER_STACK_GUARD_PAGES * B_PAGE_SIZE; + + size_t areaSize = PAGE_ALIGN(stackSize + TLS_SIZE + additionalSize); + + snprintf(nameBuffer, B_OS_NAME_LENGTH, "%s_%ld_stack", thread->name, + thread->id); + + virtual_address_restrictions virtualRestrictions = {}; + if (thread->id == team->id) { + // The main thread gets a fixed position at the top of the stack + // address range. + stackBase = (uint8*)(USER_STACK_REGION + USER_STACK_REGION_SIZE + - areaSize); + virtualRestrictions.address_specification = B_EXACT_ADDRESS; + + } else { + // not a main thread + stackBase = (uint8*)(addr_t)USER_STACK_REGION; + virtualRestrictions.address_specification = B_BASE_ADDRESS; + } + virtualRestrictions.address = (void*)stackBase; + + physical_address_restrictions physicalRestrictions = {}; + + stackArea = create_area_etc(team->id, nameBuffer, + areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA | B_STACK_AREA, + 0, &virtualRestrictions, &physicalRestrictions, + (void**)&stackBase); + if (stackArea < 0) + return stackArea; + } + + // set the stack + ThreadLocker threadLocker(thread); + thread->user_stack_base = (addr_t)stackBase; + thread->user_stack_size = stackSize; + thread->user_stack_area = stackArea; + + return B_OK; } -/*! Creates a new thread in the team with the specified team ID. - - \param threadID The ID to be assigned to the new thread. If - \code < 0 \endcode a fresh one is allocated. -*/ -static thread_id -create_thread(thread_creation_attributes& attributes, bool kernel) +status_t +thread_create_user_stack(Team* team, Thread* thread, void* stackBase, + size_t stackSize, size_t additionalSize) { - Thread *thread, *currentThread; - Team *team; - cpu_status state; - char stack_name[B_OS_NAME_LENGTH]; - status_t status; - bool abort = false; - bool debugNewThread = false; + char nameBuffer[B_OS_NAME_LENGTH]; + return create_thread_user_stack(team, thread, stackBase, stackSize, + additionalSize, nameBuffer); +} - TRACE(("create_thread(%s, id = %ld, %s)\n", attributes.name, + +/*! Creates a new thread. + + \param attributes The thread creation attributes, specifying the team in + which to create the thread, as well as a whole bunch of other arguments. + \param kernel \c true, if a kernel-only thread shall be created, \c false, + if the thread shall also be able to run in userland. + \return The ID of the newly created thread (>= 0) or an error code on + failure. +*/ +thread_id +thread_create_thread(const ThreadCreationAttributes& attributes, bool kernel) +{ + status_t status = B_OK; + + TRACE(("thread_create_thread(%s, thread = %p, %s)\n", attributes.name, attributes.thread, kernel ? "kernel" : "user")); - thread = create_thread_struct(NULL, attributes.name, attributes.thread, - NULL); - if (thread == NULL) - return B_NO_MEMORY; + // get the team + Team* team = Team::Get(attributes.team); + if (team == NULL) + return B_BAD_TEAM_ID; + BReference teamReference(team, true); + // If a thread object is given, acquire a reference to it, otherwise create + // a new thread object with the given attributes. + Thread* thread = attributes.thread; + if (thread != NULL) { + thread->AcquireReference(); + } else { + status = Thread::Create(attributes.name, thread); + if (status != B_OK) + return status; + } + BReference threadReference(thread, true); + + thread->team = team; + // set already, so, if something goes wrong, the team pointer is + // available for deinitialization thread->priority = attributes.priority == -1 ? B_NORMAL_PRIORITY : attributes.priority; thread->next_priority = thread->priority; - // ToDo: this could be dangerous in case someone calls resume_thread() on us thread->state = B_THREAD_SUSPENDED; thread->next_state = B_THREAD_SUSPENDED; + thread->sig_block_mask = attributes.signal_mask; + // init debug structure init_thread_debug_info(&thread->debug_info); - snprintf(stack_name, B_OS_NAME_LENGTH, "%s_%ld_kstack", attributes.name, + // create the kernel stack + char stackName[B_OS_NAME_LENGTH]; + snprintf(stackName, B_OS_NAME_LENGTH, "%s_%ld_kstack", thread->name, thread->id); - thread->kernel_stack_area = create_area(stack_name, + thread->kernel_stack_area = create_area(stackName, (void **)&thread->kernel_stack_base, B_ANY_KERNEL_ADDRESS, KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE, B_FULL_LOCK, @@ -435,151 +925,125 @@ create_thread(thread_creation_attributes& attributes, bool kernel) dprintf("create_thread: error creating kernel stack: %s!\n", strerror(status)); - delete_thread_struct(thread); return status; } thread->kernel_stack_top = thread->kernel_stack_base + KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE; - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + if (kernel) { + // Init the thread's kernel stack. It will start executing + // common_thread_entry() with the arguments we prepare here. + ThreadEntryArguments entryArgs; + entryArgs.kernelFunction = attributes.kernelEntry; + entryArgs.argument = attributes.kernelArgument; + entryArgs.enterUserland = false; - // If the new thread belongs to the same team as the current thread, - // it may inherit some of the thread debug flags. - currentThread = thread_get_current_thread(); - if (currentThread && currentThread->team->id == attributes.team) { - // inherit all user flags... - int32 debugFlags = currentThread->debug_info.flags - & B_THREAD_DEBUG_USER_FLAG_MASK; - - // ... save the syscall tracing flags, unless explicitely specified - if (!(debugFlags & B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS)) { - debugFlags &= ~(B_THREAD_DEBUG_PRE_SYSCALL - | B_THREAD_DEBUG_POST_SYSCALL); + init_thread_kernel_stack(thread, &entryArgs, sizeof(entryArgs)); + } else { + // create the userland stack, if the thread doesn't have one yet + if (thread->user_stack_base == 0) { + status = create_thread_user_stack(team, thread, + attributes.stack_address, attributes.stack_size, + attributes.additional_stack_size, stackName); + if (status != B_OK) + return status; } - thread->debug_info.flags = debugFlags; + // Init the thread's kernel stack. It will start executing + // common_thread_entry() with the arguments we prepare here. + UserThreadEntryArguments entryArgs; + entryArgs.kernelFunction = attributes.kernelEntry; + entryArgs.argument = attributes.kernelArgument; + entryArgs.enterUserland = true; + entryArgs.userlandEntry = (addr_t)attributes.entry; + entryArgs.userlandArgument1 = attributes.args1; + entryArgs.userlandArgument2 = attributes.args2; + entryArgs.pthread = attributes.pthread; + entryArgs.forkArgs = attributes.forkArgs; + entryArgs.flags = attributes.flags; - // stop the new thread, if desired - debugNewThread = debugFlags & B_THREAD_DEBUG_STOP_CHILD_THREADS; + init_thread_kernel_stack(thread, &entryArgs, sizeof(entryArgs)); - // copy signal handlers - memcpy(thread->sig_action, currentThread->sig_action, - sizeof(thread->sig_action)); + // create the pre-defined thread timers + status = user_timer_create_thread_timers(team, thread); + if (status != B_OK) + return status; } - // insert into global list - hash_insert(sThreadHash, thread); + // lock the team and see, if it is still alive + TeamLocker teamLocker(team); + if (team->state >= TEAM_STATE_SHUTDOWN) + return B_BAD_TEAM_ID; + + bool debugNewThread = false; + if (!kernel) { + // allocate the user_thread structure, if not already allocated + if (thread->user_thread == NULL) { + thread->user_thread = team_allocate_user_thread(team); + if (thread->user_thread == NULL) + return B_NO_MEMORY; + } + + // If the new thread belongs to the same team as the current thread, it + // may inherit some of the thread debug flags. + Thread* currentThread = thread_get_current_thread(); + if (currentThread != NULL && currentThread->team == team) { + // inherit all user flags... + int32 debugFlags = atomic_get(¤tThread->debug_info.flags) + & B_THREAD_DEBUG_USER_FLAG_MASK; + + // ... save the syscall tracing flags, unless explicitely specified + if (!(debugFlags & B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS)) { + debugFlags &= ~(B_THREAD_DEBUG_PRE_SYSCALL + | B_THREAD_DEBUG_POST_SYSCALL); + } + + thread->debug_info.flags = debugFlags; + + // stop the new thread, if desired + debugNewThread = debugFlags & B_THREAD_DEBUG_STOP_CHILD_THREADS; + } + } + + // We're going to make the thread live, now. The thread itself will take + // over a reference to its Thread object. We acquire another reference for + // our own use (and threadReference remains armed). + thread->AcquireReference(); + + ThreadLocker threadLocker(thread); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + SpinLocker threadHashLocker(sThreadHashLock); + + // make thread visible in global hash/list + thread->visible = true; sUsedThreads++; scheduler_on_thread_init(thread); - RELEASE_THREAD_LOCK(); - GRAB_TEAM_LOCK(); - // look at the team, make sure it's not being deleted - team = team_get_team_struct_locked(attributes.team); - - if (team == NULL || team->state == TEAM_STATE_DEATH - || team->death_entry != NULL) { - abort = true; - } - - if (!abort && !kernel) { - thread->user_thread = team_allocate_user_thread(team); - abort = thread->user_thread == NULL; - } - - if (!abort) { - // Debug the new thread, if the parent thread required that (see above), - // or the respective global team debug flag is set. But only, if a - // debugger is installed for the team. - debugNewThread |= (atomic_get(&team->debug_info.flags) - & B_TEAM_DEBUG_STOP_NEW_THREADS); + // Debug the new thread, if the parent thread required that (see above), + // or the respective global team debug flag is set. But only, if a + // debugger is installed for the team. + if (!kernel) { + int32 teamDebugFlags = atomic_get(&team->debug_info.flags); + debugNewThread |= (teamDebugFlags & B_TEAM_DEBUG_STOP_NEW_THREADS) != 0; if (debugNewThread - && (atomic_get(&team->debug_info.flags) - & B_TEAM_DEBUG_DEBUGGER_INSTALLED)) { + && (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) != 0) { thread->debug_info.flags |= B_THREAD_DEBUG_STOP; } - - insert_thread_into_team(team, thread); } - RELEASE_TEAM_LOCK(); - if (abort) { - GRAB_THREAD_LOCK(); - hash_remove(sThreadHash, thread); - RELEASE_THREAD_LOCK(); - } - restore_interrupts(state); - if (abort) { - delete_area(thread->kernel_stack_area); - delete_thread_struct(thread); - return B_BAD_TEAM_ID; - } + // insert thread into team + insert_thread_into_team(team, thread); - thread->args1 = attributes.args1; - thread->args2 = attributes.args2; - thread->entry = attributes.entry; - status = thread->id; + threadHashLocker.Unlock(); + schedulerLocker.Unlock(); + threadLocker.Unlock(); + teamLocker.Unlock(); // notify listeners sNotificationService.Notify(THREAD_ADDED, thread); - if (kernel) { - // this sets up an initial kthread stack that runs the entry - - // Note: whatever function wants to set up a user stack later for this - // thread must initialize the TLS for it - arch_thread_init_kthread_stack(thread, &_create_kernel_thread_kentry, - &thread_kthread_entry, &thread_kthread_exit); - } else { - // create user stack - - // the stack will be between USER_STACK_REGION and the main thread stack - // area (the user stack of the main thread is created in - // team_create_team()) - if (attributes.stack_address == NULL) { - thread->user_stack_base = USER_STACK_REGION; - if (attributes.stack_size <= 0) - thread->user_stack_size = USER_STACK_SIZE; - else - thread->user_stack_size = PAGE_ALIGN(attributes.stack_size); - thread->user_stack_size += USER_STACK_GUARD_PAGES * B_PAGE_SIZE; - - snprintf(stack_name, B_OS_NAME_LENGTH, "%s_%ld_stack", - attributes.name, thread->id); - virtual_address_restrictions virtualRestrictions = {}; - virtualRestrictions.address = (void*)thread->user_stack_base; - virtualRestrictions.address_specification = B_BASE_ADDRESS; - physical_address_restrictions physicalRestrictions = {}; - thread->user_stack_area = create_area_etc(team->id, stack_name, - thread->user_stack_size + TLS_SIZE, B_NO_LOCK, - B_READ_AREA | B_WRITE_AREA | B_STACK_AREA, 0, - &virtualRestrictions, &physicalRestrictions, - (void**)&thread->user_stack_base); - if (thread->user_stack_area < B_OK - || arch_thread_init_tls(thread) < B_OK) { - // great, we have a fully running thread without a (usable) - // stack - dprintf("create_thread: unable to create proper user stack!\n"); - status = thread->user_stack_area; - kill_thread(thread->id); - } - } else { - thread->user_stack_base = (addr_t)attributes.stack_address; - thread->user_stack_size = attributes.stack_size; - } - - user_debug_update_new_thread_flags(thread->id); - - // copy the user entry over to the args field in the thread struct - // the function this will call will immediately switch the thread into - // user space. - arch_thread_init_kthread_stack(thread, &_create_user_thread_kentry, - &thread_kthread_entry, &thread_kthread_exit); - } - - return status; + return thread->id; } @@ -588,20 +1052,20 @@ undertaker(void* /*args*/) { while (true) { // wait for a thread to bury - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); while (sUndertakerEntries.IsEmpty()) { ConditionVariableEntry conditionEntry; sUndertakerCondition.Add(&conditionEntry); - locker.Unlock(); + schedulerLocker.Unlock(); conditionEntry.Wait(); - locker.Lock(); + schedulerLocker.Lock(); } UndertakerEntry* _entry = sUndertakerEntries.RemoveHead(); - locker.Unlock(); + schedulerLocker.Unlock(); UndertakerEntry entry = *_entry; // we need a copy, since the original entry is on the thread's stack @@ -609,28 +1073,36 @@ undertaker(void* /*args*/) // we've got an entry Thread* thread = entry.thread; - // delete the old kernel stack area - delete_area(thread->kernel_stack_area); + // remove this thread from from the kernel team -- this makes it + // unaccessible + Team* kernelTeam = team_get_kernel_team(); + TeamLocker kernelTeamLocker(kernelTeam); + thread->Lock(); + schedulerLocker.Lock(); - // remove this thread from all of the global lists - disable_interrupts(); - GRAB_TEAM_LOCK(); + remove_thread_from_team(kernelTeam, thread); - remove_thread_from_team(team_get_kernel_team(), thread); - - RELEASE_TEAM_LOCK(); - enable_interrupts(); - // needed for the debugger notification below + schedulerLocker.Unlock(); + kernelTeamLocker.Unlock(); // free the thread structure - delete_thread_struct(thread); + thread->UnlockAndReleaseReference(); } - // never can get here + // can never get here return B_OK; } +/*! Returns the semaphore the thread is currently waiting on. + + The return value is purely informative. + The caller must hold the scheduler lock. + + \param thread The thread. + \return The ID of the semaphore the thread is currently waiting on or \c -1, + if it isn't waiting on a semaphore. +*/ static sem_id get_thread_wait_sem(Thread* thread) { @@ -642,9 +1114,8 @@ get_thread_wait_sem(Thread* thread) } -/*! Fills the thread_info structure with information from the specified - thread. - The thread lock must be held when called. +/*! Fills the thread_info structure with information from the specified thread. + The caller must hold the thread's lock and the scheduler lock. */ static void fill_thread_info(Thread *thread, thread_info *info, size_t size) @@ -654,6 +1125,8 @@ fill_thread_info(Thread *thread, thread_info *info, size_t size) strlcpy(info->name, thread->name, B_OS_NAME_LENGTH); + info->sem = -1; + if (thread->state == B_THREAD_WAITING) { info->state = B_THREAD_WAITING; @@ -667,6 +1140,8 @@ fill_thread_info(Thread *thread, thread_info *info, size_t size) sem_id sem = (sem_id)(addr_t)thread->wait.object; if (sem == thread->msg.read_sem) info->state = B_THREAD_RECEIVING; + else + info->sem = sem; break; } @@ -678,12 +1153,13 @@ fill_thread_info(Thread *thread, thread_info *info, size_t size) info->state = (thread_state)thread->state; info->priority = thread->priority; - info->user_time = thread->user_time; - info->kernel_time = thread->kernel_time; info->stack_base = (void *)thread->user_stack_base; info->stack_end = (void *)(thread->user_stack_base + thread->user_stack_size); - info->sem = get_thread_wait_sem(thread); + + InterruptsSpinLocker threadTimeLocker(thread->time_lock); + info->user_time = thread->user_time; + info->kernel_time = thread->kernel_time; } @@ -691,29 +1167,23 @@ static status_t send_data_etc(thread_id id, int32 code, const void *buffer, size_t bufferSize, int32 flags) { - Thread *target; - sem_id cachedSem; - cpu_status state; - status_t status; - - state = disable_interrupts(); - GRAB_THREAD_LOCK(); - target = thread_get_thread_struct_locked(id); - if (!target) { - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + // get the thread + Thread *target = Thread::Get(id); + if (target == NULL) return B_BAD_THREAD_ID; - } - cachedSem = target->msg.write_sem; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + BReference targetReference(target, true); + + // get the write semaphore + ThreadLocker targetLocker(target); + sem_id cachedSem = target->msg.write_sem; + targetLocker.Unlock(); if (bufferSize > THREAD_MAX_MESSAGE_SIZE) return B_NO_MEMORY; - status = acquire_sem_etc(cachedSem, 1, flags, 0); + status_t status = acquire_sem_etc(cachedSem, 1, flags, 0); if (status == B_INTERRUPTED) { - // We got interrupted by a signal + // we got interrupted by a signal return status; } if (status != B_OK) { @@ -733,14 +1203,11 @@ send_data_etc(thread_id id, int32 code, const void *buffer, size_t bufferSize, } else data = NULL; - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + targetLocker.Lock(); - // The target thread could have been deleted at this point - target = thread_get_thread_struct_locked(id); - if (target == NULL) { - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + // The target thread could have been deleted at this point. + if (!target->IsAlive()) { + targetLocker.Unlock(); free(data); return B_BAD_THREAD_ID; } @@ -752,8 +1219,7 @@ send_data_etc(thread_id id, int32 code, const void *buffer, size_t bufferSize, target->msg.buffer = data; cachedSem = target->msg.read_sem; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + targetLocker.Unlock(); release_sem(cachedSem); return B_OK; @@ -765,12 +1231,11 @@ receive_data_etc(thread_id *_sender, void *buffer, size_t bufferSize, int32 flags) { Thread *thread = thread_get_current_thread(); - status_t status; size_t size; int32 code; - status = acquire_sem_etc(thread->msg.read_sem, 1, flags, 0); - if (status < B_OK) { + status_t status = acquire_sem_etc(thread->msg.read_sem, 1, flags, 0); + if (status != B_OK) { // Actually, we're not supposed to return error codes // but since the only reason this can fail is that we // were killed, it's probably okay to do so (but also @@ -817,8 +1282,6 @@ common_getrlimit(int resource, struct rlimit * rlp) case RLIMIT_STACK: { Thread *thread = thread_get_current_thread(); - if (!thread) - return B_ERROR; rlp->rlim_cur = thread->user_stack_size; rlp->rlim_max = thread->user_stack_size; return B_OK; @@ -857,14 +1320,71 @@ common_setrlimit(int resource, const struct rlimit * rlp) } +static status_t +common_snooze_etc(bigtime_t timeout, clockid_t clockID, uint32 flags, + bigtime_t* _remainingTime) +{ + switch (clockID) { + case CLOCK_REALTIME: + // make sure the B_TIMEOUT_REAL_TIME_BASE flag is set and fall + // through + flags |= B_TIMEOUT_REAL_TIME_BASE; + case CLOCK_MONOTONIC: + { + // Store the start time, for the case that we get interrupted and + // need to return the remaining time. For absolute timeouts we can + // still get he time later, if needed. + bigtime_t startTime + = _remainingTime != NULL && (flags & B_RELATIVE_TIMEOUT) != 0 + ? system_time() : 0; + + Thread* thread = thread_get_current_thread(); + + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SNOOZE, + NULL); + status_t status = thread_block_with_timeout_locked(flags, timeout); + + if (status == B_TIMED_OUT || status == B_WOULD_BLOCK) + return B_OK; + + // If interrupted, compute the remaining time, if requested. + if (status == B_INTERRUPTED && _remainingTime != NULL) { + if ((flags & B_RELATIVE_TIMEOUT) != 0) { + *_remainingTime = std::max( + startTime + timeout - system_time(), (bigtime_t)0); + } else { + bigtime_t now = (flags & B_TIMEOUT_REAL_TIME_BASE) != 0 + ? real_time_clock_usecs() : system_time(); + *_remainingTime = std::max(timeout - now, (bigtime_t)0); + } + } + + return status; + } + + case CLOCK_THREAD_CPUTIME_ID: + // Waiting for ourselves to do something isn't particularly + // productive. + return B_BAD_VALUE; + + case CLOCK_PROCESS_CPUTIME_ID: + default: + // We don't have to support those, but we are allowed to. Could be + // done be creating a UserTimer on the fly with a custom UserEvent + // that would just wake us up. + return ENOTSUP; + } +} + + // #pragma mark - debugger calls static int make_thread_unreal(int argc, char **argv) { - Thread *thread; - struct hash_iterator i; int32 id = -1; if (argc > 2) { @@ -875,9 +1395,8 @@ make_thread_unreal(int argc, char **argv) if (argc > 1) id = strtoul(argv[1], NULL, 0); - hash_open(sThreadHash, &i); - - while ((thread = (Thread*)hash_next(sThreadHash, &i)) != NULL) { + for (ThreadHashTable::Iterator it = sThreadHash.GetIterator(); + Thread* thread = it.Next();) { if (id != -1 && thread->id != id) continue; @@ -887,7 +1406,6 @@ make_thread_unreal(int argc, char **argv) } } - hash_close(sThreadHash, &i, false); return 0; } @@ -895,8 +1413,6 @@ make_thread_unreal(int argc, char **argv) static int set_thread_prio(int argc, char **argv) { - Thread *thread; - struct hash_iterator i; int32 id; int32 prio; @@ -916,19 +1432,19 @@ set_thread_prio(int argc, char **argv) else id = thread_get_current_thread()->id; - hash_open(sThreadHash, &i); - - while ((thread = (Thread*)hash_next(sThreadHash, &i)) != NULL) { + bool found = false; + for (ThreadHashTable::Iterator it = sThreadHash.GetIterator(); + Thread* thread = it.Next();) { if (thread->id != id) continue; thread->priority = thread->next_priority = prio; kprintf("thread %ld set to priority %ld\n", id, prio); + found = true; break; } - if (!thread) + if (!found) kprintf("thread %ld (%#lx) not found\n", id, id); - hash_close(sThreadHash, &i, false); return 0; } @@ -936,8 +1452,6 @@ set_thread_prio(int argc, char **argv) static int make_thread_suspended(int argc, char **argv) { - Thread *thread; - struct hash_iterator i; int32 id; if (argc > 2) { @@ -950,20 +1464,20 @@ make_thread_suspended(int argc, char **argv) else id = strtoul(argv[1], NULL, 0); - hash_open(sThreadHash, &i); - - while ((thread = (Thread*)hash_next(sThreadHash, &i)) != NULL) { + bool found = false; + for (ThreadHashTable::Iterator it = sThreadHash.GetIterator(); + Thread* thread = it.Next();) { if (thread->id != id) continue; thread->next_state = B_THREAD_SUSPENDED; kprintf("thread %ld suspended\n", id); + found = true; break; } - if (!thread) + if (!found) kprintf("thread %ld (%#lx) not found\n", id, id); - hash_close(sThreadHash, &i, false); return 0; } @@ -971,8 +1485,6 @@ make_thread_suspended(int argc, char **argv) static int make_thread_resumed(int argc, char **argv) { - Thread *thread; - struct hash_iterator i; int32 id; if (argc != 2) { @@ -984,9 +1496,9 @@ make_thread_resumed(int argc, char **argv) // the current thread is usually not intended id = strtoul(argv[1], NULL, 0); - hash_open(sThreadHash, &i); - - while ((thread = (Thread*)hash_next(sThreadHash, &i)) != NULL) { + bool found = false; + for (ThreadHashTable::Iterator it = sThreadHash.GetIterator(); + Thread* thread = it.Next();) { if (thread->id != id) continue; @@ -994,12 +1506,12 @@ make_thread_resumed(int argc, char **argv) scheduler_enqueue_in_run_queue(thread); kprintf("thread %ld resumed\n", thread->id); } + found = true; break; } - if (!thread) + if (!found) kprintf("thread %ld (%#lx) not found\n", id, id); - hash_close(sThreadHash, &i, false); return 0; } @@ -1021,6 +1533,8 @@ drop_into_debugger(int argc, char **argv) id = strtoul(argv[1], NULL, 0); err = _user_debug_thread(id); + // TODO: This is a non-trivial syscall doing some locking, so this is + // really nasty and may go seriously wrong. if (err) kprintf("drop failed\n"); else @@ -1030,6 +1544,9 @@ drop_into_debugger(int argc, char **argv) } +/*! Returns a user-readable string for a thread state. + Only for use in the kernel debugger. +*/ static const char * state_to_text(Thread *thread, int32 state) { @@ -1146,13 +1663,14 @@ _dump_thread_info(Thread *thread, bool shortInfo) // print the long info - struct death_entry *death = NULL; + struct thread_death_entry *death = NULL; kprintf("THREAD: %p\n", thread); kprintf("id: %ld (%#lx)\n", thread->id, thread->id); + kprintf("serial_number: %" B_PRId64 "\n", thread->serial_number); kprintf("name: \"%s\"\n", thread->name); - kprintf("all_next: %p\nteam_next: %p\nq_next: %p\n", - thread->all_next, thread->team_next, thread->queue_next); + kprintf("hash_next: %p\nteam_next: %p\nq_next: %p\n", + thread->hash_next, thread->team_next, thread->queue_next); kprintf("priority: %ld (next %ld, I/O: %ld)\n", thread->priority, thread->next_priority, thread->io_priority); kprintf("state: %s\n", state_to_text(thread, thread->state)); @@ -1162,9 +1680,11 @@ _dump_thread_info(Thread *thread, bool shortInfo) kprintf("(%d)\n", thread->cpu->cpu_num); else kprintf("\n"); - kprintf("sig_pending: %#" B_PRIx32 " (blocked: %#" B_PRIx32 - ", temp enabled: %#" B_PRIx32 ")\n", thread->sig_pending, - thread->sig_block_mask, thread->sig_temp_enabled); + kprintf("sig_pending: %#llx (blocked: %#llx" + ", before sigsuspend(): %#llx)\n", + (long long)thread->ThreadPendingSignals(), + (long long)thread->sig_block_mask, + (long long)thread->sigsuspend_original_unblocked_mask); kprintf("in_kernel: %d\n", thread->in_kernel); if (thread->state == B_THREAD_WAITING) { @@ -1212,17 +1732,14 @@ _dump_thread_info(Thread *thread, bool shortInfo) } kprintf("fault_handler: %p\n", (void *)thread->fault_handler); - kprintf("args: %p %p\n", thread->args1, thread->args2); - kprintf("entry: %p\n", (void *)thread->entry); - kprintf("team: %p, \"%s\"\n", thread->team, thread->team->name); + kprintf("team: %p, \"%s\"\n", thread->team, + thread->team->Name()); kprintf(" exit.sem: %ld\n", thread->exit.sem); kprintf(" exit.status: %#lx (%s)\n", thread->exit.status, strerror(thread->exit.status)); - kprintf(" exit.reason: %#x\n", thread->exit.reason); - kprintf(" exit.signal: %#x\n", thread->exit.signal); kprintf(" exit.waiters:\n"); - while ((death = (struct death_entry*)list_get_next_item( + while ((death = (struct thread_death_entry*)list_get_next_item( &thread->exit.waiters, death)) != NULL) { - kprintf("\t%p (group %ld, thread %ld)\n", death, death->group_id, death->thread); + kprintf("\t%p (thread %ld)\n", death, death->thread); } kprintf("kernel_stack_area: %ld\n", thread->kernel_stack_area); @@ -1269,17 +1786,14 @@ dump_thread_info(int argc, char **argv) // walk through the thread list, trying to match name or id bool found = false; - struct hash_iterator i; - hash_open(sThreadHash, &i); - Thread *thread; - while ((thread = (Thread*)hash_next(sThreadHash, &i)) != NULL) { + for (ThreadHashTable::Iterator it = sThreadHash.GetIterator(); + Thread* thread = it.Next();) { if (!strcmp(name, thread->name) || thread->id == id) { _dump_thread_info(thread, shortInfo); found = true; break; } } - hash_close(sThreadHash, &i, false); if (!found) kprintf("thread \"%s\" (%ld) doesn't exist!\n", name, id); @@ -1292,8 +1806,6 @@ dump_thread_info(int argc, char **argv) static int dump_thread_list(int argc, char **argv) { - Thread *thread; - struct hash_iterator i; bool realTimeOnly = false; bool calling = false; const char *callSymbol = NULL; @@ -1336,8 +1848,8 @@ dump_thread_list(int argc, char **argv) print_thread_list_table_head(); - hash_open(sThreadHash, &i); - while ((thread = (Thread*)hash_next(sThreadHash, &i)) != NULL) { + for (ThreadHashTable::Iterator it = sThreadHash.GetIterator(); + Thread* thread = it.Next();) { // filter out threads not matching the search criteria if ((requiredState && thread->state != requiredState) || (calling && !arch_debug_contains_call(thread, callSymbol, @@ -1349,7 +1861,6 @@ dump_thread_list(int argc, char **argv) _dump_thread_info(thread, true); } - hash_close(sThreadHash, &i, false); return 0; } @@ -1361,16 +1872,16 @@ void thread_exit(void) { cpu_status state; - Thread *thread = thread_get_current_thread(); - Team *team = thread->team; + Thread* thread = thread_get_current_thread(); + Team* team = thread->team; + Team* kernelTeam = team_get_kernel_team(); thread_id parentID = -1; status_t status; struct thread_debug_info debugInfo; team_id teamID = team->id; - TRACE(("thread %ld exiting %s w/return code %#lx\n", thread->id, - thread->exit.reason == THREAD_RETURN_INTERRUPTED - ? "due to signal" : "normally", thread->exit.status)); + TRACE(("thread %ld exiting w/return code %#lx\n", thread->id, + thread->exit.status)); if (!are_interrupts_enabled()) panic("thread_exit() called with interrupts disabled!\n"); @@ -1378,8 +1889,31 @@ thread_exit(void) // boost our priority to get this over with thread->priority = thread->next_priority = B_URGENT_DISPLAY_PRIORITY; - // Cancel previously installed alarm timer, if any - cancel_timer(&thread->alarm); + if (team != kernelTeam) { + // Cancel previously installed alarm timer, if any. Hold the scheduler + // lock to make sure that when cancel_timer() returns, the alarm timer + // hook will not be invoked anymore (since + // B_TIMER_ACQUIRE_SCHEDULER_LOCK is used). + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + cancel_timer(&thread->alarm); + schedulerLocker.Unlock(); + + // Delete all user timers associated with the thread. + ThreadLocker threadLocker(thread); + thread->DeleteUserTimers(false); + + // detach the thread's user thread + user_thread* userThread = thread->user_thread; + thread->user_thread = NULL; + + threadLocker.Unlock(); + + // Delete the thread's user thread, if it's not the main thread. If it + // is, we can save the work, since it will be deleted with the team's + // address space. + if (thread != team->main_thread) + team_free_user_thread(team, userThread); + } // remember the user stack area -- we will delete it below area_id userStackArea = -1; @@ -1389,39 +1923,69 @@ thread_exit(void) } struct job_control_entry *death = NULL; - struct death_entry* threadDeathEntry = NULL; + struct thread_death_entry* threadDeathEntry = NULL; bool deleteTeam = false; port_id debuggerPort = -1; - if (team != team_get_kernel_team()) { + if (team != kernelTeam) { user_debug_thread_exiting(thread); if (team->main_thread == thread) { // The main thread is exiting. Shut down the whole team. deleteTeam = true; + + // kill off all other threads and the user debugger facilities + debuggerPort = team_shutdown_team(team); + + // acquire necessary locks, which are: process group lock, kernel + // team lock, parent team lock, and the team lock + team->LockProcessGroup(); + kernelTeam->Lock(); + team->LockTeamAndParent(true); } else { - threadDeathEntry = (death_entry*)malloc(sizeof(death_entry)); - team_free_user_thread(thread); + threadDeathEntry + = (thread_death_entry*)malloc(sizeof(thread_death_entry)); + + // acquire necessary locks, which are: kernel team lock and the team + // lock + kernelTeam->Lock(); + team->Lock(); } - // remove this thread from the current team and add it to the kernel - // put the thread into the kernel team until it dies + ThreadLocker threadLocker(thread); + state = disable_interrupts(); - GRAB_TEAM_LOCK(); - if (deleteTeam) - debuggerPort = team_shutdown_team(team, state); + // swap address spaces, to make sure we're running on the kernel's pgdir + vm_swap_address_space(team->address_space, VMAddressSpace::Kernel()); - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); // removing the thread and putting its death entry to the parent // team needs to be an atomic operation // remember how long this thread lasted + bigtime_t now = system_time(); + InterruptsSpinLocker threadTimeLocker(thread->time_lock); + thread->kernel_time += now - thread->last_time; + thread->last_time = now; + threadTimeLocker.Unlock(); + team->dead_threads_kernel_time += thread->kernel_time; team->dead_threads_user_time += thread->user_time; + // stop/update thread/team CPU time user timers + if (thread->HasActiveCPUTimeUserTimers() + || team->HasActiveCPUTimeUserTimers()) { + user_timer_stop_cpu_timers(thread, NULL); + } + + // deactivate CPU time user timers for the thread + if (thread->HasActiveCPUTimeUserTimers()) + thread->DeactivateCPUTimeUserTimers(); + + // put the thread into the kernel team until it dies remove_thread_from_team(team, thread); - insert_thread_into_team(team_get_kernel_team(), thread); + insert_thread_into_team(kernelTeam, thread); if (team->death_entry != NULL) { if (--team->death_entry->remaining_threads == 0) @@ -1429,14 +1993,15 @@ thread_exit(void) } if (deleteTeam) { - Team *parent = team->parent; + Team* parent = team->parent; // remember who our parent was so we can send a signal parentID = parent->id; // Set the team job control state to "dead" and detach the job // control entry from our team struct. - team_set_job_control_state(team, JOB_CONTROL_STATE_DEAD, 0, true); + team_set_job_control_state(team, JOB_CONTROL_STATE_DEAD, NULL, + true); death = team->job_control_entry; team->job_control_entry = NULL; @@ -1451,46 +2016,79 @@ thread_exit(void) parent->dead_children.count--; } else death = NULL; + } - RELEASE_THREAD_LOCK(); - } else - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); + restore_interrupts(state); - team_remove_team(team); + threadLocker.Unlock(); - send_signal_etc(parentID, SIGCHLD, - SIGNAL_FLAG_TEAMS_LOCKED | B_DO_NOT_RESCHEDULE); + // Get a temporary reference to the team's process group + // -- team_remove_team() removes the team from the group, which + // might destroy it otherwise and we wouldn't be able to unlock it. + ProcessGroup* group = team->group; + group->AcquireReference(); + + pid_t foregroundGroupToSignal; + team_remove_team(team, foregroundGroupToSignal); + + // unlock everything but the parent team + team->Unlock(); + if (parent != kernelTeam) + kernelTeam->Unlock(); + group->Unlock(); + group->ReleaseReference(); + + // Send SIGCHLD to the parent as long as we still have its lock. + // This makes job control state change + signalling atomic. + Signal childSignal(SIGCHLD, team->exit.reason, B_OK, team->id); + if (team->exit.reason == CLD_EXITED) { + childSignal.SetStatus(team->exit.status); + } else { + childSignal.SetStatus(team->exit.signal); + childSignal.SetSendingUser(team->exit.signaling_user); + } + send_signal_to_team(parent, childSignal, B_DO_NOT_RESCHEDULE); + + // also unlock the parent + parent->Unlock(); + + // If the team was a session leader with controlling TTY, we have + // to send SIGHUP to the foreground process group. + if (foregroundGroupToSignal >= 0) { + Signal groupSignal(SIGHUP, SI_USER, B_OK, team->id); + send_signal_to_process_group(foregroundGroupToSignal, + groupSignal, B_DO_NOT_RESCHEDULE); + } } else { - // The thread is not the main thread. We store a thread death - // entry for it, unless someone is already waiting it. + // The thread is not the main thread. We store a thread death entry + // for it, unless someone is already waiting for it. if (threadDeathEntry != NULL && list_is_empty(&thread->exit.waiters)) { threadDeathEntry->thread = thread->id; threadDeathEntry->status = thread->exit.status; - threadDeathEntry->reason = thread->exit.reason; - threadDeathEntry->signal = thread->exit.signal; - // add entry -- remove and old one, if we hit the limit + // add entry -- remove an old one, if we hit the limit list_add_item(&team->dead_threads, threadDeathEntry); team->dead_threads_count++; threadDeathEntry = NULL; if (team->dead_threads_count > MAX_DEAD_THREADS) { - threadDeathEntry = (death_entry*)list_remove_head_item( - &team->dead_threads); + threadDeathEntry + = (thread_death_entry*)list_remove_head_item( + &team->dead_threads); team->dead_threads_count--; } } - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); + restore_interrupts(state); + + threadLocker.Unlock(); + team->Unlock(); + kernelTeam->Unlock(); } - RELEASE_TEAM_LOCK(); - - // swap address spaces, to make sure we're running on the kernel's pgdir - vm_swap_address_space(team->address_space, VMAddressSpace::Kernel()); - restore_interrupts(state); - TRACE(("thread_exit: thread %ld now a kernel thread!\n", thread->id)); } @@ -1504,24 +2102,32 @@ thread_exit(void) delete death; } - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + ThreadLocker threadLocker(thread); - // remove thread from hash, so it's no longer accessible - hash_remove(sThreadHash, thread); + state = disable_interrupts(); + SpinLocker schedulerLocker(gSchedulerLock); + + // mark invisible in global hash/list, so it's no longer accessible + SpinLocker threadHashLocker(sThreadHashLock); + thread->visible = false; sUsedThreads--; + threadHashLocker.Unlock(); // Stop debugging for this thread + SpinLocker threadDebugInfoLocker(thread->debug_info.lock); debugInfo = thread->debug_info; clear_thread_debug_info(&thread->debug_info, true); + threadDebugInfoLocker.Unlock(); // Remove the select infos. We notify them a little later. select_info* selectInfos = thread->select_infos; thread->select_infos = NULL; - RELEASE_THREAD_LOCK(); + schedulerLocker.Unlock(); restore_interrupts(state); + threadLocker.Unlock(); + destroy_thread_debug_info(&debugInfo); // notify select infos @@ -1554,28 +2160,24 @@ thread_exit(void) // now we can safely remove the msg.read_sem delete_sem(thread->msg.read_sem); - // fill all death entries and delete the sem that others will use to wait on us + // fill all death entries and delete the sem that others will use to wait + // for us { sem_id cachedExitSem = thread->exit.sem; - cpu_status state; - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + ThreadLocker threadLocker(thread); // make sure no one will grab this semaphore again thread->exit.sem = -1; // fill all death entries - death_entry* entry = NULL; - while ((entry = (struct death_entry*)list_get_next_item( + thread_death_entry* entry = NULL; + while ((entry = (thread_death_entry*)list_get_next_item( &thread->exit.waiters, entry)) != NULL) { entry->status = thread->exit.status; - entry->reason = thread->exit.reason; - entry->signal = thread->exit.signal; } - RELEASE_THREAD_LOCK(); - restore_interrupts(state); + threadLocker.Unlock(); delete_sem(cachedExitSem); } @@ -1595,14 +2197,14 @@ thread_exit(void) } // notify the debugger - if (teamID != team_get_kernel_team_id()) + if (teamID != kernelTeam->id) user_debug_thread_deleted(teamID, thread->id); // enqueue in the undertaker list and reschedule for the last time UndertakerEntry undertakerEntry(thread, teamID); disable_interrupts(); - GRAB_THREAD_LOCK(); + schedulerLocker.Lock(); sUndertakerEntries.Add(&undertakerEntry); sUndertakerCondition.NotifyOne(true); @@ -1614,35 +2216,6 @@ thread_exit(void) } -Thread * -thread_get_thread_struct(thread_id id) -{ - Thread *thread; - cpu_status state; - - state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - thread = thread_get_thread_struct_locked(id); - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - return thread; -} - - -Thread * -thread_get_thread_struct_locked(thread_id id) -{ - struct thread_key key; - - key.id = id; - - return (Thread*)hash_lookup(sThreadHash, &key); -} - - /*! Called in the interrupt handler code when a thread enters the kernel for any reason. Only tracks time for now. @@ -1656,10 +2229,11 @@ thread_at_kernel_entry(bigtime_t now) TRACE(("thread_at_kernel_entry: entry thread %ld\n", thread->id)); // track user time + SpinLocker threadTimeLocker(thread->time_lock); thread->user_time += now - thread->last_time; thread->last_time = now; - thread->in_kernel = true; + threadTimeLocker.Unlock(); } @@ -1667,6 +2241,8 @@ thread_at_kernel_entry(bigtime_t now) Tracks time, handles signals, ... Interrupts must be enabled. When the function returns, interrupts will be disabled. + The function may not return. This e.g. happens when the thread has received + a deadly signal. */ void thread_at_kernel_exit(void) @@ -1675,17 +2251,14 @@ thread_at_kernel_exit(void) TRACE(("thread_at_kernel_exit: exit thread %ld\n", thread->id)); - while (handle_signals(thread)) { - InterruptsSpinLocker _(gThreadSpinlock); - scheduler_reschedule(); - } + handle_signals(thread); disable_interrupts(); - thread->in_kernel = false; - // track kernel time bigtime_t now = system_time(); + SpinLocker threadTimeLocker(thread->time_lock); + thread->in_kernel = false; thread->kernel_time += now - thread->last_time; thread->last_time = now; } @@ -1702,10 +2275,10 @@ thread_at_kernel_exit_no_signals(void) TRACE(("thread_at_kernel_exit_no_signals: exit thread %ld\n", thread->id)); - thread->in_kernel = false; - // track kernel time bigtime_t now = system_time(); + SpinLocker threadTimeLocker(thread->time_lock); + thread->in_kernel = false; thread->kernel_time += now - thread->last_time; thread->last_time = now; } @@ -1714,9 +2287,30 @@ thread_at_kernel_exit_no_signals(void) void thread_reset_for_exec(void) { - Thread *thread = thread_get_current_thread(); + Thread* thread = thread_get_current_thread(); - reset_signals(thread); + ThreadLocker threadLocker(thread); + + // delete user-defined timers + thread->DeleteUserTimers(true); + + // cancel pre-defined timer + if (UserTimer* timer = thread->UserTimerFor(USER_TIMER_REAL_TIME_ID)) + timer->Cancel(); + + // reset user_thread and user stack + thread->user_thread = NULL; + thread->user_stack_area = -1; + thread->user_stack_base = 0; + thread->user_stack_size = 0; + + // reset signals + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + thread->ResetSignalsOnExec(); + + // reset thread CPU time clock + thread->cpu_clock_offset = -thread->CPUTime(false); // Note: We don't cancel an alarm. It is supposed to survive exec*(). } @@ -1783,36 +2377,32 @@ thread_dequeue_id(struct thread_queue *q, thread_id id) } -Thread* -thread_iterate_through_threads(thread_iterator_callback callback, void* cookie) +thread_id +allocate_thread_id() { - struct hash_iterator iterator; - hash_open(sThreadHash, &iterator); + InterruptsSpinLocker threadHashLocker(sThreadHashLock); - Thread* thread; - while ((thread = (Thread*)hash_next(sThreadHash, &iterator)) - != NULL) { - if (callback(thread, cookie)) - break; - } + // find the next unused ID + thread_id id; + do { + id = sNextThreadID++; - hash_close(sThreadHash, &iterator, false); + // deal with integer overflow + if (sNextThreadID < 0) + sNextThreadID = 2; - return thread; + // check whether the ID is already in use + } while (sThreadHash.Lookup(id, false) != NULL); + + return id; } thread_id -allocate_thread_id(void) +peek_next_thread_id() { - return atomic_add(&sNextThreadID, 1); -} - - -thread_id -peek_next_thread_id(void) -{ - return atomic_get(&sNextThreadID); + InterruptsSpinLocker threadHashLocker(sThreadHashLock); + return sNextThreadID; } @@ -1835,16 +2425,12 @@ thread_yield(bool force) if (thread == NULL) return; - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + InterruptsSpinLocker _(gSchedulerLock); // mark the thread as yielded, so it will not be scheduled next //thread->was_yielded = true; thread->next_priority = B_LOWEST_ACTIVE_PRIORITY; scheduler_reschedule(); - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); #endif } else { Thread *thread = thread_get_current_thread(); @@ -1852,33 +2438,21 @@ thread_yield(bool force) return; // Don't force the thread off the CPU, just reschedule. - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); scheduler_reschedule(); } } /*! Kernel private thread creation function. - - \param threadID The ID to be assigned to the new thread. If - \code < 0 \endcode a fresh one is allocated. */ thread_id spawn_kernel_thread_etc(thread_func function, const char *name, int32 priority, - void *arg, team_id team, thread_id threadID) + void *arg, team_id team) { - thread_creation_attributes attributes; - attributes.entry = (thread_entry_func)function; - attributes.name = name; - attributes.priority = priority; - attributes.args1 = arg; - attributes.args2 = NULL; - attributes.stack_address = NULL; - attributes.stack_size = 0; - attributes.team = team; - attributes.thread = threadID; - - return create_thread(attributes, true); + return thread_create_thread( + ThreadCreationAttributes(function, name, priority, arg, team), + true); } @@ -1886,38 +2460,35 @@ status_t wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, status_t *_returnCode) { - sem_id exitSem = B_BAD_THREAD_ID; - struct death_entry death; job_control_entry* freeDeath = NULL; - Thread *thread; - cpu_status state; status_t status = B_OK; if (id < B_OK) return B_BAD_THREAD_ID; - // we need to resume the thread we're waiting for first + // get the thread, queue our death entry, and fetch the semaphore we have to + // wait on + sem_id exitSem = B_BAD_THREAD_ID; + struct thread_death_entry death; - state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - thread = thread_get_thread_struct_locked(id); + Thread* thread = Thread::GetAndLock(id); if (thread != NULL) { // remember the semaphore we have to wait on and place our death entry exitSem = thread->exit.sem; list_add_link_to_head(&thread->exit.waiters, &death); + + thread->UnlockAndReleaseReference(); + // Note: We mustn't dereference the pointer afterwards, only check + // it. } - death_entry* threadDeathEntry = NULL; - - RELEASE_THREAD_LOCK(); + thread_death_entry* threadDeathEntry = NULL; if (thread == NULL) { - // we couldn't find this thread - maybe it's already gone, and we'll + // we couldn't find this thread -- maybe it's already gone, and we'll // find its death entry in our team - GRAB_TEAM_LOCK(); - Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); // check the child death entries first (i.e. main threads of child // teams) @@ -1929,7 +2500,7 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, freeDeath = NULL; } else { // check the thread death entries of the team (non-main threads) - while ((threadDeathEntry = (death_entry*)list_get_next_item( + while ((threadDeathEntry = (thread_death_entry*)list_get_next_item( &team->dead_threads, threadDeathEntry)) != NULL) { if (threadDeathEntry->thread == id) { list_remove_item(&team->dead_threads, threadDeathEntry); @@ -1942,12 +2513,8 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, if (threadDeathEntry == NULL) status = B_BAD_THREAD_ID; } - - RELEASE_TEAM_LOCK(); } - restore_interrupts(state); - if (thread == NULL && status == B_OK) { // we found the thread's death entry in our team if (_returnCode) @@ -1960,7 +2527,7 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, // we need to wait for the death of the thread - if (exitSem < B_OK) + if (exitSem < 0) return B_BAD_THREAD_ID; resume_thread(id); @@ -1978,21 +2545,20 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, if (_returnCode) *_returnCode = death.status; } else { - // We were probably interrupted; we need to remove our death entry now. - state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - thread = thread_get_thread_struct_locked(id); - if (thread != NULL) + // We were probably interrupted or the timeout occurred; we need to + // remove our death entry now. + thread = Thread::GetAndLock(id); + if (thread != NULL) { list_remove_link(&death); - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - // If the thread is already gone, we need to wait for its exit semaphore - // to make sure our death entry stays valid - it won't take long - if (thread == NULL) + thread->UnlockAndReleaseReference(); + } else { + // The thread is already gone, so we need to wait uninterruptibly + // for its exit semaphore to make sure our death entry stays valid. + // It won't take long, since the thread is apparently already in the + // middle of the cleanup. acquire_sem(exitSem); + status = B_OK; + } } return status; @@ -2002,12 +2568,12 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, status_t select_thread(int32 id, struct select_info* info, bool kernel) { - InterruptsSpinLocker locker(gThreadSpinlock); - - // get thread - Thread* thread = thread_get_thread_struct_locked(id); + // get and lock the thread + Thread* thread = Thread::GetAndLock(id); if (thread == NULL) return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); // We support only B_EVENT_INVALID at the moment. info->selected_events &= B_EVENT_INVALID; @@ -2028,12 +2594,12 @@ select_thread(int32 id, struct select_info* info, bool kernel) status_t deselect_thread(int32 id, struct select_info* info, bool kernel) { - InterruptsSpinLocker locker(gThreadSpinlock); - - // get thread - Thread* thread = thread_get_thread_struct_locked(id); + // get and lock the thread + Thread* thread = Thread::GetAndLock(id); if (thread == NULL) return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); // remove info from list select_info** infoLocation = &thread->select_infos; @@ -2045,7 +2611,7 @@ deselect_thread(int32 id, struct select_info* info, bool kernel) *infoLocation = info->next; - locker.Unlock(); + threadLocker.Unlock(); // surrender sync reference put_select_sync(info->sync); @@ -2064,10 +2630,14 @@ thread_max_threads(void) int32 thread_used_threads(void) { + InterruptsSpinLocker threadHashLocker(sThreadHashLock); return sUsedThreads; } +/*! Returns a user-readable string for a thread state. + Only for use in the kernel debugger. +*/ const char* thread_state_to_text(Thread* thread, int32 state) { @@ -2078,23 +2648,20 @@ thread_state_to_text(Thread* thread, int32 state) int32 thread_get_io_priority(thread_id id) { - // take a shortcut, if it is the current thread - Thread* thread = thread_get_current_thread(); - int32 priority; - if (id == thread->id) { - int32 priority = thread->io_priority; - return priority < 0 ? thread->priority : priority; - } - - // not the current thread -- get it - InterruptsSpinLocker locker(gThreadSpinlock); - - thread = thread_get_thread_struct_locked(id); + Thread* thread = Thread::GetAndLock(id); if (thread == NULL) return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - priority = thread->io_priority; - return priority < 0 ? thread->priority : priority; + int32 priority = thread->io_priority; + if (priority < 0) { + // negative I/O priority means using the (CPU) priority + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + priority = thread->priority; + } + + return priority; } @@ -2102,6 +2669,8 @@ void thread_set_io_priority(int32 priority) { Thread* thread = thread_get_current_thread(); + ThreadLocker threadLocker(thread); + thread->io_priority = priority; } @@ -2109,13 +2678,12 @@ thread_set_io_priority(int32 priority) status_t thread_init(kernel_args *args) { - uint32 i; - TRACE(("thread_init: entry\n")); // create the thread hash table - sThreadHash = hash_init(15, offsetof(Thread, all_next), - &thread_struct_compare, &thread_struct_hash); + new(&sThreadHash) ThreadHashTable(); + if (sThreadHash.Init(128) != B_OK) + panic("thread_init(): failed to init thread hash table!"); // create the thread structure object cache sThreadCache = create_object_cache("threads", sizeof(Thread), 16, NULL, @@ -2131,16 +2699,15 @@ thread_init(kernel_args *args) sNextThreadID = B_SYSTEM_TEAM + 1; // create an idle thread for each cpu - - for (i = 0; i < args->num_cpus; i++) { + for (uint32 i = 0; i < args->num_cpus; i++) { Thread *thread; area_info info; char name[64]; sprintf(name, "idle thread %lu", i + 1); - thread = create_thread_struct(&sIdleThreads[i], name, + thread = new(&sIdleThreads[i]) Thread(name, i == 0 ? team_get_kernel_team_id() : -1, &gCPU[i]); - if (thread == NULL) { + if (thread == NULL || thread->Init(true) != B_OK) { panic("error creating idle thread struct\n"); return B_NO_MEMORY; } @@ -2153,7 +2720,6 @@ thread_init(kernel_args *args) thread->next_state = B_THREAD_READY; sprintf(name, "idle thread %lu kstack", i + 1); thread->kernel_stack_area = find_area(name); - thread->entry = NULL; if (get_area_info(thread->kernel_stack_area, &info) != B_OK) panic("error finding idle kstack area\n"); @@ -2161,7 +2727,7 @@ thread_init(kernel_args *args) thread->kernel_stack_base = (addr_t)info.address; thread->kernel_stack_top = thread->kernel_stack_base + info.size; - hash_insert(sThreadHash, thread); + thread->visible = true; insert_thread_into_team(thread->team, thread); } sUsedThreads = args->num_cpus; @@ -2267,8 +2833,8 @@ thread_preboot_init_percpu(struct kernel_args *args, int32 cpuNum) static status_t thread_block_timeout(timer* timer) { - // The timer has been installed with B_TIMER_ACQUIRE_THREAD_LOCK, so - // we're holding the thread lock already. This makes things comfortably + // The timer has been installed with B_TIMER_ACQUIRE_SCHEDULER_LOCK, so + // we're holding the scheduler lock already. This makes things comfortably // easy. Thread* thread = (Thread*)timer->user_data; @@ -2278,33 +2844,59 @@ thread_block_timeout(timer* timer) } +/*! Blocks the current thread. + + The function acquires the scheduler lock and calls thread_block_locked(). + See there for more information. +*/ status_t thread_block() { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); return thread_block_locked(thread_get_current_thread()); } -void -thread_unblock(status_t threadID, status_t status) -{ - InterruptsSpinLocker _(gThreadSpinlock); - - Thread* thread = thread_get_thread_struct_locked(threadID); - if (thread != NULL) - thread_unblock_locked(thread, status); -} - +/*! Blocks the current thread with a timeout. + Acquires the scheduler lock and calls thread_block_with_timeout_locked(). + See there for more information. +*/ status_t thread_block_with_timeout(uint32 timeoutFlags, bigtime_t timeout) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); return thread_block_with_timeout_locked(timeoutFlags, timeout); } +/*! Blocks the current thread with a timeout. + + The thread is blocked until someone else unblock it or the specified timeout + occurs. Must be called after a call to thread_prepare_to_block(). If the + thread has already been unblocked after the previous call to + thread_prepare_to_block(), this function will return immediately. See + thread_prepare_to_block() for more details. + + The caller must hold the scheduler lock. + + \param thread The current thread. + \param timeoutFlags The standard timeout flags: + - \c B_RELATIVE_TIMEOUT: \a timeout specifies the time to wait. + - \c B_ABSOLUTE_TIMEOUT: \a timeout specifies the absolute end time when + the timeout shall occur. + - \c B_TIMEOUT_REAL_TIME_BASE: Only relevant when \c B_ABSOLUTE_TIMEOUT + is specified, too. Specifies that \a timeout is a real time, not a + system time. + If neither \c B_RELATIVE_TIMEOUT nor \c B_ABSOLUTE_TIMEOUT are + specified, an infinite timeout is implied and the function behaves like + thread_block_locked(). + \return The error code passed to the unblocking function. thread_interrupt() + uses \c B_INTERRUPTED. When the timeout occurred, \c B_TIMED_OUT is + returned. By convention \c B_OK means that the wait was successful while + another error code indicates a failure (what that means depends on the + client code). +*/ status_t thread_block_with_timeout_locked(uint32 timeoutFlags, bigtime_t timeout) { @@ -2327,9 +2919,9 @@ thread_block_with_timeout_locked(uint32 timeoutFlags, bigtime_t timeout) } else { timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER; if ((timeoutFlags & B_TIMEOUT_REAL_TIME_BASE) != 0) - timeout -= rtc_boot_time(); + timerFlags |= B_TIMER_REAL_TIME_BASE; } - timerFlags |= B_TIMER_ACQUIRE_THREAD_LOCK; + timerFlags |= B_TIMER_ACQUIRE_SCHEDULER_LOCK; // install the timer thread->wait.unblock_timer.user_data = thread; @@ -2348,17 +2940,24 @@ thread_block_with_timeout_locked(uint32 timeoutFlags, bigtime_t timeout) } -/*! Thread spinlock must be held. +/*! Unblocks a userland-blocked thread. + The caller must not hold any locks. */ static status_t user_unblock_thread(thread_id threadID, status_t status) { - Thread* thread = thread_get_thread_struct_locked(threadID); + // get the thread + Thread* thread = Thread::GetAndLock(threadID); if (thread == NULL) return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); + if (thread->user_thread == NULL) return B_NOT_ALLOWED; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + if (thread->user_thread->wait_status > 0) { thread->user_thread->wait_status = status; thread_unblock_locked(thread, status); @@ -2375,16 +2974,32 @@ void exit_thread(status_t returnValue) { Thread *thread = thread_get_current_thread(); + Team* team = thread->team; thread->exit.status = returnValue; - thread->exit.reason = THREAD_RETURN_EXIT; // if called from a kernel thread, we don't deliver the signal, // we just exit directly to keep the user space behaviour of // this function - if (thread->team != team_get_kernel_team()) - send_signal_etc(thread->id, SIGKILLTHR, B_DO_NOT_RESCHEDULE); - else + if (team != team_get_kernel_team()) { + // If this is its main thread, set the team's exit status. + if (thread == team->main_thread) { + TeamLocker teamLocker(team); + + if (!team->exit.initialized) { + team->exit.reason = CLD_EXITED; + team->exit.signal = 0; + team->exit.signaling_user = 0; + team->exit.status = returnValue; + team->exit.initialized = true; + } + + teamLocker.Unlock(); + } + + Signal signal(SIGKILLTHR, SI_USER, B_OK, team->id); + send_signal_to_thread(thread, signal, B_DO_NOT_RESCHEDULE); + } else thread_exit(); } @@ -2395,7 +3010,10 @@ kill_thread(thread_id id) if (id <= 0) return B_BAD_VALUE; - return send_signal(id, SIGKILLTHR); + Thread* currentThread = thread_get_current_thread(); + + Signal signal(SIGKILLTHR, SI_USER, B_OK, currentThread->team->id); + return send_signal_to_thread_id(id, signal, 0); } @@ -2416,6 +3034,7 @@ receive_data(thread_id *sender, void *buffer, size_t bufferSize) bool has_data(thread_id thread) { + // TODO: The thread argument is ignored. int32 count; if (get_sem_count(thread_get_current_thread()->msg.read_sem, @@ -2429,29 +3048,22 @@ has_data(thread_id thread) status_t _get_thread_info(thread_id id, thread_info *info, size_t size) { - status_t status = B_OK; - Thread *thread; - cpu_status state; - if (info == NULL || size != sizeof(thread_info) || id < B_OK) return B_BAD_VALUE; - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // get the thread + Thread* thread = Thread::GetAndLock(id); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - thread = thread_get_thread_struct_locked(id); - if (thread == NULL) { - status = B_BAD_VALUE; - goto err; - } + // fill the info -- also requires the scheduler lock to be held + InterruptsSpinLocker schedulerLocker(gSchedulerLock); fill_thread_info(thread, info, size); -err: - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - return status; + return B_OK; } @@ -2464,16 +3076,12 @@ _get_next_thread_info(team_id teamID, int32 *_cookie, thread_info *info, int32 lastID = *_cookie; - InterruptsSpinLocker teamLocker(gTeamSpinlock); - - Team* team; - if (teamID == B_CURRENT_TEAM) - team = thread_get_current_thread()->team; - else - team = team_get_team_struct_locked(teamID); - + // get the team + Team* team = Team::GetAndLock(teamID); if (team == NULL) return B_BAD_VALUE; + BReference teamReference(team, true); + TeamLocker teamLocker(team, true); Thread* thread = NULL; @@ -2481,9 +3089,10 @@ _get_next_thread_info(team_id teamID, int32 *_cookie, thread_info *info, // We start with the main thread thread = team->main_thread; } else { - // Find the one thread with an ID higher than ours - // (as long as the IDs don't overlap they are always sorted from - // highest to lowest). + // Find the one thread with an ID greater than ours (as long as the IDs + // don't wrap they are always sorted from highest to lowest). + // TODO: That is broken not only when the IDs wrap, but also for the + // kernel team, to which threads are added when they are dying. for (Thread* next = team->thread_list; next != NULL; next = next->team_next) { if (next->id <= lastID) @@ -2499,7 +3108,9 @@ _get_next_thread_info(team_id teamID, int32 *_cookie, thread_info *info, lastID = thread->id; *_cookie = lastID; - SpinLocker threadLocker(gThreadSpinlock); + ThreadLocker threadLocker(thread); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + fill_thread_info(thread, info, size); return B_OK; @@ -2507,39 +3118,25 @@ _get_next_thread_info(team_id teamID, int32 *_cookie, thread_info *info, thread_id -find_thread(const char *name) +find_thread(const char* name) { - struct hash_iterator iterator; - Thread *thread; - cpu_status state; - if (name == NULL) return thread_get_current_thread_id(); - state = disable_interrupts(); - GRAB_THREAD_LOCK(); + InterruptsSpinLocker threadHashLocker(sThreadHashLock); - // ToDo: this might not be in the same order as find_thread() in BeOS - // which could be theoretically problematic. - // ToDo: scanning the whole list with the thread lock held isn't exactly - // cheap either - although this function is probably used very rarely. + // TODO: Scanning the whole hash with the thread hash lock held isn't + // exactly cheap -- although this function is probably used very rarely. - hash_open(sThreadHash, &iterator); - while ((thread = (Thread*)hash_next(sThreadHash, &iterator)) - != NULL) { - // Search through hash - if (!strcmp(thread->name, name)) { - thread_id id = thread->id; + for (ThreadHashTable::Iterator it = sThreadHash.GetIterator(); + Thread* thread = it.Next();) { + if (!thread->visible) + continue; - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - return id; - } + if (strcmp(thread->name, name) == 0) + return thread->id; } - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - return B_NAME_NOT_FOUND; } @@ -2547,26 +3144,25 @@ find_thread(const char *name) status_t rename_thread(thread_id id, const char* name) { - Thread* thread = thread_get_current_thread(); - if (name == NULL) return B_BAD_VALUE; - InterruptsSpinLocker locker(gThreadSpinlock); + // get the thread + Thread* thread = Thread::GetAndLock(id); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); - if (thread->id != id) { - thread = thread_get_thread_struct_locked(id); - if (thread == NULL) - return B_BAD_THREAD_ID; - if (thread->team != thread_get_current_thread()->team) - return B_NOT_ALLOWED; - } + // check whether the operation is allowed + if (thread->team != thread_get_current_thread()->team) + return B_NOT_ALLOWED; strlcpy(thread->name, name, B_OS_NAME_LENGTH); team_id teamID = thread->team->id; - locker.Unlock(); + threadLocker.Unlock(); // notify listeners sNotificationService.Notify(THREAD_NAME_CHANGED, teamID, id); @@ -2579,7 +3175,6 @@ rename_thread(thread_id id, const char* name) status_t set_thread_priority(thread_id id, int32 priority) { - Thread *thread; int32 oldPriority; // make sure the passed in priority is within bounds @@ -2588,28 +3183,25 @@ set_thread_priority(thread_id id, int32 priority) if (priority < THREAD_MIN_SET_PRIORITY) priority = THREAD_MIN_SET_PRIORITY; - thread = thread_get_current_thread(); - if (thread->id == id) { - if (thread_is_idle_thread(thread)) - return B_NOT_ALLOWED; + // get the thread + Thread* thread = Thread::GetAndLock(id); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); + // check whether the change is allowed + if (thread_is_idle_thread(thread)) + return B_NOT_ALLOWED; + + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + if (thread == thread_get_current_thread()) { // It's ourself, so we know we aren't in the run queue, and we can - // manipulate our structure directly + // manipulate our structure directly. oldPriority = thread->priority; - // Note that this might not return the correct value if we are - // preempted here, and another thread changes our priority before - // the next line is executed. thread->priority = thread->next_priority = priority; } else { - InterruptsSpinLocker _(gThreadSpinlock); - - thread = thread_get_thread_struct_locked(id); - if (thread == NULL) - return B_BAD_THREAD_ID; - - if (thread_is_idle_thread(thread)) - return B_NOT_ALLOWED; - oldPriority = thread->priority; scheduler_set_thread_priority(thread, priority); } @@ -2621,21 +3213,7 @@ set_thread_priority(thread_id id, int32 priority) status_t snooze_etc(bigtime_t timeout, int timebase, uint32 flags) { - status_t status; - - if (timebase != B_SYSTEM_TIMEBASE) - return B_BAD_VALUE; - - InterruptsSpinLocker _(gThreadSpinlock); - Thread* thread = thread_get_current_thread(); - - thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SNOOZE, NULL); - status = thread_block_with_timeout_locked(flags, timeout); - - if (status == B_TIMED_OUT || status == B_WOULD_BLOCK) - return B_OK; - - return status; + return common_snooze_etc(timeout, timebase, flags, NULL); } @@ -2670,7 +3248,10 @@ suspend_thread(thread_id id) if (id <= 0) return B_BAD_VALUE; - return send_signal(id, SIGSTOP); + Thread* currentThread = thread_get_current_thread(); + + Signal signal(SIGSTOP, SI_USER, B_OK, currentThread->team->id); + return send_signal_to_thread_id(id, signal, 0); } @@ -2680,10 +3261,14 @@ resume_thread(thread_id id) if (id <= 0) return B_BAD_VALUE; - return send_signal_etc(id, SIGCONT, SIGNAL_FLAG_DONT_RESTART_SYSCALL); - // This retains compatibility to BeOS which documents the - // combination of suspend_thread() and resume_thread() to - // interrupt threads waiting on semaphores. + Thread* currentThread = thread_get_current_thread(); + + // Using the kernel internal SIGNAL_CONTINUE_THREAD signal retains + // compatibility to BeOS which documents the combination of suspend_thread() + // and resume_thread() to interrupt threads waiting on semaphores. + Signal signal(SIGNAL_CONTINUE_THREAD, SI_USER, B_OK, + currentThread->team->id); + return send_signal_to_thread_id(id, signal, 0); } @@ -2691,18 +3276,9 @@ thread_id spawn_kernel_thread(thread_func function, const char *name, int32 priority, void *arg) { - thread_creation_attributes attributes; - attributes.entry = (thread_entry_func)function; - attributes.name = name; - attributes.priority = priority; - attributes.args1 = arg; - attributes.args2 = NULL; - attributes.stack_address = NULL; - attributes.stack_size = 0; - attributes.team = team_get_kernel_team()->id; - attributes.thread = -1; - - return create_thread(attributes, true); + return thread_create_thread( + ThreadCreationAttributes(function, name, priority, arg), + true); } @@ -2745,13 +3321,42 @@ _user_exit_thread(status_t returnValue) status_t _user_kill_thread(thread_id thread) { + // TODO: Don't allow kernel threads to be killed! return kill_thread(thread); } +status_t +_user_cancel_thread(thread_id threadID, void (*cancelFunction)(int)) +{ + // check the cancel function + if (cancelFunction == NULL || !IS_USER_ADDRESS(cancelFunction)) + return B_BAD_VALUE; + + // get and lock the thread + Thread* thread = Thread::GetAndLock(threadID); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference threadReference(thread, true); + ThreadLocker threadLocker(thread, true); + + // only threads of the same team can be canceled + if (thread->team != thread_get_current_thread()->team) + return B_NOT_ALLOWED; + + // set the cancel function + thread->cancel_function = cancelFunction; + + // send the cancellation signal to the thread + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + return send_signal_to_thread_locked(thread, SIGNAL_CANCEL_THREAD, NULL, 0); +} + + status_t _user_resume_thread(thread_id thread) { + // TODO: Don't allow kernel threads to be resumed! return resume_thread(thread); } @@ -2759,6 +3364,7 @@ _user_resume_thread(thread_id thread) status_t _user_suspend_thread(thread_id thread) { + // TODO: Don't allow kernel threads to be suspended! return suspend_thread(thread); } @@ -2773,6 +3379,7 @@ _user_rename_thread(thread_id thread, const char *userName) || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) return B_BAD_ADDRESS; + // TODO: Don't allow kernel threads to be renamed! return rename_thread(thread, name); } @@ -2780,6 +3387,7 @@ _user_rename_thread(thread_id thread, const char *userName) int32 _user_set_thread_priority(thread_id thread, int32 newPriority) { + // TODO: Don't allow setting priority of kernel threads! return set_thread_priority(thread, newPriority); } @@ -2787,34 +3395,16 @@ _user_set_thread_priority(thread_id thread, int32 newPriority) thread_id _user_spawn_thread(thread_creation_attributes* userAttributes) { - thread_creation_attributes attributes; - if (userAttributes == NULL || !IS_USER_ADDRESS(userAttributes) - || user_memcpy(&attributes, userAttributes, - sizeof(attributes)) != B_OK) { - return B_BAD_ADDRESS; - } + // copy the userland structure to the kernel + char nameBuffer[B_OS_NAME_LENGTH]; + ThreadCreationAttributes attributes; + status_t error = attributes.InitFromUserAttributes(userAttributes, + nameBuffer); + if (error != B_OK) + return error; - if (attributes.stack_size != 0 - && (attributes.stack_size < MIN_USER_STACK_SIZE - || attributes.stack_size > MAX_USER_STACK_SIZE)) { - return B_BAD_VALUE; - } - - char name[B_OS_NAME_LENGTH]; - thread_id threadID; - - if (!IS_USER_ADDRESS(attributes.entry) || attributes.entry == NULL - || (attributes.stack_address != NULL - && !IS_USER_ADDRESS(attributes.stack_address)) - || (attributes.name != NULL && (!IS_USER_ADDRESS(attributes.name) - || user_strlcpy(name, attributes.name, B_OS_NAME_LENGTH) < 0))) - return B_BAD_ADDRESS; - - attributes.name = attributes.name != NULL ? name : "user thread"; - attributes.team = thread_get_current_thread()->team->id; - attributes.thread = -1; - - threadID = create_thread(attributes, false); + // create the thread + thread_id threadID = thread_create_thread(attributes, false); if (threadID >= 0) user_debug_thread_created(threadID); @@ -2824,14 +3414,84 @@ _user_spawn_thread(thread_creation_attributes* userAttributes) status_t -_user_snooze_etc(bigtime_t timeout, int timebase, uint32 flags) +_user_snooze_etc(bigtime_t timeout, int timebase, uint32 flags, + bigtime_t* userRemainingTime) { - // NOTE: We only know the system timebase at the moment. - syscall_restart_handle_timeout_pre(flags, timeout); + // We need to store more syscall restart parameters than usual and need a + // somewhat different handling. Hence we can't use + // syscall_restart_handle_timeout_pre() but do the job ourselves. + struct restart_parameters { + bigtime_t timeout; + clockid_t timebase; + uint32 flags; + }; - status_t error = snooze_etc(timeout, timebase, flags | B_CAN_INTERRUPT); + Thread* thread = thread_get_current_thread(); - return syscall_restart_handle_timeout_post(error, timeout); + if ((thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0) { + // The syscall was restarted. Fetch the parameters from the stored + // restart parameters. + restart_parameters* restartParameters + = (restart_parameters*)thread->syscall_restart.parameters; + timeout = restartParameters->timeout; + timebase = restartParameters->timebase; + flags = restartParameters->flags; + } else { + // convert relative timeouts to absolute ones + if ((flags & B_RELATIVE_TIMEOUT) != 0) { + // not restarted yet and the flags indicate a relative timeout + + // Make sure we use the system time base, so real-time clock changes + // won't affect our wait. + flags &= ~(uint32)B_TIMEOUT_REAL_TIME_BASE; + if (timebase == CLOCK_REALTIME) + timebase = CLOCK_MONOTONIC; + + // get the current time and make the timeout absolute + bigtime_t now; + status_t error = user_timer_get_clock(timebase, now); + if (error != B_OK) + return error; + + timeout += now; + + // deal with overflow + if (timeout < 0) + timeout = B_INFINITE_TIMEOUT; + + flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; + } else + flags |= B_ABSOLUTE_TIMEOUT; + } + + // snooze + bigtime_t remainingTime; + status_t error = common_snooze_etc(timeout, timebase, + flags | B_CAN_INTERRUPT | B_CHECK_PERMISSION, + userRemainingTime != NULL ? &remainingTime : NULL); + + // If interrupted, copy the remaining time back to userland and prepare the + // syscall restart. + if (error == B_INTERRUPTED) { + if (userRemainingTime != NULL + && (!IS_USER_ADDRESS(userRemainingTime) + || user_memcpy(userRemainingTime, &remainingTime, + sizeof(remainingTime)) != B_OK)) { + return B_BAD_ADDRESS; + } + + // store the normalized values in the restart parameters + restart_parameters* restartParameters + = (restart_parameters*)thread->syscall_restart.parameters; + restartParameters->timeout = timeout; + restartParameters->timebase = timebase; + restartParameters->flags = flags; + + // restart the syscall, if possible + atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL); + } + + return error; } @@ -2969,8 +3629,7 @@ _user_block_thread(uint32 flags, bigtime_t timeout) flags |= B_CAN_INTERRUPT; Thread* thread = thread_get_current_thread(); - - InterruptsSpinLocker locker(gThreadSpinlock); + ThreadLocker threadLocker(thread); // check, if already done if (thread->user_thread->wait_status <= 0) @@ -2978,8 +3637,25 @@ _user_block_thread(uint32 flags, bigtime_t timeout) // nope, so wait thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_OTHER, "user"); + + threadLocker.Unlock(); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + status_t status = thread_block_with_timeout_locked(flags, timeout); - thread->user_thread->wait_status = status; + + schedulerLocker.Unlock(); + threadLocker.Lock(); + + // Interruptions or timeouts can race with other threads unblocking us. + // Favor a wake-up by another thread, i.e. if someone changed the wait + // status, use that. + status_t oldStatus = thread->user_thread->wait_status; + if (oldStatus > 0) + thread->user_thread->wait_status = status; + else + status = oldStatus; + + threadLocker.Unlock(); return syscall_restart_handle_timeout_post(status, timeout); } @@ -2988,9 +3664,11 @@ _user_block_thread(uint32 flags, bigtime_t timeout) status_t _user_unblock_thread(thread_id threadID, status_t status) { - InterruptsSpinLocker locker(gThreadSpinlock); status_t error = user_unblock_thread(threadID, status); - scheduler_reschedule_if_necessary_locked(); + + if (error == B_OK) + scheduler_reschedule_if_necessary(); + return error; } @@ -3011,11 +3689,10 @@ _user_unblock_threads(thread_id* userThreads, uint32 count, status_t status) if (user_memcpy(threads, userThreads, count * sizeof(thread_id)) != B_OK) return B_BAD_ADDRESS; - InterruptsSpinLocker locker(gThreadSpinlock); for (uint32 i = 0; i < count; i++) user_unblock_thread(threads[i], status); - scheduler_reschedule_if_necessary_locked(); + scheduler_reschedule_if_necessary(); return B_OK; } diff --git a/src/system/kernel/timer.cpp b/src/system/kernel/timer.cpp index 4766d22f1c..3f626c9eeb 100644 --- a/src/system/kernel/timer.cpp +++ b/src/system/kernel/timer.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010, Haiku. All rights reserved. + * Copyright 2002-2011, Haiku. All rights reserved. * Distributed under the terms of the MIT License. * * Copyright 2001, Travis Geiselbrecht. All rights reserved. @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -27,6 +30,7 @@ struct per_cpu_timer_data { timer* volatile events; timer* volatile current_event; vint32 current_event_in_progress; + bigtime_t real_time_offset; }; static per_cpu_timer_data sPerCPU[B_MAX_CPU_COUNT]; @@ -40,12 +44,26 @@ static per_cpu_timer_data sPerCPU[B_MAX_CPU_COUNT]; #endif -status_t -timer_init(kernel_args* args) -{ - TRACE(("timer_init: entry\n")); +/*! Sets the hardware timer to the given absolute time. - return arch_init_timer(args); + \param scheduleTime The absolute system time for the timer expiration. + \param now The current system time. +*/ +static void +set_hardware_timer(bigtime_t scheduleTime, bigtime_t now) +{ + arch_timer_set_hardware_timer(scheduleTime > now ? scheduleTime - now : 0); +} + + +/*! Sets the hardware timer to the given absolute time. + + \param scheduleTime The absolute system time for the timer expiration. +*/ +static inline void +set_hardware_timer(bigtime_t scheduleTime) +{ + set_hardware_timer(scheduleTime, system_time()); } @@ -72,6 +90,158 @@ add_event_to_list(timer* event, timer* volatile* list) } +static void +per_cpu_real_time_clock_changed(void*, int cpu) +{ + per_cpu_timer_data& cpuData = sPerCPU[cpu]; + SpinLocker cpuDataLocker(cpuData.lock); + + bigtime_t realTimeOffset = rtc_boot_time(); + if (realTimeOffset == cpuData.real_time_offset) + return; + + // The real time offset has changed. We need to update all affected + // timers. First find and dequeue them. + bigtime_t timeDiff = cpuData.real_time_offset - realTimeOffset; + cpuData.real_time_offset = realTimeOffset; + + timer* affectedTimers = NULL; + timer* volatile* it = &cpuData.events; + timer* firstEvent = *it; + while (timer* event = *it) { + // check whether it's an absolute real-time timer + uint32 flags = event->flags; + if ((flags & ~B_TIMER_FLAGS) != B_ONE_SHOT_ABSOLUTE_TIMER + || (flags & B_TIMER_REAL_TIME_BASE) == 0) { + it = &event->next; + continue; + } + + // Yep, remove the timer from the queue and add it to the + // affectedTimers list. + *it = event->next; + event->next = affectedTimers; + affectedTimers = event; + } + + // update and requeue the affected timers + bool firstEventChanged = cpuData.events != firstEvent; + firstEvent = cpuData.events; + + while (affectedTimers != NULL) { + timer* event = affectedTimers; + affectedTimers = event->next; + + bigtime_t oldTime = event->schedule_time; + event->schedule_time += timeDiff; + + // handle over-/underflows + if (timeDiff >= 0) { + if (event->schedule_time < oldTime) + event->schedule_time = B_INFINITE_TIMEOUT; + } else { + if (event->schedule_time < 0) + event->schedule_time = 0; + } + + add_event_to_list(event, &cpuData.events); + } + + firstEventChanged |= cpuData.events != firstEvent; + + // If the first event has changed, reset the hardware timer. + if (firstEventChanged) + set_hardware_timer(cpuData.events->schedule_time); +} + + +// #pragma mark - debugging + + +static int +dump_timers(int argc, char** argv) +{ + int32 cpuCount = smp_get_num_cpus(); + for (int32 i = 0; i < cpuCount; i++) { + kprintf("CPU %" B_PRId32 ":\n", i); + + if (sPerCPU[i].events == NULL) { + kprintf(" no timers scheduled\n"); + continue; + } + + for (timer* event = sPerCPU[i].events; event != NULL; + event = event->next) { + kprintf(" [%9lld] %p: ", (long long)event->schedule_time, event); + if ((event->flags & ~B_TIMER_FLAGS) == B_PERIODIC_TIMER) + kprintf("periodic %9lld, ", (long long)event->period); + else + kprintf("one shot, "); + + kprintf("flags: %#x, user data: %p, callback: %p ", + event->flags, event->user_data, event->hook); + + // look up and print the hook function symbol + const char* symbol; + const char* imageName; + bool exactMatch; + + status_t error = elf_debug_lookup_symbol_address( + (addr_t)event->hook, NULL, &symbol, &imageName, &exactMatch); + if (error == B_OK && exactMatch) { + if (const char* slash = strchr(imageName, '/')) + imageName = slash + 1; + + kprintf(" %s:%s", imageName, symbol); + } + + kprintf("\n"); + } + } + + kprintf("current time: %lld\n", (long long)system_time()); + + return 0; +} + + +// #pragma mark - kernel-private + + +status_t +timer_init(kernel_args* args) +{ + TRACE(("timer_init: entry\n")); + + if (arch_init_timer(args) != B_OK) + panic("arch_init_timer() failed"); + + add_debugger_command_etc("timers", &dump_timers, "List all timers", + "\n" + "Prints a list of all scheduled timers.\n", 0); + + return B_OK; +} + + +void +timer_init_post_rtc(void) +{ + bigtime_t realTimeOffset = rtc_boot_time(); + + int32 cpuCount = smp_get_num_cpus(); + for (int32 i = 0; i < cpuCount; i++) + sPerCPU[i].real_time_offset = realTimeOffset; +} + + +void +timer_real_time_clock_changed() +{ + call_all_cpus(&per_cpu_real_time_clock_changed, NULL); +} + + int32 timer_interrupt() { @@ -95,7 +265,6 @@ timer_interrupt() cpuData.events = (timer*)event->next; cpuData.current_event = event; cpuData.current_event_in_progress = 1; - event->schedule_time = 0; release_spinlock(spinlock); @@ -108,9 +277,9 @@ timer_interrupt() if (event->hook) { bool callHook = true; - // we may need to acquire the thread spinlock - if ((mode & B_TIMER_ACQUIRE_THREAD_LOCK) != 0) { - GRAB_THREAD_LOCK(); + // we may need to acquire the scheduler lock + if ((mode & B_TIMER_ACQUIRE_SCHEDULER_LOCK) != 0) { + acquire_spinlock(&gSchedulerLock); // If the event has been cancelled in the meantime, we don't // call the hook anymore. @@ -121,8 +290,8 @@ timer_interrupt() if (callHook) rc = event->hook(event); - if ((mode & B_TIMER_ACQUIRE_THREAD_LOCK) != 0) - RELEASE_THREAD_LOCK(); + if ((mode & B_TIMER_ACQUIRE_SCHEDULER_LOCK) != 0) + release_spinlock(&gSchedulerLock); } cpuData.current_event_in_progress = 0; @@ -132,13 +301,17 @@ timer_interrupt() if ((mode & ~B_TIMER_FLAGS) == B_PERIODIC_TIMER && cpuData.current_event != NULL) { // we need to adjust it and add it back to the list - bigtime_t scheduleTime = system_time() + event->period; - if (scheduleTime == 0) { - // if we wrapped around and happen to hit zero, set - // it to one, since zero represents not scheduled - scheduleTime = 1; + event->schedule_time += event->period; + + // If the new schedule time is a full interval or more in the past, + // skip ticks. + bigtime_t now = system_time(); + if (now >= event->schedule_time + event->period) { + // pick the closest tick in the past + event->schedule_time = now + - (now - event->schedule_time) % event->period; } - event->schedule_time = (int64)scheduleTime; + add_event_to_list(event, &cpuData.events); } @@ -148,13 +321,8 @@ timer_interrupt() } // setup the next hardware timer - if (cpuData.events != NULL) { - bigtime_t timeout = (bigtime_t)cpuData.events->schedule_time - - system_time(); - if (timeout <= 0) - timeout = 1; - arch_timer_set_hardware_timer(timeout); - } + if (cpuData.events != NULL) + set_hardware_timer(cpuData.events->schedule_time); release_spinlock(spinlock); @@ -162,10 +330,12 @@ timer_interrupt() } +// #pragma mark - public API + + status_t add_timer(timer* event, timer_hook hook, bigtime_t period, int32 flags) { - bigtime_t scheduleTime; bigtime_t currentTime = system_time(); cpu_status state; @@ -174,14 +344,19 @@ add_timer(timer* event, timer_hook hook, bigtime_t period, int32 flags) TRACE(("add_timer: event %p\n", event)); - scheduleTime = period; - if ((flags & ~B_TIMER_FLAGS) != B_ONE_SHOT_ABSOLUTE_TIMER) - scheduleTime += currentTime; - if (scheduleTime == 0) - scheduleTime = 1; + // compute the schedule time + bigtime_t scheduleTime; + if ((flags & B_TIMER_USE_TIMER_STRUCT_TIMES) != 0) { + scheduleTime = event->schedule_time; + period = event->period; + } else { + scheduleTime = period; + if ((flags & ~B_TIMER_FLAGS) != B_ONE_SHOT_ABSOLUTE_TIMER) + scheduleTime += currentTime; + event->schedule_time = (int64)scheduleTime; + event->period = period; + } - event->schedule_time = (int64)scheduleTime; - event->period = period; event->hook = hook; event->flags = flags; @@ -190,12 +365,22 @@ add_timer(timer* event, timer_hook hook, bigtime_t period, int32 flags) per_cpu_timer_data& cpuData = sPerCPU[currentCPU]; acquire_spinlock(&cpuData.lock); + // If the timer is an absolute real-time base timer, convert the schedule + // time to system time. + if ((flags & ~B_TIMER_FLAGS) == B_ONE_SHOT_ABSOLUTE_TIMER + && (flags & B_TIMER_REAL_TIME_BASE) != 0) { + if (event->schedule_time > cpuData.real_time_offset) + event->schedule_time -= cpuData.real_time_offset; + else + event->schedule_time = 0; + } + add_event_to_list(event, &cpuData.events); event->cpu = currentCPU; // if we were stuck at the head of the list, set the hardware timer if (event == cpuData.events) - arch_timer_set_hardware_timer(scheduleTime - currentTime); + set_hardware_timer(scheduleTime, currentTime); release_spinlock(&cpuData.lock); restore_interrupts(state); @@ -261,10 +446,8 @@ cancel_timer(timer* event) if (cpu == smp_get_current_cpu()) { if (cpuData.events == NULL) arch_timer_clear_hardware_timer(); - else { - arch_timer_set_hardware_timer( - (bigtime_t)cpuData.events->schedule_time - system_time()); - } + else + set_hardware_timer(cpuData.events->schedule_time); } return false; @@ -274,12 +457,12 @@ cancel_timer(timer* event) // event so that timer_interrupt() will not reschedule periodic timers. cpuData.current_event = NULL; - // Unless this is a kernel-private timer that also requires the thread + // Unless this is a kernel-private timer that also requires the scheduler // lock to be held while calling the event hook, we'll have to wait // for the hook to complete. When called from the timer hook we don't // wait either, of course. - if ((event->flags & B_TIMER_ACQUIRE_THREAD_LOCK) == 0 - || cpu == smp_get_current_cpu()) { + if ((event->flags & B_TIMER_ACQUIRE_SCHEDULER_LOCK) == 0 + && cpu != smp_get_current_cpu()) { spinLocker.Unlock(); while (cpuData.current_event_in_progress == 1) { diff --git a/src/system/kernel/usergroup.cpp b/src/system/kernel/usergroup.cpp index 52ab74c2db..33eabacf6c 100644 --- a/src/system/kernel/usergroup.cpp +++ b/src/system/kernel/usergroup.cpp @@ -3,6 +3,7 @@ * Distributed under the terms of the MIT License. */ + #include #include @@ -39,7 +40,7 @@ common_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged, bool kernel) { Team* team = thread_get_current_thread()->team; - InterruptsSpinLocker _(gTeamSpinlock); + TeamLocker teamLocker(team); bool privileged = kernel || is_privileged(team); @@ -53,6 +54,7 @@ common_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged, bool kernel) // setgid() semantics: If privileged set both, real, effective and // saved set-gid, otherwise set the effective gid. if (privileged) { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); team->saved_set_gid = rgid; team->real_gid = rgid; team->effective_gid = rgid; @@ -89,6 +91,7 @@ common_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged, bool kernel) } // Getting here means all checks were successful -- set the gids. + InterruptsSpinLocker schedulerLocker(gSchedulerLock); team->real_gid = rgid; team->effective_gid = egid; team->saved_set_gid = ssgid; @@ -102,7 +105,7 @@ common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel) { Team* team = thread_get_current_thread()->team; - InterruptsSpinLocker _(gTeamSpinlock); + TeamLocker teamLocker(team); bool privileged = kernel || is_privileged(team); @@ -116,6 +119,7 @@ common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel) // setuid() semantics: If privileged set both, real, effective and // saved set-uid, otherwise set the effective uid. if (privileged) { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); team->saved_set_uid = ruid; team->real_uid = ruid; team->effective_uid = ruid; @@ -152,6 +156,7 @@ common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel) } // Getting here means all checks were successful -- set the uids. + InterruptsSpinLocker schedulerLocker(gSchedulerLock); team->real_uid = ruid; team->effective_uid = euid; team->saved_set_uid = ssuid; @@ -165,7 +170,7 @@ common_getgroups(int groupCount, gid_t* groupList, bool kernel) { Team* team = thread_get_current_thread()->team; - InterruptsSpinLocker _(gTeamSpinlock); + TeamLocker teamLocker(team); const gid_t* groups = team->supplementary_groups; int actualCount = team->supplementary_group_count; @@ -223,15 +228,15 @@ common_setgroups(int groupCount, const gid_t* groupList, bool kernel) } } - InterruptsSpinLocker locker(gTeamSpinlock); - Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); + gid_t* toFree = team->supplementary_groups; team->supplementary_groups = newGroups; team->supplementary_group_count = groupCount; - locker.Unlock(); + teamLocker.Unlock(); malloc_referenced_release(toFree); @@ -242,9 +247,14 @@ common_setgroups(int groupCount, const gid_t* groupList, bool kernel) // #pragma mark - Kernel Private +/*! Copies the user and group information from \a parent to \a team. + The caller must hold the lock to both \a team and \a parent. +*/ void -inherit_parent_user_and_group_locked(Team* team, Team* parent) +inherit_parent_user_and_group(Team* team, Team* parent) { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + team->saved_set_uid = parent->saved_set_uid; team->real_uid = parent->real_uid; team->effective_uid = parent->effective_uid; @@ -252,20 +262,14 @@ inherit_parent_user_and_group_locked(Team* team, Team* parent) team->real_gid = parent->real_gid; team->effective_gid = parent->effective_gid; + schedulerLocker.Unlock(); + malloc_referenced_acquire(parent->supplementary_groups); team->supplementary_groups = parent->supplementary_groups; team->supplementary_group_count = parent->supplementary_group_count; } -void -inherit_parent_user_and_group(Team* team, Team* parent) -{ - InterruptsSpinLocker _(gTeamSpinlock); - inherit_parent_user_and_group_locked(team, parent); -} - - status_t update_set_id_user_and_group(Team* team, const char* file) { @@ -274,7 +278,8 @@ update_set_id_user_and_group(Team* team, const char* file) if (status != B_OK) return status; - InterruptsSpinLocker _(gTeamSpinlock); + TeamLocker teamLocker(team); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); if ((st.st_mode & S_ISUID) != 0) { team->saved_set_uid = st.st_uid; @@ -295,6 +300,8 @@ _kern_getgid(bool effective) { Team* team = thread_get_current_thread()->team; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + return effective ? team->effective_gid : team->real_gid; } @@ -304,6 +311,8 @@ _kern_getuid(bool effective) { Team* team = thread_get_current_thread()->team; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + return effective ? team->effective_uid : team->real_uid; } @@ -344,6 +353,8 @@ _user_getgid(bool effective) { Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); + return effective ? team->effective_gid : team->real_gid; } @@ -353,6 +364,8 @@ _user_getuid(bool effective) { Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); + return effective ? team->effective_uid : team->real_uid; } @@ -381,8 +394,13 @@ _user_getgroups(int groupCount, gid_t* groupList) ssize_t _user_setgroups(int groupCount, const gid_t* groupList) { - if (!is_privileged(thread_get_current_thread()->team)) - return EPERM; + // check privilege + { + Team* team = thread_get_current_thread()->team; + TeamLocker teamLocker(team); + if (!is_privileged(team)) + return EPERM; + } return common_setgroups(groupCount, groupList, false); } diff --git a/src/system/kernel/util/Jamfile b/src/system/kernel/util/Jamfile index 60616440a9..59ff30c7c6 100644 --- a/src/system/kernel/util/Jamfile +++ b/src/system/kernel/util/Jamfile @@ -5,6 +5,7 @@ UsePrivateHeaders [ FDirName kernel util ] ; KernelMergeObject kernel_util.o : AVLTreeBase.cpp kernel_cpp.cpp + KernelReferenceable.cpp khash.cpp list.cpp queue.cpp diff --git a/src/system/kernel/util/KernelReferenceable.cpp b/src/system/kernel/util/KernelReferenceable.cpp new file mode 100644 index 0000000000..af14c90d95 --- /dev/null +++ b/src/system/kernel/util/KernelReferenceable.cpp @@ -0,0 +1,19 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + + +void +KernelReferenceable::LastReferenceReleased() +{ + if (are_interrupts_enabled()) + delete this; + else + deferred_delete(this); +} diff --git a/src/system/kernel/vm/VMCache.cpp b/src/system/kernel/vm/VMCache.cpp index e513c3a980..032772f4d7 100644 --- a/src/system/kernel/vm/VMCache.cpp +++ b/src/system/kernel/vm/VMCache.cpp @@ -1350,7 +1350,7 @@ VMCache::_NotifyPageEvents(vm_page* page, uint32 events) if (waiter->page == page && (waiter->events & events) != 0) { // remove from list and unblock *it = waiter->next; - InterruptsSpinLocker threadsLocker(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(waiter->thread, B_OK); } else it = &waiter->next; diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 694ddad120..a8fc0a92aa 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -3930,9 +3930,9 @@ vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isUser, Thread* thread = thread_get_current_thread(); dprintf("vm_page_fault: thread \"%s\" (%ld) in team \"%s\" (%ld) " "tried to %s address %#lx, ip %#lx (\"%s\" +%#lx)\n", - thread->name, thread->id, thread->team->name, thread->team->id, - isWrite ? "write" : "read", address, faultAddress, - area ? area->name : "???", + thread->name, thread->id, thread->team->Name(), + thread->team->id, isWrite ? "write" : "read", address, + faultAddress, area ? area->name : "???", faultAddress - (area ? area->Base() : 0x0)); // We can print a stack trace of the userland thread here. @@ -3999,13 +3999,17 @@ vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isUser, // send it the signal. Otherwise we notify the user debugger // first. struct sigaction action; - if (sigaction(SIGSEGV, NULL, &action) == 0 - && action.sa_handler != SIG_DFL - && action.sa_handler != SIG_IGN) { - send_signal(thread->id, SIGSEGV); - } else if (user_debug_exception_occurred(B_SEGMENT_VIOLATION, + if ((sigaction(SIGSEGV, NULL, &action) == 0 + && action.sa_handler != SIG_DFL + && action.sa_handler != SIG_IGN) + || user_debug_exception_occurred(B_SEGMENT_VIOLATION, SIGSEGV)) { - send_signal(thread->id, SIGSEGV); + Signal signal(SIGSEGV, + status == B_PERMISSION_DENIED + ? SEGV_ACCERR : SEGV_MAPERR, + EFAULT, thread->team->id); + signal.SetAddress((void*)address); + send_signal_to_thread(thread, signal, 0); } } } diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index ab4f5e6c6b..3816c45375 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -1098,7 +1098,7 @@ wake_up_page_reservation_waiters() sPageReservationWaiters.Remove(waiter); - InterruptsSpinLocker threadLocker(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(waiter->thread, B_OK); } } diff --git a/src/system/libroot/libroot_init.c b/src/system/libroot/libroot_init.c index 4b65efa306..68137366eb 100644 --- a/src/system/libroot/libroot_init.c +++ b/src/system/libroot/libroot_init.c @@ -4,14 +4,19 @@ */ +#include #include -#include #include -#include -#include + #include +#include + +#include +#include +#include + void initialize_before(image_id imageID); @@ -55,12 +60,13 @@ initialize_before(image_id imageID) if (__gRuntimeLoader->program_args->umask != (mode_t)-1) umask(__gRuntimeLoader->program_args->umask); + pthread_self()->id = find_thread(NULL); + __init_time(); __init_heap(); __init_env(__gRuntimeLoader->program_args); __init_heap_post_env(); __init_pwd_backend(); - __init_pthread(); } diff --git a/src/system/libroot/libroot_versions b/src/system/libroot/libroot_versions index 46f778d932..1a641baa27 100644 --- a/src/system/libroot/libroot_versions +++ b/src/system/libroot/libroot_versions @@ -3,3 +3,6 @@ LIBROOT_BASE { LIBROOT_1_ALPHA1 { } LIBROOT_BASE; + +LIBROOT_1_ALPHA4 { +} LIBROOT_1_ALPHA1; diff --git a/src/system/libroot/os/thread.c b/src/system/libroot/os/thread.c index 128aa91ee3..7320f2924a 100644 --- a/src/system/libroot/os/thread.c +++ b/src/system/libroot/os/thread.c @@ -32,13 +32,12 @@ void _thread_do_exit_notification(void); static status_t -thread_entry(thread_func entry, void* _thread) +thread_entry(void* _entry, void* _thread) { + thread_func entry = (thread_func)_entry; pthread_thread* thread = (pthread_thread*)_thread; status_t returnCode; - *tls_address(TLS_PTHREAD_SLOT) = thread; - returnCode = entry(thread->entry_argument); _thread_do_exit_work(); @@ -85,20 +84,17 @@ spawn_thread(thread_func entry, const char *name, int32 priority, void *data) pthread_thread* thread; thread_id id; - thread = __allocate_pthread(data); + thread = __allocate_pthread(NULL, data); if (thread == NULL) return B_NO_MEMORY; _single_threaded = false; // used for I/O locking - BeOS compatibility issue - attributes.entry = &thread_entry; - attributes.name = name; + __pthread_init_creation_attributes(NULL, thread, &thread_entry, entry, + thread, name, &attributes); + attributes.priority = priority; - attributes.args1 = entry; - attributes.args2 = thread; - attributes.stack_address = NULL; - attributes.stack_size = 0; id = _kern_spawn_thread(&attributes); if (id < 0) @@ -223,20 +219,21 @@ has_data(thread_id thread) status_t snooze_etc(bigtime_t timeout, int timeBase, uint32 flags) { - return _kern_snooze_etc(timeout, timeBase, flags); + return _kern_snooze_etc(timeout, timeBase, flags, NULL); } status_t snooze(bigtime_t timeout) { - return _kern_snooze_etc(timeout, B_SYSTEM_TIMEBASE, B_RELATIVE_TIMEOUT); + return _kern_snooze_etc(timeout, B_SYSTEM_TIMEBASE, B_RELATIVE_TIMEOUT, + NULL); } status_t snooze_until(bigtime_t timeout, int timeBase) { - return snooze_etc(timeout, timeBase, B_ABSOLUTE_TIMEOUT); + return _kern_snooze_etc(timeout, timeBase, B_ABSOLUTE_TIMEOUT, NULL); } diff --git a/src/system/libroot/os/time.cpp b/src/system/libroot/os/time.cpp index 97a34a9bcc..3c8810feb3 100644 --- a/src/system/libroot/os/time.cpp +++ b/src/system/libroot/os/time.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -31,6 +32,13 @@ __init_time(void) } +bigtime_t +__get_system_time_offset() +{ + return __arch_get_system_time_offset(sRealTimeData); +} + + // #pragma mark - public API @@ -52,7 +60,7 @@ real_time_clock_usecs(void) void set_real_time_clock(uint32 secs) { - _kern_set_real_time_clock(secs); + _kern_set_real_time_clock((bigtime_t)secs * 1000000); } @@ -71,5 +79,34 @@ set_timezone(const char* /*timezone*/) bigtime_t set_alarm(bigtime_t when, uint32 mode) { - return _kern_set_alarm(when, mode); + // prepare the values to be passed to the kernel + bigtime_t interval = 0; + uint32 flags = B_RELATIVE_TIMEOUT; + + if (when == B_INFINITE_TIMEOUT) { + when = B_INFINITE_TIMEOUT; + } else { + switch (mode) { + case B_PERIODIC_ALARM: + interval = when; + break; + case B_ONE_SHOT_ABSOLUTE_ALARM: + flags = B_ABSOLUTE_TIMEOUT; + break; + case B_ONE_SHOT_RELATIVE_ALARM: + default: + break; + } + } + + // set the timer + user_timer_info oldInfo; + status_t error = _kern_set_timer(USER_TIMER_REAL_TIME_ID, find_thread(NULL), + when, interval, flags, &oldInfo); + if (error != B_OK) + return 0; + + // A remaining time of B_INFINITE_TIMEOUT means not scheduled. + return oldInfo.remaining_time != B_INFINITE_TIMEOUT + ? oldInfo.remaining_time : 0; } diff --git a/src/system/libroot/posix/arch/generic/longjmp_return.c b/src/system/libroot/posix/arch/generic/longjmp_return.c index 86b97d76b4..2fdb4e64b6 100644 --- a/src/system/libroot/posix/arch/generic/longjmp_return.c +++ b/src/system/libroot/posix/arch/generic/longjmp_return.c @@ -1,23 +1,26 @@ -/* - * Copyright 2005, Ingo Weinhold . All rights - * reserved. Distributed under the terms of the Haiku License. +/* + * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. */ + #include -/** This function is called by [sig]longjmp(). The caller has already + +/*! This function is called by [sig]longjmp(). The caller has already set up the registers and stack, so that returning from the function will return to the place where [sig]setjmp() was invoked. It resets the signal mask and validates the value supplied to [sig]longjmp(). */ - int __longjmp_return(jmp_buf buffer, int value); int __longjmp_return(jmp_buf buffer, int value) { - if (buffer[0].mask_was_saved) - sigprocmask(SIG_SETMASK, &buffer[0].saved_mask, NULL); + if (buffer[0].inverted_signal_mask != 0) { + sigset_t signalMask = ~buffer[0].inverted_signal_mask; + sigprocmask(SIG_SETMASK, &signalMask, NULL); + } return (value == 0 ? 1 : value); } diff --git a/src/system/libroot/posix/arch/generic/setjmp_save_sigs.c b/src/system/libroot/posix/arch/generic/setjmp_save_sigs.c index 8ce57afd3b..a88362cf3e 100644 --- a/src/system/libroot/posix/arch/generic/setjmp_save_sigs.c +++ b/src/system/libroot/posix/arch/generic/setjmp_save_sigs.c @@ -1,7 +1,7 @@ -/* -** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the Haiku License. -*/ +/* + * Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the Haiku License. + */ #include @@ -14,8 +14,15 @@ int __setjmp_save_sigs(jmp_buf buffer, int saveMask); int __setjmp_save_sigs(jmp_buf buffer, int saveMask) { - buffer[0].mask_was_saved = saveMask && sigprocmask(SIG_BLOCK, NULL, &buffer[0].saved_mask) == 0; - // only set mask_was_saved if sigprocmask() was successful + // If the signal mask shall be saved, we save the inverted signal mask. The + // reason for this is that due to unblockable signals the inverted signal + // mask is never zero and thus we can use a zero value to indicate that the + // mask has not been saved. + sigset_t signalMask; + if (saveMask != 0 && sigprocmask(SIG_BLOCK, NULL, &signalMask) == 0) + buffer[0].inverted_signal_mask = ~signalMask; + else + buffer[0].inverted_signal_mask = 0; return 0; } diff --git a/src/system/libroot/posix/errno.c b/src/system/libroot/posix/errno.c index 059d183017..96cb89a4ca 100644 --- a/src/system/libroot/posix/errno.c +++ b/src/system/libroot/posix/errno.c @@ -34,7 +34,7 @@ int _to_positive_error(int error) { if (error < 0) - return error == B_NO_MEMORY ? B_POSIX_ENOMEM : -error; + return error == B_NO_MEMORY ? -B_POSIX_ENOMEM : -error; return error; } diff --git a/src/system/libroot/posix/fcntl.cpp b/src/system/libroot/posix/fcntl.cpp index 22c330ed0e..1e0608b8f4 100644 --- a/src/system/libroot/posix/fcntl.cpp +++ b/src/system/libroot/posix/fcntl.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -20,14 +21,9 @@ int creat(const char *path, mode_t mode) { - int status = _kern_open(-1, path, O_CREAT | O_TRUNC | O_WRONLY, - mode & ~__gUmask); + RETURN_AND_SET_ERRNO_TEST_CANCEL( + _kern_open(-1, path, O_CREAT | O_TRUNC | O_WRONLY, mode & ~__gUmask)); // adapt the permissions as required by POSIX - if (status < 0) { - errno = status; - return -1; - } - return status; } @@ -43,12 +39,7 @@ open(const char *path, int openMode, ...) va_end(args); } - int status = _kern_open(-1, path, openMode, perms); - if (status < 0) { - errno = status; - return -1; - } - return status; + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_open(-1, path, openMode, perms)); } @@ -64,12 +55,7 @@ openat(int fd, const char *path, int openMode, ...) va_end(args); } - int status = _kern_open(fd, path, openMode, perms); - if (status < 0) { - errno = status; - return -1; - } - return status; + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_open(fd, path, openMode, perms)); } @@ -81,5 +67,10 @@ fcntl(int fd, int op, ...) uint32 argument = va_arg(args, uint32); va_end(args); - RETURN_AND_SET_ERRNO(_kern_fcntl(fd, op, argument)); + status_t error = _kern_fcntl(fd, op, argument); + + if (op == F_SETLKW) + pthread_testcancel(); + + RETURN_AND_SET_ERRNO(error); } diff --git a/src/system/libroot/posix/glibc/include/bits/types.h b/src/system/libroot/posix/glibc/include/bits/types.h index 1f2af01b52..163c17df89 100644 --- a/src/system/libroot/posix/glibc/include/bits/types.h +++ b/src/system/libroot/posix/glibc/include/bits/types.h @@ -155,7 +155,7 @@ typedef __KEY_T_TYPE __key_t; /* Type of an IPC key */ typedef __CLOCKID_T_TYPE __clockid_t; /* Timer ID returned by `timer_create'. */ -typedef __TIMER_T_TYPE __timer_t; +//typedef __TIMER_T_TYPE __timer_t; /* Type to represent block size. */ typedef __BLKSIZE_T_TYPE __blksize_t; diff --git a/src/system/libroot/posix/glibc/include/bits/typesizes.h b/src/system/libroot/posix/glibc/include/bits/typesizes.h index 6d6de21fab..7dd1cd4c1f 100644 --- a/src/system/libroot/posix/glibc/include/bits/typesizes.h +++ b/src/system/libroot/posix/glibc/include/bits/typesizes.h @@ -54,7 +54,7 @@ #define __SWBLK_T_TYPE __SLONGWORD_TYPE #define __KEY_T_TYPE __S32_TYPE #define __CLOCKID_T_TYPE __S32_TYPE -#define __TIMER_T_TYPE __S32_TYPE +//#define __TIMER_T_TYPE __S32_TYPE #define __BLKSIZE_T_TYPE __SLONGWORD_TYPE #define __FSID_T_TYPE struct { int __val[2]; } diff --git a/src/system/libroot/posix/poll.c b/src/system/libroot/posix/poll.c index e55b3efef1..7898059296 100644 --- a/src/system/libroot/posix/poll.c +++ b/src/system/libroot/posix/poll.c @@ -1,21 +1,21 @@ -/* +/* * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ -#include #include + +#include +#include + +#include + #include int poll(struct pollfd *fds, nfds_t numfds, int timeout) { - int result = _kern_poll(fds, numfds, timeout * 1000LL); - if (result < 0) { - errno = result; - return -1; - } - return result; + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_poll(fds, numfds, timeout * 1000LL)); } diff --git a/src/system/libroot/posix/pthread/Jamfile b/src/system/libroot/posix/pthread/Jamfile index 087b5f8d08..bc7a36d0f2 100644 --- a/src/system/libroot/posix/pthread/Jamfile +++ b/src/system/libroot/posix/pthread/Jamfile @@ -6,7 +6,7 @@ UsePrivateHeaders kernel ; UsePrivateHeaders libroot shared ; MergeObject posix_pthread.o : - pthread.c + pthread.cpp pthread_atfork.c pthread_attr.c pthread_cancel.cpp diff --git a/src/system/libroot/posix/pthread/pthread.c b/src/system/libroot/posix/pthread/pthread.cpp similarity index 69% rename from src/system/libroot/posix/pthread/pthread.c rename to src/system/libroot/posix/pthread/pthread.cpp index b67408c1c9..4c5104a06b 100644 --- a/src/system/libroot/posix/pthread/pthread.c +++ b/src/system/libroot/posix/pthread/pthread.cpp @@ -5,8 +5,7 @@ */ -#include -#include +#include "pthread_private.h" #include #include @@ -14,13 +13,13 @@ #include +#include + #include #include #include - -#define THREAD_DETACHED 0x01 -#define THREAD_DEAD 0x02 +#include static const pthread_attr pthread_attr_default = { @@ -35,13 +34,10 @@ static int sConcurrencyLevel; static status_t -pthread_thread_entry(thread_func _unused, void* _thread) +pthread_thread_entry(void*, void* _thread) { pthread_thread* thread = (pthread_thread*)_thread; - // store thread data in TLS - *tls_address(TLS_PTHREAD_SLOT) = thread; - pthread_exit(thread->entry(thread->entry_argument)); return 0; } @@ -54,7 +50,7 @@ void __pthread_destroy_thread(void) { pthread_thread* thread = pthread_self(); - + // call cleanup handlers while (true) { struct __pthread_cleanup_handler* handler @@ -73,31 +69,61 @@ __pthread_destroy_thread(void) pthread_thread* -__allocate_pthread(void *data) +__allocate_pthread(void* (*entry)(void*), void *data) { pthread_thread* thread = (pthread_thread*)malloc(sizeof(pthread_thread)); if (thread == NULL) return NULL; - thread->entry = NULL; - thread->entry_argument = data; - thread->exit_value = NULL; - thread->cancel_state = PTHREAD_CANCEL_ENABLE; - thread->cancel_type = PTHREAD_CANCEL_DEFERRED; - thread->cancelled = false; - thread->cleanup_handlers = NULL; - thread->flags = 0; - - memset(thread->specific, 0, sizeof(thread->specific)); + __init_pthread(thread, entry, data); return thread; } void -__init_pthread(void) +__init_pthread(pthread_thread* thread, void* (*entry)(void*), void* data) { - sMainThread.id = find_thread(NULL); + thread->entry = entry; + thread->entry_argument = data; + thread->exit_value = NULL; + thread->cleanup_handlers = NULL; + thread->flags = THREAD_CANCEL_ENABLED; + // thread cancellation enabled, but deferred + + memset(thread->specific, 0, sizeof(thread->specific)); +} + + +status_t +__pthread_init_creation_attributes(const pthread_attr_t* pthreadAttributes, + pthread_t thread, status_t (*entryFunction)(void*, void*), + void* argument1, void* argument2, const char* name, + thread_creation_attributes* attributes) +{ + const pthread_attr* attr = NULL; + if (pthreadAttributes == NULL) { + attr = &pthread_attr_default; + } else { + attr = *pthreadAttributes; + if (attr == NULL) + return EINVAL; + } + + attributes->entry = entryFunction; + attributes->name = name; + attributes->priority = attr->sched_priority; + attributes->args1 = argument1; + attributes->args2 = argument2; + attributes->stack_address = NULL; + attributes->stack_size = attr->stack_size; + attributes->pthread = thread; + attributes->flags = 0; + + if (thread != NULL && attr->detach_state == PTHREAD_CREATE_DETACHED) + thread->flags |= THREAD_DETACHED; + + return B_OK; } @@ -105,40 +131,23 @@ __init_pthread(void) int -pthread_create(pthread_t* _thread, const pthread_attr_t* _attr, +pthread_create(pthread_t* _thread, const pthread_attr_t* attr, void* (*startRoutine)(void*), void* arg) { - const pthread_attr* attr = NULL; - pthread_thread* thread; - struct thread_creation_attributes attributes; - if (_thread == NULL) return EINVAL; - if (_attr == NULL) - attr = &pthread_attr_default; - else { - attr = *_attr; - if (attr == NULL) - return EINVAL; - } - - thread = __allocate_pthread(arg); + pthread_thread* thread = __allocate_pthread(startRoutine, arg); if (thread == NULL) return EAGAIN; - thread->entry = startRoutine; - - if (attr->detach_state == PTHREAD_CREATE_DETACHED) - thread->flags |= THREAD_DETACHED; - - attributes.entry = pthread_thread_entry; - attributes.name = "pthread func"; - attributes.priority = attr->sched_priority; - attributes.args1 = NULL; - attributes.args2 = thread; - attributes.stack_address = NULL; - attributes.stack_size = attr->stack_size; + thread_creation_attributes attributes; + status_t error = __pthread_init_creation_attributes(attr, thread, + &pthread_thread_entry, NULL, thread, "pthread func", &attributes); + if (error != B_OK) { + free(thread); + return error; + } thread->id = _kern_spawn_thread(&attributes); if (thread->id < 0) { @@ -157,9 +166,7 @@ pthread_create(pthread_t* _thread, const pthread_attr_t* _attr, pthread_t pthread_self(void) { - pthread_thread* thread; - - thread = (pthread_thread*)tls_get(TLS_PTHREAD_SLOT); + pthread_thread* thread = get_user_thread()->pthread; if (thread == NULL) return &sMainThread; @@ -180,7 +187,7 @@ pthread_join(pthread_t thread, void** _value) status_t dummy; status_t error = wait_for_thread(thread->id, &dummy); if (error == B_BAD_THREAD_ID) - return ESRCH; + RETURN_AND_TEST_CANCEL(ESRCH); if (_value != NULL) *_value = thread->exit_value; @@ -188,7 +195,7 @@ pthread_join(pthread_t thread, void** _value) if ((atomic_or(&thread->flags, THREAD_DETACHED) & THREAD_DEAD) != 0) free(thread); - return B_TO_POSIX_ERROR(error); + RETURN_AND_TEST_CANCEL(error); } @@ -208,7 +215,7 @@ pthread_kill(pthread_t thread, int sig) if (status == B_BAD_THREAD_ID) return ESRCH; - return B_TO_POSIX_ERROR(status); + return status; } return 0; @@ -267,7 +274,7 @@ pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param) int -pthread_setschedparam(pthread_t thread, int policy, +pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) { status_t status; diff --git a/src/system/libroot/posix/pthread/pthread_cancel.cpp b/src/system/libroot/posix/pthread/pthread_cancel.cpp index b52b9c6ac6..5627b670ba 100644 --- a/src/system/libroot/posix/pthread/pthread_cancel.cpp +++ b/src/system/libroot/posix/pthread/pthread_cancel.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ @@ -6,32 +7,82 @@ #include "pthread_private.h" -#include +#include + + +static inline void +test_asynchronous_cancel(int32 flags) +{ + static const int32 kFlags = THREAD_CANCELED | THREAD_CANCEL_ENABLED + | THREAD_CANCEL_ASYNCHRONOUS; + + if ((~flags & kFlags) == 0) + pthread_exit(PTHREAD_CANCELED); +} + + +/*! Signal handler like function invoked when this thread has been canceled. + Has the simple signal handler signature, since it is invoked just like a + signal handler. +*/ +static void +asynchronous_cancel_thread(int) +{ + pthread_t thread = pthread_self(); + + // Exit when asynchronous cancellation is enabled, otherwise we don't have + // to do anything -- the syscall interrupting side effect is all we need. + if ((atomic_get(&thread->flags) & THREAD_CANCEL_ASYNCHRONOUS) != 0) + pthread_exit(PTHREAD_CANCELED); +} + + +// #pragma mark - public API int pthread_cancel(pthread_t thread) { - // TODO: notify thread of being cancelled. - fprintf(stderr, "pthread_cancel() is not yet implemented!\n"); - return EINVAL; + // set the canceled flag + int32 oldFlags = atomic_or(&thread->flags, THREAD_CANCELED); + + // If the flag was already set, we're done. + if ((oldFlags & THREAD_CANCELED) != 0) + return 0; + + // If cancellation is enabled, notify the thread. This will call the + // asynchronous_cancel_thread() handler. + if ((oldFlags & THREAD_CANCEL_ENABLED) != 0) + return _kern_cancel_thread(thread->id, &asynchronous_cancel_thread); + + return 0; } int pthread_setcancelstate(int state, int *_oldState) { - if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE) - return EINVAL; - pthread_thread* thread = pthread_self(); if (thread == NULL) return EINVAL; - if (_oldState != NULL) - *_oldState = thread->cancel_state; + // set the new flags + int32 oldFlags; + if (state == PTHREAD_CANCEL_ENABLE) { + oldFlags = atomic_or(&thread->flags, THREAD_CANCEL_ENABLED); + test_asynchronous_cancel(oldFlags | THREAD_CANCEL_ENABLED); + } else if (state == PTHREAD_CANCEL_DISABLE) { + oldFlags = atomic_and(&thread->flags, ~(int32)THREAD_CANCEL_ENABLED); + test_asynchronous_cancel(oldFlags); + } else + return EINVAL; + + // return the old state + if (_oldState != NULL) { + *_oldState = (oldFlags & PTHREAD_CANCEL_ENABLE) != 0 + ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE; + } - thread->cancel_state = state; return 0; } @@ -39,17 +90,28 @@ pthread_setcancelstate(int state, int *_oldState) int pthread_setcanceltype(int type, int *_oldType) { - if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS) - return EINVAL; - pthread_thread* thread = pthread_self(); if (thread == NULL) return EINVAL; - if (_oldType != NULL) - *_oldType = thread->cancel_type; + // set the new type + int32 oldFlags; + if (type == PTHREAD_CANCEL_DEFERRED) { + oldFlags = atomic_and(&thread->flags, + ~(int32)THREAD_CANCEL_ASYNCHRONOUS); + test_asynchronous_cancel(oldFlags); + } else if (type == PTHREAD_CANCEL_ASYNCHRONOUS) { + oldFlags = atomic_or(&thread->flags, THREAD_CANCEL_ASYNCHRONOUS); + test_asynchronous_cancel(oldFlags | THREAD_CANCEL_ASYNCHRONOUS); + } else + return EINVAL; + + // return the old type + if (_oldType != NULL) { + *_oldType = (oldFlags & THREAD_CANCEL_ASYNCHRONOUS) != 0 + ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED; + } - thread->cancel_type = type; return 0; } @@ -61,7 +123,8 @@ pthread_testcancel(void) if (thread == NULL) return; - if (thread->cancelled && thread->cancel_state == PTHREAD_CANCEL_ENABLE) + static const int32 kFlags = THREAD_CANCELED | THREAD_CANCEL_ENABLED; + + if ((~atomic_get(&thread->flags) & kFlags) == 0) pthread_exit(NULL); } - diff --git a/src/system/libroot/posix/pthread/pthread_cond.cpp b/src/system/libroot/posix/pthread/pthread_cond.cpp index 0b7f065756..4938090027 100644 --- a/src/system/libroot/posix/pthread/pthread_cond.cpp +++ b/src/system/libroot/posix/pthread/pthread_cond.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -109,7 +111,7 @@ cond_signal(pthread_cond_t* cond, bool broadcast) int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* _mutex) { - return cond_wait(cond, _mutex, B_INFINITE_TIMEOUT); + RETURN_AND_TEST_CANCEL(cond_wait(cond, _mutex, B_INFINITE_TIMEOUT)); } @@ -118,10 +120,10 @@ pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* tv) { if (tv == NULL || tv->tv_nsec < 0 || tv->tv_nsec >= 1000 * 1000 * 1000) - return EINVAL; + RETURN_AND_TEST_CANCEL(EINVAL); - return cond_wait(cond, mutex, - tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL); + RETURN_AND_TEST_CANCEL( + cond_wait(cond, mutex, tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL)); } diff --git a/src/system/libroot/posix/pthread/pthread_once.cpp b/src/system/libroot/posix/pthread/pthread_once.cpp index cde4bdf164..e83627a571 100644 --- a/src/system/libroot/posix/pthread/pthread_once.cpp +++ b/src/system/libroot/posix/pthread/pthread_once.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -16,6 +16,27 @@ enum { }; +/*! Called when the thread performing the initialization function was canceled. + + \param data Pointer to the \c pthread_once_t structure in question. +*/ +static void +init_function_canceled(void* data) +{ + pthread_once_t* onceControl = (pthread_once_t*)data; + + // reset the control state to uninitialized + int32 value = atomic_set((vint32*)&onceControl->state, STATE_UNINITIALIZED); + + // If someone has set a semaphore, delete it. + if (value >= 0) + delete_sem(value); +} + + +// #pragma mark - + + int pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void)) { @@ -32,57 +53,59 @@ pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void)) // STATE_INITIALIZED: Set by the first thread when it returns from // initRoutine. All following threads will return right away. - int32 value = atomic_test_and_set((vint32*)&onceControl->state, - STATE_INITIALIZING, STATE_UNINITIALIZED); + while (true) { + int32 value = atomic_test_and_set((vint32*)&onceControl->state, + STATE_INITIALIZING, STATE_UNINITIALIZED); - if (value == STATE_INITIALIZED) - return 0; + if (value == STATE_INITIALIZED) + return 0; - if (value == STATE_UNINITIALIZED) { - // we're the first -- perform the initialization - initRoutine(); + if (value == STATE_UNINITIALIZED) { + // we're the first -- perform the initialization + pthread_cleanup_push(&init_function_canceled, onceControl); + initRoutine(); + pthread_cleanup_pop(false); - value = atomic_set((vint32*)&onceControl->state, STATE_INITIALIZED); + value = atomic_set((vint32*)&onceControl->state, STATE_INITIALIZED); - // If someone else is waiting, we need to delete the semaphore. - if (value >= 0) - delete_sem(value); + // If someone else is waiting, we need to delete the semaphore. + if (value >= 0) + delete_sem(value); - return 0; - } + return 0; + } - if (value == STATE_INITIALIZING) { - // someone is initializing -- we need to create a semaphore we can wait - // on - sem_id semaphore = create_sem(0, "pthread once"); - if (semaphore >= 0) { - // successfully created -- set it - value = atomic_test_and_set((vint32*)&onceControl->state, - semaphore, STATE_INITIALIZING); - if (value == STATE_INITIALIZING) - value = semaphore; - else - delete_sem(semaphore); - } else { - // Failed to create the semaphore. Can only happen when the system - // runs out of semaphores, but we can still handle the situation - // gracefully by spinning. - value = atomic_test_and_set((vint32*)&onceControl->state, - STATE_SPINNING, STATE_INITIALIZING); - if (value == STATE_INITIALIZING) - value = STATE_SPINNING; + if (value == STATE_INITIALIZING) { + // someone is initializing -- we need to create a semaphore we can + // wait on + sem_id semaphore = create_sem(0, "pthread once"); + if (semaphore >= 0) { + // successfully created -- set it + value = atomic_test_and_set((vint32*)&onceControl->state, + semaphore, STATE_INITIALIZING); + if (value == STATE_INITIALIZING) + value = semaphore; + else + delete_sem(semaphore); + } else { + // Failed to create the semaphore. Can only happen when the + // system runs out of semaphores, but we can still handle the + // situation gracefully by spinning. + value = atomic_test_and_set((vint32*)&onceControl->state, + STATE_SPINNING, STATE_INITIALIZING); + if (value == STATE_INITIALIZING) + value = STATE_SPINNING; + } + } + + if (value >= 0) { + // wait on the semaphore + while (acquire_sem(value) == B_INTERRUPTED); + + return 0; + } else if (value == STATE_SPINNING) { + // out of semaphores -- spin + while (atomic_get((vint32*)&onceControl->state) == STATE_SPINNING); } } - - if (value >= 0) { - // wait on the semaphore - while (acquire_sem(value) == B_INTERRUPTED); - - return 0; - } else if (value == STATE_SPINNING) { - // out of semaphores -- spin - while (atomic_get((vint32*)&onceControl->state) == STATE_SPINNING); - } - - return 0; } diff --git a/src/system/libroot/posix/semaphore.cpp b/src/system/libroot/posix/semaphore.cpp index ac08dc3699..95647835c7 100644 --- a/src/system/libroot/posix/semaphore.cpp +++ b/src/system/libroot/posix/semaphore.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -114,7 +114,8 @@ sem_timedwait(sem_t* semaphore, const struct timespec* timeout) bigtime_t timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000 + timeout->tv_nsec / 1000; - RETURN_AND_SET_ERRNO(_kern_realtime_sem_wait(semaphore->id, timeoutMicros)); + RETURN_AND_SET_ERRNO_TEST_CANCEL( + _kern_realtime_sem_wait(semaphore->id, timeoutMicros)); } @@ -128,8 +129,8 @@ sem_trywait(sem_t* semaphore) int sem_wait(sem_t* semaphore) { - RETURN_AND_SET_ERRNO(_kern_realtime_sem_wait(semaphore->id, - B_INFINITE_TIMEOUT)); + RETURN_AND_SET_ERRNO_TEST_CANCEL( + _kern_realtime_sem_wait(semaphore->id, B_INFINITE_TIMEOUT)); } diff --git a/src/system/libroot/posix/signal/Jamfile b/src/system/libroot/posix/signal/Jamfile index 562b81dff2..11d753a235 100644 --- a/src/system/libroot/posix/signal/Jamfile +++ b/src/system/libroot/posix/signal/Jamfile @@ -1,26 +1,33 @@ SubDir HAIKU_TOP src system libroot posix signal ; UsePrivateSystemHeaders ; +UsePrivateHeaders libroot shared ; MergeObject posix_signal.o : kill.c killpg.cpp + psiginfo.cpp + psignal.cpp raise.c send_signal.c - set_signal_disposition.cpp + set_signal_mask.cpp set_signal_stack.c - sigaction.c + sigaction.cpp sigaltstack.c sighold.cpp sigignore.cpp siginterrupt.cpp - signal.c + signal.cpp + signal_limits.cpp sigpause.cpp - sigpending.c - sigprocmask.c + sigpending.cpp + sigqueue.cpp sigrelse.cpp - sigset.c - sigsuspend.c - sigwait.c - strsignal.c + sigset.cpp + sigset_accessors.cpp + sigsuspend.cpp + sigtimedwait.cpp + sigwait.cpp + sigwaitinfo.cpp + strsignal.cpp ; diff --git a/src/system/libroot/posix/signal/kill.c b/src/system/libroot/posix/signal/kill.c index 78e0abbd41..ed01a3949f 100644 --- a/src/system/libroot/posix/signal/kill.c +++ b/src/system/libroot/posix/signal/kill.c @@ -4,18 +4,28 @@ */ +#include +#include + #include -#include -#include +#include int kill(pid_t pid, int sig) { - status_t status = send_signal(pid, (uint)sig); - if (status < B_OK) { - if (status == B_BAD_THREAD_ID) + status_t status; + + if (sig < 0) { + errno = EINVAL; + return -1; + } + + status = _kern_send_signal(pid, sig, NULL, 0); + if (status != B_OK) { + // translate B_BAD_THREAD_ID/B_BAD_TEAM_ID to ESRCH + if (status == B_BAD_THREAD_ID || status == B_BAD_TEAM_ID) status = ESRCH; errno = status; diff --git a/src/system/libroot/posix/signal/psiginfo.cpp b/src/system/libroot/posix/signal/psiginfo.cpp new file mode 100644 index 0000000000..9052610dda --- /dev/null +++ b/src/system/libroot/posix/signal/psiginfo.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + + +void +psiginfo(const siginfo_t* info, const char* message) +{ + psignal(info->si_signo, message); +} diff --git a/src/system/libroot/posix/signal/psignal.cpp b/src/system/libroot/posix/signal/psignal.cpp new file mode 100644 index 0000000000..8e45a9bfd6 --- /dev/null +++ b/src/system/libroot/posix/signal/psignal.cpp @@ -0,0 +1,20 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include + + +void +psignal(int signal, const char* message) +{ + if (message != NULL && message[0] != '\0') + fprintf(stderr, "%s: %s\n", message, strsignal(signal)); + else + fprintf(stderr, "%s\n", strsignal(signal)); +} diff --git a/src/system/libroot/posix/signal/raise.c b/src/system/libroot/posix/signal/raise.c index 1fe454adb7..f40822ba5a 100644 --- a/src/system/libroot/posix/signal/raise.c +++ b/src/system/libroot/posix/signal/raise.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2006, Haiku Project. All rights reserved. + * Copyright (c) 2002-2011, Haiku Project. All rights reserved. * Distributed under the terms of the Haiku license. * * Author(s): @@ -7,13 +7,18 @@ */ -#include #include +#include + +#include + +#include + int raise(int sig) { - return kill(find_thread(NULL), sig); + RETURN_AND_SET_ERRNO(send_signal(find_thread(NULL), sig)); } diff --git a/src/system/libroot/posix/signal/send_signal.c b/src/system/libroot/posix/signal/send_signal.c index 06482aa0cd..d8e2806e0f 100644 --- a/src/system/libroot/posix/signal/send_signal.c +++ b/src/system/libroot/posix/signal/send_signal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2004, Haiku Project. All rights reserved. + * Copyright (c) 2002-2011, Haiku Project. All rights reserved. * Distributed under the terms of the Haiku license. * * Author(s): @@ -7,13 +7,14 @@ */ +#include + +#include #include -#include int -send_signal(pid_t thread, uint sig) +send_signal(thread_id thread, uint sig) { - return _kern_send_signal(thread, sig); + return _kern_send_signal(thread, sig, NULL, SIGNAL_FLAG_SEND_TO_THREAD); } - diff --git a/src/system/libroot/posix/signal/set_signal_disposition.cpp b/src/system/libroot/posix/signal/set_signal_disposition.cpp deleted file mode 100644 index 1954cd38a1..0000000000 --- a/src/system/libroot/posix/signal/set_signal_disposition.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2007, Vasilis Kaoutsis, kaoutsis@sch.gr - * Distributed under the terms of the MIT License. - */ - - -#include - - -sighandler_t -sigset(int signal, sighandler_t signalHandler) -{ - struct sigaction newAction; - struct sigaction oldAction; - - newAction.sa_handler = signalHandler; - newAction.sa_flags = 0; - if (signal == SIGCHLD && signalHandler == SIG_IGN) { - // In case of SIGCHLD the specification requires SA_NOCLDWAIT behavior. - newAction.sa_flags |= SA_NOCLDWAIT; - } - - sigemptyset(&newAction.sa_mask); - if (sigaction(signal, signalHandler == SIG_HOLD ? NULL : &newAction, - &oldAction) == -1) { - return SIG_ERR; - } - - sigset_t newSet; - sigset_t oldSet; - - sigemptyset(&newSet); - sigaddset(&newSet, signal); - - if (signalHandler == SIG_HOLD) { - if (sigprocmask(SIG_BLOCK, &newSet, &oldSet) == -1) - return SIG_ERR; - } else { - // Disposition is not equal to SIG_HOLD, so sig will be - // removed from the calling process' signal mask. - if (sigprocmask(SIG_UNBLOCK, &newSet, &oldSet) == -1) - return SIG_ERR; - } - - if (sigismember(&oldSet, signal)) - return SIG_HOLD; - else - return oldAction.sa_handler; -} diff --git a/src/system/libroot/posix/signal/set_signal_mask.cpp b/src/system/libroot/posix/signal/set_signal_mask.cpp new file mode 100644 index 0000000000..9696693a01 --- /dev/null +++ b/src/system/libroot/posix/signal/set_signal_mask.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. + * All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + +#include +#include + +#include + + +int +__sigprocmask_beos(int how, const sigset_t_beos* beosSet, + sigset_t_beos* beosOldSet) +{ + RETURN_AND_SET_ERRNO(__pthread_sigmask_beos(how, beosSet, beosOldSet)); +} + + +int +__pthread_sigmask_beos(int how, const sigset_t_beos* beosSet, + sigset_t_beos* beosOldSet) +{ + // convert new signal set + sigset_t set; + if (beosSet != NULL) + set = from_beos_sigset(*beosSet); + + // set the mask + sigset_t oldSet; + status_t error = _kern_set_signal_mask(how, beosSet != NULL ? &set : NULL, + beosOldSet != NULL ? &oldSet : NULL); + if (error != B_OK) + return error; + + // convert old signal set back + if (beosOldSet != NULL) + *beosOldSet = to_beos_sigset(oldSet); + + return 0; +} + + +int +__sigprocmask(int how, const sigset_t* set, sigset_t* oldSet) +{ + RETURN_AND_SET_ERRNO(_kern_set_signal_mask(how, set, oldSet)); +} + + +int +__pthread_sigmask(int how, const sigset_t* set, sigset_t* oldSet) +{ + return _kern_set_signal_mask(how, set, oldSet); +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigprocmask_beos", + "sigprocmask@", "BASE"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__pthread_sigmask_beos", + "pthread_sigmask@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigprocmask", "sigprocmask@@", + "1_ALPHA4"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__pthread_sigmask", "pthread_sigmask@@", + "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/set_signal_stack.c b/src/system/libroot/posix/signal/set_signal_stack.c index f544e31ebd..84e048116b 100644 --- a/src/system/libroot/posix/signal/set_signal_stack.c +++ b/src/system/libroot/posix/signal/set_signal_stack.c @@ -4,8 +4,9 @@ */ +#include + #include -#include #include diff --git a/src/system/libroot/posix/signal/sigaction.c b/src/system/libroot/posix/signal/sigaction.c deleted file mode 100644 index 716ff8139f..0000000000 --- a/src/system/libroot/posix/signal/sigaction.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2002-2007, Haiku Inc. All rights reserved. - * Distributed under the terms of the MIT license. - * - * Author: - * Daniel Reinhold (danielre@users.sf.net) - */ - - -#include -#include - -#include - - -int -sigaction(int sig, const struct sigaction *action, struct sigaction *oldAction) -{ - status_t status = _kern_sigaction(sig, action, oldAction); - if (status < B_OK) { - errno = status; - return -1; - } - - return 0; -} - diff --git a/src/system/libroot/posix/signal/sigaction.cpp b/src/system/libroot/posix/signal/sigaction.cpp new file mode 100644 index 0000000000..8025ff4f55 --- /dev/null +++ b/src/system/libroot/posix/signal/sigaction.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2011, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Author: + * Daniel Reinhold, danielre@users.sf.net + * Ingo Weinhod, ingo_weinhold@gmx.de + */ + + +#include +#include + +#include + +#include +#include +#include + +#include + + +int +__sigaction_beos(int signal, const struct sigaction_beos* beosAction, + struct sigaction_beos* beosOldAction) +{ + // convert the new action + struct sigaction action; + if (beosAction != NULL) { + action.sa_handler = beosAction->sa_handler; + action.sa_mask = from_beos_sigset(beosAction->sa_mask); + action.sa_flags = beosAction->sa_flags | SA_BEOS_COMPATIBLE_HANDLER; + action.sa_userdata = beosAction->sa_userdata; + } + + struct sigaction oldAction; + if (__sigaction(signal, beosAction != NULL ? &action : NULL, + beosOldAction != NULL ? &oldAction : NULL) != 0) { + return -1; + } + + // If the signal is SIGSEGV, set the same signal action for SIGBUS. Those + // constants had the same value under BeOS. + if (beosAction != NULL && signal == SIGSEGV) + __sigaction(SIGBUS, &action, NULL); + + // convert the old action back + if (beosOldAction != NULL) { + beosOldAction->sa_handler = oldAction.sa_handler; + beosOldAction->sa_mask = to_beos_sigset(oldAction.sa_mask); + beosOldAction->sa_flags + = oldAction.sa_flags & ~(int)SA_BEOS_COMPATIBLE_HANDLER; + beosOldAction->sa_userdata = oldAction.sa_userdata; + } + + return 0; +} + + +int +__sigaction(int signal, const struct sigaction* action, + struct sigaction* oldAction) +{ + RETURN_AND_SET_ERRNO(_kern_sigaction(signal, action, oldAction)); +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigaction_beos", "sigaction@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigaction", "sigaction@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sighold.cpp b/src/system/libroot/posix/signal/sighold.cpp index 9ce10907d9..6c6343bb2d 100644 --- a/src/system/libroot/posix/signal/sighold.cpp +++ b/src/system/libroot/posix/signal/sighold.cpp @@ -6,17 +6,37 @@ #include +#include + +#include + int -sighold(int signal) +__sighold_beos(int signal) { - // make an empty set - // and add the signal - sigset_t tempSignalSet; - sigemptyset(&tempSignalSet); + // make an empty set and add the signal + sigset_t_beos tempSignalSet = 0; + if (__sigaddset_beos(&tempSignalSet, signal) == -1) + return -1; + + // add the signal to the calling process' signal mask + return __pthread_sigmask_beos(SIG_BLOCK, &tempSignalSet, NULL); +} + + +int +__sighold(int signal) +{ + // make an empty set and add the signal + sigset_t tempSignalSet = 0; if (sigaddset(&tempSignalSet, signal) == -1) return -1; - + // add the signal to the calling process' signal mask return sigprocmask(SIG_BLOCK, &tempSignalSet, NULL); } + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sighold_beos", "sighold@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sighold", "sighold@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigignore.cpp b/src/system/libroot/posix/signal/sigignore.cpp index b925967b48..9334968ef7 100644 --- a/src/system/libroot/posix/signal/sigignore.cpp +++ b/src/system/libroot/posix/signal/sigignore.cpp @@ -6,15 +6,32 @@ #include +#include + +#include + int -sigignore(int signal) +__sigignore_beos(int signal) { - struct sigaction ignoreSignalAction; - // create an action to ignore the signal + // create an action to ignore the signal + struct sigaction_beos ignoreSignalAction; + ignoreSignalAction.sa_handler = SIG_IGN; + ignoreSignalAction.sa_flags = 0; - // request that the signal will be ignored - // by the handler of the action + // In case of SIGCHLD the specification requires SA_NOCLDWAIT behavior. + if (signal == SIGCHLD) + ignoreSignalAction.sa_flags |= SA_NOCLDWAIT; + + return __sigaction_beos(signal, &ignoreSignalAction, NULL); +} + + +int +__sigignore(int signal) +{ + // create an action to ignore the signal + struct sigaction ignoreSignalAction; ignoreSignalAction.sa_handler = SIG_IGN; ignoreSignalAction.sa_flags = 0; @@ -24,3 +41,8 @@ sigignore(int signal) return sigaction(signal, &ignoreSignalAction, NULL); } + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigignore_beos", "sigignore@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigignore", "sigignore@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/siginterrupt.cpp b/src/system/libroot/posix/signal/siginterrupt.cpp index 139208502f..35afca564a 100644 --- a/src/system/libroot/posix/signal/siginterrupt.cpp +++ b/src/system/libroot/posix/signal/siginterrupt.cpp @@ -6,20 +6,41 @@ #include -#include +#include + +#include -extern "C" int -siginterrupt(int sig, int flag) +int +__siginterrupt_beos(int signal, int flag) { - struct sigaction action; - - sigaction(sig, NULL, &action); + struct sigaction_beos action; + __sigaction_beos(signal, NULL, &action); if (flag) action.sa_flags &= ~SA_RESTART; else action.sa_flags |= SA_RESTART; - return sigaction(sig, &action, NULL); + return __sigaction_beos(signal, &action, NULL); } + +int +__siginterrupt(int signal, int flag) +{ + struct sigaction action; + sigaction(signal, NULL, &action); + if (flag) + action.sa_flags &= ~SA_RESTART; + else + action.sa_flags |= SA_RESTART; + + return sigaction(signal, &action, NULL); +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__siginterrupt_beos", "siginterrupt@", + "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__siginterrupt", "siginterrupt@@", + "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/signal.c b/src/system/libroot/posix/signal/signal.c deleted file mode 100644 index 3e58bffa53..0000000000 --- a/src/system/libroot/posix/signal/signal.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2002-2007, Haiku Inc. All rights reserved. - * Distributed under the terms of the MIT license. - * - * Author: - * Daniel Reinhold (danielre@users.sf.net) - */ - - -#include -#include - - -sighandler_t -signal(int sig, sighandler_t signalHandler) -{ - struct sigaction newAction, oldAction; - - // setup the replacement sigaction - newAction.sa_handler = signalHandler; - newAction.sa_mask = 0; - newAction.sa_flags = 0; - - if (sigaction(sig, &newAction, &oldAction) != 0) - return SIG_ERR; - - // success, return the original handler - return oldAction.sa_handler; -} - diff --git a/src/system/libroot/posix/signal/signal.cpp b/src/system/libroot/posix/signal/signal.cpp new file mode 100644 index 0000000000..8f3495f139 --- /dev/null +++ b/src/system/libroot/posix/signal/signal.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2011, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Author: + * Daniel Reinhold, danielre@users.sf.net + * Ingo Weinhold, ingo_weinhold@gmx.de + */ + + +#include + +#include +#include + +#include + +#include + + +static __sighandler_t +signal_common(int signal, __sighandler_t signalHandler, int flags) +{ + struct sigaction newAction, oldAction; + + // setup the replacement sigaction + newAction.sa_handler = signalHandler; + newAction.sa_mask = 0; + newAction.sa_flags = flags; + + if (__sigaction(signal, &newAction, &oldAction) != 0) + return SIG_ERR; + + // success, return the original handler + return oldAction.sa_handler; +} + + +__sighandler_t +__signal_beos(int signal, __sighandler_t signalHandler) +{ + // check signal range + if (signal < 0 || signal > MAX_SIGNAL_NUMBER_BEOS) { + errno = EINVAL; + return SIG_ERR; + } + + // set the signal handler + __sighandler_t result = signal_common(signal, signalHandler, + SA_BEOS_COMPATIBLE_HANDLER); + if (result == SIG_ERR) + return SIG_ERR; + + // If the signal is SIGSEGV, set the same signal handler for SIGBUS. Those + // constants had the same value under BeOS. + if (signal == SIGSEGV) + signal_common(SIGBUS, signalHandler, SA_BEOS_COMPATIBLE_HANDLER); + + return result; +} + + +__sighandler_t +__signal(int signal, __sighandler_t signalHandler) +{ + return signal_common(signal, signalHandler, 0); +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__signal_beos", "signal@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__signal", "signal@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/signal_limits.cpp b/src/system/libroot/posix/signal/signal_limits.cpp new file mode 100644 index 0000000000..1532cf468b --- /dev/null +++ b/src/system/libroot/posix/signal/signal_limits.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + + +int +__signal_get_sigrtmin() +{ + return SIGNAL_REALTIME_MIN; +} + + +int +__signal_get_sigrtmax() +{ + return SIGNAL_REALTIME_MAX; +} diff --git a/src/system/libroot/posix/signal/sigpause.cpp b/src/system/libroot/posix/signal/sigpause.cpp index 17489e650c..c87b701235 100644 --- a/src/system/libroot/posix/signal/sigpause.cpp +++ b/src/system/libroot/posix/signal/sigpause.cpp @@ -6,12 +6,29 @@ #include +#include + +#include + int -sigpause(int signal) +__sigpause_beos(int signal) +{ + sigset_t_beos processSignalSet; + if (__pthread_sigmask_beos(SIG_SETMASK, NULL, &processSignalSet) == -1) + return -1; + + if (__sigdelset_beos(&processSignalSet, signal) == -1) + return -1; + + return __sigsuspend_beos(&processSignalSet); +} + + +int +__sigpause(int signal) { sigset_t processSignalSet; - if (sigprocmask(SIG_SETMASK, NULL, &processSignalSet) == -1) return -1; @@ -20,3 +37,8 @@ sigpause(int signal) return sigsuspend(&processSignalSet); } + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigpause_beos", "sigpause@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigpause", "sigpause@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigpending.c b/src/system/libroot/posix/signal/sigpending.c deleted file mode 100644 index 1c15f3bc5e..0000000000 --- a/src/system/libroot/posix/signal/sigpending.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2005, Haiku Project. All rights reserved. - * Distributed under the terms of the MIT license. - * - * Author(s): - * Jérôme Duval - */ - - -#include - -#include -#include - - -int -sigpending(sigset_t *set) -{ - int err = _kern_sigpending(set); - if (err < B_OK) { - errno = err; - return -1; - } - return 0; -} - diff --git a/src/system/libroot/posix/signal/sigpending.cpp b/src/system/libroot/posix/signal/sigpending.cpp new file mode 100644 index 0000000000..24d6288d08 --- /dev/null +++ b/src/system/libroot/posix/signal/sigpending.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2005-2011, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Author(s): + * Jérôme Duval + * Ingo Weinhold, ingo_weinhold@gmx.de + */ + + +#include + +#include + +#include + +#include +#include + +#include + + +int +__sigpending_beos(sigset_t_beos* beosSet) +{ + sigset_t set; + if (__sigpending(&set) != 0) + return -1; + + *beosSet = to_beos_sigset(set); + return 0; +} + + +int +__sigpending(sigset_t* set) +{ + RETURN_AND_SET_ERRNO(_kern_sigpending(set)); +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigpending_beos", "sigpending@", + "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigpending", "sigpending@@", + "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigprocmask.c b/src/system/libroot/posix/signal/sigprocmask.c deleted file mode 100644 index f49547950f..0000000000 --- a/src/system/libroot/posix/signal/sigprocmask.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include - -#include -#include - - -int -sigprocmask(int how, const sigset_t *set, sigset_t *oldSet) -{ - int status = _kern_sigprocmask(how, set, oldSet); - if (status < B_OK) { - errno = status; - return -1; - } - - return 0; -} - - -int -pthread_sigmask(int how, const sigset_t *set, sigset_t *oldSet) -{ - // TODO: This is obviously broken as sigprocmask() should set the team's - // signal mask! - return sigprocmask(how, set, oldSet); -} - diff --git a/src/system/libroot/posix/signal/sigqueue.cpp b/src/system/libroot/posix/signal/sigqueue.cpp new file mode 100644 index 0000000000..129e29cf21 --- /dev/null +++ b/src/system/libroot/posix/signal/sigqueue.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + +#include +#include + + +int +sigqueue(pid_t pid, int signal, const union sigval userValue) +{ + if (signal < 0) + RETURN_AND_SET_ERRNO(EINVAL); + + if (pid <= 0) + RETURN_AND_SET_ERRNO(ESRCH); + + status_t error = _kern_send_signal(pid, signal, &userValue, + SIGNAL_FLAG_QUEUING_REQUIRED); + if (error != B_OK) { + // translate B_BAD_THREAD_ID/B_BAD_TEAM_ID to ESRCH + if (error == B_BAD_THREAD_ID || error == B_BAD_TEAM_ID) + error = ESRCH; + } + + RETURN_AND_SET_ERRNO(error); +} diff --git a/src/system/libroot/posix/signal/sigrelse.cpp b/src/system/libroot/posix/signal/sigrelse.cpp index bcaa4e279f..f49a9eb62e 100644 --- a/src/system/libroot/posix/signal/sigrelse.cpp +++ b/src/system/libroot/posix/signal/sigrelse.cpp @@ -6,17 +6,37 @@ #include +#include + +#include + int -sigrelse(int signal) +__sigrelse_beos(int signal) { - // make an empty set - // and add the signal - sigset_t tempSignalSet; - sigemptyset(&tempSignalSet); + // make an empty set and add the signal + sigset_t_beos tempSignalSet = 0; + if (__sigaddset_beos(&tempSignalSet, signal) == -1) + return -1; + + // remove the signal from the calling process' signal mask + return __pthread_sigmask_beos(SIG_UNBLOCK, &tempSignalSet, NULL); +} + + +int +__sigrelse(int signal) +{ + // make an empty set and add the signal + sigset_t tempSignalSet = 0; if (sigaddset(&tempSignalSet, signal) == -1) return -1; - + // remove the signal from the calling process' signal mask return sigprocmask(SIG_UNBLOCK, &tempSignalSet, NULL); } + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigrelse_beos", "sigrelse@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigrelse", "sigrelse@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigset.c b/src/system/libroot/posix/signal/sigset.c deleted file mode 100644 index 34b2fe2e9f..0000000000 --- a/src/system/libroot/posix/signal/sigset.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2007, Haiku Inc. All rights reserved. - * Distributed under the terms of the MIT license. - * - * Author: - * Daniel Reinhold (danielre@users.sf.net) - */ - - -#include -#include -#include - - -int -sigemptyset(sigset_t *set) -{ - *set = (sigset_t) 0L; - return 0; -} - - -int -sigfillset(sigset_t *set) -{ - *set = (sigset_t) ~(0UL); - return 0; -} - - -int -sigismember(const sigset_t *set, int sig) -{ - sigset_t mask; - - if (sig <= 0 || sig >= NSIG) { - errno = EINVAL; - return -1; - } - - mask = (((sigset_t)1) << (sig - 1)) ; - return (*set & mask) ? 1 : 0 ; -} - - -int -sigaddset(sigset_t *set, int sig) -{ - sigset_t mask; - - if (sig <= 0 || sig >= NSIG) { - errno = EINVAL; - return -1; - } - - mask = (((sigset_t)1) << (sig - 1)) ; - *set |= mask; - return 0; -} - - -int -sigdelset(sigset_t *set, int sig) -{ - sigset_t mask; - - if (sig <= 0 || sig >= NSIG) { - errno = EINVAL; - return -1; - } - - mask = (((sigset_t)1) << (sig - 1)) ; - *set &= ~mask; - return 0; -} diff --git a/src/system/libroot/posix/signal/sigset.cpp b/src/system/libroot/posix/signal/sigset.cpp new file mode 100644 index 0000000000..93c8660818 --- /dev/null +++ b/src/system/libroot/posix/signal/sigset.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2007, Vasilis Kaoutsis, kaoutsis@sch.gr + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + + +__sighandler_t +__sigset_beos(int signal, __sighandler_t signalHandler) +{ + // prepare new action + struct sigaction_beos newAction; + newAction.sa_handler = signalHandler; + newAction.sa_flags = 0; + if (signal == SIGCHLD && signalHandler == SIG_IGN) { + // In case of SIGCHLD the specification requires SA_NOCLDWAIT behavior. + newAction.sa_flags |= SA_NOCLDWAIT; + } + newAction.sa_mask = 0; + + // set action + struct sigaction_beos oldAction; + if (__sigaction_beos(signal, + signalHandler == SIG_HOLD ? NULL : &newAction, &oldAction) == -1) { + return SIG_ERR; + } + + // prepare new signal mask + sigset_t_beos newSet = 0; + __sigaddset_beos(&newSet, signal); + + // set signal mask + sigset_t_beos oldSet; + if (signalHandler == SIG_HOLD) { + if (__pthread_sigmask_beos(SIG_BLOCK, &newSet, &oldSet) == -1) + return SIG_ERR; + } else { + // Disposition is not equal to SIG_HOLD, so sig will be removed from the + // calling process' signal mask. + if (__pthread_sigmask_beos(SIG_UNBLOCK, &newSet, &oldSet) == -1) + return SIG_ERR; + } + + return __sigismember_beos(&oldSet, signal) + ? SIG_HOLD : oldAction.sa_handler; +} + + +__sighandler_t +__sigset(int signal, __sighandler_t signalHandler) +{ + // prepare new action + struct sigaction newAction; + newAction.sa_handler = signalHandler; + newAction.sa_flags = 0; + if (signal == SIGCHLD && signalHandler == SIG_IGN) { + // In case of SIGCHLD the specification requires SA_NOCLDWAIT behavior. + newAction.sa_flags |= SA_NOCLDWAIT; + } + newAction.sa_mask = 0; + + // set action + struct sigaction oldAction; + if (__sigaction(signal, + signalHandler == SIG_HOLD ? NULL : &newAction, &oldAction) == -1) { + return SIG_ERR; + } + + // prepare new signal mask + sigset_t newSet = 0; + sigaddset(&newSet, signal); + + // set signal mask + sigset_t oldSet; + if (signalHandler == SIG_HOLD) { + if (sigprocmask(SIG_BLOCK, &newSet, &oldSet) == -1) + return SIG_ERR; + } else { + // Disposition is not equal to SIG_HOLD, so sig will be + // removed from the calling process' signal mask. + if (sigprocmask(SIG_UNBLOCK, &newSet, &oldSet) == -1) + return SIG_ERR; + } + + return sigismember(&oldSet, signal) ? SIG_HOLD : oldAction.sa_handler; +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigset_beos", "sigset@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigset", "sigset@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigset_accessors.cpp b/src/system/libroot/posix/signal/sigset_accessors.cpp new file mode 100644 index 0000000000..cf304b6c25 --- /dev/null +++ b/src/system/libroot/posix/signal/sigset_accessors.cpp @@ -0,0 +1,156 @@ +/* + * Copyright 2002-2011, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Author: + * Daniel Reinhold, danielre@users.sf.net + * Ingo Weinhold, ingo_weinhold@gmx.de + */ + + +#include + +#include + +#include +#include + +#include + +#include + + +// #pragma - backward compatibility implementations + + +int +__sigemptyset_beos(sigset_t_beos* set) +{ + *set = (sigset_t_beos)0; + return 0; +} + + +int +__sigfillset_beos(sigset_t_beos* set) +{ + *set = ~(sigset_t_beos)0; + return 0; +} + + +int +__sigismember_beos(const sigset_t_beos* set, int signal) +{ + if (signal <= 0 || signal > MAX_SIGNAL_NUMBER_BEOS) { + errno = EINVAL; + return -1; + } + + return (*set & SIGNAL_TO_MASK(signal)) != 0 ? 1 : 0; +} + + +int +__sigaddset_beos(sigset_t_beos* set, int signal) +{ + if (signal <= 0 || signal > MAX_SIGNAL_NUMBER_BEOS) { + errno = EINVAL; + return -1; + } + + *set |= SIGNAL_TO_MASK(signal); + return 0; +} + + +int +__sigdelset_beos(sigset_t_beos* set, int signal) +{ + if (signal <= 0 || signal > MAX_SIGNAL_NUMBER_BEOS) { + errno = EINVAL; + return -1; + } + + *set &= ~SIGNAL_TO_MASK(signal); + return 0; +} + + +// #pragma - current implementations + + +int +__sigemptyset(sigset_t* set) +{ + *set = (sigset_t)0; + return 0; +} + + +int +__sigfillset(sigset_t* set) +{ + *set = ~(sigset_t)0; + return 0; +} + + +int +__sigismember(const sigset_t* set, int signal) +{ + if (signal <= 0 || signal > MAX_SIGNAL_NUMBER) { + errno = EINVAL; + return -1; + } + + return (*set & SIGNAL_TO_MASK(signal)) != 0 ? 1 : 0; +} + + +int +__sigaddset(sigset_t* set, int signal) +{ + if (signal <= 0 || signal > MAX_SIGNAL_NUMBER) { + errno = EINVAL; + return -1; + } + + *set |= SIGNAL_TO_MASK(signal); + return 0; +} + + +int +__sigdelset(sigset_t* set, int signal) +{ + if (signal <= 0 || signal > MAX_SIGNAL_NUMBER) { + errno = EINVAL; + return -1; + } + + *set &= ~SIGNAL_TO_MASK(signal); + return 0; +} + + +// #pragma - versioned symbols + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigemptyset_beos", "sigemptyset@", + "BASE"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigfillset_beos", "sigfillset@", + "BASE"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigismember_beos", "sigismember@", + "BASE"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigaddset_beos", "sigaddset@", "BASE"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigdelset_beos", "sigdelset@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigemptyset", "sigemptyset@@", + "1_ALPHA4"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigfillset", "sigfillset@@", + "1_ALPHA4"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigismember", "sigismember@@", + "1_ALPHA4"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigaddset", "sigaddset@@", "1_ALPHA4"); +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigdelset", "sigdelset@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigsuspend.c b/src/system/libroot/posix/signal/sigsuspend.c deleted file mode 100644 index 8fcfba756f..0000000000 --- a/src/system/libroot/posix/signal/sigsuspend.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2005, Haiku Project. All rights reserved. - * Distributed under the terms of the MIT license. - * - * Author(s): - * Jérôme Duval - */ - - -#include - -#include -#include - - -int -sigsuspend(const sigset_t *mask) -{ - errno = _kern_sigsuspend(mask); - return -1; -} - diff --git a/src/system/libroot/posix/signal/sigsuspend.cpp b/src/system/libroot/posix/signal/sigsuspend.cpp new file mode 100644 index 0000000000..5b9a1c57af --- /dev/null +++ b/src/system/libroot/posix/signal/sigsuspend.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2005-2011, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Author(s): + * Jérôme Duval + * Ingo Weinhold, ingo_weinhold@gmx.de + */ + + +#include + +#include +#include + +#include + +#include +#include + +#include + + +int +__sigsuspend_beos(const sigset_t_beos* beosMask) +{ + sigset_t mask = from_beos_sigset(*beosMask); + return __sigsuspend(&mask); +} + + +int +__sigsuspend(const sigset_t* mask) +{ + errno = _kern_sigsuspend(mask); + + pthread_testcancel(); + + return -1; +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigsuspend_beos", "sigsuspend@", + "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigsuspend", "sigsuspend@@", + "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigtimedwait.cpp b/src/system/libroot/posix/signal/sigtimedwait.cpp new file mode 100644 index 0000000000..1ae8382bbd --- /dev/null +++ b/src/system/libroot/posix/signal/sigtimedwait.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include + +#include + +#include + + +int +sigtimedwait(const sigset_t* set, siginfo_t* info, + const struct timespec* timeout) +{ + // make info non-NULL to simplify things + siginfo_t stackInfo; + if (info == NULL) + info = &stackInfo; + + // translate the timeout + uint32 flags; + bigtime_t timeoutMicros; + if (timeout != NULL) { + timeoutMicros = system_time(); + flags = B_ABSOLUTE_TIMEOUT; + timeoutMicros += (bigtime_t)timeout->tv_sec * 1000000 + + timeout->tv_nsec / 1000; + } else { + flags = 0; + timeoutMicros = 0; + } + + status_t error = _kern_sigwait(set, info, flags, timeoutMicros); + + pthread_testcancel(); + + if (error != B_OK) + RETURN_AND_SET_ERRNO(error); + + return info->si_signo; +} diff --git a/src/system/libroot/posix/signal/sigwait.c b/src/system/libroot/posix/signal/sigwait.c deleted file mode 100644 index dc0479f66b..0000000000 --- a/src/system/libroot/posix/signal/sigwait.c +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include - -#include - - -int -sigwait(const sigset_t *set, int *_signal) -{ - return _kern_sigwait(set, _signal); - // does not set errno, but returns the error value directly -} - diff --git a/src/system/libroot/posix/signal/sigwait.cpp b/src/system/libroot/posix/signal/sigwait.cpp new file mode 100644 index 0000000000..29ff9ad878 --- /dev/null +++ b/src/system/libroot/posix/signal/sigwait.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + +#include + +#include + + +int +__sigwait_beos(const sigset_t_beos* beosSet, int* _signal) +{ + // convert the given signal set and call the current version + sigset_t set = from_beos_sigset(*beosSet); + int error = __sigwait(&set, _signal); + if (error != 0) + return error; + + // translate SIGBUS to SIGSEGV + if (*_signal == SIGBUS) + *_signal = SIGSEGV; + + return 0; +} + + +int +__sigwait(const sigset_t* set, int* _signal) +{ + siginfo_t info; + status_t error = _kern_sigwait(set, &info, 0, 0); + + pthread_testcancel(); + + if (error != B_OK) + return error; + + *_signal = info.si_signo; + return 0; +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigwait_beos", "sigwait@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sigwait", "sigwait@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/signal/sigwaitinfo.cpp b/src/system/libroot/posix/signal/sigwaitinfo.cpp new file mode 100644 index 0000000000..515c5d25a6 --- /dev/null +++ b/src/system/libroot/posix/signal/sigwaitinfo.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + + +int +sigwaitinfo(const sigset_t* set, siginfo_t* info) +{ + return sigtimedwait(set, info, NULL); +} diff --git a/src/system/libroot/posix/signal/strsignal.c b/src/system/libroot/posix/signal/strsignal.c deleted file mode 100644 index f7e7d2a827..0000000000 --- a/src/system/libroot/posix/signal/strsignal.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2002-2007, Haiku Inc. All rights reserved. - * Distributed under the terms of the MIT license. - * - * Author: - * Daniel Reinhold (danielre@users.sf.net) - */ - - -#include -#include -#include - - -const char * const -sys_siglist[NSIG] = { - /* 0 */ "Signal 0", - /* 1 - SIGHUP */ "Hangup", - /* 2 - SIGINT */ "Interrupt", - /* 3 - SIGQUIT */ "Quit", - /* 4 - SIGILL */ "Illegal instruction", - /* 5 - SIGCHLD */ "Child exited", - /* 6 - SIGABRT */ "Abort", - /* 7 - SIGPIPE */ "Broken pipe", - /* 8 - SIGFPE */ "Floating point exception", - /* 9 - SIGKILL */ "Killed (by death)", - /* 10 - SIGSTOP */ "Stopped", - /* 11 - SIGSEGV */ "Segmentation violation", - /* 12 - SIGCONT */ "Continued", - /* 13 - SIGTSTP */ "Stopped (tty output)", - /* 14 - SIGALRM */ "Alarm", - /* 15 - SIGTERM */ "Termination requested", - /* 16 - SIGTTIN */ "Stopped (tty input)", - /* 17 - SIGTTOU */ "Stopped (tty output)", - /* 18 - SIGUSR1 */ "User defined signal 1", - /* 19 - SIGUSR2 */ "User defined signal 2", - /* 20 - SIGWINCH */ "Window size changed", - /* 21 - SIGKILLTHR */ "Kill Thread", - /* 22 - SIGTRAP */ "Trace/breakpoint trap", - /* 23 - SIGPOLL */ "Pollable event", - /* 24 - SIGPROF */ "Profiling timer expired", - /* 25 - SIGSYS */ "Bad system call", - /* 26 - SIGURG */ "High bandwidth data is available at socket", - /* 27 - SIGVTALRM */ "Virtual timer expired", - /* 28 - SIGXCPU */ "CPU time limit exceeded", - /* 29 - SIGXFSZ */ "File size limit exceeded", -}; - - -const char * -strsignal(int sig) -{ - const char *s = NULL; - - if (sig < 0 || sig > MAX_SIGNO) - return "Bogus signal number"; - - if (sig < NSIG) - s = sys_siglist[sig]; - - if (s) - return s; - else { - static char buf[40]; - sprintf(buf, "Unknown Signal %d", sig); - return (const char *) buf; - } -} - diff --git a/src/system/libroot/posix/signal/strsignal.cpp b/src/system/libroot/posix/signal/strsignal.cpp new file mode 100644 index 0000000000..3ba17f9583 --- /dev/null +++ b/src/system/libroot/posix/signal/strsignal.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2011, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Author: + * Daniel Reinhold (danielre@users.sf.net) + * Ingo Weinhold, ingo_weinhold@gmx.de + */ + + +#include + +#include +#include +#include + +#include + + +static const char* const kInvalidSignalNumberText = "Bogus signal number"; + + +const char * const +sys_siglist[NSIG] = { + /* 0 */ "Signal 0", + /* 1 - SIGHUP */ "Hangup", + /* 2 - SIGINT */ "Interrupt", + /* 3 - SIGQUIT */ "Quit", + /* 4 - SIGILL */ "Illegal instruction", + /* 5 - SIGCHLD */ "Child exited", + /* 6 - SIGABRT */ "Abort", + /* 7 - SIGPIPE */ "Broken pipe", + /* 8 - SIGFPE */ "Floating point exception", + /* 9 - SIGKILL */ "Killed (by death)", + /* 10 - SIGSTOP */ "Stopped", + /* 11 - SIGSEGV */ "Segmentation violation", + /* 12 - SIGCONT */ "Continued", + /* 13 - SIGTSTP */ "Stopped (tty output)", + /* 14 - SIGALRM */ "Alarm", + /* 15 - SIGTERM */ "Termination requested", + /* 16 - SIGTTIN */ "Stopped (tty input)", + /* 17 - SIGTTOU */ "Stopped (tty output)", + /* 18 - SIGUSR1 */ "User defined signal 1", + /* 19 - SIGUSR2 */ "User defined signal 2", + /* 20 - SIGWINCH */ "Window size changed", + /* 21 - SIGKILLTHR */ "Kill Thread", + /* 22 - SIGTRAP */ "Trace/breakpoint trap", + /* 23 - SIGPOLL */ "Pollable event", + /* 24 - SIGPROF */ "Profiling timer expired", + /* 25 - SIGSYS */ "Bad system call", + /* 26 - SIGURG */ "High bandwidth data is available at socket", + /* 27 - SIGVTALRM */ "Virtual timer expired", + /* 28 - SIGXCPU */ "CPU time limit exceeded", + /* 29 - SIGXFSZ */ "File size limit exceeded", + /* 30 - SIGBUS */ "Bus error", + /* 31 - SIGRESERVED1 */ "Reserved signal 1", + /* 32 - SIGRESERVED2 */ "Reserved signal 2", + /* 33 - realtime 1 */ "Realtime signal 1", + /* 34 - realtime 2 */ "Realtime signal 2", + /* 35 - realtime 3 */ "Realtime signal 3", + /* 36 - realtime 4 */ "Realtime signal 4", + /* 37 - realtime 5 */ "Realtime signal 5", + /* 38 - realtime 6 */ "Realtime signal 6", + /* 39 - realtime 7 */ "Realtime signal 7", + /* 40 - realtime 8 */ "Realtime signal 8", + /* 41 - invalid */ kInvalidSignalNumberText, + /* 42 - invalid */ kInvalidSignalNumberText, + /* 43 - invalid */ kInvalidSignalNumberText, + /* 44 - invalid */ kInvalidSignalNumberText, + /* 45 - invalid */ kInvalidSignalNumberText, + /* 46 - invalid */ kInvalidSignalNumberText, + /* 47 - invalid */ kInvalidSignalNumberText, + /* 48 - invalid */ kInvalidSignalNumberText, + /* 49 - invalid */ kInvalidSignalNumberText, + /* 50 - invalid */ kInvalidSignalNumberText, + /* 51 - invalid */ kInvalidSignalNumberText, + /* 52 - invalid */ kInvalidSignalNumberText, + /* 53 - invalid */ kInvalidSignalNumberText, + /* 54 - invalid */ kInvalidSignalNumberText, + /* 55 - invalid */ kInvalidSignalNumberText, + /* 56 - invalid */ kInvalidSignalNumberText, + /* 57 - invalid */ kInvalidSignalNumberText, + /* 58 - invalid */ kInvalidSignalNumberText, + /* 59 - invalid */ kInvalidSignalNumberText, + /* 60 - invalid */ kInvalidSignalNumberText, + /* 61 - invalid */ kInvalidSignalNumberText, + /* 62 - invalid */ kInvalidSignalNumberText, + /* 63 - invalid */ kInvalidSignalNumberText, + /* 64 - invalid */ kInvalidSignalNumberText +}; + + +const char* +strsignal(int sig) +{ + if (sig < 0 || sig > __MAX_SIGNO) + return kInvalidSignalNumberText; + + return sys_siglist[sig]; +} + diff --git a/src/system/libroot/posix/sys/Jamfile b/src/system/libroot/posix/sys/Jamfile index 17601c8bb6..3f439cbffd 100644 --- a/src/system/libroot/posix/sys/Jamfile +++ b/src/system/libroot/posix/sys/Jamfile @@ -10,7 +10,7 @@ MergeObject posix_sys.o : ftok.c getrusage.c gettimeofday.c - itimer.c + itimer.cpp mkdir.c mkfifo.c mman.cpp @@ -18,12 +18,12 @@ MergeObject posix_sys.o : select.c stat.c statvfs.c - times.c + times.cpp uio.c umask.c uname.c utimes.c - wait.c + wait.cpp xsi_msg_queue.cpp xsi_sem.cpp ; diff --git a/src/system/libroot/posix/sys/flock.c b/src/system/libroot/posix/sys/flock.c index dd1b8dedac..3967c115b0 100644 --- a/src/system/libroot/posix/sys/flock.c +++ b/src/system/libroot/posix/sys/flock.c @@ -4,16 +4,21 @@ */ -#include - #include + #include +#include + +#include int flock(int fd, int op) { status_t status = _kern_flock(fd, op); + + pthread_testcancel(); + if (status < B_OK) { errno = status; return -1; diff --git a/src/system/libroot/posix/sys/itimer.c b/src/system/libroot/posix/sys/itimer.c deleted file mode 100644 index f2802d05a5..0000000000 --- a/src/system/libroot/posix/sys/itimer.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include - -#include -#include -#include - - -#define USEC_PER_SECOND 1000000 - - -int -getitimer(int which, struct itimerval *value) -{ - // ToDo: implement me! - return -1; -} - - -int -setitimer(int which, const struct itimerval *value, struct itimerval *oldValue) -{ - // TODO: implement me properly! - // We probably need a better internal set_alarm() implementation to do this - - bigtime_t interval = value->it_interval.tv_sec * USEC_PER_SECOND + value->it_interval.tv_usec; - bigtime_t remaining; - - // Only real time timers work at all. - if (which != ITIMER_REAL) { - errno = B_NOT_SUPPORTED; - return -1; - } - - if (interval != 0) - remaining = set_alarm(interval, B_PERIODIC_ALARM); - else { - bigtime_t timeout = value->it_value.tv_sec * USEC_PER_SECOND + value->it_value.tv_usec; - if (timeout != 0) - remaining = set_alarm(timeout, B_ONE_SHOT_RELATIVE_ALARM); - else { - // cancel alarm - remaining = set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM); - } - } - - if (oldValue != NULL) { - // Record the time left of any previous itimer - oldValue->it_value.tv_sec = remaining / USEC_PER_SECOND; - oldValue->it_value.tv_usec = remaining % USEC_PER_SECOND; - } - - return 0; -} - diff --git a/src/system/libroot/posix/sys/itimer.cpp b/src/system/libroot/posix/sys/itimer.cpp new file mode 100644 index 0000000000..fe47269579 --- /dev/null +++ b/src/system/libroot/posix/sys/itimer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. + * All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include + +#include + +#include + +#include + +#include + + +static bool +itimerval_to_itimerspec(const itimerval& val, itimerspec& spec) +{ + return timeval_to_timespec(val.it_value, spec.it_value) + && timeval_to_timespec(val.it_interval, spec.it_interval); +} + + +static void +itimerspec_to_itimerval(const itimerspec& spec, itimerval& val) +{ + timespec_to_timeval(spec.it_value, val.it_value); + timespec_to_timeval(spec.it_interval, val.it_interval); +} + + +static bool +prepare_timer(__timer_t& timer, int which) +{ + switch (which) { + case ITIMER_REAL: + timer.SetTo(USER_TIMER_REAL_TIME_ID, -1); + return true; + case ITIMER_VIRTUAL: + timer.SetTo(USER_TIMER_TEAM_USER_TIME_ID, -1); + return true; + case ITIMER_PROF: + timer.SetTo(USER_TIMER_TEAM_TOTAL_TIME_ID, -1); + return true; + } + + return false; +} + + +// #pragma mark - + + +int +getitimer(int which, struct itimerval* value) +{ + // prepare the respective timer + __timer_t timer; + if (!prepare_timer(timer, which)) + RETURN_AND_SET_ERRNO(EINVAL); + + // let timer_gettime() do the job + itimerspec valueSpec; + if (timer_gettime(&timer, &valueSpec) != 0) + return -1; + + // convert back to itimerval value + itimerspec_to_itimerval(valueSpec, *value); + + return 0; +} + + +int +setitimer(int which, const struct itimerval* value, struct itimerval* oldValue) +{ + // prepare the respective timer + __timer_t timer; + if (!prepare_timer(timer, which)) + RETURN_AND_SET_ERRNO(EINVAL); + + // convert value to itimerspec + itimerspec valueSpec; + if (!itimerval_to_itimerspec(*value, valueSpec)) + RETURN_AND_SET_ERRNO(EINVAL); + + // let timer_settime() do the job + itimerspec oldValueSpec; + if (timer_settime(&timer, 0, &valueSpec, + oldValue != NULL ? &oldValueSpec : NULL) != 0) { + return -1; + } + + // convert back to itimerval oldValue + if (oldValue != NULL) + itimerspec_to_itimerval(oldValueSpec, *oldValue); + + return 0; +} diff --git a/src/system/libroot/posix/sys/mman.cpp b/src/system/libroot/posix/sys/mman.cpp index e0551bbf8d..28886e5e1b 100644 --- a/src/system/libroot/posix/sys/mman.cpp +++ b/src/system/libroot/posix/sys/mman.cpp @@ -154,7 +154,7 @@ mprotect(void* address, size_t length, int protection) int msync(void* address, size_t length, int flags) { - RETURN_AND_SET_ERRNO(_kern_sync_memory(address, length, flags)); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_sync_memory(address, length, flags)); } diff --git a/src/system/libroot/posix/sys/select.c b/src/system/libroot/posix/sys/select.c index cb1c5250b1..d0549d9176 100644 --- a/src/system/libroot/posix/sys/select.c +++ b/src/system/libroot/posix/sys/select.c @@ -1,38 +1,70 @@ -/* - * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +/* + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. + * All rights reserved. * Distributed under the terms of the MIT License. */ -#include #include + +#include +#include + +#include + +#include #include - -#define RETURN_AND_SET_ERRNO(err) \ - if (err < 0) { \ - errno = err; \ - return -1; \ - } \ - return err; +#include -int -pselect(int numBits, struct fd_set *readBits, struct fd_set *writeBits, - struct fd_set *errorBits, const struct timespec *tv, const sigset_t *sigMask) +int __pselect_beos(int numBits, struct fd_set *readBits, + struct fd_set *writeBits, struct fd_set *errorBits, + const struct timespec *tv, const sigset_t *beosSignalMask); +int __pselect(int numBits, struct fd_set *readBits, struct fd_set *writeBits, + struct fd_set *errorBits, const struct timespec *tv, + const sigset_t *sigMask); + + +int +__pselect_beos(int numBits, struct fd_set *readBits, struct fd_set *writeBits, + struct fd_set *errorBits, const struct timespec *tv, + const sigset_t *beosSignalMask) +{ + int status; + sigset_t signalMask; + bigtime_t timeout = -1LL; + if (tv) + timeout = tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL; + + if (beosSignalMask != NULL) + signalMask = from_beos_sigset(*beosSignalMask); + + status = _kern_select(numBits, readBits, writeBits, errorBits, timeout, + beosSignalMask != NULL ? &signalMask : NULL); + + RETURN_AND_SET_ERRNO_TEST_CANCEL(status); +} + + +int +__pselect(int numBits, struct fd_set *readBits, struct fd_set *writeBits, + struct fd_set *errorBits, const struct timespec *tv, + const sigset_t *sigMask) { int status; bigtime_t timeout = -1LL; if (tv) timeout = tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL; - status = _kern_select(numBits, readBits, writeBits, errorBits, timeout, sigMask); + status = _kern_select(numBits, readBits, writeBits, errorBits, timeout, + sigMask); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(status); } -int +int select(int numBits, struct fd_set *readBits, struct fd_set *writeBits, struct fd_set *errorBits, struct timeval *tv) { @@ -41,7 +73,13 @@ select(int numBits, struct fd_set *readBits, struct fd_set *writeBits, if (tv) timeout = tv->tv_sec * 1000000LL + tv->tv_usec; - status = _kern_select(numBits, readBits, writeBits, errorBits, timeout, NULL); + status = _kern_select(numBits, readBits, writeBits, errorBits, timeout, + NULL); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(status); } + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__pselect_beos", "pselect@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__pselect", "pselect@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/sys/times.c b/src/system/libroot/posix/sys/times.c deleted file mode 100644 index 84c165725e..0000000000 --- a/src/system/libroot/posix/sys/times.c +++ /dev/null @@ -1,39 +0,0 @@ -/* -** Copyright 2004, Jérôme Duval, jerome.duval@free.fr. -** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. -** Distributed under the terms of the MIT License. -*/ - -#include - -#include -#include -#include - - -clock_t -times(struct tms *buffer) -{ - team_usage_info info; - status_t err; - int32 clk_tck = 1000000 / CLK_TCK; /* to avoid overflow */ - - if ((err = get_team_usage_info(B_CURRENT_TEAM, RUSAGE_SELF, &info)) != B_OK) { - errno = err; - return -1; - } - - buffer->tms_utime = info.user_time / clk_tck; - buffer->tms_stime = info.kernel_time / clk_tck; - - if ((err = get_team_usage_info(B_CURRENT_TEAM, RUSAGE_CHILDREN, &info)) != B_OK) { - errno = err; - return -1; - } - - buffer->tms_cutime = info.user_time / clk_tck; - buffer->tms_cstime = info.kernel_time / clk_tck; - - return system_time() / clk_tck; -} - diff --git a/src/system/libroot/posix/sys/times.cpp b/src/system/libroot/posix/sys/times.cpp new file mode 100644 index 0000000000..50d3cc5b20 --- /dev/null +++ b/src/system/libroot/posix/sys/times.cpp @@ -0,0 +1,63 @@ +/* +** Copyright 2004, Jérôme Duval, jerome.duval@free.fr. +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. +** Distributed under the terms of the MIT License. +*/ + +#include + +#include +#include +#include + +#include + +#include +#include + + +static inline clock_t +times_common(struct tms* buffer, bigtime_t microSecondsPerClock) +{ + team_usage_info info; + status_t err; + + if ((err = get_team_usage_info(B_CURRENT_TEAM, RUSAGE_SELF, &info)) + != B_OK) { + errno = err; + return -1; + } + + buffer->tms_utime = info.user_time / microSecondsPerClock; + buffer->tms_stime = info.kernel_time / microSecondsPerClock; + + if ((err = get_team_usage_info(B_CURRENT_TEAM, RUSAGE_CHILDREN, &info)) + != B_OK) { + errno = err; + return -1; + } + + buffer->tms_cutime = info.user_time / microSecondsPerClock; + buffer->tms_cstime = info.kernel_time / microSecondsPerClock; + + return system_time() / microSecondsPerClock; +} + + +clock_t +__times_beos(struct tms* buffer) +{ + return times_common(buffer, MICROSECONDS_PER_CLOCK_TICK_BEOS); +} + + +clock_t +__times(struct tms* buffer) +{ + return times_common(buffer, MICROSECONDS_PER_CLOCK_TICK); +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__times_beos", "times@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__times", "times@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/sys/wait.c b/src/system/libroot/posix/sys/wait.c deleted file mode 100644 index b3b6d740ff..0000000000 --- a/src/system/libroot/posix/sys/wait.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - -#include - -#include -#include - -#include -#include - - -// ToDo: properly implement waitpid(), and waitid() - - -pid_t -wait(int *_status) -{ - return waitpid(-1, _status, 0); -} - - -pid_t -waitpid(pid_t pid, int *_status, int options) -{ - status_t returnCode; - int32 reason; - - thread_id thread = _kern_wait_for_child(pid, options, &reason, &returnCode); - - if (thread >= B_OK && _status != NULL) { - - // See kernel's wait_for_child() for how the return information is - // encoded: - // reason = (signal << 16) | reason - - int status; - switch (reason & 0xffff) { - case THREAD_RETURN_INTERRUPTED: - // fill in signal for WIFSIGNALED() and WTERMSIG() - status = (reason >> 8) & 0xff00; - break; - case THREAD_STOPPED: - // WIFSTOPPED() and WSTOPSIG() - status = reason & 0xff0000; - break; - case THREAD_CONTINUED: - // WIFCONTINUED() - status = 0x20000; - break; - case THREAD_RETURN_EXIT: - default: - // WEXITSTATUS() - status = returnCode & 0xff; - break; - } - - *_status = status; - } - - if (thread < B_OK) { - errno = thread; - if (thread == B_WOULD_BLOCK && (options & WNOHANG) != 0) - return 0; - return -1; - } - - return thread; -} - - -// TODO: Implement as part of real-time signal support! -//int -//waitid(idtype_t idType, id_t id, siginfo_t *info, int options) -//{ -// // waitid() is not available on BeOS so we may be lazy here and remove it... -// fprintf(stderr, "waitid(): NOT IMPLEMENTED\n"); -// return -1; -//} - diff --git a/src/system/libroot/posix/sys/wait.cpp b/src/system/libroot/posix/sys/wait.cpp new file mode 100644 index 0000000000..05fe759a1b --- /dev/null +++ b/src/system/libroot/posix/sys/wait.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. + * All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include + +#include +#include + +#include + +#include +#include + + +pid_t +wait(int* _status) +{ + return waitpid(-1, _status, 0); +} + + +pid_t +waitpid(pid_t pid, int* _status, int options) +{ + // wait + siginfo_t info; + pid_t child = _kern_wait_for_child(pid, options, &info); + + pthread_testcancel(); + + if (child < 0) + RETURN_AND_SET_ERRNO(child); + + // prepare the status + if (_status != NULL) { + int status; + switch (info.si_code) { + case CLD_EXITED: + // fill in exit status for WIFEXITED() and WEXITSTATUS() + status = info.si_status & 0xff; + break; + + case CLD_KILLED: + case CLD_DUMPED: + // fill in signal for WIFSIGNALED() and WTERMSIG() + status = (info.si_status << 8) & 0xff00; + // if core dumped, set flag for WIFCORED() + if (info.si_code == CLD_DUMPED) + status |= 0x10000; + break; + + case CLD_CONTINUED: + // set flag for WIFCONTINUED() + status = 0x20000; + break; + + case CLD_STOPPED: + // fill in signal for WIFSTOPPED() and WSTOPSIG() + status = (info.si_status << 16) & 0xff0000; + break; + + case CLD_TRAPPED: + // we don't do that + default: + // should never get here -- assume exited + status = 0; + break; + } + + *_status = status; + } + + return child; +} + + +int +waitid(idtype_t idType, id_t id, siginfo_t* info, int options) +{ + // translate the idType, id pair to a waitpid() style ID + switch (idType) { + case P_ALL: + // any child + id = -1; + break; + + case P_PID: + // the child with the given ID + if (id <= 0) + RETURN_AND_SET_ERRNO_TEST_CANCEL(EINVAL); + break; + + case P_PGID: + // any child in the given process group + if (id <= 1) + RETURN_AND_SET_ERRNO_TEST_CANCEL(EINVAL); + id = -id; + break; + + default: + RETURN_AND_SET_ERRNO_TEST_CANCEL(EINVAL); + } + + pid_t child = _kern_wait_for_child(id, options, info); + if (child >= 0 || child == B_WOULD_BLOCK) + return 0; + + RETURN_AND_SET_ERRNO_TEST_CANCEL(child); +} diff --git a/src/system/libroot/posix/sys/xsi_msg_queue.cpp b/src/system/libroot/posix/sys/xsi_msg_queue.cpp index cf3429718e..6489782a43 100644 --- a/src/system/libroot/posix/sys/xsi_msg_queue.cpp +++ b/src/system/libroot/posix/sys/xsi_msg_queue.cpp @@ -37,8 +37,8 @@ ssize_t msgrcv(int messageQueueID, void *messagePointer, size_t messageSize, long messageType, int messageFlags) { - RETURN_AND_SET_ERRNO(_kern_xsi_msgrcv(messageQueueID, messagePointer, - messageSize, messageType, messageFlags)); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_xsi_msgrcv(messageQueueID, + messagePointer, messageSize, messageType, messageFlags)); } @@ -46,6 +46,6 @@ int msgsnd(int messageQueueID, const void *messagePointer, size_t messageSize, int messageFlags) { - RETURN_AND_SET_ERRNO(_kern_xsi_msgsnd(messageQueueID, messagePointer, - messageSize, messageFlags)); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_xsi_msgsnd(messageQueueID, + messagePointer, messageSize, messageFlags)); } diff --git a/src/system/libroot/posix/time/Jamfile b/src/system/libroot/posix/time/Jamfile index a27f922174..c1d4b7cdd5 100644 --- a/src/system/libroot/posix/time/Jamfile +++ b/src/system/libroot/posix/time/Jamfile @@ -5,14 +5,17 @@ SubDirCcFlags -DNOID -DTZDIR='get_timezones_directory()' -DUSG_COMPAT -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DPCTS=1 -DSTD_INSPIRED ; UsePrivateHeaders + libroot [ FDirName libroot locale ] [ FDirName libroot time ] + shared ; UsePrivateSystemHeaders ; MergeObject posix_time.o : asctime.cpp - clock.c + clock.cpp + clock_support.cpp ctime.c difftime.c localtime_fading_out.c @@ -23,6 +26,7 @@ MergeObject posix_time.o : strptime.c timelocal.cpp time.c + timer_support.cpp wcsftime.c ; diff --git a/src/system/libroot/posix/time/clock.c b/src/system/libroot/posix/time/clock.c deleted file mode 100644 index fc16c9c463..0000000000 --- a/src/system/libroot/posix/time/clock.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include -#include - - -clock_t -clock(void) -{ - thread_info info; - get_thread_info(find_thread(NULL), &info); - - return (clock_t)((info.kernel_time + info.user_time) / 1000); - // unlike the XSI specs say, CLOCKS_PER_SEC is 1000 and not 1000000 in - // BeOS - that means we have to convert the bigtime_t to CLOCKS_PER_SEC - // before we return it -} - diff --git a/src/system/libroot/posix/time/clock.cpp b/src/system/libroot/posix/time/clock.cpp new file mode 100644 index 0000000000..9abfda1a01 --- /dev/null +++ b/src/system/libroot/posix/time/clock.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include + +#include + +#include + + +clock_t +__clock_beos(void) +{ + thread_info info; + get_thread_info(find_thread(NULL), &info); + + return (clock_t)((info.kernel_time + info.user_time) + / MICROSECONDS_PER_CLOCK_TICK_BEOS); +} + + +clock_t +__clock(void) +{ + thread_info info; + get_thread_info(find_thread(NULL), &info); + + return (clock_t)((info.kernel_time + info.user_time) + / MICROSECONDS_PER_CLOCK_TICK); +} + + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__clock_beos", "clock@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__clock", "clock@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/time/clock_support.cpp b/src/system/libroot/posix/time/clock_support.cpp new file mode 100644 index 0000000000..f49082655c --- /dev/null +++ b/src/system/libroot/posix/time/clock_support.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include +#include +#include + +#include + +#include + +#include + + +int +clock_getres(clockid_t clockID, struct timespec* resolution) +{ + // check the clock ID + switch (clockID) { + case CLOCK_MONOTONIC: + case CLOCK_REALTIME: + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: + break; + default: + if (clockID < 0) + RETURN_AND_SET_ERRNO(EINVAL); + + // For clock IDs we can't otherwise verify, try to get the time. + if (clockID != getpid()) { + timespec dummy; + if (clock_gettime(clockID, &dummy) != 0) + return -1; + } + } + + // currently resolution is always 1us + if (resolution != NULL) { + resolution->tv_sec = 0; + resolution->tv_nsec = 1000; + } + + return 0; +} + + +int +clock_gettime(clockid_t clockID, struct timespec* time) +{ + // get the time in microseconds + bigtime_t microSeconds; + + switch (clockID) { + case CLOCK_MONOTONIC: + microSeconds = system_time(); + break; + case CLOCK_REALTIME: + microSeconds = real_time_clock_usecs(); + break; + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: + default: + { + status_t error = _kern_get_clock(clockID, µSeconds); + if (error != B_OK) + RETURN_AND_SET_ERRNO(error); + } + } + + // set the result + time->tv_sec = microSeconds / 1000000; + time->tv_nsec = (microSeconds % 1000000) * 1000; + + return 0; +} + + +int +clock_settime(clockid_t clockID, const struct timespec* time) +{ + // can't set the monotonic clock + if (clockID == CLOCK_MONOTONIC) + RETURN_AND_SET_ERRNO(EINVAL); + + // check timespec validity + if (time->tv_sec < 0 || time->tv_nsec < 0 || time->tv_nsec >= 1000000000) + RETURN_AND_SET_ERRNO(EINVAL); + + // convert to microseconds and set the clock + bigtime_t microSeconds = (bigtime_t)time->tv_sec * 1000000 + + time->tv_nsec / 1000; + + RETURN_AND_SET_ERRNO(_kern_set_clock(clockID, microSeconds)); +} + + +int +clock_nanosleep(clockid_t clockID, int flags, const struct timespec* time, + struct timespec* remainingTime) +{ + // convert time to microseconds (round up) + if (time->tv_sec < 0 || time->tv_nsec < 0 || time->tv_nsec >= 1000000000) + RETURN_AND_TEST_CANCEL(EINVAL); + + bigtime_t microSeconds = (bigtime_t)time->tv_sec * 1000000 + + (time->tv_nsec + 999) / 1000; + + // get timeout flags + uint32 timeoutFlags; + if ((flags & TIMER_ABSTIME) != 0) { + timeoutFlags = B_ABSOLUTE_TIMEOUT; + + // ignore remainingTime for absolute waits + remainingTime = NULL; + } else + timeoutFlags = B_RELATIVE_TIMEOUT; + + // wait + bigtime_t remainingMicroSeconds; + status_t error = _kern_snooze_etc(microSeconds, clockID, timeoutFlags, + remainingTime != NULL ? &remainingMicroSeconds : NULL); + + // If interrupted and this is a relative wait, compute and return the + // remaining wait time. + if (error == B_INTERRUPTED && remainingTime != NULL) { + if (remainingMicroSeconds > 0) { + remainingTime->tv_sec = remainingMicroSeconds / 1000000; + remainingTime->tv_nsec = (remainingMicroSeconds % 1000000) * 1000; + } else { + // We were slow enough that the wait time passed anyway. + error = B_OK; + } + } + + RETURN_AND_TEST_CANCEL(error); +} + + +int +clock_getcpuclockid(pid_t pid, clockid_t* _clockID) +{ + if (pid < 0) + return ESRCH; + + // The CPU clock ID for a process is simply the team ID. For pid == 0 we're + // supposed to return the current process' clock. + if (pid == 0) { + *_clockID = getpid(); + return 0; + } + + // test-get the time to verify the team exists and we have permission + bigtime_t microSeconds; + status_t error = _kern_get_clock(pid, µSeconds); + if (error != B_OK) { + // Since pid is > 0, B_BAD_VALUE always means a team with that ID + // doesn't exist. Translate the error code accordingly. + if (error == B_BAD_VALUE) + return ESRCH; + return error; + } + + *_clockID = pid; + return 0; +} diff --git a/src/system/libroot/posix/time/nanosleep.c b/src/system/libroot/posix/time/nanosleep.c index 2f7a0cb9d2..eb59b0f29a 100644 --- a/src/system/libroot/posix/time/nanosleep.c +++ b/src/system/libroot/posix/time/nanosleep.c @@ -1,35 +1,20 @@ -/* -** Copyright 2007, Jérôme Duval. All rights reserved. -** Distributed under the terms of the Haiku License. -*/ -#include +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + #include -#include + +#include + +#include + int -nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +nanosleep(const struct timespec* time, struct timespec* remainingTime) { - bigtime_t ms, begin_ms; - status_t err; - - if (!rqtp) { - errno = EINVAL; - return -1; - } - - // round up tv_nsec - ms = rqtp->tv_sec * 1000000LL + (rqtp->tv_nsec + 999) / 1000; - begin_ms = system_time(); - err = snooze(ms); - if (err == B_INTERRUPTED) { - errno = EINTR; - if (rmtp) { - bigtime_t remain_ms = ms - system_time() + begin_ms; - rmtp->tv_sec = remain_ms / 1000000; - rmtp->tv_nsec = (remain_ms - rmtp->tv_sec) * 1000; - } - return -1; - } - return 0; + RETURN_AND_SET_ERRNO( + clock_nanosleep(CLOCK_MONOTONIC, 0, time, remainingTime)); } diff --git a/src/system/libroot/posix/time/stime.c b/src/system/libroot/posix/time/stime.c index 1d7105a184..1948bd17c1 100644 --- a/src/system/libroot/posix/time/stime.c +++ b/src/system/libroot/posix/time/stime.c @@ -1,4 +1,4 @@ -/* +/* ** Copyright 2004, Jérôme Duval. All rights reserved. ** Distributed under the terms of the Haiku License. */ @@ -7,7 +7,6 @@ #include #include "syscalls.h" -// ToDo: replace zero by a ROOT_UID when usergroup.c is implemented int stime(const time_t *tp) @@ -18,11 +17,8 @@ stime(const time_t *tp) errno = EINVAL; return -1; } - if (geteuid() != 0) { - errno = EPERM; - return -1; - } - status = _kern_set_real_time_clock(*tp); + + status = _kern_set_real_time_clock((bigtime_t)*tp * 1000000); if (status < B_OK) { errno = status; return -1; diff --git a/src/system/libroot/posix/time/timer_support.cpp b/src/system/libroot/posix/time/timer_support.cpp new file mode 100644 index 0000000000..1c2f8222d0 --- /dev/null +++ b/src/system/libroot/posix/time/timer_support.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +static void +info_to_itimerspec(const user_timer_info& info, itimerspec& spec) +{ + bigtime_to_timespec(info.interval, spec.it_interval); + + // A remaining_time of B_INFINITE_TIMEOUT means the timer isn't scheduled. + if (info.remaining_time != B_INFINITE_TIMEOUT) { + bigtime_to_timespec(info.remaining_time, spec.it_value); + } else { + spec.it_value.tv_sec = 0; + spec.it_value.tv_nsec = 0; + } +} + + +static bool +itimerspec_to_bigtimes(const itimerspec& spec, bigtime_t& _nextTime, + bigtime_t& _interval) +{ + if (!timespec_to_bigtime(spec.it_interval, _interval) + || !timespec_to_bigtime(spec.it_value, _nextTime)) { + return false; + } + + if (_nextTime == 0) + _nextTime = B_INFINITE_TIMEOUT; + + return true; +} + + +static status_t +timer_thread_entry(void* _entry, void* _value) +{ + // init a pthread + pthread_thread thread; + __init_pthread(&thread, NULL, NULL); + thread.flags |= THREAD_DETACHED; + + get_user_thread()->pthread = &thread; + + // we have been started with deferred signals -- undefer them + undefer_signals(); + + // prepare the arguments + union sigval value; + value.sival_ptr = _value; + void (*entry)(union sigval) = (void (*)(union sigval))_entry; + + // call the entry function + entry(value); + + return B_OK; +} + + +// #pragma mark - + + +int +timer_create(clockid_t clockID, struct sigevent* event, timer_t* _timer) +{ + // create a timer object + __timer_t* timer = new(std::nothrow) __timer_t; + if (timer == NULL) + RETURN_AND_SET_ERRNO(ENOMEM); + ObjectDeleter<__timer_t> timerDeleter(timer); + + // If the notification method is SIGEV_THREAD, initialize thread creation + // attributes. + bool isThreadEvent = event != NULL && event->sigev_notify == SIGEV_THREAD; + thread_creation_attributes threadAttributes; + if (isThreadEvent) { + status_t error = __pthread_init_creation_attributes( + event->sigev_notify_attributes, NULL, &timer_thread_entry, + (void*)event->sigev_notify_function, event->sigev_value.sival_ptr, + "timer notify", &threadAttributes); + if (error != B_OK) + RETURN_AND_SET_ERRNO(error); + + threadAttributes.flags |= THREAD_CREATION_FLAG_DEFER_SIGNALS; + } + + // create the timer + int32 timerID = _kern_create_timer(clockID, -1, 0, event, + isThreadEvent ? &threadAttributes : NULL); + if (timerID < 0) + RETURN_AND_SET_ERRNO(timerID); + + // init the object members + timer->SetTo(timerID, -1); + + *_timer = timerDeleter.Detach(); + return 0; +} + + +int +timer_delete(timer_t timer) +{ + status_t error = _kern_delete_timer(timer->id, timer->thread); + if (error != B_OK) + RETURN_AND_SET_ERRNO(error); + + delete timer; + return 0; +} + + +int +timer_gettime(timer_t timer, struct itimerspec* value) +{ + user_timer_info info; + status_t error = _kern_get_timer(timer->id, timer->thread, &info); + if (error != B_OK) + RETURN_AND_SET_ERRNO(error); + + info_to_itimerspec(info, *value); + + return 0; +} + + +int +timer_settime(timer_t timer, int flags, const struct itimerspec* value, + struct itimerspec* oldValue) +{ + // translate new value + bigtime_t nextTime; + bigtime_t interval; + if (!itimerspec_to_bigtimes(*value, nextTime, interval)) + RETURN_AND_SET_ERRNO(EINVAL); + + uint32 timeoutFlags = (flags & TIMER_ABSTIME) != 0 + ? B_ABSOLUTE_TIMEOUT : B_RELATIVE_TIMEOUT; + + // set the timer + user_timer_info oldInfo; + status_t error = _kern_set_timer(timer->id, timer->thread, nextTime, + interval, timeoutFlags, oldValue != NULL ? &oldInfo : NULL); + if (error != B_OK) + RETURN_AND_SET_ERRNO(error); + + // translate old info back + if (oldValue != NULL) + info_to_itimerspec(oldInfo, *oldValue); + + return 0; +} + + +int +timer_getoverrun(timer_t timer) +{ + user_timer_info info; + status_t error = _kern_get_timer(timer->id, timer->thread, &info); + if (error != B_OK) + RETURN_AND_SET_ERRNO(error); + + return info.overrun_count; +} diff --git a/src/system/libroot/posix/unistd/Jamfile b/src/system/libroot/posix/unistd/Jamfile index 05429f9fda..91e85ee168 100644 --- a/src/system/libroot/posix/unistd/Jamfile +++ b/src/system/libroot/posix/unistd/Jamfile @@ -9,7 +9,7 @@ MergeObject posix_unistd.o : chown.c chroot.cpp close.c - conf.c + conf.cpp directory.c dup.c exec.cpp diff --git a/src/system/libroot/posix/unistd/close.c b/src/system/libroot/posix/unistd/close.c index 2cee9a8b0a..c44573deed 100644 --- a/src/system/libroot/posix/unistd/close.c +++ b/src/system/libroot/posix/unistd/close.c @@ -1,22 +1,21 @@ -/* +/* ** Copyright 2001, Manuel J. Petit. All rights reserved. ** Distributed under the terms of the NewOS License. */ #include -#include + #include +#include + +#include + +#include int close(int fd) { - int retval = _kern_close(fd); - if (retval < 0) { - errno = retval; - retval = -1; - } - - return retval; + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_close(fd)); } diff --git a/src/system/libroot/posix/unistd/conf.c b/src/system/libroot/posix/unistd/conf.cpp similarity index 87% rename from src/system/libroot/posix/unistd/conf.c rename to src/system/libroot/posix/unistd/conf.cpp index 70e7de934e..c54ecf1baa 100644 --- a/src/system/libroot/posix/unistd/conf.c +++ b/src/system/libroot/posix/unistd/conf.cpp @@ -16,12 +16,18 @@ #include #include -#include #include +#include +#include +#include #include #include +#include + +#include +#include +#include -bool _kern_cpu_enabled(int cpu); int getdtablesize(void) @@ -35,10 +41,23 @@ getdtablesize(void) long -sysconf(int name) +__sysconf_beos(int name) +{ + switch (name) { + case _SC_CLK_TCK: + return CLK_TCK_BEOS; + } + + return __sysconf(name); +} + + +long +__sysconf(int name) { int err; - // TODO: This is about what BeOS does, better POSIX conformance would be nice, though + // TODO: This is about what BeOS does, better POSIX conformance would be + // nice, though switch (name) { case _SC_ARG_MAX: @@ -147,6 +166,22 @@ sysconf(int name) return _POSIX_REALTIME_SIGNALS; case _SC_MEMORY_PROTECTION: return _POSIX_MEMORY_PROTECTION; + case _SC_SIGQUEUE_MAX: + return MAX_QUEUED_SIGNALS; + case _SC_RTSIG_MAX: + return SIGRTMAX - SIGRTMIN + 1; + case _SC_MONOTONIC_CLOCK: + return _POSIX_MONOTONIC_CLOCK; + case _SC_DELAYTIMER_MAX: + return MAX_USER_TIMER_OVERRUN_COUNT; + case _SC_TIMER_MAX: + return MAX_USER_TIMERS_PER_TEAM; + case _SC_TIMERS: + return _POSIX_TIMERS; + case _SC_CPUTIME: + return _POSIX_CPUTIME; + case _SC_THREAD_CPUTIME: + return _POSIX_THREAD_CPUTIME; // not POSIX (anymore) case _SC_PIPE: @@ -367,3 +402,7 @@ confstr(int name, char *buffer, size_t length) return stringLength; } + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sysconf_beos", "sysconf@", "BASE"); + +DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__sysconf", "sysconf@@", "1_ALPHA4"); diff --git a/src/system/libroot/posix/unistd/pause.c b/src/system/libroot/posix/unistd/pause.c index 7a716b2e43..8ac7e69a0b 100644 --- a/src/system/libroot/posix/unistd/pause.c +++ b/src/system/libroot/posix/unistd/pause.c @@ -10,6 +10,7 @@ #include #include +#include #include @@ -20,6 +21,9 @@ pause(void) sigemptyset(&mask); errno = _kern_sigsuspend(&mask); + + pthread_testcancel(); + return -1; } diff --git a/src/system/libroot/posix/unistd/read.c b/src/system/libroot/posix/unistd/read.c index 35151ec368..ca43642b35 100644 --- a/src/system/libroot/posix/unistd/read.c +++ b/src/system/libroot/posix/unistd/read.c @@ -1,25 +1,22 @@ -/* +/* * Copyright 2001, Manuel J. Petit. All rights reserved. * Distributed under the terms of the NewOS License. */ -/* +/* * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ #include -#include + #include +#include +#include -#define RETURN_AND_SET_ERRNO(err) \ - if (err < 0) { \ - errno = err; \ - return -1; \ - } \ - return err; +#include ssize_t @@ -27,33 +24,25 @@ read(int fd, void* buffer, size_t bufferSize) { ssize_t status = _kern_read(fd, -1, buffer, bufferSize); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(status); } ssize_t read_pos(int fd, off_t pos, void* buffer, size_t bufferSize) { - ssize_t status; - if (pos < 0) { - errno = B_BAD_VALUE; - return -1; - } - status = _kern_read(fd, pos, buffer, bufferSize); + if (pos < 0) + RETURN_AND_SET_ERRNO_TEST_CANCEL(B_BAD_VALUE); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_read(fd, pos, buffer, bufferSize)); } ssize_t pread(int fd, void* buffer, size_t bufferSize, off_t pos) { - ssize_t status; - if (pos < 0) { - errno = B_BAD_VALUE; - return -1; - } - status = _kern_read(fd, pos, buffer, bufferSize); + if (pos < 0) + RETURN_AND_SET_ERRNO_TEST_CANCEL(B_BAD_VALUE); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_read(fd, pos, buffer, bufferSize)); } diff --git a/src/system/libroot/posix/unistd/sleep.c b/src/system/libroot/posix/unistd/sleep.c index 621b9b0d7c..ec5061d775 100644 --- a/src/system/libroot/posix/unistd/sleep.c +++ b/src/system/libroot/posix/unistd/sleep.c @@ -1,9 +1,12 @@ -/* +/* ** Copyright 2001, Manuel J. Petit. All rights reserved. ** Distributed under the terms of the NewOS License. */ #include + +#include + #include @@ -20,6 +23,9 @@ sleep(unsigned seconds) usecs *= (bigtime_t) seconds; err = snooze_until(start + usecs, B_SYSTEM_TIMEBASE); + + pthread_testcancel(); + if (err) return seconds - (unsigned)((system_time() - start) / 1000000); diff --git a/src/system/libroot/posix/unistd/sync.c b/src/system/libroot/posix/unistd/sync.c index 955c1632b2..033622d2db 100644 --- a/src/system/libroot/posix/unistd/sync.c +++ b/src/system/libroot/posix/unistd/sync.c @@ -1,24 +1,23 @@ -/* +/* ** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. ** Distributed under the terms of the Haiku License. */ #include -#include + #include +#include + +#include + +#include int fsync(int fd) { - int status = _kern_fsync(fd); - if (status < 0) { - errno = status; - status = -1; - } - - return status; + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_fsync(fd)); } diff --git a/src/system/libroot/posix/unistd/system.cpp b/src/system/libroot/posix/unistd/system.cpp index c3285ad87e..8168a76cdf 100644 --- a/src/system/libroot/posix/unistd/system.cpp +++ b/src/system/libroot/posix/unistd/system.cpp @@ -1,15 +1,19 @@ /* - * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2004-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include #include +#include #include #include #include +#include + + extern "C" int system(const char *command) @@ -21,10 +25,8 @@ system(const char *command) int argc = 3; thread_id thread = load_image(argc, argv, (const char **)environ); - if (thread < 0) { - errno = thread; - return -1; - } + if (thread < 0) + RETURN_AND_SET_ERRNO_TEST_CANCEL(thread); resume_thread(thread); diff --git a/src/system/libroot/posix/unistd/write.c b/src/system/libroot/posix/unistd/write.c index 41a76dcae6..27febf3f51 100644 --- a/src/system/libroot/posix/unistd/write.c +++ b/src/system/libroot/posix/unistd/write.c @@ -1,23 +1,20 @@ -/* +/* * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. - * + * * Copyright 2001, Manuel J. Petit. All rights reserved. * Distributed under the terms of the NewOS License. */ #include -#include + #include +#include +#include -#define RETURN_AND_SET_ERRNO(err) \ - if (err < 0) { \ - errno = err; \ - return -1; \ - } \ - return err; +#include ssize_t @@ -25,33 +22,25 @@ write(int fd, void const *buffer, size_t bufferSize) { int status = _kern_write(fd, -1, buffer, bufferSize); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(status); } ssize_t write_pos(int fd, off_t pos, const void *buffer, size_t bufferSize) { - ssize_t status; - if (pos < 0) { - errno = B_BAD_VALUE; - return -1; - } - status = _kern_write(fd, pos, buffer, bufferSize); + if (pos < 0) + RETURN_AND_SET_ERRNO_TEST_CANCEL(B_BAD_VALUE); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_write(fd, pos, buffer, bufferSize)); } ssize_t pwrite(int fd, const void *buffer, size_t bufferSize, off_t pos) { - ssize_t status; - if (pos < 0) { - errno = B_BAD_VALUE; - return -1; - } - status = _kern_write(fd, pos, buffer, bufferSize); + if (pos < 0) + RETURN_AND_SET_ERRNO_TEST_CANCEL(B_BAD_VALUE); - RETURN_AND_SET_ERRNO(status); + RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_write(fd, pos, buffer, bufferSize)); } diff --git a/src/tests/system/kernel/unit/kernel_unit_tests.cpp b/src/tests/system/kernel/unit/kernel_unit_tests.cpp index e056c3b39a..c4f30a3aba 100644 --- a/src/tests/system/kernel/unit/kernel_unit_tests.cpp +++ b/src/tests/system/kernel/unit/kernel_unit_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -185,7 +185,7 @@ device_write(void* cookie, off_t position, const void* data, size_t* numBytes) return B_NO_MEMORY; MemoryDeleter bufferDeleter(buffer); - struct thread* thread = thread_get_current_thread(); + Thread* thread = thread_get_current_thread(); if ((thread->flags & THREAD_FLAGS_SYSCALL) != 0) { if (!IS_USER_ADDRESS(data) || user_memcpy(buffer, data, *numBytes) != B_OK) { diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/5-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/5-1.c index 2ad98bedf7..de87a41de8 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/5-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/5-1.c @@ -1,23 +1,71 @@ -/* +/* * Copyright (c) 2002, Intel Corporation. All rights reserved. * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this + * of this license, see the COPYING file at the top level of this * source tree. * Test that clock_getres() returns -1 on failure. */ #include +#include +#include #include +#include +#include #include "posixtest.h" -#define INVALIDCLOCKID 99999 +static clockid_t get_invalid_clock_id() +{ +#if _POSIX_CPUTIME == -1 + printf("clock_getcpuclockid() not supported\n"); + exit(PTS_UNTESTED); +#else + clockid_t clockID; + int error; + + if (sysconf(_SC_CPUTIME) == -1) { + printf("clock_getcpuclockid() not supported\n"); + exit(PTS_UNTESTED); + } + + int pid = fork(); + if (pid < 0) { + perror("fork() failed"); + exit(PTS_UNRESOLVED); + } + + if (pid == 0) { + // child -- just wait a second + sleep(1); + exit(0); + } + + // parent -- get the child's CPU clock ID + error = clock_getcpuclockid(pid, &clockID); + if (error != 0) { + printf("clock_getcpuclockid() failed: %s\n", strerror(error)); + exit(PTS_UNRESOLVED); + } + + // wait for the child + if (wait(NULL) != pid) { + perror("wait() failed"); + exit(PTS_UNRESOLVED); + } + + return clockID; +#endif +} int main(int argc, char *argv[]) { struct timespec res; + clockid_t invalidClockID; - if (clock_getres(INVALIDCLOCKID, &res) == -1) { + invalidClockID = get_invalid_clock_id(); + + if (clock_getres(invalidClockID, &res) == -1) { printf("Test PASSED\n"); return PTS_PASS; } else { diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/6-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/6-1.c index e89696f9cd..8ba804a27f 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/6-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/6-1.c @@ -1,25 +1,73 @@ -/* +/* * Copyright (c) 2002, Intel Corporation. All rights reserved. * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this + * of this license, see the COPYING file at the top level of this * source tree. - * Test that clock_getres() sets errno=EINVAL if clock_id does not + * Test that clock_getres() sets errno=EINVAL if clock_id does not * refer to a known clock. */ -#include -#include #include +#include +#include +#include +#include +#include +#include #include "posixtest.h" -#define INVALIDCLOCKID 99999 +static clockid_t get_invalid_clock_id() +{ +#if _POSIX_CPUTIME == -1 + printf("clock_getcpuclockid() not supported\n"); + exit(PTS_UNTESTED); +#else + clockid_t clockID; + int error; + + if (sysconf(_SC_CPUTIME) == -1) { + printf("clock_getcpuclockid() not supported\n"); + exit(PTS_UNTESTED); + } + + int pid = fork(); + if (pid < 0) { + perror("fork() failed"); + exit(PTS_UNRESOLVED); + } + + if (pid == 0) { + // child -- just wait a second + sleep(1); + exit(0); + } + + // parent -- get the child's CPU clock ID + error = clock_getcpuclockid(pid, &clockID); + if (error != 0) { + printf("clock_getcpuclockid() failed: %s\n", strerror(error)); + exit(PTS_UNRESOLVED); + } + + // wait for the child + if (wait(NULL) != pid) { + perror("wait() failed"); + exit(PTS_UNRESOLVED); + } + + return clockID; +#endif +} int main(int argc, char *argv[]) { struct timespec res; + clockid_t invalidClockID; - if (clock_getres(INVALIDCLOCKID, &res) == -1) { + invalidClockID = get_invalid_clock_id(); + + if (clock_getres(invalidClockID, &res) == -1) { if (EINVAL == errno) { printf("Test PASSED\n"); return PTS_PASS; diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/6-2.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/6-2.c deleted file mode 100644 index 6a2b1300f8..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_getres/6-2.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - - * Test that clock_getres() sets errno=EINVAL for a variety of - * invalid clock_ids. - * - * The following invalid clock_ids will be used: - * - Boundary values for integers (TBD if clock_id is an integer) - * MIN INT = INT32_MIN - * MAX INT = INT32_MAX - * MIN INT - 1 = 2147483647 (this is what gcc will set to) - * MAX INT + 1 = -2147483647 (this is what gcc will set to) - * unassigned value = -1073743192 (ex. of what gcc will set to) - * unassigned value = 1073743192 (ex. of what gcc will set to) - * -1 - * 17 (currently not = to any clock) - * - */ -#include -#include -#include -#include -#include "posixtest.h" - -#define NUMINVALIDTESTS 8 - -int main(int argc, char *argv[]) -{ - struct timespec res; - int invalid_tests[NUMINVALIDTESTS] = { - INT32_MIN, INT32_MAX, 2147483647, -2147483647, -1073743192, - 1073743192, -1, 17}; - int i; - int failure = 0; - - for (i = 0; i < NUMINVALIDTESTS; i++) { - printf("clock_getres(%d, &res);\n", invalid_tests[i]); - if (clock_getres(invalid_tests[i], &res) == -1) { - if (EINVAL != errno) { - printf("errno != EINVAL\n"); - failure = 1; - } - } else { - printf("clock_getres() != -1\n"); - failure = 1; - } - } - - if (failure) { - printf("At least one test FAILED -- see above\n"); - return PTS_FAIL; - } else { - printf("All tests PASSED\n"); - return PTS_PASS; - } - - printf("This code should not be executed.\n"); - return PTS_UNRESOLVED; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_gettime/8-2.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_gettime/8-2.c deleted file mode 100644 index 76c487b5bf..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_gettime/8-2.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - - * Error condition API test of the clock_gettime() function. - * Test calling clock_gettime() with the following values: - * - Boundary values for integers (TBD if clock_id is an integer) - * MIN INT = INT32_MIN - * MAX INT = INT32_MAX - * MIN INT - 1 = 2147483647 (this is what gcc will set to) - * MAX INT + 1 = -2147483647 (this is what gcc will set to) - * unassigned value = -1073743192 (ex. of what gcc will set to) - * unassigned value = 1073743192 (ex. of what gcc will set to) - * -1 - * 17 (currently not = to any clock) - */ -#include -#include -#include -#include -#include "posixtest.h" - -#define NUMINVALIDTESTS 8 - -static int invalid_tests[NUMINVALIDTESTS] = { - INT32_MIN, INT32_MAX, 2147483647, -2147483647, -1073743192, - 1073743192, -1, 17 -}; - -int main(int argc, char *argv[]) -{ - struct timespec tp; - int i; - int failure = 0; - - for (i = 0; i < NUMINVALIDTESTS; i++) { - if (clock_gettime(invalid_tests[i], &tp) == 0) { - printf("failure: clock_gettime() using %d succeeded\n", - invalid_tests[i]); - failure = 1; - } else { - if (EINVAL != errno) { - printf("errno not correctly set\n"); - failure = 1; - } - } - } - - if (failure) { - printf("At least one test FAILED -- see above\n"); - return PTS_FAIL; - } else { - printf("Test PASSED\n"); - return PTS_PASS; - } - - printf("This code should not be executed.\n"); - return PTS_UNRESOLVED; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_gettime/coverage.txt b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_gettime/coverage.txt index 0eace07a11..f77f36927f 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_gettime/coverage.txt +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_gettime/coverage.txt @@ -8,7 +8,7 @@ Assertion Complete? 5 NO - process clocks not part of implementation under test 6 YES 7 YES -8 YES +8 NO - impossible to reliably guess invalid clock IDs *Note: To run against current implementation, I have to add in a line: #include "/usr/src/linux/Documentation/high-res-timers/lib/posix_time.h" diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/17-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/17-1.c deleted file mode 100644 index 6181479a27..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/17-1.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test that clock_settime() sets errno to EINVAL if clock_id does not - * specify a known clock. - * - * The date chosen is Nov 12, 2002 ~11:13am (date when test was first - * written). - */ -#include -#include -#include -#include "posixtest.h" - -#define TESTTIME 1037128358 - -#define BOGUSCLOCKID 9999 - -int main(int argc, char *argv[]) -{ - struct timespec tpset; - - tpset.tv_sec = TESTTIME; - tpset.tv_nsec = 0; - if (clock_settime(BOGUSCLOCKID, &tpset) == -1) { - if (EINVAL == errno) { - printf("Test PASSED\n"); - return PTS_PASS; - } else { - printf("errno != EINVAL\n"); - return PTS_FAIL; - } - } else { - printf("clock_settime() did not return -1\n"); - return PTS_UNRESOLVED; - } - - printf("This test should not be executed.\n"); - return PTS_UNRESOLVED; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/17-2.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/17-2.c index 6cd108c0ee..a22f50163a 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/17-2.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/17-2.c @@ -8,63 +8,84 @@ * Error condition API test for the clock_id parameter of the * clock_settime() function. * - * Test calling clock_settime() with the following clock_id values: - * MIN INT = INT32_MIN - * MAX INT = INT32_MAX - * MIN INT - 1 = 2147483647 (this is what gcc will set to) - * MAX INT + 1 = -2147483647 (this is what gcc will set to) - * unassigned value = -1073743192 (ex. of what gcc will set to) - * unassigned value = 1073743192 (ex. of what gcc will set to) - * -1 - * 17 (currently not = to any clock) - * * The date chosen is Nov 12, 2002 ~11:13am (date when test was first * written). */ -#include -#include #include #include +#include +#include +#include +#include +#include +#include #include "posixtest.h" #define TESTTIME 1037128358 -#define NUMINVALIDTESTS 8 +static clockid_t get_invalid_clock_id() +{ +#if _POSIX_CPUTIME == -1 + printf("clock_getcpuclockid() not supported\n"); + exit(PTS_UNTESTED); +#else + clockid_t clockID; + int error; -static int invalid_tests[NUMINVALIDTESTS] = { - INT32_MIN, INT32_MAX, 2147483647, -2147483647, -1073743192, - 1073743192, -1, 17 -}; + if (sysconf(_SC_CPUTIME) == -1) { + printf("clock_getcpuclockid() not supported\n"); + exit(PTS_UNTESTED); + } + + int pid = fork(); + if (pid < 0) { + perror("fork() failed"); + exit(PTS_UNRESOLVED); + } + + if (pid == 0) { + // child -- just wait a second + sleep(1); + exit(0); + } + + // parent -- get the child's CPU clock ID + error = clock_getcpuclockid(pid, &clockID); + if (error != 0) { + printf("clock_getcpuclockid() failed: %s\n", strerror(error)); + exit(PTS_UNRESOLVED); + } + + // wait for the child + if (wait(NULL) != pid) { + perror("wait() failed"); + exit(PTS_UNRESOLVED); + } + + return clockID; +#endif +} int main(int argc, char *argv[]) { struct timespec tpset; - int i; - int failure = 0; + clockid_t invalidClockID; + + invalidClockID = get_invalid_clock_id(); tpset.tv_sec = TESTTIME; tpset.tv_nsec = 0; - for (i = 0; i < NUMINVALIDTESTS; i++) { - if (clock_settime(invalid_tests[i], &tpset) == -1) { - if (EINVAL != errno) { - printf("errno != EINVAL\n"); - failure = 1; - } - } else { - printf("clock_settime() did not return -1\n"); - failure = 1; + if (clock_settime(invalidClockID, &tpset) == -1) { + if (EINVAL != errno) { + printf("errno != EINVAL\n"); + return PTS_FAIL; } - } - - if (failure) { - printf("At least one test FAILED -- see above\n"); - return PTS_FAIL; } else { - printf("All tests PASSED\n"); - return PTS_PASS; + printf("clock_settime() did not return -1\n"); + return PTS_FAIL; } - printf("This code should not be executed.\n"); - return PTS_UNRESOLVED; + printf("All tests PASSED\n"); + return PTS_PASS; } diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/coverage.txt b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/coverage.txt index 99450429f9..58440536a5 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/coverage.txt +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/clock_settime/coverage.txt @@ -17,7 +17,7 @@ Assertion Covered? 14 NO** Skipping CPU-time tests for now 15 YES 16 YES -17 YES +17 NO - impossible to guess an invalid clock ID 18 YES 19 YES - FAILing on this implementation (possibly serious) 20 YES diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/pthread_once/6-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/pthread_once/6-1.c index 11585d6c93..e88c73e361 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/pthread_once/6-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/pthread_once/6-1.c @@ -181,7 +181,6 @@ void initializer( void ) void * test( void * arg ) { int ret = 0; - pthread_once_t once_ctl = PTHREAD_ONCE_INIT; /* We don't block the signals SIGUSR1 and SIGUSR2 for this THREAD */ ret = pthread_sigmask( SIG_UNBLOCK, &usersigs, NULL ); @@ -194,6 +193,8 @@ void * test( void * arg ) while ( do_it ) { + pthread_once_t once_ctl = PTHREAD_ONCE_INIT; + count_ope++; init_ctl = 0; diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/12-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/12-1.c index 103942df76..9432cc9688 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/12-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/12-1.c @@ -32,7 +32,7 @@ int set_nonroot() setpwent(); /* search for the first user which is non root */ while((pw = getpwent()) != NULL) - if(strcmp(pw->pw_name, "root")) + if(pw->pw_uid != 0) break; endpwent(); if(pw == NULL) { diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/3-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/3-1.c index 3e7fece05c..7baddd1fc2 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/3-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigqueue/3-1.c @@ -35,7 +35,7 @@ int set_nonroot() setpwent(); /* search for the first user which is non root */ while((pw = getpwent()) != NULL) - if(strcmp(pw->pw_name, "root")) + if(pw->pw_uid != 0) break; endpwent(); if(pw == NULL) { diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/6-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/6-1.c index c9d0b947e3..17ced2c2c7 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/6-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/6-1.c @@ -42,7 +42,7 @@ int main() return PTS_UNRESOLVED; } - if (sigset(SIGCHLD,SIG_HOLD) != SIG_HOLD) { + if (sigset(SIGCHLD,SIG_HOLD) != myhandler) { perror("Unexpected error while using sigset()"); return PTS_UNRESOLVED; } diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/7-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/7-1.c index 67ec3cee52..f30288e844 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/7-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/7-1.c @@ -46,7 +46,7 @@ int main() return PTS_UNRESOLVED; } - if (sigset(SIGCHLD,SIG_HOLD) != SIG_HOLD) { + if (sigset(SIGCHLD,SIG_HOLD) != myhandler) { perror("Unexpected error while using sigset()"); return PTS_UNRESOLVED; } diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/8-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/8-1.c index f344b196b7..4b1800882e 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/8-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigset/8-1.c @@ -5,20 +5,39 @@ * of this license, see the COPYING file at the top level of this * source tree. - This program tests the assertion that if signal has been blocked, then - sigset shall return SIG_HOLD + This program tests the assertion that if signal had been blocked, then + sigset shall return SIG_HOLD. Steps: + 1. block SIGCHLD via sigprocmask() + 2. use sigset() */ #define _XOPEN_SOURCE 600 +#include #include #include #include +#include #include "posixtest.h" int main() { + sigset_t signalMask; + if (sigemptyset(&signalMask) != 0) { + printf("sigemtpyset() failed unexpectedly: %s\n", strerror(errno)); + return PTS_UNRESOLVED; + } + + if (sigaddset(&signalMask, SIGCHLD) != 0) { + printf("sigaddset() failed unexpectedly: %s\n", strerror(errno)); + return PTS_UNRESOLVED; + } + + if (sigprocmask(SIG_BLOCK, &signalMask, NULL) != 0) { + printf("sigprocmask() failed unexpectedly: %s\n", strerror(errno)); + return PTS_UNRESOLVED; + } if (sigset(SIGCHLD,SIG_HOLD) != SIG_HOLD) { printf("Test FAILED: sigset() didn't return SIG_HOLD\n"); diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/3-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/3-1.c deleted file mode 100644 index 8a69b6fe9a..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/3-1.c +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include -#include "posixtest.h" - -/* - - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: rolla.n.selbak REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - - * Test that the sigwait() function. - * If prior to the call to sigwait() there are multiple pending instances of - * a single signal number (and it is implementation-defined that the signal - * number DOES NOT support queued signals), then there should be no remaining - * pending signals for that signal number. - * Steps are: - * 1) Block a signal that doesn't support queueing from delivery. - * 2) Raise that signal 4 times. - * 3) Call sigwait() - * 4) Verify it cleared the signal from the pending signals and there - * are no signals left in the pending list. - * - */ - - -int main() -{ - sigset_t newmask, pendingset; - int sig; - - /* Empty set of blocked signals */ - if ( (sigemptyset(&newmask) == -1) || - (sigemptyset(&pendingset) == -1) ) - { - printf("Error in sigemptyset()\n"); - return PTS_UNRESOLVED; - } - - /* Add SIGALRM to the set of blocked signals */ - if (sigaddset(&newmask, SIGALRM) == -1) - { - perror("Error in sigaddset()\n"); - return PTS_UNRESOLVED; - } - - /* Block SIGALRM */ - if (sigprocmask(SIG_SETMASK, &newmask, NULL) == -1) - { - printf("Error in sigprocmask()\n"); - return PTS_UNRESOLVED; - } - - /* Send SIGALRM signal 4 times to this process. Since it is blocked, - * it should be pending. */ - if (raise(SIGALRM) != 0) { - printf("Could not raise SIGALRM\n"); - return PTS_UNRESOLVED; - } - if (raise(SIGALRM) != 0) { - printf("Could not raise SIGALRM\n"); - return PTS_UNRESOLVED; - } - if (raise(SIGALRM) != 0) { - printf("Could not raise SIGALRM\n"); - return PTS_UNRESOLVED; - } - if (raise(SIGALRM) != 0) { - printf("Could not raise SIGALRM\n"); - return PTS_UNRESOLVED; - } - - /* Obtain a set of pending signals */ - if (sigpending(&pendingset) == -1) { - printf("Error calling sigpending()\n"); - return PTS_UNRESOLVED; - } - - /* Make sure SIGALRM is pending */ - if (sigismember(&pendingset, SIGALRM) == 0) { - printf("Error: signal SIGALRM not pending\n"); - return PTS_UNRESOLVED; - } - - /* Call sigwait */ - if (sigwait(&newmask, &sig) != 0) - { - printf("Error in sigwait\n"); - return PTS_UNRESOLVED; - } - - /* Make sure SIGALRM is not in the pending list anymore */ - if (sigpending(&pendingset) == -1) { - printf("Error calling sigpending()\n"); - return PTS_UNRESOLVED; - } - - if (sigismember(&pendingset, SIGALRM) == 1) - { - printf("Test FAILED\n"); - return PTS_FAIL; - } - - printf("Test PASSED\n"); - return PTS_PASS; - -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/assertions.xml b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/assertions.xml index 82db68647d..f289c1e22c 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/assertions.xml +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/assertions.xml @@ -9,12 +9,6 @@ If prior to the call to sigwait() there are multiple pending instances of a single signal number (and it is implementation-defined that the signal number supports queued signals), then the first such queued signal shall cause a return from sigwait() and the remainder shall remain queued. - - -If prior to the call to sigwait() there are multiple pending instances of a -single signal number (and it is implementation-defined that the signal number -DOES NOT support queued signals), then there should be no remaining pending -signals for that signal number. If no signal in 'set' is pending at the time of the call, the thread shall diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/coverage.txt b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/coverage.txt index 07b818732e..600c636c48 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/coverage.txt +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/sigwait/coverage.txt @@ -3,7 +3,6 @@ This document defines the coverage for function sigwait(). Assertion Covered? 1 YES 2 YES -3 YES 4 YES 5 NOT DONE YET 6 NOT DONE YET diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/10-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/10-1.c index cbdc7600ad..b6d8401bd4 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/10-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/10-1.c @@ -1,7 +1,7 @@ -/* +/* * Copyright (c) 2002, Intel Corporation. All rights reserved. * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this + * of this license, see the COPYING file at the top level of this * source tree. * adam li * @@ -23,10 +23,72 @@ #define TIMERSEC 2 #define SLEEPDELTA 3 #define ACCEPTABLEDELTA 1 +#define CLOCK_TO_TEST CLOCK_PROCESS_CPUTIME_ID + +static int handlerInvoked = 0; + +static void sub_timespec(const struct timespec *a, const struct timespec *b, + struct timespec *result) +{ + result->tv_sec = a->tv_sec - b->tv_sec; + result->tv_nsec = a->tv_nsec - b->tv_nsec; + if (result->tv_nsec < 0 && result->tv_sec > 0) { + result->tv_sec--; + result->tv_nsec += 1000000000; + } else if (result->tv_nsec >= 1000000000) { + result->tv_sec++; + result->tv_nsec -= 1000000000; + } +} + +static int cmp_timespec(const struct timespec *a, const struct timespec *b) +{ + if (a->tv_sec < b->tv_sec) + return -1; + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_nsec < b->tv_nsec) + return -1; + if (a->tv_nsec > b->tv_nsec) + return 1; + + return 0; +} + +static int spin(const struct timespec *time, struct timespec *timeLeft) +{ + struct timespec startTime; + + if (clock_gettime(CLOCK_TO_TEST, &startTime) != 0) { + perror("clock_gettime() failed"); + exit(PTS_UNRESOLVED); + } + + for (;;) { + struct timespec tempTime; + if (clock_gettime(CLOCK_TO_TEST, &tempTime) != 0) { + perror("clock_gettime() failed"); + exit(PTS_UNRESOLVED); + } + + + sub_timespec(&tempTime, &startTime, &tempTime); + if (cmp_timespec(time, &tempTime) <= 0) + return 0; + + if (handlerInvoked) { + if (timeLeft) + sub_timespec(time, &tempTime, timeLeft); + return -1; + } + } +} void handler(int signo) { printf("Caught signal\n"); + handlerInvoked = 1; } int main(int argc, char *argv[]) @@ -45,9 +107,9 @@ int main(int argc, char *argv[]) rc = sysconf(_SC_CPUTIME); printf("sysconf(_SC_CPUTIME) returns: %d\n", rc); if (rc <= 0) { - return PTS_UNRESOLVED; + return PTS_UNRESOLVED; } - + ev.sigev_notify = SIGEV_SIGNAL; ev.sigev_signo = SIGTOTEST; @@ -71,7 +133,7 @@ int main(int argc, char *argv[]) return PTS_UNRESOLVED; } - if (timer_create(CLOCK_PROCESS_CPUTIME_ID, &ev, &tid) != 0) { + if (timer_create(CLOCK_TO_TEST, &ev, &tid) != 0) { perror("timer_create() did not return success"); return PTS_UNRESOLVED; } @@ -81,8 +143,8 @@ int main(int argc, char *argv[]) return PTS_UNRESOLVED; } - if (nanosleep(&ts, &tsleft) != -1) { - perror("nanosleep() not interrupted"); + if (spin(&ts, &tsleft) != -1) { + perror("spin() not interrupted"); return PTS_FAIL; } @@ -91,7 +153,7 @@ int main(int argc, char *argv[]) return PTS_PASS; } else { printf("Timer did not last for correct amount of time\n"); - printf("timer: %d != correct %d\n", + printf("timer: %d != correct %d\n", (int) ts.tv_sec- (int) tsleft.tv_sec, TIMERSEC); return PTS_FAIL; diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/11-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/11-1.c index de2c861593..0a1f39c509 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/11-1.c +++ b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_create/11-1.c @@ -1,7 +1,7 @@ -/* +/* * Copyright (c) 2002, Intel Corporation. All rights reserved. * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this + * of this license, see the COPYING file at the top level of this * source tree. * adam li * @@ -21,10 +21,72 @@ #define TIMERSEC 2 #define SLEEPDELTA 3 #define ACCEPTABLEDELTA 1 +#define CLOCK_TO_TEST CLOCK_THREAD_CPUTIME_ID + +static int handlerInvoked = 0; + +static void sub_timespec(const struct timespec *a, const struct timespec *b, + struct timespec *result) +{ + result->tv_sec = a->tv_sec - b->tv_sec; + result->tv_nsec = a->tv_nsec - b->tv_nsec; + if (result->tv_nsec < 0 && result->tv_sec > 0) { + result->tv_sec--; + result->tv_nsec += 1000000000; + } else if (result->tv_nsec >= 1000000000) { + result->tv_sec++; + result->tv_nsec -= 1000000000; + } +} + +static int cmp_timespec(const struct timespec *a, const struct timespec *b) +{ + if (a->tv_sec < b->tv_sec) + return -1; + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_nsec < b->tv_nsec) + return -1; + if (a->tv_nsec > b->tv_nsec) + return 1; + + return 0; +} + +static int spin(const struct timespec *time, struct timespec *timeLeft) +{ + struct timespec startTime; + + if (clock_gettime(CLOCK_TO_TEST, &startTime) != 0) { + perror("clock_gettime() failed"); + exit(PTS_UNRESOLVED); + } + + for (;;) { + struct timespec tempTime; + if (clock_gettime(CLOCK_TO_TEST, &tempTime) != 0) { + perror("clock_gettime() failed"); + exit(PTS_UNRESOLVED); + } + + + sub_timespec(&tempTime, &startTime, &tempTime); + if (cmp_timespec(time, &tempTime) <= 0) + return 0; + + if (handlerInvoked) { + if (timeLeft) + sub_timespec(time, &tempTime, timeLeft); + return -1; + } + } +} void handler(int signo) { printf("Caught signal\n"); + handlerInvoked = 1; } int main(int argc, char *argv[]) @@ -68,7 +130,7 @@ int main(int argc, char *argv[]) return PTS_UNRESOLVED; } - if (timer_create(CLOCK_THREAD_CPUTIME_ID, &ev, &tid) != 0) { + if (timer_create(CLOCK_TO_TEST, &ev, &tid) != 0) { perror("timer_create() did not return success"); return PTS_UNRESOLVED; } @@ -78,7 +140,7 @@ int main(int argc, char *argv[]) return PTS_UNRESOLVED; } - if (nanosleep(&ts, &tsleft) != -1) { + if (spin(&ts, &tsleft) != -1) { perror("nanosleep() not interrupted"); return PTS_FAIL; } @@ -88,7 +150,7 @@ int main(int argc, char *argv[]) return PTS_PASS; } else { printf("Timer did not last for correct amount of time\n"); - printf("timer: %d != correct %d\n", + printf("timer: %d != correct %d\n", (int) ts.tv_sec- (int) tsleft.tv_sec, TIMERSEC); return PTS_FAIL; diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_delete/speculative/5-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_delete/speculative/5-1.c deleted file mode 100644 index 056e57d3a2..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_delete/speculative/5-1.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - - * Test to see if timer_delete() returns -1 and sets errno==EINVAL if - * timerid is not a valid timer ID or not. - * Since this is a "may" requirement, either option is a PASS. - */ - -#include -#include -#include -#include "posixtest.h" - -#define BOGUSTIMERID 99999 - -int main(int argc, char *argv[]) -{ - timer_t tid; - - tid = (timer_t) BOGUSTIMERID; - - if (timer_delete(tid) == -1) { - if (errno==EINVAL) { - printf("timer_delete() returned -1 and set errno=EINVAL\n"); - return PTS_PASS; - } else { - printf("timer_delete() returned -1, but didn't set errno!=EINVAL\n"); - return PTS_FAIL; - } - } - - printf("timer_delete() did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_delete/speculative/5-2.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_delete/speculative/5-2.c deleted file mode 100644 index 9c0a6fc82b..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_delete/speculative/5-2.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - - * Test to see if timer_delete() returns -1 and sets errno==EINVAL when - * trying to delete a timer that has already been deleted or not. - * Since this is a "may" assertion, either option is a pass. - * Steps: - * - Run test case 1-1.c and then try to delete the timer again. - * - * For this test, signal SIGTOTEST will be used, clock CLOCK_REALTIME - * will be used. - */ - -#include -#include -#include -#include -#include -#include -#include "posixtest.h" - -#define SIGTOTEST SIGALRM -#define TIMERSEC 3 - -void handler(int signo) -{ - printf("Should not have caught signal\n"); - exit(PTS_FAIL); -} - -int main(int argc, char *argv[]) -{ - struct sigevent ev; - struct sigaction act; - timer_t tid; - struct itimerspec its; - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGTOTEST; - - act.sa_handler=handler; - act.sa_flags=0; - - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = TIMERSEC; - its.it_value.tv_nsec = 0; - - if (sigemptyset(&act.sa_mask) == -1) { - perror("Error calling sigemptyset\n"); - return PTS_UNRESOLVED; - } - if (sigaction(SIGTOTEST, &act, 0) == -1) { - perror("Error calling sigaction\n"); - return PTS_UNRESOLVED; - } - - if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) { - perror("timer_create() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_settime(tid, 0, &its, NULL) != 0) { - perror("timer_settime() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_delete(tid) != 0) { - perror("timer_delete() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (sleep(TIMERSEC) != 0) { - printf("sleep() did not sleep full time\n"); - return PTS_UNRESOLVED; - } - - if (timer_delete(tid) == -1) { - if (errno==EINVAL) { - printf("fcn returned -1 and set errno=EINVAL\n"); - return PTS_PASS; - } else { - printf("errno!=EINVAL, but fcn returned -1\n"); - return PTS_FAIL; - } - } - - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-1.c deleted file mode 100644 index c5085a5e2a..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-1.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_getoverrun() sets errno=EINVAL if no timers have been - * created yet or not. Since this is a "may" assertion, either way is - * a pass. - */ - -#include -#include -#include -#include "posixtest.h" - -#define BOGUSTID 9999 - -int main(int argc, char *argv[]) -{ - timer_t tid; - - tid = (timer_t) BOGUSTID; - - if (timer_getoverrun(tid) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno=EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1, but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-2.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-2.c deleted file mode 100644 index b4f61feef7..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-2.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_getoverrun() returns -1 and sets errno==EINVAL when - * trying to call timer_getoverrun on a timer that has been deleted or not. - * Since this assertion is a "may," either option is a pass. - * - * For this test, signal SIGCONT will be used, clock CLOCK_REALTIME - * will be used. - */ - -#include -#include -#include -#include -#include -#include -#include "posixtest.h" - -#define TIMERSEC 3 - -int main(int argc, char *argv[]) -{ - struct sigevent ev; - timer_t tid; - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGCONT; - - if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) { - perror("timer_create() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_delete(tid) != 0) { - perror("timer_delete() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_getoverrun(tid) == -1) { - if (errno==EINVAL) { - printf("fcn returned -1 and set errno=EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1 but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-3.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-3.c deleted file mode 100644 index cb7d8fd08b..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_getoverrun/speculative/6-3.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_getoverrun() sets errno = EINVAL for timerid != a - * timer ID created via timer_create(). [Try to set timerid to a timer ID - * created + 1.] - * Since this assertion is a "may," either option is a pass. - * - * For this test, signal SIGCONT will be used, clock CLOCK_REALTIME - * will be used. - */ - -#include -#include -#include -#include -#include "posixtest.h" - -int main(int argc, char *argv[]) -{ - struct sigevent ev; - timer_t tid; - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGCONT; - - if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) { - perror("timer_create() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_getoverrun(tid+1) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno==EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1 but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-1.c deleted file mode 100644 index f4369da8d9..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-1.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_gettime() sets errno = EINVAL if no timers have been - * created yet and it is called with a bogus timer ID. Since this is a "may" - * assertion, either way is pass. - */ - -#include -#include -#include -#include "posixtest.h" - -#define BOGUSTID 9999 - -int main(int argc, char *argv[]) -{ - timer_t tid; - struct itimerspec its; - - tid = (timer_t) BOGUSTID; - if (timer_gettime(tid, &its) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno==EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1 but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-2.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-2.c deleted file mode 100644 index 4bcb1fbb39..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-2.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_gettime() sets errno = EINVAL timerid != a timer ID - * created via timer_create(). [Try to set timerid to a timer ID - * created + 1.] Since this assertion is a "may," either way is a pass. - * - * For this test, signal SIGCONT will be used, clock CLOCK_REALTIME - * will be used. - */ - -#include -#include -#include -#include -#include "posixtest.h" - -int main(int argc, char *argv[]) -{ - struct sigevent ev; - timer_t tid; - struct itimerspec its; - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGCONT; - - if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) { - perror("timer_create() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_gettime(tid+1, &its) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno==EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1 but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-3.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-3.c deleted file mode 100644 index a675d2af02..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_gettime/speculative/6-3.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_gettime() sets errno = EINVAL when timerid = - * a timer ID of a deleted timer. Since this assertion is a "may" - * assertion, either way is a pass. - * - * For this test, signal SIGCONT will be used. - * Clock CLOCK_REALTIME will be used. - */ - -#include -#include -#include -#include -#include "posixtest.h" - -int main(int argc, char *argv[]) -{ - struct sigevent ev; - timer_t tid; - struct itimerspec its; - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGCONT; - - if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) { - perror("timer_create() did not return success\n"); - return PTS_UNRESOLVED; - } - if (timer_delete(tid) != 0) { - perror("timer_delete() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_gettime(tid, &its) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno==EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1 but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-1.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-1.c deleted file mode 100644 index e4944e859e..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-1.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_settime() sets errno = EINVAL if no timers have been - * created yet. Since this is a "may" assertion, either way is a pass. - */ - -#include -#include -#include -#include "posixtest.h" - -#define BOGUSTID 9999 - -int main(int argc, char *argv[]) -{ - timer_t tid; - struct itimerspec its; - - tid = (timer_t) BOGUSTID; - its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = 0; its.it_value.tv_nsec = 0; - - if (timer_settime(tid, 0, &its, NULL) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno==EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1, but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-2.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-2.c deleted file mode 100644 index c5ea94891c..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-2.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_settime() sets errno = EINVAL timerid != a timer ID - * created via timer_create(). [Try to set timerid to a timer ID - * created + 1.] Since this test is a "may" assertion, either way is a - * pass. - * - * For this test, signal SIGTOTEST will be used, clock CLOCK_REALTIME - * will be used. - */ - -#include -#include -#include -#include -#include "posixtest.h" - -#define SIGTOTEST SIGALRM - -int main(int argc, char *argv[]) -{ - struct sigevent ev; - timer_t tid; - struct itimerspec its; - - its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = 0; its.it_value.tv_nsec = 0; - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGTOTEST; - - if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) { - perror("timer_create() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_settime(tid+1, 0, &its, NULL) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno==EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1 but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - printf("fcn did not return -1\n"); - return PTS_PASS; -} diff --git a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-3.c b/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-3.c deleted file mode 100644 index 74989610b1..0000000000 --- a/src/tests/system/libroot/posix/posixtestsuite/conformance/interfaces/timer_settime/speculative/12-3.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2002, Intel Corporation. All rights reserved. - * Created by: julie.n.fleischer REMOVE-THIS AT intel DOT com - * This file is licensed under the GPL license. For the full content - * of this license, see the COPYING file at the top level of this - * source tree. - * - * Test to see if timer_settime() sets errno = EINVAL when timerid = - * a timer ID of a deleted timer. Since this is a "may" assertion, either - * way is a pass. - * - * For this test, signal SIGTOTEST will be used. - * Clock CLOCK_REALTIME will be used. - */ - -#include -#include -#include -#include -#include "posixtest.h" - -#define SIGTOTEST SIGALRM - -int main(int argc, char *argv[]) -{ - struct sigevent ev; - timer_t tid; - struct itimerspec its; - - its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = 0; its.it_value.tv_nsec = 0; - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGTOTEST; - - if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) { - perror("timer_create() did not return success\n"); - return PTS_UNRESOLVED; - } - if (timer_delete(tid) != 0) { - perror("timer_delete() did not return success\n"); - return PTS_UNRESOLVED; - } - - if (timer_settime(tid, 0, &its, NULL) == -1) { - if (EINVAL == errno) { - printf("fcn returned -1 and errno==EINVAL\n"); - return PTS_PASS; - } else { - printf("fcn returned -1 but errno!=EINVAL\n"); - printf("Test FAILED\n"); - return PTS_FAIL; - } - } - printf("fcn did not return -1\n"); - return PTS_PASS; -}