* Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org.
* 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>
UserEvent::~UserEvent()
{
}
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()
{
atomic_set(&fInUse, 0);
}
virtual void Handled()
{
SetUnused();
Signal::Handled();
}
private:
int32 fInUse;
};
SignalEvent::SignalEvent(EventSignal* signal)
:
fSignal(signal),
fPendingDPC(0)
{
}
SignalEvent::~SignalEvent()
{
fSignal->ReleaseReference();
}
void
SignalEvent::SetUserValue(union sigval userValue)
{
fSignal->SetUserValue(userValue);
}
status_t
SignalEvent::Fire()
{
bool wasPending = atomic_get_and_set(&fPendingDPC, 1) != 0;
if (wasPending)
return B_BUSY;
if (fSignal->MarkUsed()) {
atomic_set(&fPendingDPC, 0);
return B_BUSY;
}
AcquireReference();
DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this);
return B_OK;
}
TeamSignalEvent::TeamSignalEvent(Team* team, EventSignal* signal)
:
SignalEvent(signal),
fTeam(team)
{
}
TeamSignalEvent*
TeamSignalEvent::Create(Team* team, uint32 signalNumber, int32 signalCode,
int32 errorCode)
{
EventSignal* signal = new(std::nothrow) EventSignal(signalNumber,
signalCode, errorCode, team->id);
if (signal == NULL)
return NULL;
TeamSignalEvent* event = new(std::nothrow) TeamSignalEvent(team, signal);
if (event == NULL) {
delete signal;
return NULL;
}
return event;
}
status_t
TeamSignalEvent::Fire()
{
fTeam->AcquireReference();
status_t result = SignalEvent::Fire();
if (result != B_OK)
fTeam->ReleaseReference();
return result;
}
void
TeamSignalEvent::DoDPC(DPCQueue* queue)
{
fSignal->AcquireReference();
InterruptsSpinLocker locker(fTeam->signal_lock);
status_t error = send_signal_to_team_locked(fTeam, fSignal->Number(),
fSignal, B_DO_NOT_RESCHEDULE);
locker.Unlock();
fTeam->ReleaseReference();
if (error != B_OK || !fSignal->IsPending())
fSignal->SetUnused();
atomic_set(&fPendingDPC, 0);
ReleaseReference();
}
ThreadSignalEvent::ThreadSignalEvent(Thread* thread, EventSignal* signal)
:
SignalEvent(signal),
fThread(thread)
{
}
ThreadSignalEvent*
ThreadSignalEvent::Create(Thread* thread, uint32 signalNumber, int32 signalCode,
int32 errorCode, pid_t sendingTeam)
{
EventSignal* signal = new(std::nothrow) EventSignal(signalNumber,
signalCode, errorCode, sendingTeam);
if (signal == NULL)
return NULL;
ThreadSignalEvent* event = new(std::nothrow) ThreadSignalEvent(thread, signal);
if (event == NULL) {
delete signal;
return NULL;
}
return event;
}
status_t
ThreadSignalEvent::Fire()
{
fThread->AcquireReference();
status_t result = SignalEvent::Fire();
if (result != B_OK)
fThread->ReleaseReference();
return result;
}
void
ThreadSignalEvent::DoDPC(DPCQueue* queue)
{
fSignal->AcquireReference();
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();
fThread->ReleaseReference();
if (error != B_OK || !fSignal->IsPending())
fSignal->SetUnused();
atomic_set(&fPendingDPC, 0);
ReleaseReference();
}
CreateThreadEvent::CreateThreadEvent(const ThreadCreationAttributes& attributes)
:
fCreationAttributes(attributes),
fPendingDPC(0)
{
strlcpy(fThreadName, attributes.name, sizeof(fThreadName));
fCreationAttributes.name = fThreadName;
}
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;
AcquireReference();
DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this);
return B_OK;
}
void
CreateThreadEvent::DoDPC(DPCQueue* queue)
{
atomic_set(&fPendingDPC, 0);
thread_id threadID = thread_create_thread(fCreationAttributes, false);
if (threadID >= 0)
resume_thread(threadID);
ReleaseReference();
}