PageRenderTime 15ms CodeModel.GetById 1ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llpumpio.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 453 lines | 120 code | 47 blank | 286 comment | 0 complexity | 123d07f129c20bb3dbaaba3624eba769 MD5 | raw file
  1/** 
  2 * @file llpumpio.h
  3 * @author Phoenix
  4 * @date 2004-11-19
  5 * @brief Declaration of pump class which manages io chains.
  6 *
  7 * $LicenseInfo:firstyear=2004&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#ifndef LL_LLPUMPIO_H
 30#define LL_LLPUMPIO_H
 31
 32#include <set>
 33#if LL_LINUX  // needed for PATH_MAX in APR.
 34#include <sys/param.h>
 35#endif
 36
 37#include "apr_pools.h"
 38#include "llbuffer.h"
 39#include "llframetimer.h"
 40#include "lliopipe.h"
 41#include "llrun.h"
 42
 43// Define this to enable use with the APR thread library.
 44//#define LL_THREADS_APR 1
 45
 46// some simple constants to help with timeouts
 47extern const F32 DEFAULT_CHAIN_EXPIRY_SECS;
 48extern const F32 SHORT_CHAIN_EXPIRY_SECS;
 49extern const F32 NEVER_CHAIN_EXPIRY_SECS;
 50
 51/** 
 52 * @class LLPumpIO
 53 * @brief Class to manage sets of io chains.
 54 *
 55 * The pump class provides a thread abstraction for doing IO based
 56 * communication between two threads in a structured and optimized for
 57 * processor time. The primary usage is to create a pump, and call
 58 * <code>pump()</code> on a thread used for IO and call
 59 * <code>respond()</code> on a thread that is expected to do higher
 60 * level processing. You can call almost any other method from any
 61 * thread - see notes for each method for details. In order for the
 62 * threading abstraction to work, you need to call <code>prime()</code>
 63 * with a valid apr pool.
 64 * A pump instance manages much of the state for the pipe, including
 65 * the list of pipes in the chain, the channel for each element in the
 66 * chain, the buffer, and if any pipe has marked the stream or process
 67 * as done. Pipes can also set file descriptor based conditional
 68 * statements so that calls to process do not happen until data is
 69 * ready to be read or written.  Pipes control execution of calls to
 70 * process by returning a status code such as STATUS_OK or
 71 * STATUS_BREAK.
 72 * One way to conceptualize the way IO will work is that a pump
 73 * combines the unit processing of pipes to behave like file pipes on
 74 * the unix command line.
 75 */
 76class LLPumpIO
 77{
 78public:
 79	/**
 80	 * @brief Constructor.
 81	 */
 82	LLPumpIO(apr_pool_t* pool);
 83
 84	/**
 85	 * @brief Destructor.
 86	 */
 87	~LLPumpIO();
 88
 89	/**
 90	 * @brief Prepare this pump for usage.
 91	 *
 92	 * If you fail to call this method prior to use, the pump will
 93	 * try to work, but will not come with any thread locking
 94	 * mechanisms.
 95	 * @param pool The apr pool to use.
 96	 * @return Returns true if the pump is primed.
 97	 */
 98	bool prime(apr_pool_t* pool);
 99
100	/**
101	 * @brief Typedef for having a chain of pipes.
102	 */
103	typedef std::vector<LLIOPipe::ptr_t> chain_t;
104
105	/** 
106	 * @brief Add a chain to this pump and process in the next cycle.
107	 *
108	 * This method will automatically generate a buffer and assign
109	 * each link in the chain as if it were the consumer to the
110	 * previous.
111	 * @param chain The pipes for the chain
112	 * @param timeout The number of seconds in the future to
113	 * expire. Pass in 0.0f to never expire.
114	 * @param has_curl_request The chain contains LLURLRequest if true.
115	 * @return Returns true if anything was added to the pump.
116	 */
117	bool addChain(const chain_t& chain, F32 timeout, bool has_curl_request = false);
118	
119	/** 
120	 * @brief Struct to associate a pipe with it's buffer io indexes.
121	 */
122	struct LLLinkInfo
123	{
124		LLIOPipe::ptr_t mPipe;
125		LLChannelDescriptors mChannels;
126	};
127
128	/** 
129	 * @brief Typedef for having a chain of <code>LLLinkInfo</code>
130	 * instances.
131	 */
132	typedef std::vector<LLLinkInfo> links_t;
133
134	/** 
135	 * @brief Add a chain to this pump and process in the next cycle.
136	 *
137	 * This method provides a slightly more sophisticated method for
138	 * adding a chain where the caller can specify which link elements
139	 * are on what channels. This method will fail if no buffer is
140	 * provided since any calls to generate new channels for the
141	 * buffers will cause unpredictable interleaving of data.
142	 * @param links The pipes and io indexes for the chain
143	 * @param data Shared pointer to data buffer
144	 * @param context Potentially undefined context meta-data for chain.
145	 * @param timeout The number of seconds in the future to
146	 * expire. Pass in 0.0f to never expire.
147	 * @return Returns true if anything was added to the pump.
148	 */
149	bool addChain(
150		const links_t& links,
151		LLIOPipe::buffer_ptr_t data,
152		LLSD context,
153		F32 timeout);
154
155	/** 
156	 * @brief Set or clear a timeout for the running chain
157	 *
158	 * @param timeout The number of seconds in the future to
159	 * expire. Pass in 0.0f to never expire.
160	 * @return Returns true if the timer was set.
161	 */
162	bool setTimeoutSeconds(F32 timeout);
163
164	/** 
165	 * @brief Adjust the timeout of the running chain.
166	 *
167	 * This method has no effect if there is no timeout on the chain.
168	 * @param delta The number of seconds to add to/remove from the timeout.
169	 */
170	void adjustTimeoutSeconds(F32 delta);
171
172	/** 
173	 * @brief Set up file descriptors for for the running chain.
174	 * @see rebuildPollset()
175	 *
176	 * There is currently a limit of one conditional per pipe.
177	 * *NOTE: The internal mechanism for building a pollset based on
178	 * pipe/pollfd/chain generates an epoll error on linux (and
179	 * probably behaves similarly on other platforms) because the
180	 * pollset rebuilder will add each apr_pollfd_t serially. This
181	 * does not matter for pipes on the same chain, since any
182	 * signalled pipe will eventually invoke a call to process(), but
183	 * is a problem if the same apr_pollfd_t is on different
184	 * chains. Once we have more than just network i/o on the pump,
185	 * this might matter.
186	 * *FIX: Given the structure of the pump and pipe relationship,
187	 * this should probably go through a different mechanism than the
188	 * pump. I think it would be best if the pipe had some kind of
189	 * controller which was passed into <code>process()</code> rather
190	 * than the pump which exposed this interface.
191	 * @param pipe The pipe which is setting a conditional
192	 * @param poll The entire socket and read/write condition - null to remove
193	 * @return Returns true if the poll state was set.
194	 */
195	bool setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll);
196
197	/** 
198	 * @brief Lock the current chain.
199	 * @see sleepChain() since it relies on the implementation of this method.
200	 *
201	 * This locks the currently running chain so that no more calls to
202	 * <code>process()</code> until you call <code>clearLock()</code>
203	 * with the lock identifier.
204	 * *FIX: Given the structure of the pump and pipe relationship,
205	 * this should probably go through a different mechanism than the
206	 * pump. I think it would be best if the pipe had some kind of
207	 * controller which was passed into <code>process()</code> rather
208	 * than the pump which exposed this interface.
209	 * @return Returns the lock identifer to be used in
210	 * <code>clearLock()</code> or 0 on failure.
211	 */
212	S32 setLock();
213
214	/** 
215	 * @brief Clears the identified lock.
216	 *
217	 * @param links A container for the links which will be appended
218	 */
219	void clearLock(S32 key);
220
221	/**
222	 * @brief Stop processing a chain for a while.
223	 * @see setLock()
224	 *
225	 * This method will <em>not</em> update the timeout for this
226	 * chain, so it is possible to sleep the chain until it is
227	 * collected by the pump during a timeout cleanup.
228	 * @param seconds The number of seconds in the future to
229	 * resume processing.
230	 * @return Returns true if the 
231	 */
232	bool sleepChain(F64 seconds);
233
234	/** 
235	 * @brief Copy the currently running chain link info
236	 *
237	 * *FIX: Given the structure of the pump and pipe relationship,
238	 * this should probably go through a different mechanism than the
239	 * pump. I think it would be best if the pipe had some kind of
240	 * controller which was passed into <code>process()</code> rather
241	 * than the pump which exposed this interface.
242	 * @param links A container for the links which will be appended
243	 * @return Returns true if the currently running chain was copied.
244	 */
245	bool copyCurrentLinkInfo(links_t& links) const;
246
247	/** 
248	 * @brief Call this method to call process on all running chains.
249	 *
250	 * This method iterates through the running chains, and if all
251	 * pipe on a chain are unconditionally ready or if any pipe has
252	 * any conditional processiong condition then process will be
253	 * called on every chain which has requested processing.  that
254	 * chain has a file descriptor ready, <code>process()</code> will
255	 * be called for all pipes which have requested it.
256	 */
257	void pump(const S32& poll_timeout);
258	void pump();
259
260	/** 
261	 * @brief Add a chain to a special queue which will be called
262	 * during the next call to <code>callback()</code> and then
263	 * dropped from the queue.
264	 *
265	 * @param chain The IO chain that will get one <code>process()</code>.
266	 */
267	//void respond(const chain_t& pipes);
268
269	/** 
270	 * @brief Add pipe to a special queue which will be called
271	 * during the next call to <code>callback()</code> and then dropped
272	 * from the queue.
273	 *
274	 * This call will add a single pipe, with no buffer, context, or
275	 * channel information to the callback queue. It will be called
276	 * once, and then dropped.
277	 * @param pipe A single io pipe which will be called
278	 * @return Returns true if anything was added to the pump.
279	 */
280	bool respond(LLIOPipe* pipe);
281
282	/** 
283	 * @brief Add a chain to a special queue which will be called
284	 * during the next call to <code>callback()</code> and then
285	 * dropped from the queue.
286	 *
287	 * It is important to remember that you should not add a data
288	 * buffer or context which may still be in another chain - that
289	 * will almost certainly lead to a problems. Ensure that you are
290	 * done reading and writing to those parameters, have new
291	 * generated, or empty pointers. 
292	 * @param links The pipes and io indexes for the chain
293	 * @param data Shared pointer to data buffer
294	 * @param context Potentially undefined context meta-data for chain.
295	 * @return Returns true if anything was added to the pump.
296	 */
297	bool respond(
298		const links_t& links,
299		LLIOPipe::buffer_ptr_t data,
300		LLSD context);
301
302	/** 
303	 * @brief Run through the callback queue and call <code>process()</code>.
304	 *
305	 * This call will process all prending responses and call process
306	 * on each. This method will then drop all processed callback
307	 * requests which may lead to deleting the referenced objects.
308	 */
309	void callback();
310
311	/** 
312	 * @brief Enumeration to send commands to the pump.
313	 */
314	enum EControl
315	{
316		PAUSE,
317		RESUME,
318	};
319	
320	/** 
321	 * @brief Send a command to the pump.
322	 *
323	 * @param op What control to send to the pump.
324	 */
325	void control(EControl op);
326
327protected:
328	/** 
329	 * @brief State of the pump
330	 */
331	enum EState
332	{
333		NORMAL,
334		PAUSING,
335		PAUSED
336	};
337
338	// instance data
339	EState mState;
340	bool mRebuildPollset;
341	apr_pollset_t* mPollset;
342	S32 mPollsetClientID;
343	S32 mNextLock;
344	std::set<S32> mClearLocks;
345
346	// This is the pump's runnable scheduler used for handling
347	// expiring locks.
348	LLRunner mRunner;
349
350	// This structure is the stuff we track while running chains.
351	struct LLChainInfo
352	{
353		// methods
354		LLChainInfo();
355		void setTimeoutSeconds(F32 timeout);
356		void adjustTimeoutSeconds(F32 delta);
357
358		// basic member data
359		bool mInit;
360		bool mEOS;
361		bool mHasCurlRequest;
362		S32 mLock;
363		LLFrameTimer mTimer;
364		links_t::iterator mHead;
365		links_t mChainLinks;
366		LLIOPipe::buffer_ptr_t mData;		
367		LLSD mContext;
368
369		// tracking inside the pump
370		typedef std::pair<LLIOPipe::ptr_t, apr_pollfd_t> pipe_conditional_t;
371		typedef std::vector<pipe_conditional_t> conditionals_t;
372		conditionals_t mDescriptors;
373	};
374
375	// All the running chains & info
376 	typedef std::vector<LLChainInfo> pending_chains_t;
377	pending_chains_t mPendingChains;
378	typedef std::list<LLChainInfo> running_chains_t;
379	running_chains_t mRunningChains;
380
381	typedef running_chains_t::iterator current_chain_t;
382	current_chain_t mCurrentChain;
383
384	// structures necessary for doing callbacks
385	// since the callbacks only get one chance to run, we do not have
386	// to maintain a list.
387	typedef std::vector<LLChainInfo> callbacks_t;
388	callbacks_t mPendingCallbacks;
389	callbacks_t mCallbacks;
390
391	// memory allocator for pollsets & mutexes.
392	apr_pool_t* mPool;
393	apr_pool_t* mCurrentPool;
394	S32 mCurrentPoolReallocCount;
395
396#if LL_THREADS_APR
397	apr_thread_mutex_t* mChainsMutex;
398	apr_thread_mutex_t* mCallbackMutex;
399#else
400	int* mChainsMutex;
401	int* mCallbackMutex;
402#endif
403
404protected:
405	void initialize(apr_pool_t* pool);
406	void cleanup();
407	current_chain_t removeRunningChain(current_chain_t& chain) ;
408	/** 
409	 * @brief Given the internal state of the chains, rebuild the pollset
410	 * @see setConditional()
411	 */
412	void rebuildPollset();
413
414	/** 
415	 * @brief Process the chain passed in.
416	 *
417	 * This method will potentially modify the internals of the
418	 * chain. On end, the chain.mHead will equal
419	 * chain.mChainLinks.end().
420	 * @param chain The LLChainInfo object to work on.
421	 */
422	void processChain(LLChainInfo& chain);
423
424	/** 
425	 * @brief Rewind through the chain to try to recover from an error.
426	 *
427	 * This method will potentially modify the internals of the
428	 * chain.
429	 * @param chain The LLChainInfo object to work on.
430	 * @return Retuns true if someone handled the error
431	 */
432	bool handleChainError(LLChainInfo& chain, LLIOPipe::EStatus error);
433
434	//if the chain is expired, remove it
435	bool isChainExpired(LLChainInfo& chain) ;
436
437public:
438	/** 
439	 * @brief Return number of running chains.
440	 *
441	 * *NOTE: This is only used in debugging and not considered
442	 * efficient or safe enough for production use.
443	 */
444	running_chains_t::size_type runningChains() const
445	{
446		return mRunningChains.size();
447	}
448
449
450};
451
452
453#endif // LL_LLPUMPIO_H