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