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( &current, NULL );
82 		TIMEVAL_TO_TIMESPEC( &current, &then );
83 	}
84 
85 	gettimeofday( &current, NULL );
86 	timespec now;
87 	TIMEVAL_TO_TIMESPEC( &current, &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( &current, &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