1 #ifndef Platoon_H
2 #define Platoon_H
3 #include "robouser.h"
4 #include <list>
5 #include <cstring>
6 
7 /* FIXME: 'reasonable' upper bound on number of users CPU can handle */
8 #define Platoon_MAXUSERS (1 << 16)
9 
10 /**----------------------------------------------------------------------
11  Platoon of robousers.
12 ----------------------------------------------------------------------*/
13 class Platoon : public SkedClient {
14 public:
15 	/**----------------------------------------------------------------------
16 	 Set up parameters common to all instances of this class,
17 	 and initialize the container for all instances.
18 	 sked is a pointer to the Sked object to use to schedule delayed actions,
19 	 and whose runAll() method should be called periodically by main().
20 
21 	 filename is what file to fetch from the remote server (will be a template
22 	 later).
23 	 maxBytesPerSec sets how much bandwidth each client tries to use.
24 	 minBytesPerSec is the lowest bandwidth per client we accept (else abort).
25 	 bytesPerRead sets how many bytes to read each time.
26 	 servername and port specify the server and port the user will connect to.
27 	 username and passwd are the user's username and password.
28 	 @param local_addrs 0, or array of local addresses to assign to users
29 	 @param n_local_addrs number of elements in local_addrs
30 	----------------------------------------------------------------------*/
31 	void init(Poller *poller, Sked *sked, const char *filename,
32 		int maxBytesPerSec, int minBytesPerSec, int bytesPerRead,
33 		const char *servername, int port,
34 		const char *username, const char *passwd,
35 		struct sockaddr_in *local_addrs, int n_local_addrs);
36 
37 	/**----------------------------------------------------------------------
38 	 Stops any activity.  After this call, you may call init again.
39 	----------------------------------------------------------------------*/
40 	void reset();
41 
42 	/**----------------------------------------------------------------------
43 	 Set desired number of simulated users.  Platoon will ramp up to this.
44 	----------------------------------------------------------------------*/
set_nuserTarget(int utarget)45 	void set_nuserTarget(int utarget) {
46 		m_nuserTarget = utarget;
47 		wakeUp();
48 	}
49 
50 	/**----------------------------------------------------------------------
51 	 How many simulated users should try to connect at once?
52 	 Default is 1.  Higher values make Platoon ramp up more quickly.
53 	----------------------------------------------------------------------*/
set_nconnectingTarget(int ctarget)54 	void set_nconnectingTarget(int ctarget) {
55 		m_nconnectingTarget = ctarget;
56 		wakeUp();
57 	}
58 
59 	/**----------------------------------------------------------------------
60 	 What's the last state in which a robouser is considered to be connecting?
61 	 Default value is robouser_t::CONNECTING (after tcp connection complete).
62 	 Other possible value is robouser_t::CONNECT (when tcp connection starts).
63 	 Lower numerical values make Platoon ramp up more quickly.
64 	----------------------------------------------------------------------*/
set_lastConnectingState(robouser_t::state_t s)65 	void set_lastConnectingState(robouser_t::state_t s) {
66 		m_lastConnectingState = s;
67 		wakeUp();
68 	}
69 
70 	/**----------------------------------------------------------------------
71 	 Call this periodically to check on status of the clients.
72 	 *nconnecting = # of robousers still trying to connect
73 	 *nalive      = # of robousers who connected ok and are in good shape
74 	 *ndead       = # of robousers who have failed, and are out of action
75 
76 	 Returns total number of bytes transferred so far.
77 	----------------------------------------------------------------------*/
78 	long getStatus(int *nconnecting, int *nalive, int *ndead);
79 
80 	/**----------------------------------------------------------------------
81 	 Reap any dead users.  Call this after each call to Sked->runAll().
82 	----------------------------------------------------------------------*/
83 	void reap();
84 
~Platoon()85 	virtual ~Platoon() {}
86 
87 // Private methods
88 private:
89 	/** Schedule a wake-up call to check if we should start a new connection. */
wakeUp()90 	void wakeUp() {
91 		m_sked->delClient(this);	/* just in case some event is pending */
92 		m_sked->addClient(this, eclock()); /* Prepare check */
93 	}
94 
95 	/// Return the sum of m_numInState[first ... last]
96 	int sumInState(robouser_t::state_t first, robouser_t::state_t last);
97 
98 	/**----------------------------------------------------------------------
99 	 Start another one of the robousers.
100 	 getStatus() should be called periodically to check on their status.
101 	----------------------------------------------------------------------*/
102 	int startUser();
103 
104 private:
105 
106 	/// How we get network events from the operating system
107 	Poller *m_poller;
108 
109 	/// The server part of the URL to fetch
110 	char m_servername[128];
111 	/// The port number part of the URL to fetch
112 	int  m_port;
113 	/// The user name part of the URL to fetch
114 	char m_username[128];
115 	/// The password part of the URL to fetch
116 	char m_passwd[128];
117 	/// Requested Lower bound on bytes each user should read per second
118 	int m_minBytesPerSec;
119 	/// Requested Upper bound on bytes each user should read per second
120 	int m_maxBytesPerSec;
121 	/// Requested number of bytes each user should read at a time
122 	int m_bytesPerRead;
123 	/// Filename part of the URL to fetch
124 	const char *m_filename;
125 
126 	/// whether to print stuff out
127 	int m_verbosity;
128 
129 	/** How we schedule timeouts */
130 	Sked *m_sked;
131 
132 	/// How we deterimine whether a session is still connecting
133 	robouser_t::state_t m_lastConnectingState;
134 
135 	/** Desired number of sessions in process of connecting.
136 	    sum(m_nInState[CONNECT,CONNECTING]) aspires to reach this goal.
137 	 */
138 	int m_nconnectingTarget;
139 
140 	/** Desired number of users.
141 	    sum(m_nInState[CONNECT,CONNECTING,GET,GETTING]) aspires to reach this goal.
142 	 */
143 	int m_nuserTarget;
144 
145 	/// Count of users in each state
146 	int m_nInState[robouser_t::NUMSTATES];
147 
148 	/* Each element is NULL or a nondead user */
149 	robouser_t *m_users[Platoon_MAXUSERS];
150 
151 	/** List of dead robousers waiting to be reaped */
152 	std::list<robouser_t *> m_deadlist;
153 
154 	/** Health: count of bytes fetched so far from all files */
155 	size_t m_bytesFetched;
156 
157 	/** Health: count of reads performed so far from all files */
158 	int m_nreads;
159 
160 	/** pool of local addresses, or NULL */
161 	struct sockaddr_in *m_local_addrs;
162 
163 	/** size of pool */
164 	int m_n_local_addrs;
165 
166 // Accessors for robouser use
167 public:
getLocalAddrs()168 	struct sockaddr_in *getLocalAddrs() { return m_local_addrs; }
getNLocalAddrs()169 	int getNLocalAddrs() { return m_n_local_addrs; }
getSked()170 	Sked *getSked() { return m_sked; }
getMaxBytesPerSec()171 	int getMaxBytesPerSec() { return m_maxBytesPerSec; }
getPoller()172 	Poller *getPoller() { return m_poller; }
getBytesPerRead()173 	int getBytesPerRead() { return m_bytesPerRead; }
getFilename()174 	const char *getFilename() { return m_filename; }
getMinBytesPerSec()175 	int getMinBytesPerSec() { return m_minBytesPerSec; }
getServername()176 	const char *getServername() { return m_servername; }
getPort()177 	int getPort() { return m_port; }
getUsername()178 	const char *getUsername() { return m_username; }
getPasswd()179 	const char *getPasswd() { return m_passwd; }
setVerbosity(int v)180 	void setVerbosity(int v) { m_verbosity = v; }
getVerbosity()181 	int getVerbosity() { return m_verbosity; }
182 
183 	/**----------------------------------------------------------------------
184 	 Update counts on state change.  For robouser use only.
185 	----------------------------------------------------------------------*/
186 	void countStateChange(int user, robouser_t::state_t oldstate, robouser_t::state_t newstate);
187 
188 // Mutators
addToDeadlist(robouser_t * corpse)189 	void addToDeadlist(robouser_t *corpse) { m_deadlist.push_back(corpse); }
incBytesFetched(int nread)190 	void incBytesFetched(int nread) { m_bytesFetched += nread; }
incNReads()191 	void incNReads() { m_nreads++; }
192 
193 /* Callback functions - called by Sked only */
194 
195 	/**----------------------------------------------------------------------
196 	 When the time specified by addClient() has elapsed, Sked calls this method.
197 	----------------------------------------------------------------------*/
198 	void skedCallback(clock_t now);
199 };
200 #endif
201