summaryrefslogtreecommitdiff
path: root/src/timer.cpp
blob: d9b1b6414f9c9b6a9bce03979b2f855b1735875c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*       +------------------------------------+
 *       | Inspire Internet Relay Chat Daemon |
 *       +------------------------------------+
 *
 *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
 * See: http://www.inspircd.org/wiki/index.php/Credits
 *
 * This program is free but copyrighted software; see
 *            the file COPYING for details.
 *
 * ---------------------------------------------------
 */

/* $Core: libIRCDtimer */

#include "inspircd.h"
#include "timer.h"

TimerManager::TimerManager(InspIRCd* Instance) : CantDeleteHere(false), ServerInstance(Instance)
{
}

void TimerManager::TickTimers(time_t TIME)
{
	this->CantDeleteHere = true;
	timerlist::iterator found = Timers.find(TIME);

	if (found != Timers.end())
	{
		timergroup* x = found->second;
		/* There are pending timers to trigger.
		 * WARNING: Timers may delete themselves from within
		 * their own Tick methods! see the comment below in
		 * the DelTimer method.
		 */
		for (timergroup::iterator y = x->begin(); y != x->end(); y++)
		{
			Timer* n = *y;
			n->Tick(TIME);
			if (n->GetRepeat())
			{
				AddTimer(n, n->GetSecs());
			}
			else
			{
				delete n;
			}
		}

		Timers.erase(found);
		delete x;
	}

	this->CantDeleteHere = false;
}

void TimerManager::DelTimer(Timer* T)
{
	if (this->CantDeleteHere)
	{
		/* If a developer tries to delete a timer from within its own Tick method,
		 * then chances are this is just going to totally fuck over the timergroup
		 * and timerlist iterators and cause a crash. Thanks to peavey and Bricker
		 * for noticing this bug.
		 * If we're within the tick loop when the DelTimer is called (signified
		 * by the var 'CantDeleteHere') then we simply return for non-repeating
		 * timers, and cancel the repeat on repeating timers. We can do this because
		 * we know that the timer tick loop will safely delete the timer for us
		 * anyway and therefore we avoid stack corruption.
		 */
		if (T->GetRepeat())
			T->CancelRepeat();
		else
			return;
	}

	timerlist::iterator found = Timers.find(T->GetTimer());

	if (found != Timers.end())
	{
		timergroup* x = found->second;
		for (timergroup::iterator y = x->begin(); y != x->end(); y++)
		{
			Timer* n = *y;
			if (n == T)
			{
				delete n;
				x->erase(y);
				if (!x->size())
				{
					Timers.erase(found);
					delete x;
				}
				return;
			}
		}
	}
}

/** Because some muppets may do odd things, and their ircd may lock up due
 * to crappy 3rd party modules, or they may change their system time a bit,
 * this accounts for shifts of up to 120 secs by looking behind for missed
 * timers and executing them. This is only executed once every 5 secs.
 * If you move your clock BACK, and your timers move further ahead as a result,
 * then tough titty you'll just have to wait.
 */
void TimerManager::TickMissedTimers(time_t TIME)
{
	for (time_t n = TIME-1; n > TIME-120; n--)
		this->TickTimers(TIME);
}

void TimerManager::AddTimer(Timer* T, long secs_from_now)
{
	timergroup* x = NULL;

	int time_to_trigger = 0;
	if (!secs_from_now)
		time_to_trigger = T->GetTimer();
	else
		time_to_trigger = secs_from_now + ServerInstance->Time();

	timerlist::iterator found = Timers.find(time_to_trigger);

	if (found != Timers.end())
	{
		x = found->second;
	}
	else
	{
		x = new timergroup;
		Timers[time_to_trigger] = x;
	}

	x->push_back(T);
}