Panda3D
pStatServer.cxx
1 // Filename: pStatServer.cxx
2 // Created by: drose (09Jul00)
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 "pStatServer.h"
16 #include "pStatReader.h"
17 #include "thread.h"
18 #include "config_pstats.h"
19 
20 ////////////////////////////////////////////////////////////////////
21 // Function: PStatServer::Constructor
22 // Access: Public
23 // Description:
24 ////////////////////////////////////////////////////////////////////
25 PStatServer::
26 PStatServer() {
27  _listener = new PStatListener(this);
28  _next_udp_port = 0;
29 }
30 
31 ////////////////////////////////////////////////////////////////////
32 // Function: PStatServer::Destructor
33 // Access: Public
34 // Description:
35 ////////////////////////////////////////////////////////////////////
36 PStatServer::
37 ~PStatServer() {
38  delete _listener;
39 }
40 
41 
42 ////////////////////////////////////////////////////////////////////
43 // Function: PStatServer::listen
44 // Access: Public
45 // Description: Establishes a port number that the manager will
46 // listen on for TCP connections. This may be called
47 // more than once to listen simulataneously on multiple
48 // connections, as if that were at all useful.
49 //
50 // The default parameter, -1, indicates the use of
51 // whatever port number has been indicated in the Config
52 // file.
53 //
54 // This function returns true if the port was
55 // successfully opened, or false if it could not open
56 // the port.
57 ////////////////////////////////////////////////////////////////////
58 bool PStatServer::
59 listen(int port) {
60  if (port < 0) {
61  port = pstats_port;
62  }
63 
64  // Now try to listen to the port.
65  PT(Connection) rendezvous = open_TCP_server_rendezvous(port, 5);
66 
67  if (rendezvous.is_null()) {
68  // Couldn't get it.
69  return false;
70  }
71 
72  // Tell the listener about the new port.
73  _listener->add_connection(rendezvous);
74 
75  if (_next_udp_port == 0) {
76  _next_udp_port = port + 1;
77  }
78  return true;
79 }
80 
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: PStatServer::poll
84 // Access: Public
85 // Description: Checks for any network activity and handles it, if
86 // appropriate, and then returns. This must be called
87 // periodically unless is_thread_safe() is redefined to
88 // return true on this class and also on all
89 // PStatMonitors in use.
90 //
91 // Alternatively, a program may call main_loop() and
92 // yield control of the program entirely to the
93 // PStatServer.
94 ////////////////////////////////////////////////////////////////////
95 void PStatServer::
96 poll() {
97  // Delete all the readers that we couldn't delete before.
98  while (!_lost_readers.empty()) {
99  PStatReader *reader = _lost_readers.back();
100  _lost_readers.pop_back();
101 
102  reader->lost_connection();
103  delete reader;
104  }
105  while (!_removed_readers.empty()) {
106  PStatReader *reader = _removed_readers.back();
107  _removed_readers.pop_back();
108  delete reader;
109  }
110 
111  _listener->poll();
112 
113  Readers::const_iterator ri = _readers.begin();
114  while (ri != _readers.end()) {
115  // Preincrement the iterator, in case we remove it as a result of
116  // calling poll().
117  Readers::const_iterator rnext = ri;
118  ++rnext;
119  PStatReader *reader = (*ri).second;
120 
121  reader->poll();
122  reader->idle();
123 
124  ri = rnext;
125  }
126 }
127 
128 ////////////////////////////////////////////////////////////////////
129 // Function: PStatServer::main_loop
130 // Access: Public
131 // Description: An alternative to repeatedly calling poll(), this
132 // function yields control of the program to the
133 // PStatServer. It does not return until the program
134 // is done.
135 //
136 // If interrupt_flag is non-NULL, it is the address of a
137 // bool variable that is initially false, and may be
138 // asynchronously set true to indicate the loop should
139 // terminate.
140 ////////////////////////////////////////////////////////////////////
141 void PStatServer::
142 main_loop(bool *interrupt_flag) {
143  while (interrupt_flag == (bool *)NULL || !*interrupt_flag) {
144  poll();
145  Thread::sleep(0.1);
146  }
147 }
148 
149 ////////////////////////////////////////////////////////////////////
150 // Function: PStatServer::add_reader
151 // Access: Public
152 // Description: Adds the newly-created PStatReader to the list of
153 // currently active readers.
154 ////////////////////////////////////////////////////////////////////
155 void PStatServer::
156 add_reader(Connection *connection, PStatReader *reader) {
157  _readers[connection] = reader;
158 }
159 
160 ////////////////////////////////////////////////////////////////////
161 // Function: PStatServer::remove_reader
162 // Access: Public
163 // Description: Removes the indicated reader.
164 ////////////////////////////////////////////////////////////////////
165 void PStatServer::
166 remove_reader(Connection *connection, PStatReader *reader) {
167  Readers::iterator ri;
168  ri = _readers.find(connection);
169  if (ri == _readers.end() || (*ri).second != reader) {
170  nout << "Attempt to remove undefined reader.\n";
171  } else {
172  _readers.erase(ri);
173  _removed_readers.push_back(reader);
174  }
175 }
176 
177 ////////////////////////////////////////////////////////////////////
178 // Function: PStatServer::get_udp_port
179 // Access: Public
180 // Description: Returns a new port number that will probably be free
181 // to use as a UDP port. The caller should be prepared
182 // to accept the possibility that it will be already in
183 // use by another process, however.
184 ////////////////////////////////////////////////////////////////////
185 int PStatServer::
187  if (_available_udp_ports.empty()) {
188  return _next_udp_port++;
189  }
190  int udp_port = _available_udp_ports.front();
191  _available_udp_ports.pop_front();
192  return udp_port;
193 }
194 
195 ////////////////////////////////////////////////////////////////////
196 // Function: PStatServer::release_udp_port
197 // Access: Public
198 // Description: Indicates that the given UDP port is once again free
199 // for use.
200 ////////////////////////////////////////////////////////////////////
201 void PStatServer::
202 release_udp_port(int port) {
203  _available_udp_ports.push_back(port);
204 }
205 
206 ////////////////////////////////////////////////////////////////////
207 // Function: PStatServer::get_num_user_guide_bars
208 // Access: Public
209 // Description: Returns the current number of user-defined guide
210 // bars.
211 ////////////////////////////////////////////////////////////////////
212 int PStatServer::
214  return _user_guide_bars.size();
215 }
216 
217 ////////////////////////////////////////////////////////////////////
218 // Function: PStatServer::get_user_guide_bar_height
219 // Access: Public
220 // Description: Returns the height of the nth user-defined guide bar.
221 ////////////////////////////////////////////////////////////////////
222 double PStatServer::
224  nassertr(n >= 0 && n < (int)_user_guide_bars.size(), 0.0f);
225  return _user_guide_bars[n];
226 }
227 
228 ////////////////////////////////////////////////////////////////////
229 // Function: PStatServer::move_user_guide_bar
230 // Access: Public
231 // Description: Adjusts the height of the nth user-defined guide bar.
232 ////////////////////////////////////////////////////////////////////
233 void PStatServer::
234 move_user_guide_bar(int n, double height) {
235  nassertv(n >= 0 && n < (int)_user_guide_bars.size());
236  _user_guide_bars[n] = height;
237  user_guide_bars_changed();
238 }
239 
240 ////////////////////////////////////////////////////////////////////
241 // Function: PStatServer::add_user_guide_bar
242 // Access: Public
243 // Description: Creates a new user guide bar and returns its index
244 // number.
245 ////////////////////////////////////////////////////////////////////
246 int PStatServer::
247 add_user_guide_bar(double height) {
248  int n = (int)_user_guide_bars.size();
249  _user_guide_bars.push_back(height);
250  user_guide_bars_changed();
251 
252  return n;
253 }
254 
255 ////////////////////////////////////////////////////////////////////
256 // Function: PStatServer::remove_user_guide_bar
257 // Access: Public
258 // Description: Removes the user guide bar with the indicated index
259 // number. All subsequent index numbers are adjusted
260 // down one.
261 ////////////////////////////////////////////////////////////////////
262 void PStatServer::
264  nassertv(n >= 0 && n < (int)_user_guide_bars.size());
265  _user_guide_bars.erase(_user_guide_bars.begin() + n);
266  user_guide_bars_changed();
267 }
268 
269 ////////////////////////////////////////////////////////////////////
270 // Function: PStatServer::find_user_guide_bar
271 // Access: Public
272 // Description: Returns the index number of the first user guide bar
273 // found whose height is within the indicated range, or
274 // -1 if no user guide bars fall within the range.
275 ////////////////////////////////////////////////////////////////////
276 int PStatServer::
277 find_user_guide_bar(double from_height, double to_height) const {
278  GuideBars::const_iterator gbi;
279  for (gbi = _user_guide_bars.begin();
280  gbi != _user_guide_bars.end();
281  ++gbi) {
282  double height = (*gbi);
283  if (height >= from_height && height <= to_height) {
284  return (int)(gbi - _user_guide_bars.begin());
285  }
286  }
287 
288  return -1;
289 }
290 
291 ////////////////////////////////////////////////////////////////////
292 // Function: PStatServer::user_guide_bars_changed
293 // Access: Priate
294 // Description: Called when the user guide bars have been changed.
295 ////////////////////////////////////////////////////////////////////
296 void PStatServer::
297 user_guide_bars_changed() {
298  Readers::iterator ri;
299  for (ri = _readers.begin(); ri != _readers.end(); ++ri) {
300  (*ri).second->get_monitor()->user_guide_bars_changed();
301  }
302 }
303 
304 ////////////////////////////////////////////////////////////////////
305 // Function: PStatServer::is_thread_safe
306 // Access: Public
307 // Description: This should be redefined to return true in derived
308 // classes that want to deal with multithreaded readers
309 // and such. If this returns true, the manager will
310 // create the listener in its own thread, and thus the
311 // PStatReader constructors at least will run in a
312 // different thread.
313 //
314 // This is not related to the question of whether the
315 // reader can handle multiple different
316 // PStatThreadDatas; it's strictly a question of whether
317 // the readers themselves can run in a separate thread.
318 ////////////////////////////////////////////////////////////////////
319 bool PStatServer::
321  return false;
322 }
323 
324 ////////////////////////////////////////////////////////////////////
325 // Function: PStatServer::connection_reset
326 // Access: Protected, Virtual
327 // Description: Called when a lost connection is detected by the net
328 // code, this should pass the word on to the interested
329 // parties and clean up gracefully.
330 ////////////////////////////////////////////////////////////////////
331 void PStatServer::
332 connection_reset(const PT(Connection) &connection, bool okflag) {
333  // Was this a client connection? Tell the reader about it if it
334  // was.
335  close_connection(connection);
336 
337  Readers::iterator ri;
338  ri = _readers.find(connection);
339  if (ri != _readers.end()) {
340  PStatReader *reader = (*ri).second;
341  _readers.erase(ri);
342 
343  // Unfortunately, we can't delete the reader right away, because
344  // we might have been called from a method on the reader! We'll
345  // have to save the reader pointer and delete it some time later.
346  _lost_readers.push_back(reader);
347  }
348 }
int find_user_guide_bar(double from_height, double to_height) const
Returns the index number of the first user guide bar found whose height is within the indicated range...
int add_user_guide_bar(double height)
Creates a new user guide bar and returns its index number.
void remove_user_guide_bar(int n)
Removes the user guide bar with the indicated index number.
int get_udp_port()
Returns a new port number that will probably be free to use as a UDP port.
void idle()
Called each frame to do what needs to be done for the monitor&#39;s user-defined idle routines...
int get_num_user_guide_bars() const
Returns the current number of user-defined guide bars.
void poll()
Explicitly polls the available sockets to see if any of them have any noise.
double get_user_guide_bar_height(int n) const
Returns the height of the nth user-defined guide bar.
This is the class that does all the work for handling communications from a single Panda client...
Definition: pStatReader.h:44
This is the TCP rendezvous socket listener.
Definition: pStatListener.h:32
void lost_connection()
This is called by the PStatServer when it detects that the connection has been lost.
void add_reader(Connection *connection, PStatReader *reader)
Adds the newly-created PStatReader to the list of currently active readers.
void remove_reader(Connection *connection, PStatReader *reader)
Removes the indicated reader.
void poll()
Checks for any network activity and handles it, if appropriate, and then returns. ...
Definition: pStatServer.cxx:96
void move_user_guide_bar(int n, double height)
Adjusts the height of the nth user-defined guide bar.
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
Definition: thread.I:236
virtual bool is_thread_safe()
This should be redefined to return true in derived classes that want to deal with multithreaded reade...
bool add_connection(Connection *connection)
Adds a new socket to the list of sockets the ConnectionReader will monitor.
bool listen(int port=-1)
Establishes a port number that the manager will listen on for TCP connections.
Definition: pStatServer.cxx:59
void main_loop(bool *interrupt_flag=NULL)
An alternative to repeatedly calling poll(), this function yields control of the program to the PStat...
Represents a single TCP or UDP socket for input or output.
Definition: connection.h:32
void release_udp_port(int port)
Indicates that the given UDP port is once again free for use.
bool close_connection(const PT(Connection) &connection)
Terminates a UDP or TCP socket previously opened.