router.h

Go to the documentation of this file.
00001 ///
00002 /// \file       router.h
00003 ///             Support classes for the pluggable socket routing system.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #ifndef __BARRY_ROUTER_H__
00023 #define __BARRY_ROUTER_H__
00024 
00025 #include "dll.h"
00026 #include <stdint.h>
00027 #include <map>
00028 #include <tr1/memory>
00029 #include <stdexcept>
00030 #include <pthread.h>
00031 #include "dataqueue.h"
00032 #include "error.h"
00033 #include "usbwrap.h"
00034 
00035 namespace Barry {
00036 
00037 class DataHandle;
00038 
00039 class BXEXPORT SocketRoutingQueue
00040 {
00041         friend class DataHandle;
00042 
00043 public:
00044         // Interface class for socket data callbacks
00045         // See RegisterInterest() for more information.
00046         class BXEXPORT SocketDataHandler
00047         {
00048         public:
00049                 // Called when data is received on the socket
00050                 // for which interest has been registered.
00051                 //
00052                 // The lifetime of the data parameter is only valid
00053                 // for the duration of this method call.
00054                 virtual void DataReceived(Data& data) = 0;
00055 
00056                 // Called when an error has occured on the socket
00057                 // for which interest has been registered.
00058                 //
00059                 // The lifetime of the error parameter is only valid
00060                 // for the lifetime of this method call.
00061                 virtual void Error(Barry::Error &error);
00062 
00063                 virtual ~SocketDataHandler();
00064         };
00065 
00066         typedef std::tr1::shared_ptr<SocketDataHandler> SocketDataHandlerPtr;
00067 
00068         // Simple wrapper template class for SocketDataHandler which provides a basic data recieved callback
00069         template<typename T> class SimpleSocketDataHandler : public SocketDataHandler
00070         {
00071                 void (*m_callback)(T&, Data*);
00072                 T& m_context;
00073         public:
00074                 SimpleSocketDataHandler<T>(T& context, void (*callback)(T& context, Data* data))
00075                         : m_callback(callback)
00076                         , m_context(context)
00077                 {}
00078                 virtual void DataReceived(Data& data)
00079                 {
00080                         m_callback(m_context, &data);
00081                 }
00082         };
00083 
00084         struct QueueEntry
00085         {
00086                 SocketDataHandlerPtr m_handler;
00087                 DataQueue m_queue;
00088 
00089                 QueueEntry(SocketDataHandlerPtr h)
00090                         : m_handler(h)
00091                         {}
00092         };
00093         typedef std::tr1::shared_ptr<QueueEntry>        QueueEntryPtr;
00094         typedef uint16_t                                SocketId;
00095         typedef std::map<SocketId, QueueEntryPtr>       SocketQueueMap;
00096 
00097 private:
00098         Usb::Device * volatile m_dev;
00099         volatile int m_writeEp, m_readEp;
00100 
00101         volatile bool m_interest; // true if at least one socket has an interest.
00102                                 // used to optimize the reading
00103 
00104         mutable pthread_mutex_t m_mutex;// controls access to local data, but not
00105                                 // DataQueues, as they have their own
00106                                 // locking per queue
00107 
00108         pthread_mutex_t m_readwaitMutex;
00109         pthread_cond_t m_readwaitCond;
00110         bool m_seen_usb_error;
00111         SocketDataHandlerPtr m_usb_error_dev_callback;
00112 
00113         DataQueue m_free;
00114         DataQueue m_default;
00115         SocketQueueMap m_socketQueues;
00116 
00117         int m_timeout;
00118 
00119         // thread state
00120         pthread_t m_usb_read_thread;
00121         volatile bool m_continue_reading;// set to true when the thread is created,
00122                                 // then set to false in the destructor
00123                                 // to signal the end of the thread
00124                                 // and handle the join
00125 
00126 protected:
00127         // Provides a method of returning a buffer to the free queue
00128         // after processing.  The DataHandle class calls this automatically
00129         // from its destructor.
00130         void ReturnBuffer(Data *buf);
00131 
00132         // Thread function for the simple read behaviour... thread is
00133         // created in the SpinoffSimpleReadThread() member below.
00134         static void *SimpleReadThread(void *userptr);
00135 
00136 public:
00137         SocketRoutingQueue(int prealloc_buffer_count = 4,
00138                 int default_read_timeout = USBWRAP_DEFAULT_TIMEOUT);
00139         ~SocketRoutingQueue();
00140 
00141         //
00142         // data access
00143         //
00144         int GetWriteEp() const { return m_writeEp; }
00145         int GetReadEp() const { return m_readEp; }
00146 
00147 
00148         // These functions connect the router to an external Usb::Device
00149         // object.  Normally this is handled automatically by the
00150         // Controller class, but are public here in case they are needed.
00151         //
00152         // If DoRead encounters an error, it sets a flag and stops
00153         // reading.  To recover, you should handle the Error() call in
00154         // the callback, fix the USB device, and then call
00155         // ClearUsbError() to clear the flag.
00156         //
00157         void SetUsbDevice(Usb::Device *dev, int writeEp, int readEp,
00158                 SocketDataHandlerPtr callback = SocketDataHandlerPtr());
00159         void ClearUsbDevice();
00160         bool UsbDeviceReady();
00161         Usb::Device* GetUsbDevice() { return m_dev; }
00162         void ClearUsbError();
00163 
00164 
00165         // This class starts out with no buffers, and will grow one buffer
00166         // at a time if needed.  Call this to allocate count buffers
00167         // all at once and place them on the free queue.
00168         void AllocateBuffers(int count);
00169 
00170         // Returns the data for the next unregistered socket.
00171         // Blocks until timeout or data is available.
00172         // Returns false (or null pointer) on timeout and no data.
00173         // With the return version of the function, there is no
00174         // copying performed.
00175         //
00176         // Timeout is in milliseconds.  Default timeout set by constructor
00177         // is used if set to -1.
00178         bool DefaultRead(Data &receive, int timeout = -1);
00179         DataHandle DefaultRead(int timeout = -1);
00180 
00181         // Register an interest in data from a certain socket.  To read
00182         // from that socket, use the SocketRead() function from then on.
00183         // Any non-registered socket goes in the default queue
00184         // and must be read by DefaultRead()
00185         // If not null, handler is called when new data is read.  It will
00186         // be called in the same thread instance that DoRead() is called from.
00187         // Handler is passed the DataQueue Data object, and so no
00188         // copying is done.  Once the handler returns, the data is
00189         // considered processed and not added to the interested queue,
00190         // but instead returned to m_free.
00191         void RegisterInterest(SocketId socket, SocketDataHandlerPtr handler);
00192 
00193         // Unregisters interest in data from the given socket, and discards
00194         // any existing data in its interest queue.  Any new incoming data
00195         // for this socket will be placed in the default queue.
00196         void UnregisterInterest(SocketId socket);
00197 
00198         // Reads data from the interested socket cache.  Can only read
00199         // from sockets that have been previously registered.
00200         // Blocks until timeout or data is available.
00201         // Returns false (or null pointer) on timeout and no data.
00202         // With the return version of the function, there is no
00203         // copying performed.
00204         //
00205         // Timeout is in milliseconds.  Default timeout set by constructor
00206         // is used if set to -1.
00207         bool SocketRead(SocketId socket, Data &receive, int timeout = -1);
00208         DataHandle SocketRead(SocketId socket, int timeout = -1);
00209 
00210         // Returns true if data is available for that socket.
00211         bool IsAvailable(SocketId socket) const;
00212 
00213         // Called by the application's "read thread" to read the next usb
00214         // packet and route it to the correct queue.  Returns after every
00215         // read, even if a handler is associated with a queue.
00216         // Note: this function is safe to call before SetUsbDevice() is
00217         // called... it just doesn't do anything if there is no usb
00218         // device to work with.
00219         //
00220         // Timeout is in milliseconds.  Default is default USB timeout.
00221         void DoRead(int timeout = -1);
00222 
00223         // Utility function to make it easier for the user to create the
00224         // USB pure-read thread.  If the user wants anything more complicated
00225         // in this background thread, he can implement it himself and call
00226         // the above DoRead() in a loop.  If only the basics are needed,
00227         // then this makes it easy.
00228         // Throws Barry::ErrnoError on thread creation error.
00229         void SpinoffSimpleReadThread();
00230 };
00231 
00232 
00233 //
00234 // DataHandle
00235 //
00236 /// std::auto_ptr like class that handles pointers to Data, but instead of
00237 /// freeing them completely, the Data objects are turned to the
00238 /// SocketRoutingQueue from whence they came.
00239 ///
00240 class BXEXPORT DataHandle
00241 {
00242 private:
00243         SocketRoutingQueue &m_queue;
00244         mutable Data *m_data;
00245 
00246 protected:
00247         void clear()
00248         {
00249                 if( m_data ) {
00250                         m_queue.ReturnBuffer(m_data);
00251                         m_data = 0;
00252                 }
00253         }
00254 
00255 public:
00256         DataHandle(SocketRoutingQueue &q, Data *data)
00257                 : m_queue(q)
00258                 , m_data(data)
00259         {
00260         }
00261 
00262         DataHandle(const DataHandle &other)
00263                 : m_queue(other.m_queue)
00264                 , m_data(other.m_data)
00265         {
00266                 // we now own the pointer
00267                 other.m_data = 0;
00268         }
00269 
00270         ~DataHandle()
00271         {
00272                 clear();
00273         }
00274 
00275         Data* get()
00276         {
00277                 return m_data;
00278         }
00279 
00280         Data* release() // no longer owns the pointer
00281         {
00282                 Data *ret = m_data;
00283                 m_data = 0;
00284                 return ret;
00285         }
00286 
00287         Data* operator->()
00288         {
00289                 return m_data;
00290         }
00291 
00292         const Data* operator->() const
00293         {
00294                 return m_data;
00295         }
00296 
00297         DataHandle& operator=(const DataHandle &other)
00298         {
00299                 if( &m_queue != &other.m_queue )
00300                         throw std::logic_error("Trying to copy DataHandles of different queues!");
00301 
00302                 // remove our current data
00303                 clear();
00304 
00305                 // accept the new
00306                 m_data = other.m_data;
00307 
00308                 // we now own it
00309                 other.m_data = 0;
00310 
00311                 return *this;
00312         }
00313 
00314 };
00315 
00316 
00317 } // namespace Barry
00318 
00319 #endif
00320