(*---------------------------------------------------------------------------*
  INTERFACE  cf_poll.mli

  Copyright (c) 2003-2006, James H. Woodyatt
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

    Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

    Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in
    the documentation and/or other materials provided with the
    distribution

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  OF THE POSSIBILITY OF SUCH DAMAGE. 
 *---------------------------------------------------------------------------*)

(** I/O event multiplexing. *)

(** {6 Overview}

    This module implements an abstraction useful for multiplexing I/O events
    within a single thread of control.  Use this module in cases when a program
    must block until an I/O event is ready at one of a collection of system
    resources.
    
    {b Note}: On some platforms in the future, this module may be implemented
    around a specialized kernel event queue, e.g. {i /dev/poll } on Solaris, or
    {i kevent/kqueue } on Mac OS X.  In this early implementation, however, the
    module is implemented as a wrapper around [Unix.select] on all platforms.
*)

(** {6 Types} *)

(** A polling mux. *)
type t

(** The sum type of results from cycling a polling mux. *)
type more = More | Last

(** The basic polymorphic sum type for the state of an I/O event.
    Specializations of the basic I/O event class type may need more states.
    The basic states are: {ul
        {- [`Unloaded], not loaded into a polling event queue.}
        {- [`Loaded q], loaded into the polling event queue [q].}
        {- [`Final v] finished with a normal result [v].}
        {- [`Exception x], finished with an exceptional result [x].}
    }
*)
type 'a state =
    | Unloaded             (* Not loaded into a polling mux. *)
    | Loaded of t          (* Loaded and pending in a polling mux. *)
    | Working of t * 'a    (* Loaded while intermediate result available. *)
    | Final of 'a          (* Finished with a normal result. *)
    | Exception of exn     (* Finished with an exceptional result. *)

(** The type of objects representing an I/O events that produce results of
    type ['a].
*)
class type virtual ['a] event =
    object        
        (** The current state of the I/O event. *)
        val mutable state_: 'a state
        
        (** An optional queue of I/O events producing results of type ['a]
            provided in the [load] method.
        *)
        val mutable put_: <get: 'a> Queue.t option

        (** The [self#service p] method is invoked in the cycle for the
            polling mux [p] when the event is ready to be serviced.  The method
            is virtual to accomodate specializations with intermediate state
            and event handling functionality.
        *)
        method private virtual service: t -> 'a state

        (** The [self#load_ p] method is invoked in the [load] method to load
            the specialized event into the polling mux [p].
        *)
        method private load_: t -> unit

        (** The [self#unload_ p] method is invoked in the [unload] method to
            unload the specialized event from the polling mux [p].
        *)
        method private unload_: t -> unit
        
        (** Use [obj#load ?q p] to load the event into the collection of
            pending events at the polling mux [p].  Sets the [state_] member
            to [`Loaded p].  If the event is already loaded into a polling mux,
            it is unloaded first.  If the optional [?q] argument is provided,
            then the [put_] member is set accordingly.
        *)
        method load: ?q:(<get: 'a> Queue.t) -> t -> unit
        
        (** Use [obj#unload] to remove the event from the collection of pending
            events in a polling mux.  Sets the [state_] member to [`Unloaded].
        *)
        method unload: unit
        
        (** Returns [false] if the event requires service by a polling mux
            before a result may be obtained with the [get] method.
        *)
        method canget: bool
        
        (** Get the final result of the I/O event.  Raises [Not_ready] if the
            event requires more service by a polling mux.  If the state of
            the event is [`Exception x], then the exception [x] is raised.
        *)
        method get: 'a
    end

(** The exception raised when the [get] method is applied to an I/O event
    object that has not been serviced by a polling mux.
*)
exception Not_ready

(** {6 Functions and Classes}

    To use a polling mux, follow these steps:
    
    - construct a mux with [create ()];
    - construct any number of [file], [signal], [time] and [idle] I/O events;
    - apply the [load] method on each one using the new mux;
    - apply the [cycle] function to the mux;
    - apply the [canget] and [get] methods to the events to obtain the results.
*)

(** Use [create ()] to construct a new polling mux. *)
val create: unit -> t

(** Use [cycle p] to wait until one or more of the I/O event objects loaded
    into the mux [p] is ready to be serviced, then service them (which includes
    invoking their [obj#service] method).  Returns [Last] if there are no more
    events loaded into the polling mux.  Otherwise, returns [More].
*)
val cycle: t -> more

(** File events are associated with read, write or exception I/O events. *)
type rwx = [ `R | `W | `X ]

(** Use [inherit file rwx fd] to derive an I/O event object that waits for the
    file descriptor [fd] to be ready for reading, writing, or exception
    (according to the value of [rwx]).
*)
class virtual ['a] file: [< rwx ] -> Unix.file_descr -> ['a] event        

(** Use [inherit signal n] to derive an I/O event that is serviced when the
    system delivers the signal [n].
*)
class virtual ['a] signal: int -> ['a] event

(** Use [inherit time ?start interval] to derive an I/O event that is
    serviced after the system clock reaches a specific time and at constrant
    intervals thereafter.  If [?start] is not provided, then the start time
    is immediately after the first interval.
*)
class virtual ['a] time:
    ?t0:Cf_tai64n.t -> float ->
    object
        inherit ['a] event
        
        (** The epoch when the event is to be serviced. *)
        val mutable epoch_: Cf_tai64n.t
    end

(** Use [inherit idle] to derive an I/O event that is serviced whenever a
    polling mux cycle would otherwise block for any non-zero length of time.
*)
class virtual ['a] idle:
    object
        inherit ['a] event

        (** The epoch when the event is was serviced. *)
        val mutable epoch_: Cf_tai64n.t option
    end

(*--- End of File [ cf_poll.mli ] ---*)
