// -*- c++ -*-
//------------------------------------------------------------------------------
//                             Reactor.h
//------------------------------------------------------------------------------
//  Copyright (C) 1997-2002  Vladislav Grinchenko <vladg@erols.com>
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//------------------------------------------------------------------------------
//  Created: 05/25/1999
//------------------------------------------------------------------------------
#ifndef REACTOR_H
#define REACTOR_H

#include <sys/time.h>			// select(2)
#include <sys/resource.h>		// getrlimit(2)

#include "assa/EventHandler.h"
#include "assa/Singleton.h"
#include "assa/MaskSet.h"
#include "assa/TimerQueue.h"
#include "assa/TimerCountdown.h"

namespace ASSA {

/** @file Reactor.h 

An implementation of Reactor pattern.

This class takes after Reactor pattern described in "An Object Behavioral
Pattern for Demultiplexing and Dispatching Handles for Synchronous Events"
by D.C. Schmidt. 

Design of it is highly influenced by ACE's own 
implementation of Reactor class as well as by InterViews V 3.1 from
Stanford University.

Reactor reads data from a file descriptor, writes data to a file 
descriptor, handles an I/O exception on a file descriptor, or handles
a timer's expiration. Once the user has requested the Reactor to attach
EventHandler to a file descriptor or a timer, Reactor will 
automatically notify the EventHandler when the file descriptor's
I/O condition changes to the timer expires.

Most of the time, however, event processing is shared with some other
event processor, such as X event loop routine of Gtk+ or another communication
framework such as CORBA. In this case, timed event loop is used. 
This can be achieved by calling Reactor::waitForEvents (TimeVal* tv_).
*/

class Reactor 
{
public:
	/// Constructor. 
	Reactor ();

	/// Destructor.
	~Reactor();

	/** Register Timer Event handler with Reactor. Reactor will dispatch
	    appropriate callback when event of EventType is received.

	    @param eh_ [RW] Pointer to the EventHandler
	    @param tv_ [RO] Timeout value
	    @return Timer id that can be used to cancel timer.
	*/
	TimerId registerTimerHandler (EventHandler* eh_, const TimeVal& tv_);

	/** Register I/O Event handler with Reactor. Reactor will dispatch
	    appropriate callback when event of EventType is received.

	    @param eh_ Pointer to the EventHandler
	    @param fd_ File descriptor
	    @param et_ Event Type
	    @return true if success, false if error
	 */
	bool registerIOHandler (EventHandler* eh_, int fd_, 
							EventType et_ = RWE_EVENTS);

	/** Remove Event handler from reactor for either all I/O events
	    or timeout event or both. If et_ is TIMEOUT_EVENT, all timers
	    associated with Event Handler eh_ will be removed.

		@param eh_ Pointer to the EventHandler
	    @param et_ Event Type to remove. Default will remove
	               Event Handler for all events.
	    @return true if success, false if wasn't registered for any events.
	 */
	bool removeHandler (EventHandler* eh_, EventType et_ = ALL_EVENTS);

	/** Remove Timer event from the queue. This removes particular  event.
	    @param id_ Timer Id returned by registerTimer.
	    @return true if timer found and removed; false otherwise
	*/
	bool removeTimerHandler (TimerId id_);

	/** Remove IO Event handler from reactor. This will remove
	    handler from receiving all I/O events.
	    @param fd_ File descriptor
	    @return true on success, false if fd_ is out of range
	*/
	bool removeIOHandler (int fd_);

	/// Main waiting loop that blocks indefinitely processing events. 
	void waitForEvents (void);

	/** Wait for events for time specified. Passing NULL replicates
	    behavior of waitForEvents(void). Passing tv_ {0, 0} will
	    cause non-blocking polling for all events. This method
	    blocks up to tv_ time interval processing event. If an event
	    occurs, it will process event(s) and return. tv_ time is adjusted
	    by substracting time spent in event processing.
	    @param tv_ [RW] is time to wait for. 
	*/
	void waitForEvents (TimeVal* tv_);

	/** Stop Reactor's activity. This effectively removes all handlers
	    from under Reactor's supervision. As of now, there is
	    no way to re-activate the Reactor. This method is 
	    typically called from method other then EventHandler::signal_handler().
		EventHandler::handle_read () is a good candidate.
		Calling it from EventHandler::handle_close () will most likely
		cause an infinite loop of recursive calls.
	*/
	void stopReactor (void);

    /** Deactivate Reactor. This function sets internal flag which
        notifies Reactor's internal event handling loop to abort
        its activity. It is mostly used when a *slow* system call is
        interrupted by the signal handler. The system call will be
        restarted by OS after control returns from the signal handler.
        Signal handler (GenServer::handle_signal()) should call this method 
		to delay Reactor's deactivation.
    */
    void deactivate (void);

private:
	Reactor (const Reactor&);	         /// no cloning 
	Reactor& operator= (const Reactor&); /// no cloning

private:
	/// Handle error in select(2) loop appropriately. 
	bool handleError (void);

	/** Notify all EventHandlers registered on respecful events
	    occured.
	    @param minimum_ number of file descriptors ready.
	 */
	bool dispatch (int minimum_);

	/// Return number of file descriptors ready accross all sets.
	int isAnyReady (void);

	/** Check mask for bad file descriptors.
	    @return true if any fd(s) were found and removed; 
	    false otherwise
	*/
	bool checkFDs (void);

	/** Call handler's callback and, if callback returns negative
	    value, remove it from the Reactor.
	*/
	void dispatchHandler ( FdSet& mask_, EventHandler** fdSet_,
			       EH_IO_Callback callback_);
	
	/** Calculate closest timeout. If TimerQueue is not empty,
	    then return smallest of maxtimeout and first in the queue.
	    Otherwise, return maxtimeout.
	    @param maxwait_ (in) how long we are expected to wait for event(s).
	    @param howlong_ (out) how long we are going to wait.
	*/
	void calculateTimeout (TimeVal*& howlong_, TimeVal* maxwait_);

	
private:
	/** Max number of open files per process. This is the soft
	    limit enforced by the kernel. It can be obtained/manipulated
	    from the shell with ulimit/limit utilities, but may not
	    exceed the hard limit.
	*/
	int m_noFiles;

	/// Max file descriptor number plus 1
	int m_maxfd;

	/// Flag that indicates whether Reactor is active or had been stopped.
	bool m_active;

	/// Event handlers awaiting on READ_EVENT
	EventHandler** m_readSet;

	/// Event handlers awaiting on WRITE_EVENT
	EventHandler** m_writeSet;

	/// Event handlers awaiting on EXCEPT_EVENT
	EventHandler** m_exceptSet;

	/// Handlers to wait for event on
	MaskSet m_waitSet;

	/// Handlers that are ready for processing
	MaskSet m_readySet;

	/// The queue of Timers.
	TimerQueue m_tqueue;
};
/*******************************************************************************
 Inline functions
*******************************************************************************/

inline void Reactor::deactivate (void) {  m_active = false; }

} // end namespace ASSA


#endif /* REACTOR_H */  
