Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

LimService.H

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // Last modified on Sun May 22 22:57:35 EDT 2005 by ken@xorian.net   
00020 //      modified on Fri Feb 11 17:13:07 PST 2000 by mann   
00021 //      modified on Mon Jun 16 11:07:42 PDT 1997 by heydon 
00022 //      modified on Thu Aug  1 14:42:14 PDT 1996 by levin  
00023 
00024 // LimService -- An object for exporting an SRPC interface with limits on
00025 //   the resources it will consume.
00026 
00027 #ifndef _LIM_SERVICE_H
00028 #define _LIM_SERVICE_H
00029 
00030 #include <Basics.H>
00031 #include "SRPC.H"
00032 
00033 /* A "LimService" object is an object for exporting an "SRPC" interface. The
00034   "LimService" constructor takes as arguments the name of the interface to
00035   export and a server function to be called in response to a remote procedure
00036   call. Once the remote call completes, the "SRPC" connection used for that
00037   call is maintained for a future connection.
00038 
00039   The "LimService" constructor also takes two numeric parameters that allow
00040   the client to specify limits on the resources consumed by the service. One
00041   parameter is an upper bound on the number of server threads allowed to
00042   run simultaneously. If that number of threads are already running, and
00043   another call comes in, the latter call will block until one of the other
00044   running threads completes. The second parameter is an integer defining the
00045   additional number of threads that may be blocked. If a new call comes in,
00046   and the number of blocked threads is at the client-specified limit, then a
00047   failure is thrown in response to the new call.
00048 
00049   The type of the callback procedure throws the "SRPC::failure" exception. It
00050   is imperative that if that exception is thrown during execution of the
00051   callback procedure as a result of a method call on its "srpc" argument that
00052   the exception is also thrown by the callback. Hence, if the callback
00053   procedure catches that exception internally, it must re-throw it. Otherwise,
00054   the LimService machinery cannot de-allocate the resources associated with
00055   the failed (and hence, dead) SRPC connection.
00056 
00057   It is important that the "LimService" object not be deleted by the main
00058   server thread so long as the server is in operation. */
00059 
00060 // Callback procedure types ---------------------------------------------------
00061 
00062 typedef void (*LimService_Callback)(SRPC *srpc, int procId, void *arg)
00063   /*throw (SRPC::failure)*/;
00064 /* "LimService_Callback" is the type of the procedure called in response to a
00065    new remote call. */
00066 
00067 typedef void (*LimService_Callback2)(SRPC *srpc, int intfVersion,
00068                                      int procId, void *arg)
00069   /*throw (SRPC::failure)*/;
00070 /* "LimService_Callback2" is used in place of LimService_Callback if the
00071    server wants to accept multiple interface versions. */
00072 
00073 typedef void (*LimService_FailureCallback)(
00074   SRPC *srpc, const SRPC::failure &f, void *arg);
00075 /* "LimService_FailureCallback" is the type of the procedure called in
00076    response to an SRPC failure that occurs during the execution of a server
00077    callback. */
00078 
00079 // The "LimService" object ----------------------------------------------------
00080 
00081 // LimService worker thread object (see below)
00082 class LSWorker;
00083 
00084 class LimService {
00085   public:
00086     LimService(const Text &intfName, const int intfVersion,
00087       int maxRunning, int maxBlocked, LimService_Callback callback,
00088       LimService_FailureCallback failureCallback = NULL,
00089       void *arg = NULL, long stacksize = -1,
00090       const Text &hostName = "", int failCode = 1,
00091       const Text &failMessage = Text("connection refused: server busy"))
00092       throw ();
00093     /* Initialize a "LimService" object that, when run, will export the
00094        service named "intfName" on the host named "hostName". If "stacksize"
00095        is not defaulted, it specifies the stack size (in bytes) of each
00096        thread forked by the "LimService" object. Use the "Run" method below
00097        to run an initialized "LimService" object. */
00098 
00099     LimService(const int intfVersion,
00100       int maxRunning, int maxBlocked, LimService_Callback callback,
00101       LimService_FailureCallback failureCallback = NULL,
00102       void *arg = NULL, long stacksize = -1,
00103       const Text &hostName = "", int failCode = 1,
00104       const Text &failMessage = Text("connection refused: server busy"))
00105       throw ();
00106     /* Initialize a "LimService" object that, when run, will export an
00107        anonymous service (i.e., an arbitrarily chosen unused port number)
00108        on the host named "hostName". If "stacksize" is not defaulted,
00109        it specifies the stack size (in bytes) of each thread forked by
00110        the "LimService" object. Use the "Run" method below to run an
00111        initialized "LimService" object. */
00112 
00113     LimService(const Text &intfName,
00114       int maxRunning, int maxBlocked, LimService_Callback2 callback,
00115       LimService_FailureCallback failureCallback = NULL,
00116       void *arg = NULL, long stacksize = -1,
00117       const Text &hostName = "", int failCode = 1,
00118       const Text &failMessage = Text("connection refused: server busy"))
00119       throw ();
00120     /* Initialize a "LimService" object that, when run, will export the
00121        service named "intfName" on the host named "hostName". If "stacksize"
00122        is not defaulted, it specifies the stack size (in bytes) of each
00123        thread forked by the "LimService" object. Use the "Run" method below
00124        to run an initialized "LimService" object.  Calls with any interface
00125        version will be accepted and passed on to the callback. */
00126     LimService(const Text &intfName,
00127       int maxRunning, int maxBlocked, LimService_Callback2 callback,
00128       LimService_FailureCallback failureCallback,
00129       void *arg, const Basics::thread_attr &worker_attr,
00130       const Text &hostName = "", int failCode = 1,
00131       const Text &failMessage = Text("connection refused: server busy"))
00132       throw ();
00133 
00134     LimService(
00135       int maxRunning, int maxBlocked, LimService_Callback2 callback,
00136       LimService_FailureCallback failureCallback = NULL,
00137       void *arg = NULL, long stacksize = -1,
00138       const Text &hostName = "", int failCode = 1,
00139       const Text &failMessage = Text("connection refused: server busy"))
00140       throw ();
00141     /* Initialize a "LimService" object that, when run, will export an
00142        anonymous service (i.e., an arbitrarily chosen unused port number)
00143        on the host named "hostName". If "stacksize" is not defaulted,
00144        it specifies the stack size (in bytes) of each thread forked by
00145        the "LimService" object. Use the "Run" method below to run an
00146        initialized "LimService" object. Calls with any interface
00147        version will be accepted and passed on to the callback. */
00148 
00149     ~LimService() throw ()
00150         { if (this->sock != (TCP_sock *)NULL) delete this->sock; }
00151 
00152     void Run() throw (SRPC::failure);
00153     /* In the following, references to variable names like "intf" correspond
00154        to the values of the parameters to the LimService object's constructor.
00155 
00156        The "Run" method exports the service named "intfName" from the host
00157        named "hostName". It blocks until a fatal failure occurs, in which case
00158        "SRPC::failure" will be thrown (see below). To run a LimService object
00159        in a forked thread, use the "ForkedRun" method below.
00160 
00161        If "hostName" is defaulted, then the interface is exported from the
00162        current host. However, if the local machine has multiple IP addresses,
00163        "hostName" can be used to differentiate among them. The interface will
00164        be exported with the version number "intfVersion".
00165 
00166        When the exported interface is called in the normal case, a thread is
00167        forked that calls the user-supplied "callback" function. This
00168        function is invoked with three arguments: "srpc" is a 
00169        pointer to the "SRPC" connection for the call, "procId" is the
00170        identifier for the procedure to invoke, and "arg" is the "arg" value
00171        passed to the constructor. The "arg" mechanism provides a way of
00172        communicating data to each invoked thread. In order that the SRPC
00173        connection may be reused for future calls, the "callback" function
00174        is required *not* to delete the "srpc" at the end of the call. 
00175 
00176        In the event that there are already "maxRunning" callback functions
00177        currently running, the new incoming call blocks until one or more of
00178        those threads complete.
00179 
00180        In the event that there are already "maxBlocked" threads blocked (in 
00181        addition to the "maxRunning" running threads), the service immediately
00182        throws "SRPC::failure" down the connection with the integer code
00183        "failCode" and the message "failMessage". In this case, the "callback"
00184        function is not invoked.
00185 
00186        If the exception "SRPC::failure" is thrown in the process of creating
00187        or waiting on an SRPC connection, the "Run" method exits with that
00188        exception. Any threads that were already running the "callback"
00189        function in the server will continue to run until they complete.
00190 
00191        If the "SRPC::failure" exception is thrown by the user-supplied
00192        "callback" function, the exception is caught internally. If the
00193        exception indicates a programmer error, an error message is
00194        written to the standard error output, and the entire process exits.
00195        Otherwise, the "failureCallback" is invoked with the current SRPC
00196        connection, the failure obect itself, and the "arg" value passed to
00197        the "LimService" constructor as arguments. If "failureCallback" is
00198        "NULL", then the failure is silently ignored. */
00199 
00200     Basics::thread Forked_Run() throw (SRPC::failure);
00201     /* This function forks a thread to execute a call of Run() and returns
00202        the thread as its result.  Forked_Run doesn't return until the
00203        SRPC connection passed to the LimService_Callback invoked from Run
00204        is ready to accept connection requests.  This is a stronger guarantee
00205        than if a client simply forked the Run() operation directly. */
00206 
00207     Text IntfName() throw () { return this->intfName; }
00208       /* returns the interface name used by the LimService object.  If
00209          the first constructor above was used, this is simply its first
00210          parameter; otherwise, it is the port number (converted to ASCII)
00211          chosen for the (anonymous) interface. */
00212 
00213   private:
00214     friend void* LimService_Worker(void *) throw ();
00215 
00216     void DecNumRunning() throw ();
00217     /* Decrement the number of running threads and signal the condition
00218        variable "server_avail". */
00219 
00220     void HandleFailure(const SRPC::failure& f) throw ();
00221     /* Stash the failure "f" in "this->server_failure", and signal a state
00222        change to "ForkedRun". */
00223 
00224     friend void *LimService_StartServer(void *) throw ();
00225     /* Callback function used by "ForkedRun". This invokes the "Run" method to
00226        run the server. In the event of an "SRPC::failure", it invokes the
00227        "HandleFailure" method to synchronize with the "Forked_Run" method. */
00228 
00229     void NewConnection(SRPC *srpc, int intfVersion, int procId) throw ();
00230     /* Callback function invoked when a new SRPC connection is established.
00231        It checks that the server can run another thread, and then invokes
00232        the client's "callback" function. Once the client's callback completes,
00233        this function waits for another connection on the same SRPC object.
00234        The loop exits if there is an SRPC either while waiting for a new
00235        connection or in executing the "callback". In either case, the
00236        client's "failure_callback" is invoked. */
00237 
00238     LSWorker *NewWorker() throw ();
00239     /* Return an available worker thread. To start the thread running, invoke
00240        the LSWorker::Start method below. */
00241 
00242     void RegisterIdleWorker(LSWorker *worker) throw ();
00243     /* Return "worker" to the list of available idle worker threads. This
00244        method should be called by the worker thread when it is done with
00245        its job. */
00246 
00247     // linked list of worker threads
00248     struct WorkerList {
00249         LSWorker *worker;
00250         WorkerList *next;
00251     };
00252 
00253     // readonly after initialization (constructor arguments)
00254     Text intfName;              // first constructor only
00255     int intfVersion;
00256     int maxRunning;
00257     int maxTotal;               // max total = #(running and blocked threads)
00258     LimService_Callback callback;   // exactly one of callback, callback2
00259     LimService_Callback2 callback2; //  is filled in; the other is NULL.
00260     LimService_FailureCallback failureCallback;
00261     void *arg;
00262     Basics::thread_attr worker_attributes;
00263     Text hostName;
00264     int failCode;
00265     Text failMessage;
00266     TCP_sock *sock;             // used by second constructor only
00267 
00268     // mutable fields
00269     Basics::mutex mu;              // protects the following fields:
00270     int numRunning, numBlocked;    // # of threads running and blocked
00271     Basics::cond server_avail;     // this->numRunning < this->maxRunning
00272     WorkerList *idleWorkers;       // linked list of idle worker threads
00273     bool server_state_change;      // true iff "Forked_Run" done
00274     Basics::cond state_change;     // == server_state_change
00275     SRPC::failure server_failure;  // saved failure used by "Forked_Run"
00276 
00277     // Invariants:
00278     // I1. numRunning <= maxRunning
00279     // I2. numRunning + numBlocked <= maxTotal
00280 
00281     /* prevent copying */
00282 
00283     LimService(const LimService &);
00284     LimService(LimService &);
00285     // Copy constructor; should never be accessed.
00286 
00287     void operator=(LimService &);
00288 
00289 };
00290 
00291 class LSWorker {
00292   public:
00293     LSWorker(LimService *ls, const Basics::thread_attr &attr) throw ();
00294     /* Construct a new LSWorker thread for "ls" that is waiting to run.
00295        Use the "Start" method below to start the thread running. */
00296 
00297     void Start(SRPC *srpc, int intfVer, int procId) throw ();
00298     /* Run this worker thread on the given arguments. */
00299 
00300     void Finish() throw ();
00301     /* This function must be called once the worker callback completes. */
00302 
00303   private:
00304     friend void *LimService_Worker(void *arg) throw ();
00305     /* Application function for the forked thread invoked by the
00306        "LSWorker" constructor. This function never returns. */
00307 
00308     // the thread itself
00309     Basics::thread th;
00310 
00311     // thread arguments
00312     LimService *ls;
00313     SRPC *srpc;
00314     int intfVersion;
00315     int procId;
00316 
00317     // synchronization fields
00318     Basics::mutex mu;      // protects following fields
00319     bool argsReady;        // does thread have work to do?
00320     Basics::cond workToDo; // == argsReady
00321 };
00322 
00323 #endif // _LIM_SERVICE_H

Generated on Mon May 8 00:48:57 2006 for Vesta by  doxygen 1.4.2