1 /* Declarations and macros for managing simulated time.
2 Copyright 2002, 2004 Paul Twohey and Brian Gaeke.
3
4 This file is part of VMIPS.
5
6 VMIPS is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10
11 VMIPS is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with VMIPS; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 #include "clock.h"
21 #include "task.h"
22 #include "error.h"
23 #include "wipe.h"
24
25 #include <algorithm>
26 #include <cassert>
27 #include <functional>
28
29 using namespace std;
30
31
Clock(const timespec & start_time)32 Clock::Clock( const timespec &start_time )
33 : time( start_time ), spill_ns(0)
34 {
35 assert( time.tv_sec >= 0 );
36 assert( time.tv_nsec >=0 );
37
38 then.tv_sec = -1;
39 then.tv_nsec = 0;
40 }
41
~Clock()42 Clock::~Clock()
43 {
44 for_each( deferred_tasks.begin(), deferred_tasks.end(),
45 wipe< DeferredTasks * > );
46 }
47
increment_time(long nanoseconds)48 void Clock::increment_time( long nanoseconds )
49 {
50 assert( nanoseconds > 0 );
51
52 timespec increment = { 0, nanoseconds };
53 timespecadd( &this->time, &increment );
54
55 if( deferred_tasks.empty() )
56 return;
57
58 for( DeferredTasks *tasks = deferred_tasks.front();
59 !deferred_tasks.empty(); tasks = deferred_tasks.front() ) {
60 nanoseconds = tasks->pass_time( nanoseconds );
61 if( nanoseconds <= 0 ) {
62 deferred_tasks.pop_front();
63 delete tasks;
64 }
65
66 if( nanoseconds >= 0 )
67 break;
68
69 nanoseconds = -nanoseconds;
70 }
71 }
72
pass_realtime(uint32 timeratio)73 void Clock::pass_realtime( uint32 timeratio )
74 {
75 assert( timeratio > 0 );
76
77 timeval current;
78
79 // first time pass_realtime() has been called
80 if( then.tv_sec == -1 ) {
81 gettimeofday( ¤t, NULL );
82 TIMEVAL_TO_TIMESPEC( ¤t, &then );
83 }
84
85 gettimeofday( ¤t, NULL );
86 timespec now;
87 TIMEVAL_TO_TIMESPEC( ¤t, &now );
88
89 timespecsub( &now , &then );
90 while( now.tv_sec > 0 ) {
91 increment_time( 1000000000 / timeratio );
92 now.tv_sec--;
93 }
94
95 long increase = (now.tv_nsec + spill_ns) / timeratio;
96 if( increase == 0 )
97 spill_ns = now.tv_nsec + spill_ns;
98 else {
99 spill_ns = 0;
100 increment_time( increase );
101 }
102
103 TIMEVAL_TO_TIMESPEC( ¤t, &then );
104 }
105
add_deferred_task(Task * task,long nanoseconds)106 void Clock::add_deferred_task( Task *task, long nanoseconds )
107 {
108 assert( task );
109 assert( nanoseconds > 0 );
110
111 if( deferred_tasks.empty() ) {
112 DeferredTasks *t = new DeferredTasks( nanoseconds, task );
113 deferred_tasks.push_front( t );
114 return;
115 }
116
117 long wait_time = nanoseconds;
118 for( list< DeferredTasks*>::iterator i = deferred_tasks.begin();
119 i != deferred_tasks.end(); i++ ) {
120 wait_time = nanoseconds;
121 nanoseconds -= (*i)->get_nanoseconds_left();
122
123 // insert a new set of deferred tasks before i
124 if( nanoseconds < 0 ) {
125 DeferredTasks *tasks=new DeferredTasks(wait_time,task);
126 deferred_tasks.insert( i, tasks );
127
128 // decrement the waiting time for the next task by
129 // wait_time so the task executes at the right time
130 (*i)->pass_time( wait_time );
131
132 return;
133 }
134 // the wait time for TASK is the same as these tasks
135 else if( nanoseconds == 0 ) {
136 (*i)->add_task( task );
137 return;
138 }
139 }
140
141 DeferredTasks *tasks = new DeferredTasks( nanoseconds, task );
142 deferred_tasks.push_back( tasks );
143 }
144
get_time()145 timespec Clock::get_time()
146 {
147 return time;
148 }
149
set_time(const timespec & new_time)150 void Clock::set_time( const timespec& new_time )
151 {
152 assert( new_time.tv_sec >= 0 && new_time.tv_nsec >= 0 );
153
154 time = new_time;
155 }
156
157
DeferredTasks(long nanoseconds_left,Task * task)158 Clock::DeferredTasks::DeferredTasks( long nanoseconds_left, Task *task)
159 : nanoseconds_left( nanoseconds_left )
160 {
161 assert( nanoseconds_left > 0 );
162 add_task( task );
163 }
164
~DeferredTasks()165 Clock::DeferredTasks::~DeferredTasks()
166 {
167 for_each( tasks.begin(), tasks.end(), wipe< Task * > );
168 }
169
add_task(Task * task)170 void Clock::DeferredTasks::add_task( Task *task )
171 {
172 assert( task );
173 tasks.push_back( task );
174 }
175
pass_time(long nanoseconds)176 long Clock::DeferredTasks::pass_time( long nanoseconds )
177 {
178 assert( nanoseconds > 0 );
179 assert( nanoseconds_left > 0 );
180 nanoseconds_left -= nanoseconds;
181
182 if( nanoseconds_left <= 0 )
183 for_each( tasks.begin(), tasks.end(), mem_fun( &Task::task) );
184
185 return nanoseconds_left;
186 }
187
get_nanoseconds_left()188 inline long Clock::DeferredTasks::get_nanoseconds_left()
189 {
190 return nanoseconds_left;
191 }
192