#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <mutex>
#include "common.h"
#include "address.h"
#include "connection.h"
#include "server.h"
enum : ErrorCode {
ERR_PROTOCOL_COMMON = ERR_SERVER,
ERR_PROTOCOL_NOT_INITIALIZED,
ERR_PROTOCOL_INITIALIZATION_FAILED,
ERR_PROTOCOL_UNFINISHED_CONNECTIONS,
ERR_PROTOCOL_IS_SWITCHING,
};
class Protocol;
class Socket {
public:
enum : unsigned int {
CHANGED = 1 << 0,
CREATED = 1 << 1,
REMOVED = 1 << 2,
WANTREAD = 1 << 3,
WANTWRITE = 1 << 4, };
Protocol &protocol;
const Connection::Handle connection;
const Server::Handle server;
const Address &address;
private:
friend class Protocol;
// these vars protected by protocol.mutex
unsigned int flags;
Socket *prev, *next;
Socket *chPrev, *chNext;
// protocol.mutex must be already locked
void setChanged(bool changed);
protected:
// protocol.mutex must be already locked
Socket(const Socket&) = delete;
Socket(
Protocol &protocol,
const Connection::Handle &connection,
const Server::Handle &server,
const Address &address );
~Socket();
void onConstructionComplete();
public:
// thread-saef methods
void setWantRead(bool want);
void setWantWrite(bool want);
void finalize();
};
class Protocol: public Shared {
public:
typedef THandle<Protocol> Handle;
typedef RecursiveMutex Mutex;
typedef std::lock_guard<Mutex> Lock;
private:
friend class Socket;
Socket *first, *last;
Socket *chFirst, *chLast;
bool started;
bool switching;
bool stopRequested;
unsigned long long stopTimeUs;
std::condition_variable_any stopWaitCondition;
protected:
Mutex mutexStartStop; // using only inside start and stop functions
Mutex mutex;
private:
inline void socketCheck(Socket *socket, bool mustBeChanged = false) {
assert(socket);
assert(&socket->protocol == this);
assert(!mustBeChanged || (socket->flags & Socket::CHANGED));
}
protected:
// any class derived from Protocol may access to Socket private members via these methods
// mutex must be locked before call
inline bool isStarted() const
{ return started; }
inline bool isStarting() const
{ return !started && switching; }
inline bool isStopping() const
{ return started && switching; }
inline bool isStopRequested() const
{ return stopRequested; }
inline bool isActive() const
{ return isStarted() && !isStopping(); }
inline bool isFullActive() const
{ return isActive() && !isStopRequested(); }
inline Socket* socketFirst()
{ return first; }
inline Socket* socketNext(Socket *socket)
{ socketCheck(socket); return socket->next; }
inline Socket* socketChFirst()
{ return chFirst; }
inline Socket* socketChNext(Socket *socket)
{ socketCheck(socket, true); return socket->chNext; }
inline unsigned int socketFlags(Socket *socket)
{ socketCheck(socket); return socket->flags; }
inline unsigned int socketToRemove(Socket *socket)
{ socketCheck(socket); return socket->flags & Socket::REMOVED; }
inline Socket* socketRemove(Socket *socket)
{ socketCheck(socket); Socket *next = socket->next; delete socket; return next; }
inline Socket* socketRemoveChanged(Socket *socket)
{ socketCheck(socket, true); Socket *chNext = socket->chNext; delete socket; return chNext; }
inline Socket* socketSetUnchanged(Socket *socket)
{ socketCheck(socket, true); Socket *chNext = socket->chNext; socket->setChanged(false); return chNext; }
friend class Server;
friend class Connection;
ErrorCode connectionOpen(const Connection::Handle &connection, Socket *socket);
ErrorCode serverStart(const Server::Handle &server, Socket *socket);
Connection::Handle serverConnect(const Server::Handle &server, const Address &address);
void serverDisconnect(const Server::Handle &server, const Connection::Handle &connection);
private:
// mutex must be locked before call
// used in start and stop
void clean();
// functions used by the Server class
// thread-safe
friend class Server;
int stopServer(Server &server);
int stopServerReq(Server &server);
public:
// thread-safe methods
Protocol();
virtual ~Protocol();
ErrorCode start();
void stop(ErrorCode errorCode = ERR_NONE);
void stopReq();
void stopWait(unsigned long long timeoutUs, bool withRequest = true);
ErrorCode connect(const Connection::Handle &connection, const Address &address);
ErrorCode listen(const Server::Handle &server, const Address &address);
virtual ErrorCode resolve(Address &address);
protected:
// mutex must be locked before call
virtual ErrorCode onStart();
virtual void onStopRequested();
virtual void onStop(ErrorCode errorCode);
virtual void onSocketChanged(Socket *socket);
virtual ErrorCode onConnect(const Connection::Handle &connection, const Address &address);
virtual ErrorCode onListen(const Server::Handle &server, const Address &address);
};
#endif