Bug Summary

File:src/libraries/tools/Timer.cc
Location:line 258, column 40
Description:Use of memory after it is freed

Annotated Source Code

1/*
2 * ORXONOX - the hottest 3D action shooter ever to exist
3 * > www.orxonox.net <
4 *
5 *
6 * License notice:
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 *
22 * Author:
23 * Fabian 'x3n' Landau
24 * Co-authors:
25 * ...
26 *
27 */
28
29/**
30 @file
31 @brief Implementation of the Timer class.
32*/
33
34#include "Timer.h"
35
36#include <set>
37
38#include <boost/bimap.hpp>
39#include <functional>
40
41#include "util/Clock.h"
42#include "core/CoreIncludes.h"
43#include "core/command/ConsoleCommandIncludes.h"
44#include "core/command/CommandExecutor.h"
45#include "core/command/Executor.h"
46#include "core/command/Functor.h"
47#include "tools/interfaces/TimeFactorListener.h"
48
49namespace orxonox
50{
51 SetConsoleCommand("delay", &delay)static orxonox::ConsoleCommand& __consolecommand_51 = (new
orxonox::SI_CC(new orxonox::ConsoleCommand("", "delay", orxonox
::createExecutor(orxonox::createFunctor(&delay)))))->getCommand
()
.argumentCompleter(1, autocompletion::command());
52 SetConsoleCommand("delayreal", &delayreal)static orxonox::ConsoleCommand& __consolecommand_52 = (new
orxonox::SI_CC(new orxonox::ConsoleCommand("", "delayreal", orxonox
::createExecutor(orxonox::createFunctor(&delayreal)))))->
getCommand()
.argumentCompleter(1, autocompletion::command());
53 SetConsoleCommand("killdelay", &killdelay)static orxonox::ConsoleCommand& __consolecommand_53 = (new
orxonox::SI_CC(new orxonox::ConsoleCommand("", "killdelay", orxonox
::createExecutor(orxonox::createFunctor(&killdelay)))))->
getCommand()
;
54 SetConsoleCommand("killdelays", &killdelays)static orxonox::ConsoleCommand& __consolecommand_54 = (new
orxonox::SI_CC(new orxonox::ConsoleCommand("", "killdelays",
orxonox::createExecutor(orxonox::createFunctor(&killdelays
)))))->getCommand()
;
55
56 static boost::bimap<unsigned int, Timer*> delaytimers;
57 static unsigned int delayHandleCounter = 0;
58
59 /**
60 @brief Console-command: Calls another console command after @a delay seconds (game time).
61 @param delay The delay in seconds
62 @param command The console command
63 @return The handle of the delayed command, can be used as argument for killdelay()
64 */
65 unsigned int delay(float delay, const std::string& command)
66 {
67 return addDelayedCommand(new Timer(), delay, command);
68 }
69
70 /**
71 @brief Console-command: Calls another console command after @a delay seconds (real time)
72 @param delay The delay in seconds
73 @param command The console command
74 @return The handle of the delayed command, can be used as argument for killdelay()
75 */
76 unsigned int delayreal(float delay, const std::string& command)
77 {
78 return addDelayedCommand(new RealTimer(), delay, command);
79 }
80
81 /**
82 @brief Helper function, used by delay() and delayreal() to add a delayed command.
83 @param timer The timer which will execute the command
84 @param delay The delay in seconds
85 @param command The console command
86 @return The handle of the delayed command, can be used as argument for killdelay()
87 */
88 unsigned int addDelayedCommand(Timer* timer, float delay, const std::string& command)
89 {
90 delaytimers.insert(boost::bimap<unsigned int, Timer*>::value_type(++delayHandleCounter, timer));
91
92 const ExecutorStaticPtr& delayexecutor = createExecutor(createFunctor(&executeDelayedCommand));
93 delayexecutor->setDefaultValues(timer, command);
94 timer->setTimer(delay, false, delayexecutor);
95
96 return delayHandleCounter;
97 }
98
99 /**
100 @brief Helper function for delay(), executes the command and destroys the timer.
101 @param timer The timer which called this function.
102 @param command The command to execute
103 */
104 void executeDelayedCommand(Timer* timer, const std::string& command)
105 {
106 CommandExecutor::execute(command);
107 delete timer;
108 delaytimers.right.erase(timer);
109 }
110
111 /**
112 @brief Console-command: Kills all scheduled commands that were delayed using delay().
113 */
114 void killdelays()
115 {
116 for (boost::bimap<unsigned int, Timer*>::left_map::iterator it = delaytimers.left.begin(); it != delaytimers.left.end(); ++it)
117 delete it->second;
118
119 delaytimers.clear();
120 }
121
122 /**
123 @brief Console-command: Kills a delayed command with given handle.
124 */
125 void killdelay(unsigned int handle)
126 {
127 boost::bimap<unsigned int, Timer*>::left_map::iterator it = delaytimers.left.find(handle);
128 if (it != delaytimers.left.end())
129 {
130 delete it->second;
131 delaytimers.left.erase(it);
132 }
133 }
134
135 RegisterClassNoArgs(Timer)orxonox::SI_I& _TimerIdentifier = (*new orxonox::SI_I(orxonox
::registerClass<Timer>("Timer", new orxonox::ClassFactoryNoArgs
<Timer>(), true)))
;
136
137 /**
138 @brief Constructor: Sets the default-values.
139 */
140 Timer::Timer()
141 {
142 this->init();
143 RegisterObject(Timer)if (ClassIdentifier<Timer>::getIdentifier()->initializeObject
(this)) return; else ((void)0)
;
144 }
145
146 /**
147 @brief Constructor: Initializes and starts the timer, which will call an executor after some time.
148 @param interval The timer-interval in seconds
149 @param bLoop If true, the executor gets called every @a interval seconds
150 @param executor The executor that will be called
151 @param bKillAfterCall If true, the timer will be deleted after the executor was called
152 */
153 Timer::Timer(float interval, bool bLoop, const ExecutorPtr& executor, bool bKillAfterCall)
154 {
155 this->init();
156 RegisterObject(Timer)if (ClassIdentifier<Timer>::getIdentifier()->initializeObject
(this)) return; else ((void)0)
;
157
158 this->setTimer(interval, bLoop, executor, bKillAfterCall);
159 }
160
161 Timer::Timer(float interval, bool bLoop, std::function<void ()> func, bool bKillAfterCall)
162 {
163 this->init();
164 RegisterObject(Timer)if (ClassIdentifier<Timer>::getIdentifier()->initializeObject
(this)) return; else ((void)0)
;
165
166 this->setTimer(interval, bLoop, func, bKillAfterCall);
167 }
168
169 /**
170 @brief Initializes the Timer
171 */
172 void Timer::init()
173 {
174 this->executor_ = nullptr;
175 this->interval_ = 0;
176 this->bLoop_ = false;
177 this->bActive_ = false;
178 this->bKillAfterCall_ = false;
179
180 this->time_ = 0;
181 }
182
183 /**
184 @brief Returns the current time factor of the game.
185 */
186 float Timer::getTimeFactor()
187 {
188 return TimeFactorListener::getTimeFactor();
189 }
190
191 /**
192 @brief Initializes and starts the timer, which will call an executor after some time.
193 @param interval The timer-interval in seconds
194 @param bLoop If true, the executor gets called every @a interval seconds
195 @param executor The executor that will be called
196 @param bKillAfterCall If true, the timer will be deleted after the executor was called
197 */
198 void Timer::setTimer(float interval, bool bLoop, const ExecutorPtr& executor, bool bKillAfterCall)
199 {
200 this->setInterval(interval);
201 this->bLoop_ = bLoop;
202 this->executor_ = executor;
203 this->bActive_ = true;
204 this->isStdFunction_ = false;
205
206 this->time_ = this->interval_;
207 this->bKillAfterCall_ = bKillAfterCall;
208
209 if(executor != nullptr)
210 executor->getFunctor()->setSafeMode(true);
211 }
212
213 void Timer::setTimer(float interval, bool bLoop, std::function<void ()> func, bool bKillAfterCall)
214 {
215 // Without the cast, the call would be ambiguous, because nullptr is castable to
216 // both, ExecutorPtr and std::function.
217 this->setTimer(interval, bLoop, static_cast<ExecutorPtr>(nullptr), bKillAfterCall);
218 this->function_ = func;
219 this->isStdFunction_ = true;
220 }
221
222 /**
223 @brief Calls the executor and destroys the timer if requested.
224 */
225 void Timer::run()
226 {
227 bool temp = this->bKillAfterCall_; // to avoid errors with bKillAfterCall_=false and an exutors which destroy the timer
228
229 if(this->isStdFunction_)
7
Taking true branch
230 this->function_();
231 else
232 (*this->executor_)();
233
234 if (temp)
8
Assuming 'temp' is not equal to 0
9
Taking true branch
235 delete this;
10
Memory is released
236 }
237
238 /**
239 @brief Updates the timer before the frames are rendered.
240 */
241 void Timer::tick(const Clock& time)
242 {
243 if (this->bActive_)
1
Taking true branch
244 {
245 // If active: Decrease the timer by the duration of the last frame
246 this->time_ -= static_cast<long long>(time.getDeltaTimeMicroseconds() * this->getTimeFactor());
247
248 if (this->time_ <= 0)
2
Taking true branch
249 {
250 // It's time to call the function
251 if (this->bLoop_ && !this->bKillAfterCall_)
3
Taking true branch
252 {
253 this->time_ += this->interval_; // Q: Why '+=' and not '='? A: Think about it. It's more accurate like that. Seriously.
254 while (this->time_ <= 0)
4
Loop condition is true. Entering loop body
5
Loop condition is true. Entering loop body
255 {
256 // The interval was shorter than one tick, so execute the function more than once
257 this->run();
6
Calling 'Timer::run'
11
Returning; memory was released
258 this->time_ += this->interval_;
12
Use of memory after it is freed
259 }
260 }
261 else
262 this->stopTimer(); // Stop the timer if we don't want to loop
263
264 this->run();
265 }
266 }
267 }
268
269 ///////////////
270 // RealTimer //
271 ///////////////
272
273 RegisterClassNoArgs(RealTimer)orxonox::SI_I& _RealTimerIdentifier = (*new orxonox::SI_I
(orxonox::registerClass<RealTimer>("RealTimer", new orxonox
::ClassFactoryNoArgs<RealTimer>(), true)))
;
274
275 /// @copydoc Timer::Timer
276 RealTimer::RealTimer()
277 {
278 RegisterObject(RealTimer)if (ClassIdentifier<RealTimer>::getIdentifier()->initializeObject
(this)) return; else ((void)0)
;
279 }
280
281 /// @copydoc Timer::Timer(float, bool, const ExecutorPtr&, bool)
282 RealTimer::RealTimer(float interval, bool bLoop, const ExecutorPtr& executor, bool bKillAfterCall) : Timer(interval, bLoop, executor, bKillAfterCall)
283 {
284 RegisterObject(RealTimer)if (ClassIdentifier<RealTimer>::getIdentifier()->initializeObject
(this)) return; else ((void)0)
;
285 }
286
287 /// Returns always 1 because RealTimer doesn't depend on the game time.
288 float RealTimer::getTimeFactor()
289 {
290 return 1;
291 }
292}