241 lines
4.5 KiB
C++
241 lines
4.5 KiB
C++
/*
|
|
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include <UserEvent.h>
|
|
|
|
#include <ksignal.h>
|
|
#include <thread_types.h>
|
|
#include <util/AutoLock.h>
|
|
|
|
|
|
// #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(0)
|
|
{
|
|
}
|
|
|
|
bool MarkUsed()
|
|
{
|
|
return atomic_get_and_set(&fInUse, 1) != 0;
|
|
}
|
|
|
|
void SetUnused()
|
|
{
|
|
// mark not-in-use
|
|
atomic_set(&fInUse, 0);
|
|
}
|
|
|
|
virtual void Handled()
|
|
{
|
|
SetUnused();
|
|
|
|
Signal::Handled();
|
|
}
|
|
|
|
private:
|
|
int32 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()
|
|
{
|
|
if (fSignal->MarkUsed())
|
|
return B_BUSY;
|
|
|
|
fSignal->AcquireReference();
|
|
// one reference is transferred to send_signal_to_team_locked
|
|
|
|
InterruptsSpinLocker locker(fTeam->signal_lock);
|
|
status_t error = send_signal_to_team_locked(fTeam, fSignal->Number(),
|
|
fSignal, B_DO_NOT_RESCHEDULE);
|
|
locker.Unlock();
|
|
|
|
// There are situations (for certain signals), in which
|
|
// send_signal_to_team_locked() succeeds without queuing the signal.
|
|
if (error != B_OK || !fSignal->IsPending())
|
|
fSignal->SetUnused();
|
|
|
|
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()
|
|
{
|
|
if (fSignal->MarkUsed())
|
|
return B_BUSY;
|
|
|
|
fSignal->AcquireReference();
|
|
// one reference is transferred to send_signal_to_team_locked
|
|
InterruptsReadSpinLocker teamLocker(fThread->team_lock);
|
|
SpinLocker locker(fThread->team->signal_lock);
|
|
status_t error = send_signal_to_thread_locked(fThread, fSignal->Number(),
|
|
fSignal, B_DO_NOT_RESCHEDULE);
|
|
locker.Unlock();
|
|
teamLocker.Unlock();
|
|
|
|
// There are situations (for certain signals), in which
|
|
// send_signal_to_team_locked() succeeds without queuing the signal.
|
|
if (error != B_OK || !fSignal->IsPending())
|
|
fSignal->SetUnused();
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
// #pragma mark - UserEvent
|
|
|
|
|
|
CreateThreadEvent::CreateThreadEvent(const ThreadCreationAttributes& attributes)
|
|
:
|
|
fCreationAttributes(attributes),
|
|
fPendingDPC(0)
|
|
{
|
|
// 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()
|
|
{
|
|
bool wasPending = atomic_get_and_set(&fPendingDPC, 1) != 0;
|
|
if (wasPending)
|
|
return B_BUSY;
|
|
|
|
DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
CreateThreadEvent::DoDPC(DPCQueue* queue)
|
|
{
|
|
// We're no longer queued in the DPC queue, so we can be reused.
|
|
atomic_set(&fPendingDPC, 0);
|
|
|
|
// create the thread
|
|
thread_id threadID = thread_create_thread(fCreationAttributes, false);
|
|
if (threadID >= 0)
|
|
resume_thread(threadID);
|
|
}
|