-#define kqueue_structs struct kevent ke; \
- struct kevent ke_list[33]; \
- struct timespec ts;
-
-#define epoll_structs struct epoll_event event[33];
-
-#define select_structs fd_set sfd;
-
-#ifdef USE_EPOLL
-#define engine_structs epoll_structs
-#endif
-#ifdef USE_KQUEUE
-#define engine_structs kqueue_structs
-#endif
-#ifdef USE_SELECT
-#define engine_structs select_structs
-#endif
-
-// how to initialise the engine before using it
-
-#define select_init while(0);
-
-#define kqueue_init kq = kqueue(); \
- lkq = kqueue(); \
- skq = kqueue(); \
- if ((kq == -1) || (lkq == -1) || (skq == -1)) \
- { \
- log(DEFAULT,"main: kqueue() failed!"); \
- printf("ERROR: could not initialise kqueue event system. Shutting down.\n"); \
- Exit(ERROR); \
- }
-
-#define epoll_init ep = epoll_create(MAXCLIENTS); \
- lep = epoll_create(32); \
- sep = epoll_create(128); \
- if ((ep == -1) || (lep == -1) || (sep == -1)) \
- { \
- log(DEFAULT,"main: epoll_create() failed!"); \
- printf("ERROR: could not initialise epoll event system. Shutting down.\n"); \
- Exit(ERROR); \
- }
-
-
-#ifdef USE_EPOLL
-#define engine_init epoll_init
-#endif
-#ifdef USE_KQUEUE
-#define engine_init kqueue_init
-#endif
-#ifdef USE_SELECT
-#define engine_init select_init
-#endif
-
-// how to delete a client fd from the engine
-
-#define kqueue_delete_fd struct kevent ke; \
- EV_SET(&ke, user->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); \
- int i = kevent(kq, &ke, 1, 0, 0, NULL); \
- if (i == -1) \
- { \
- log(DEBUG,"kqueue: Failed to remove user from queue!"); \
- }
-
-#define epoll_delete_fd struct epoll_event ev; \
- ev.events = EPOLLIN | EPOLLET; \
- ev.data.fd = user->fd; \
- int i = epoll_ctl(ep, EPOLL_CTL_DEL, user->fd, &ev); \
- if (i < 0) \
- { \
- log(DEBUG,"epoll: List deletion failure!"); \
- }
-
-#define select_delete_fd while(0);
-
-
-#ifdef USE_EPOLL
-#define engine_delete_fd epoll_delete_fd
-#endif
-#ifdef USE_KQUEUE
-#define engine_delete_fd kqueue_delete_fd
-#endif
-#ifdef USE_SELECT
-#define engine_delete_fd select_delete_fd
-#endif
-
-// how to add a client fd to the engine
-
-#define select_add_fd while(0);
-
-#define epoll_add_fd struct epoll_event ev; \
- log(DEBUG,"epoll: Adduser to events, ep=%d socket=%d",ep,socket); \
- ev.events = EPOLLIN | EPOLLET; \
- ev.data.fd = socket; \
- int i = epoll_ctl(ep, EPOLL_CTL_ADD, socket, &ev); \
- if (i < 0) \
- { \
- log(DEBUG,"epoll: List insertion failure!"); \
- }
-
-#define kqueue_add_fd struct kevent ke; \
- log(DEBUG,"kqueue: Add user to events, kq=%d socket=%d",kq,socket);\
- EV_SET(&ke, socket, EVFILT_READ, EV_ADD, 0, 0, NULL); \
- int i = kevent(kq, &ke, 1, 0, 0, NULL); \
- if (i == -1) \
- { \
- log(DEBUG,"kqueue: List insertion failure!"); \
- }
-
-#ifdef USE_EPOLL
-#define engine_add_fd epoll_add_fd
-#endif
-#ifdef USE_KQUEUE
-#define engine_add_fd kqueue_add_fd
-#endif
-#ifdef USE_SELECT
-#define engine_add_fd select_add_fd
+/**
+ * Event mask for SocketEngine events
+ */
+enum EventMask
+{
+ /** Do not test this socket for readability
+ */
+ FD_WANT_NO_READ = 0x1,
+ /** Give a read event at all times when reads will not block.
+ */
+ FD_WANT_POLL_READ = 0x2,
+ /** Give a read event when there is new data to read.
+ *
+ * An event MUST be sent if there is new data to be read, and the most
+ * recent read/recv() on this FD returned EAGAIN. An event MAY be sent
+ * at any time there is data to be read on the socket.
+ */
+ FD_WANT_FAST_READ = 0x4,
+ /** Give an optional read event when reads begin to unblock
+ *
+ * This state is useful if you want to leave data in the OS receive
+ * queue but not get continuous event notifications about it, because
+ * it may not require a system call to transition from FD_WANT_FAST_READ
+ */
+ FD_WANT_EDGE_READ = 0x8,
+
+ /** Mask for all read events */
+ FD_WANT_READ_MASK = 0x0F,
+
+ /** Do not test this socket for writeability
+ */
+ FD_WANT_NO_WRITE = 0x10,
+ /** Give a write event at all times when writes will not block.
+ *
+ * You probably shouldn't use this state; if it's likely that the write
+ * will not block, try it first, then use FD_WANT_FAST_WRITE if it
+ * fails. If it's likely to block (or you are using polling-style reads)
+ * then use FD_WANT_SINGLE_WRITE.
+ */
+ FD_WANT_POLL_WRITE = 0x20,
+ /** Give a write event when writes don't block any more
+ *
+ * An event MUST be sent if writes will not block, and the most recent
+ * write/send() on this FD returned EAGAIN, or connect() returned
+ * EINPROGRESS. An event MAY be sent at any time that writes will not
+ * block.
+ *
+ * Before calling OnEventHandler*(), a socket engine MAY change the state of
+ * the FD back to FD_WANT_EDGE_WRITE if it is simpler (for example, if a
+ * one-shot notification was registered). If further writes are needed,
+ * it is the responsibility of the event handler to change the state to
+ * one that will generate the required notifications
+ */
+ FD_WANT_FAST_WRITE = 0x40,
+ /** Give an optional write event on edge-triggered write unblock.
+ *
+ * This state is useful to avoid system calls when moving to/from
+ * FD_WANT_FAST_WRITE when writing data to a mostly-unblocked socket.
+ */
+ FD_WANT_EDGE_WRITE = 0x80,
+ /** Request a one-shot poll-style write notification. The socket will
+ * return to the FD_WANT_NO_WRITE state before OnEventHandler*() is called.
+ */
+ FD_WANT_SINGLE_WRITE = 0x100,
+
+ /** Mask for all write events */
+ FD_WANT_WRITE_MASK = 0x1F0,
+
+ /** Add a trial read. During the next DispatchEvents invocation, this
+ * will call OnEventHandlerRead() unless reads are known to be
+ * blocking.
+ */
+ FD_ADD_TRIAL_READ = 0x1000,
+ /** Assert that reads are known to block. This cancels FD_ADD_TRIAL_READ.
+ * Reset by SE before running OnEventHandlerRead().
+ */
+ FD_READ_WILL_BLOCK = 0x2000,
+
+ /** Add a trial write. During the next DispatchEvents invocation, this
+ * will call OnEventHandlerWrite() unless writes are known to be
+ * blocking.
+ *
+ * This could be used to group several writes together into a single
+ * send() syscall, or to ensure that writes are blocking when attempting
+ * to use FD_WANT_FAST_WRITE.
+ */
+ FD_ADD_TRIAL_WRITE = 0x4000,
+ /** Assert that writes are known to block. This cancels FD_ADD_TRIAL_WRITE.
+ * Reset by SE before running OnEventHandlerWrite().
+ */
+ FD_WRITE_WILL_BLOCK = 0x8000,
+
+ /** Mask for trial read/trial write */
+ FD_TRIAL_NOTE_MASK = 0x5000
+};
+
+/** This class is a basic I/O handler class.
+ * Any object which wishes to receive basic I/O events
+ * from the socketengine must derive from this class and
+ * implement the OnEventHandler*() methods. The derived class
+ * must then be added to SocketEngine using the method
+ * SocketEngine::AddFd(), after which point the derived
+ * class will receive events to its OnEventHandler*() methods.
+ * The event mask passed to SocketEngine::AddFd() determines
+ * what events the EventHandler gets notified about and with
+ * what semantics. SocketEngine::ChangeEventMask() can be
+ * called to update the event mask later. The only
+ * requirement beyond this for an event handler is that it
+ * must have a file descriptor. What this file descriptor
+ * is actually attached to is completely up to you.
+ */
+class CoreExport EventHandler : public classbase
+{
+ private:
+ /** Private state maintained by socket engine */
+ int event_mask;
+
+ void SetEventMask(int mask) { event_mask = mask; }
+
+ protected:
+ /** File descriptor.
+ * All events which can be handled must have a file descriptor. This
+ * allows you to add events for sockets, fifo's, pipes, and various
+ * other forms of IPC. Do not change this while the object is
+ * registered with the SocketEngine
+ */
+ int fd;
+
+ /** Swaps the internals of this EventHandler with another one.
+ * @param other A EventHandler to swap internals with.
+ */
+ void SwapInternals(EventHandler& other);
+
+ public:
+ /** Get the current file descriptor
+ * @return The file descriptor of this handler
+ */
+ inline int GetFd() const { return fd; }
+
+ /** Checks if this event handler has a fd associated with it. */
+ inline bool HasFd() const { return fd >= 0; }
+
+ inline int GetEventMask() const { return event_mask; }
+
+ /** Set a new file descriptor
+ * @param FD The new file descriptor. Do not call this method without
+ * first deleting the object from the SocketEngine if you have
+ * added it to a SocketEngine instance.
+ */
+ void SetFd(int FD);
+
+ /** Constructor
+ */
+ EventHandler();
+
+ /** Destructor
+ */
+ virtual ~EventHandler() {}
+
+ /** Called by the socket engine in case of a read event
+ */
+ virtual void OnEventHandlerRead() = 0;
+
+ /** Called by the socket engine in case of a write event.
+ * The default implementation does nothing.
+ */
+ virtual void OnEventHandlerWrite();
+
+ /** Called by the socket engine in case of an error event.
+ * The default implementation does nothing.
+ * @param errornum Error code
+ */
+ virtual void OnEventHandlerError(int errornum);
+
+ friend class SocketEngine;
+};
+
+/** Provides basic file-descriptor-based I/O support.
+ * The actual socketengine class presents the
+ * same interface on all operating systems, but
+ * its private members and internal behaviour
+ * should be treated as blackboxed, and vary
+ * from system to system and upon the config
+ * settings chosen by the server admin.
+ */
+class CoreExport SocketEngine
+{
+ public:
+ /** Socket engine statistics: count of various events, bandwidth usage
+ */
+ class Statistics
+ {
+ mutable size_t indata;
+ mutable size_t outdata;
+ mutable time_t lastempty;
+
+ /** Reset the byte counters and lastempty if there wasn't a reset in this second.
+ */
+ void CheckFlush() const;
+
+ public:
+ /** Constructor, initializes member vars except indata and outdata because those are set to 0
+ * in CheckFlush() the first time Update() or GetBandwidth() is called.
+ */
+ Statistics() : lastempty(0), TotalEvents(0), ReadEvents(0), WriteEvents(0), ErrorEvents(0) { }
+
+ /** Update counters for network data received.
+ * This should be called after every read-type syscall.
+ * @param len_in Number of bytes received, or -1 for error, as typically
+ * returned by a read-style syscall.
+ */
+ void UpdateReadCounters(int len_in);
+
+ /** Update counters for network data sent.
+ * This should be called after every write-type syscall.
+ * @param len_out Number of bytes sent, or -1 for error, as typically
+ * returned by a read-style syscall.
+ */
+ void UpdateWriteCounters(int len_out);
+
+ /** Get data transfer statistics.
+ * @param kbitpersec_in Filled with incoming traffic in this second in kbit/s.
+ * @param kbitpersec_out Filled with outgoing traffic in this second in kbit/s.
+ * @param kbitpersec_total Filled with total traffic in this second in kbit/s.
+ */
+ void CoreExport GetBandwidth(float& kbitpersec_in, float& kbitpersec_out, float& kbitpersec_total) const;
+
+ unsigned long TotalEvents;
+ unsigned long ReadEvents;
+ unsigned long WriteEvents;
+ unsigned long ErrorEvents;
+ };
+
+ private:
+ /** Reference table, contains all current handlers
+ **/
+ static std::vector<EventHandler*> ref;
+
+ /** Current number of descriptors in the engine. */
+ static size_t CurrentSetSize;
+
+ /** The maximum number of descriptors in the engine. */
+ static size_t MaxSetSize;
+
+ /** List of handlers that want a trial read/write
+ */
+ static std::set<int> trials;
+
+ /** Socket engine statistics: count of various events, bandwidth usage
+ */
+ static Statistics stats;
+
+ /** Look up the fd limit using rlimit. */
+ static void LookupMaxFds();
+
+ /** Terminates the program when the socket engine fails to initialize. */
+ static void InitError();
+
+ static void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
+
+ /** Add an event handler to the base socket engine. AddFd(EventHandler*, int) should call this.
+ */
+ static bool AddFdRef(EventHandler* eh);
+
+ static void DelFdRef(EventHandler* eh);
+
+ template <typename T>
+ static void ResizeDouble(std::vector<T>& vect)
+ {
+ if (SocketEngine::CurrentSetSize > vect.size())
+ vect.resize(SocketEngine::CurrentSetSize * 2);
+ }
+
+public:
+#ifndef _WIN32
+ typedef iovec IOVector;
+#else
+ typedef WindowsIOVec IOVector;