Panda3D
queuedConnectionReader.cxx
1 // Filename: queuedConnectionReader.cxx
2 // Created by: drose (08Feb00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "queuedConnectionReader.h"
16 #include "config_net.h"
17 #include "trueClock.h"
18 #include "lightMutexHolder.h"
19 
20 ////////////////////////////////////////////////////////////////////
21 // Function: QueuedConnectionReader::Constructor
22 // Access: Published
23 // Description:
24 ////////////////////////////////////////////////////////////////////
25 QueuedConnectionReader::
26 QueuedConnectionReader(ConnectionManager *manager, int num_threads) :
27  ConnectionReader(manager, num_threads)
28 {
29 #ifdef SIMULATE_NETWORK_DELAY
30  _delay_active = false;
31  _min_delay = 0.0;
32  _delay_variance = 0.0;
33 #endif // SIMULATE_NETWORK_DELAY
34 }
35 
36 ////////////////////////////////////////////////////////////////////
37 // Function: QueuedConnectionReader::Destructor
38 // Access: Published, Virtual
39 // Description:
40 ////////////////////////////////////////////////////////////////////
41 QueuedConnectionReader::
42 ~QueuedConnectionReader() {
43  // We call shutdown() here to guarantee that all threads are gone
44  // before the QueuedReturn destructs.
45  shutdown();
46 }
47 
48 ////////////////////////////////////////////////////////////////////
49 // Function: QueuedConnectionReader::data_available
50 // Access: Published
51 // Description: Returns true if a datagram is available on the queue;
52 // call get_data() to extract the datagram.
53 ////////////////////////////////////////////////////////////////////
56  poll();
57 #ifdef SIMULATE_NETWORK_DELAY
58  get_delayed();
59 #endif // SIMULATE_NETWORK_DELAY
60  return thing_available();
61 }
62 
63 ////////////////////////////////////////////////////////////////////
64 // Function: QueuedConnectionReader::get_data
65 // Access: Published
66 // Description: If a previous call to data_available() returned
67 // true, this function will return the datagram that has
68 // become available.
69 //
70 // The return value is true if a datagram was
71 // successfully returned, or false if there was, in
72 // fact, no datagram available. (This may happen if
73 // there are multiple threads accessing the
74 // QueuedConnectionReader).
75 ////////////////////////////////////////////////////////////////////
78  return get_thing(result);
79 }
80 
81 ////////////////////////////////////////////////////////////////////
82 // Function: QueuedConnectionReader::get_data
83 // Access: Published
84 // Description: This flavor of QueuedConnectionReader::get_data(),
85 // works like the other, except that it only fills a
86 // Datagram object, not a NetDatagram object. This
87 // means that the Datagram cannot be queried for its
88 // source Connection and/or NetAddress, but it is useful
89 // in all other respects.
90 ////////////////////////////////////////////////////////////////////
92 get_data(Datagram &result) {
93  NetDatagram nd;
94  if (!get_thing(nd)) {
95  return false;
96  }
97  result = nd;
98  return true;
99 }
100 
101 ////////////////////////////////////////////////////////////////////
102 // Function: QueuedConnectionReader::receive_datagram
103 // Access: Protected, Virtual
104 // Description: An internal function called by ConnectionReader()
105 // when a new datagram has become available. The
106 // QueuedConnectionReader simply queues it up for later
107 // retrieval by get_data().
108 ////////////////////////////////////////////////////////////////////
109 void QueuedConnectionReader::
110 receive_datagram(const NetDatagram &datagram) {
111  /*
112  if (net_cat.is_spam()) {
113  net_cat.spam()
114  << "Received datagram of " << datagram.get_length()
115  << " bytes\n";
116  }
117  */
118 
119 #ifdef SIMULATE_NETWORK_DELAY
120  delay_datagram(datagram);
121 
122 #else // SIMULATE_NETWORK_DELAY
123  if (!enqueue_thing(datagram)) {
124  net_cat.error()
125  << "QueuedConnectionReader queue full!\n";
126  }
127 #endif // SIMULATE_NETWORK_DELAY
128 }
129 
130 
131 #ifdef SIMULATE_NETWORK_DELAY
132 ////////////////////////////////////////////////////////////////////
133 // Function: QueuedConnectionReader::start_delay
134 // Access: Published
135 // Description: Enables a simulated network latency. All packets
136 // received from this point on will be held for a random
137 // interval of least min_delay seconds, and no more than
138 // max_delay seconds, before being visible to the
139 // data_available()/get_data() interface. It is as if
140 // packets suddenly took much longer to arrive.
141 ////////////////////////////////////////////////////////////////////
142 void QueuedConnectionReader::
143 start_delay(double min_delay, double max_delay) {
144  LightMutexHolder holder(_dd_mutex);
145  _min_delay = min_delay;
146  _delay_variance = max(max_delay - min_delay, 0.0);
147  _delay_active = true;
148 }
149 
150 ////////////////////////////////////////////////////////////////////
151 // Function: QueuedConnectionReader::stop_delay
152 // Access: Published
153 // Description: Disables the simulated network latency started by a
154 // previous call to start_delay(). Packets will once
155 // again be visible as soon as they are received.
156 ////////////////////////////////////////////////////////////////////
157 void QueuedConnectionReader::
158 stop_delay() {
159  LightMutexHolder holder(_dd_mutex);
160  _delay_active = false;
161 
162  // Copy the entire contents of the delay queue to the normal queue.
163  while (!_delayed.empty()) {
164  const DelayedDatagram &dd = _delayed.front();
165  if (!enqueue_thing(dd._datagram)) {
166  net_cat.error()
167  << "QueuedConnectionReader queue full!\n";
168  }
169  _delayed.pop_front();
170  }
171 }
172 
173 ////////////////////////////////////////////////////////////////////
174 // Function: QueuedConnectionReader::get_delayed
175 // Access: Private
176 // Description: Checks the delayed queue for any now available
177 // datagrams, and adds them to the normal queue if they
178 // are available.
179 ////////////////////////////////////////////////////////////////////
180 void QueuedConnectionReader::
181 get_delayed() {
182  if (_delay_active) {
183  LightMutexHolder holder(_dd_mutex);
184  double now = TrueClock::get_global_ptr()->get_short_time();
185  while (!_delayed.empty()) {
186  const DelayedDatagram &dd = _delayed.front();
187  if (dd._reveal_time > now) {
188  // Not yet.
189  break;
190  }
191  if (!enqueue_thing(dd._datagram)) {
192  net_cat.error()
193  << "QueuedConnectionReader queue full!\n";
194  }
195  _delayed.pop_front();
196  }
197  }
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: QueuedConnectionReader::delay_datagram
202 // Access: Private
203 // Description: Adds the datagram to the delay queue for a random
204 // time interval.
205 ////////////////////////////////////////////////////////////////////
206 void QueuedConnectionReader::
207 delay_datagram(const NetDatagram &datagram) {
208  if (!_delay_active) {
209  if (!enqueue_thing(datagram)) {
210  net_cat.error()
211  << "QueuedConnectionReader queue full!\n";
212  }
213  } else {
214  LightMutexHolder holder(_dd_mutex);
215  // Check the delay_active flag again, now that we have grabbed the
216  // mutex.
217  if (!_delay_active) {
218  if (!enqueue_thing(datagram)) {
219  net_cat.error()
220  << "QueuedConnectionReader queue full!\n";
221  }
222 
223  } else {
224  double now = TrueClock::get_global_ptr()->get_short_time();
225  double reveal_time = now + _min_delay;
226 
227  if (_delay_variance > 0.0) {
228  reveal_time += _delay_variance * ((double)rand() / (double)RAND_MAX);
229  }
230  _delayed.push_back(DelayedDatagram());
231  DelayedDatagram &dd = _delayed.back();
232  dd._reveal_time = reveal_time;
233  dd._datagram = datagram;
234  }
235  }
236 }
237 
238 #endif // SIMULATE_NETWORK_DELAY
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:81
A specific kind of Datagram, especially for sending across or receiving from a network.
Definition: netDatagram.h:43
The primary interface to the low-level networking layer in this package.
void shutdown()
Terminates all threads cleanly.
This is an abstract base class for a family of classes that listen for activity on a socket and respo...
void poll()
Explicitly polls the available sockets to see if any of them have any noise.
bool data_available()
Returns true if a datagram is available on the queue; call get_data() to extract the datagram...
Similar to MutexHolder, but for a light mutex.
bool get_data(NetDatagram &result)
If a previous call to data_available() returned true, this function will return the datagram that has...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43