Merged signals-merge branch into trunk with the following changes:

* Reorganized the kernel locking related to threads and teams.
* We now discriminate correctly between process and thread signals. Signal
  handlers have been moved to teams. Fixes #5679.
* Implemented real-time signal support, including signal queuing, SA_SIGINFO
  support, sigqueue(), sigwaitinfo(), sigtimedwait(), waitid(), and the addition
  of the real-time signal range. Closes #1935 and #2695.
* Gave SIGBUS a separate signal number. Fixes #6704.
* Implemented <time.h> clock and timer support, and fixed/completed alarm() and
  [set]itimer(). Closes #5682.
* Implemented support for thread cancellation. Closes #5686.
* Moved send_signal() from <signal.h> to <OS.h>. Fixes #7554.
* Lots over smaller more or less related changes.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42116 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2011-06-12 00:00:23 +00:00
parent ccd31b93f1
commit 24df65921b
235 changed files with 14975 additions and 6594 deletions
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright 2011, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_DPC_H
#define _KERNEL_DPC_H
#include <sys/cdefs.h>
#include <KernelExport.h>
#include <util/DoublyLinkedList.h>
#include <condition_variable.h>
namespace BKernel {
class DPCQueue;
class DPCCallback : public DoublyLinkedListLinkImpl<DPCCallback> {
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<DPCCallback> 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
+109
View File
@@ -0,0 +1,109 @@
/*
* Copyright 2011, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_USER_EVENT_H
#define _KERNEL_USER_EVENT_H
#include <signal.h>
#include <SupportDefs.h>
#include <DPC.h>
#include <thread.h>
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
+273
View File
@@ -0,0 +1,273 @@
/*
* Copyright 2011, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_USER_TIMER_H
#define _KERNEL_USER_TIMER_H
#include <sys/cdefs.h>
#include <time.h>
#include <util/DoublyLinkedList.h>
#include <ksignal.h>
#include <timer.h>
#include <user_timer_defs.h>
struct thread_creation_attributes;
namespace BKernel {
struct UserEvent;
struct Team;
struct UserTimer : DoublyLinkedListLinkImpl<UserTimer> {
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<RealTimeUserTimer> 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<TeamTimeUserTimer> 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<TeamUserTimeUserTimer> 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<ThreadTimeUserTimer> 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<UserTimer> TimerList;
private:
TimerList fTimers;
};
typedef DoublyLinkedList<RealTimeUserTimer,
DoublyLinkedListMemberGetLink<RealTimeUserTimer,
&RealTimeUserTimer::fGlobalListLink> > RealTimeUserTimerList;
typedef DoublyLinkedList<TeamTimeUserTimer,
DoublyLinkedListMemberGetLink<TeamTimeUserTimer,
&TeamTimeUserTimer::fCPUTimeListLink> > TeamTimeUserTimerList;
typedef DoublyLinkedList<TeamUserTimeUserTimer,
DoublyLinkedListMemberGetLink<TeamUserTimeUserTimer,
&TeamUserTimeUserTimer::fCPUTimeListLink> > TeamUserTimeUserTimerList;
typedef DoublyLinkedList<ThreadTimeUserTimer,
DoublyLinkedListMemberGetLink<ThreadTimeUserTimer,
&ThreadTimeUserTimer::fCPUTimeListLink> > 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
+5 -5
View File
@@ -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);
+26 -1
View File
@@ -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);
@@ -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);
+10 -10
View File
@@ -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);
}
+1
View File
@@ -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;
+7
View File
@@ -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,
+50 -16
View File
@@ -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);
}
}
+207 -24
View File
@@ -1,57 +1,240 @@
/*
* Copyright 2003-2008, Axel Dörfler, [email protected]. All rights reserved.
* Copyright 2011, Ingo Weinhold, [email protected].
* Copyright 2003-2008, Axel Dörfler, [email protected].
* All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_SIGNAL_H
#define _KERNEL_SIGNAL_H
#include <KernelExport.h>
#include <signal.h>
#include <KernelExport.h>
#include <signal_defs.h>
#include <heap.h>
#include <util/DoublyLinkedList.h>
#include <util/KernelReferenceable.h>
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<Signal> {
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<Signal> 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
}
+5 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2008-2010, Ingo Weinhold, [email protected].
* Copyright 2008-2011, Ingo Weinhold, [email protected].
* Copyright 2002-2009, Axel Dörfler, [email protected].
* 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);
+3 -1
View File
@@ -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,
+7 -13
View File
@@ -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);
+235 -23
View File
@@ -19,6 +19,7 @@
#include <ksignal.h>
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)
{
+505 -104
View File
@@ -10,30 +10,23 @@
#ifndef _ASSEMBLER
#include <Referenceable.h>
#include <pthread.h>
#include <arch/thread_types.h>
#include <condition_variable.h>
#include <heap.h>
#include <ksignal.h>
#include <lock.h>
#include <signal.h>
#include <smp.h>
#include <thread_defs.h>
#include <timer.h>
#include <UserTimer.h>
#include <user_debugger.h>
#include <util/DoublyLinkedList.h>
#include <util/KernelReferenceable.h>
#include <util/list.h>
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_entry> {
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<job_control_entry> {
// 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 <signal.h>
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<typename IDType>
struct TeamThreadIteratorEntry
: DoublyLinkedListLinkImpl<TeamThreadIteratorEntry<IDType> > {
typedef IDType id_type;
typedef TeamThreadIteratorEntry<id_type> iterator_type;
id_type id; // -1 for iterator entries, >= 0 for actual elements
bool visible; // the entry is publicly visible
};
struct Team : TeamThreadIteratorEntry<team_id>, KernelReferenceable,
AssociatedDataOwner {
DoublyLinkedListLink<Team> 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 <signal.h>
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<thread_id>, 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<ProcessGroup> fOrphanedCheckListLink;
private:
mutex fLock;
ProcessSession* fSession;
bool fInOrphanedCheckList; // protected by
// sOrphanedCheckLock
};
typedef SinglyLinkedList<ProcessGroup,
SinglyLinkedListMemberGetLink<ProcessGroup,
&ProcessGroup::fOrphanedCheckListLink> > ProcessGroupList;
/*! \brief Allows to iterate through all teams.
*/
struct TeamListIterator {
TeamListIterator();
~TeamListIterator();
Team* Next();
private:
TeamThreadIteratorEntry<team_id> fEntry;
};
/*! \brief Allows to iterate through all threads.
*/
struct ThreadListIterator {
ThreadListIterator();
~ThreadListIterator();
Thread* Next();
private:
TeamThreadIteratorEntry<thread_id> 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 {
+18 -3
View File
@@ -1,4 +1,4 @@
/*
/*
** Copyright 2003-2004, Axel Dörfler, [email protected]. 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
+41 -19
View File
@@ -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);
-1
View File
@@ -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
+7
View File
@@ -177,6 +177,10 @@ public:
typedef AutoLocker<Thread, ThreadCPUPinLocking> ThreadCPUPinner;
typedef AutoLocker<Team> TeamLocker;
typedef AutoLocker<Thread> 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
@@ -0,0 +1,29 @@
/*
* Copyright 2011, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_UTIL_KERNEL_REFERENCEABLE_H
#define _KERNEL_UTIL_KERNEL_REFERENCEABLE_H
#include <Referenceable.h>
#include <heap.h>
namespace BKernel {
struct KernelReferenceable : BReferenceable, DeferredDeletable {
protected:
virtual void LastReferenceReleased();
};
} // namespace BKernel
using BKernel::KernelReferenceable;
#endif /* _KERNEL_UTIL_KERNEL_REFERENCEABLE_H */