Blob Blame Raw
#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