#ifndef __SocketInfo_h_ #define __SocketInfo_h_ #include #include #include #include #include #include #include #include "MiscSupport.h" class SocketInfo : NoAssign, NoCopy { public: enum Valid { valid, // Everything was created okay. invalidRetry, /* The initial creation failed, but there is every * reason to try again. man 2 accept for more info */ invalidFail, /* This failed for unexpected reason. Consider this a * failed assertion and a bug in the code, if you are * creating this in an accept() statement. */ invalidTooMany /* Bind failed with an error of too many open files. This * is a strange one. Naievely, this would be an * invalidRetry, because sooner or later someone should * close a socket, making a new socket available. In this * application that doesn't work. The problem is that, * if a thread gets stuck in a bad place, we can't * successfully close any sockets. So this demands an * immediate abort, as the only fix. Even without this * feature of the application's design, this would be a * tricky error. I'm not sure what happens when we get * this message. Does the attempted connection get closed * immediately? Do we drive the CPU to 100% as we * constantly retry the same connection? If we want to * try to recover, some research is required. */ }; typedef uint64_t SerialNumber; private: int _socketHandle; bool _EOF; // This field says that we are in the process of deleting this object. When // someone requests that we delete a SocketInfo object, // DeleteSocketThread::deleteSocket() will set this to true, then it will // notify all threads to start their cleanup. It will delete this object // after all threads have confirmed the delete request. // // This information used to be stored in a std::set in Messages.C called // currentlyGoingDown. That variable had to be protected by a mutex. // By moving this information into each socket we can remove a global // variable, and we no longer need to a mutex to read or write this value. volatile int _goingDown; Valid _valid; struct sockaddr_in incoming_addr; mutable std::string _remoteAddr; // mutable because it's cached. static SerialNumber _lastSerialNumber; static SerialNumber getNextSerialNumber(); SerialNumber _serialNumber; const int64_t _validator; int64_t expectedValidator() const; public: Valid getValid() const { return _valid; } std::string getValidString() const; SocketInfo(int parentSocket); SocketInfo(std::string host, std::string port); // isGoingDown() is really aimed at the RequestQueue class. I suppose other // people could call isGoingDown() if they wanted to know sooner, but most // code will wait until it gets a message from DeleteSocketThread in the // queue. bool isGoingDown() const { return _goingDown; } // setGoingDown() should only be called by // DeleteSocketThread::deleteSocket(). deleteSocket() will do a number of // things in a specific order to make sure that all threads get the cleanup // message. // // This will return the value that isGoingDown() would have returned before // this call. This is atomic. If you make multiple calls to setGoingDown(), // the first one will return false and all others will return true. This // works even in a multithreaded program. bool setGoingDown() { return __sync_val_compare_and_swap(&_goingDown, false, true); } // This function should really only be used in poll() or select(). Otherwise // use the methods of this class. int getSocketHandle() const { return _socketHandle; } bool atEOF() const { return _EOF; } std::string readOnce(); ssize_t write(std::string const &buffer, int maxCount) const; ssize_t write(std::string const &buffer) const; ssize_t write(const char *buffer, int maxCount) const; ~SocketInfo(); std::string remoteAddr() const; void setRemoteAddr(std::string value); // This is a wrapper around the shutdown() system call. This will notify the // remote side that we've shut down, but will keep the internal resources. // In particular, the file descriptor will not be reused until the call to // close(). close() will automatically call shutdown(), but sometimes you // want to call shutdown sooner. void shutDown() const; // This is a unique value. 1 is used for the first socket, and numbers // count from there. This is mostly aimed at the log file. It is safe // to call this on the null object. In that case we return 0, which is // not used by any real socket. static SerialNumber getSerialNumber(SocketInfo* socket) { return socket?socket->_serialNumber:0; } // In the past getSerialNumber() worked even if the "this" pointer was null. // Newer versions of the compiler complained, so we changed getSerialNumber() // to be a static function. That works but sometimes it's a pain. So now // you have two choices. // Call getSerialNumber() if the socket might be null. // Call mySerialNumber() if you know the socket is not null. // I could have named this getSerialNumber(), like the original. But I // wanted to make sure that old code didn't compile. If there's still any // old code around that tried to call object.getSerialNumber() the compiler // will tell us, and we can look closely and decide which of these two // functions to use. SerialNumber mySerialNumber() const { return _serialNumber; } // This has nothing to do with the underlying connection. This says that // the object has been constructed and has not yet been destructed. This // should always be true! bool objectIsValid() const; TclList debugDump() const; }; class NewSocketListener { public: virtual void listenToNewSocket(SocketInfo *socketInfo) =0; virtual ~NewSocketListener() { } }; #endif