The Pedigree Project 0.1
Delay.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Atomic.h"
21#include "pedigree/kernel/machine/Machine.h"
22#include "pedigree/kernel/machine/Timer.h"
23#include "pedigree/kernel/process/Event.h"
24#include "pedigree/kernel/process/PerProcessorScheduler.h"
25#include "pedigree/kernel/process/Thread.h"
26#include "pedigree/kernel/process/eventNumbers.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/processor/ProcessorInformation.h"
29#include "pedigree/kernel/processor/types.h"
30#include "pedigree/kernel/time/Time.h"
31#include "pedigree/kernel/utilities/assert.h"
32#include "pedigree/kernel/utilities/new"
33
34namespace Time {
35static void delayTimerFired(uint8_t* pBuffer) {
36 Processor::information().getCurrentThread()->markTimeoutInterruptedWait();
37}
38
39class DelayTimerEvent : public Event {
40 public:
41 explicit DelayTimerEvent(Thread* owner, size_t nestingLevel = ~0UL);
42 virtual ~DelayTimerEvent();
43
44 virtual size_t serialize(uint8_t* pBuffer);
45 static bool unserialize(uint8_t* pBuffer, DelayTimerEvent& event);
46 virtual size_t getNumber();
47
48 Thread* owner() const {
49 return m_pOwner;
50 }
51
52 private:
53 Thread* m_pOwner;
54};
55
56#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
57Atomic<size_t> g_HostedAlarmCreates(0);
58Atomic<size_t> g_HostedAlarmDestroys(0);
59#endif
60
61namespace {
62struct DelayDiscard {
63 void* alarm;
64};
65
66void discardDelay(void* context) {
67 DelayDiscard* discard = reinterpret_cast<DelayDiscard*>(context);
68 void* alarm = discard->alarm;
69 discard->alarm = nullptr;
70 if (alarm) {
71 removeAlarm(alarm);
72 }
73}
74} // namespace
75
76bool delay(Timestamp nanoseconds) {
77 Thread* pThread = Processor::information().getCurrentThread();
78 DelayDiscard discard = {addAlarm(nanoseconds)};
79 Thread::StackDiscardScope discardScope(&discardDelay, &discard);
80
81 while (true) {
82 if (!pThread->wasInterrupted())
83 pThread->waitForEvent();
84
85 const Thread::InterruptionReason interruption = pThread->getInterruptionReason();
86 if (interruption == Thread::InterruptedByTimeout) {
87 discardDelay(&discard);
88 pThread->clearInterruption();
89 break;
90 } else if (interruption == Thread::InterruptedBySignal) {
91 discardDelay(&discard);
92 return false;
93 } else if (pThread->getUnwindState() != Thread::Continue) {
94 discardDelay(&discard);
95 return false;
96 }
97
98 pThread->clearInterruption();
99 }
100
101 return true;
102}
103
104void* addAlarm(Timestamp nanoseconds) {
105 Thread* pThread = Processor::information().getCurrentThread();
106 Event* pEvent = new DelayTimerEvent(pThread, pThread->getStateLevel());
107#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
108 g_HostedAlarmCreates += 1;
109#endif
110 uint64_t usecs = nanoseconds / Multiplier::Microsecond;
111 if (!usecs)
112 ++usecs;
113
114 pThread->clearInterruption();
115 Machine::instance().getTimer()->addAlarm(pEvent, 0, usecs);
116
117 return pEvent;
118}
119
120void removeAlarm(void* handle) {
121 assert(handle != nullptr);
122
123 DelayTimerEvent* pEvent = reinterpret_cast<DelayTimerEvent*>(handle);
124 Machine::instance().getTimer()->removeAlarm(pEvent);
125
126 // Handle a race condition where the timeout triggers but hasn't been
127 // handled by the time we clean up the alarm. That leaves the deleted event
128 // in this thread's event queue. We also do so after removing the alarm
129 // from the Machine implementation so that we don't get new events added
130 // after our cull.
131 pEvent->owner()->cullEvent(pEvent);
132
133 // A timer dispatch may already have admitted this event while the alarm
134 // was being removed. Close admission and drain that lease before delete.
135 pEvent->waitForDeliveries();
136 delete pEvent;
137}
138
139DelayTimerEvent::DelayTimerEvent(Thread* owner, size_t nestingLevel)
140 : Event(reinterpret_cast<uintptr_t>(&delayTimerFired), false, nestingLevel), m_pOwner(owner) {}
141DelayTimerEvent::~DelayTimerEvent() {
142#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
143 g_HostedAlarmDestroys += 1;
144#endif
145}
146
147size_t DelayTimerEvent::serialize(uint8_t* pBuffer) {
148 return 0;
149}
150
151bool DelayTimerEvent::unserialize(uint8_t* pBuffer, DelayTimerEvent& event) {
152 return true;
153}
154
156 return EventNumbers::DelayTimer;
157}
158
159#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
160size_t getHostedAlarmCreateCount() {
161 return g_HostedAlarmCreates;
162}
163
164size_t getHostedAlarmDestroyCount() {
165 return g_HostedAlarmDestroys;
166}
167#endif
168} // namespace Time
Definition Event.h:49
virtual Timer * getTimer()=0
static ProcessorInformation & information()
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:517
void waitForEvent(WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
Definition Thread.cc:1202
UnwindType getUnwindState()
Definition Thread.h:535
size_t getStateLevel() const
Definition Thread.h:316
bool wasInterrupted()
Definition Thread.h:493
virtual size_t serialize(uint8_t *pBuffer)
Definition Delay.cc:147
virtual size_t getNumber()
Definition Delay.cc:155
virtual void removeAlarm(class Event *pEvent)=0
virtual void addAlarm(class Event *pEvent, size_t alarmSecs, size_t alarmUsecs=0)=0