00001 /* 00002 * Phusion Passenger - http://www.modrails.com/ 00003 * Copyright (c) 2010 Phusion 00004 * 00005 * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. 00006 * 00007 * Permission is hereby granted, free of charge, to any person obtaining a copy 00008 * of this software and associated documentation files (the "Software"), to deal 00009 * in the Software without restriction, including without limitation the rights 00010 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00011 * copies of the Software, and to permit persons to whom the Software is 00012 * furnished to do so, subject to the following conditions: 00013 * 00014 * The above copyright notice and this permission notice shall be included in 00015 * all copies or substantial portions of the Software. 00016 * 00017 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00020 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00022 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 00023 * THE SOFTWARE. 00024 */ 00025 #ifndef _PASSENGER_EVENTED_CLIENT_H_ 00026 #define _PASSENGER_EVENTED_CLIENT_H_ 00027 00028 #include <ev++.h> 00029 #include <string> 00030 #include <sys/types.h> 00031 #include <cstdlib> 00032 #include <cerrno> 00033 #include <cassert> 00034 00035 #include <boost/function.hpp> 00036 #include <oxt/system_calls.hpp> 00037 #include <oxt/thread.hpp> 00038 00039 #include "FileDescriptor.h" 00040 #include "Utils/IOUtils.h" 00041 00042 namespace Passenger { 00043 00044 using namespace std; 00045 using namespace boost; 00046 using namespace oxt; 00047 00048 00049 /** 00050 * A utility class for making I/O handling in non-blocking libev evented servers 00051 * much easier. 00052 * - An EventedClient is associated with a reference counted file descriptor. 00053 * - It contains connection state information (i.e. whether the connection is 00054 * established or closed). Callbacks are provided for watching connection 00055 * state changes (e.g. <tt>onDisconnect</tt>). 00056 * - It provides reference counting features for simpler memory management 00057 * (<tt>ref()</tt> and <tt>unref()</tt>). 00058 * - It installs input and output readiness watchers that are unregistered 00059 * when the EventedClient is destroyed. One can hook into input readiness 00060 * watcher with the <tt>onReadable</tt> callback. 00061 * - Makes zero-copy writes easy. The <tt>write()</tt> method accepts an array 00062 * of buffers. Whenever possible, all of these buffers are written out in 00063 * the given order, using a single system call, without copying them into a 00064 * single temporary buffer. 00065 * - Makes non-blocking writes easy. Normally a write() system call on a 00066 * non-blocking socket can fail with EAGAIN if the socket send buffer is 00067 * full. EventedClient schedules the data to be sent later when the socket is 00068 * writable again. It automatically integrates into the main loop in order 00069 * to do this. This allows one to have write operations occur concurrently 00070 * with read operations. 00071 * In case too many scheduled writes are being piled up, EventedClient 00072 * is smart enough to temporarily disable read notifications and wait until 00073 * everything is written out before enabling read notifications again. 00074 * The definition of "too many" is customizable (<tt>setOutboxLimit()</tt>). 00075 * - EventedClient's <tt>disconnect</tt> method respects pending writes. It 00076 * will disconnect after all pending outgoing data have been written out. 00077 * 00078 * <h2>Basic usage</h2> 00079 * Construct an EventedClient with a libev loop and a file descriptor: 00080 * 00081 * @code 00082 * EventedClient *client = new EventedClient(loop, fd); 00083 * @endcode 00084 * 00085 * You are probably interested in read readiness notifications on <tt>fd</tt>. 00086 * However these notifications are disabled by default. You need to set the 00087 * <tt>onReadable</tt> callback (which is called every time the fd is 00088 * readable) and enable read notifications. 00089 * 00090 * @code 00091 * void onReadable(EventedClient *client) { 00092 * // do whatever you want 00093 * } 00094 * 00095 * ... 00096 * client->onReadable = onReadable; 00097 * client->notifyReads(true); 00098 * @endcode 00099 * 00100 * <h2>Error handling</h2> 00101 * EventedClient never raises exceptions, except when your callbacks do. 00102 * It reports errors with the <tt>onSystemError</tt> callback. That said, 00103 * EventedClient is exception-aware and will ensure that its internal 00104 * state stays consistent even when your callbacks throw exceptions. 00105 */ 00106 class EventedClient { 00107 public: 00108 typedef void (*Callback)(EventedClient *client); 00109 typedef void (*SystemErrorCallback)(EventedClient *client, const string &message, int code); 00110 00111 private: 00112 enum { 00113 /** 00114 * This is the initial state for a client. It means we're 00115 * connected to the client, ready to receive data and 00116 * there's no pending outgoing data. In this state we will 00117 * only be watching for read events. 00118 */ 00119 EC_CONNECTED, 00120 00121 /** 00122 * This state is entered from EC_CONNECTED when the write() 00123 * method fails to send all data immediately and EventedClient 00124 * schedules some data to be sent later, when the socket becomes 00125 * readable again. In here we will be watching for read 00126 * and write events. Once all data has been sent out the system 00127 * will transition back to EC_CONNECTED. 00128 */ 00129 EC_WRITES_PENDING, 00130 00131 /** 00132 * This state is entered from EC_WRITES_PENDING or from EC_CONNECTED 00133 * when the write() method fails to send all data immediately, and 00134 * the amount of data to be scheduled to be sent later is larger 00135 * than the specified outbox limit. In this state, EventedClient 00136 * will not watch for read events and will instead concentrate on 00137 * sending out all pending data before watching read events again. 00138 * When all pending data has been sent out the system will transition 00139 * to EC_CONNECTED. 00140 */ 00141 EC_TOO_MANY_WRITES_PENDING, 00142 00143 /** 00144 * This state is like EC_CONNECTED, but indicates that the write 00145 * side of the connection has been closed. In this state write() 00146 * calls won't have any effect. 00147 */ 00148 EC_RO_CONNECTED, 00149 00150 /** 00151 * This state is entered from EC_WRITES_PENDING when 00152 * closeWrite() has been called. The system will continue 00153 * to send out pending data but write() calls won't append more 00154 * data to the outbox. After pending data has been sent out, 00155 * the system will transition to EC_RO_CONNECTED. 00156 */ 00157 EC_RO_CONNECTED_WITH_WRITES_PENDING, 00158 00159 /** 00160 * This state is entered from the EC_WRITES_PENDING, 00161 * EC_TOO_MANY_WRITES_PENDING, EC_RO_CONNECTED_WITH_WIRTES_PENDING 00162 * or EC_RO_CONNECTED_WITH_TOO_MANY_WRITES_PENDING state when 00163 * disconnect() is called. 00164 * It means that we want to close the connection as soon as all 00165 * pending outgoing data has been sent. As soon as that happens 00166 * it'll transition to EC_DISCONNECTED. In this state no further 00167 * I/O should be allowed. 00168 */ 00169 EC_DISCONNECTING_WITH_WRITES_PENDING, 00170 00171 /** 00172 * Final state. Client connection has been closed. No 00173 * I/O with the client is possible. 00174 */ 00175 EC_DISCONNECTED 00176 } state; 00177 00178 /** A libev watcher on for watching read events on <tt>fd</tt>. */ 00179 ev::io readWatcher; 00180 /** A libev watcher on for watching write events on <tt>fd</tt>. */ 00181 ev::io writeWatcher; 00182 /** Storage for data that could not be sent out immediately. */ 00183 string outbox; 00184 int refcount; 00185 unsigned int outboxLimit; 00186 bool m_notifyReads; 00187 00188 void _onReadable(ev::io &w, int revents) { 00189 emitEvent(onReadable); 00190 } 00191 00192 void onWritable(ev::io &w, int revents) { 00193 assert(state != EC_CONNECTED); 00194 assert(state != EC_RO_CONNECTED); 00195 assert(state != EC_DISCONNECTED); 00196 00197 this_thread::disable_interruption di; 00198 this_thread::disable_syscall_interruption dsi; 00199 size_t sent = 0; 00200 bool done = outbox.empty(); 00201 00202 while (!done) { 00203 ssize_t ret = syscalls::write(fd, 00204 outbox.data() + sent, 00205 outbox.size() - sent); 00206 if (ret == -1) { 00207 if (errno != EAGAIN) { 00208 int e = errno; 00209 if (writeErrorAction == DISCONNECT_FULL) { 00210 disconnect(true); 00211 } else { 00212 closeWrite(); 00213 } 00214 emitSystemErrorEvent("Cannot write data to client", e); 00215 return; 00216 } 00217 done = true; 00218 } else { 00219 sent += ret; 00220 done = sent == outbox.size(); 00221 } 00222 } 00223 if (sent > 0) { 00224 outbox.erase(0, sent); 00225 } 00226 00227 updateWatcherStates(); 00228 if (outbox.empty()) { 00229 emitEvent(onPendingDataFlushed); 00230 } 00231 } 00232 00233 bool outboxTooLarge() { 00234 return outbox.size() > 0 && outbox.size() >= outboxLimit; 00235 } 00236 00237 void updateWatcherStates() { 00238 if (outbox.empty()) { 00239 switch (state) { 00240 case EC_CONNECTED: 00241 case EC_RO_CONNECTED: 00242 watchReadEvents(m_notifyReads); 00243 watchWriteEvents(false); 00244 break; 00245 case EC_WRITES_PENDING: 00246 case EC_TOO_MANY_WRITES_PENDING: 00247 state = EC_CONNECTED; 00248 watchReadEvents(m_notifyReads); 00249 watchWriteEvents(false); 00250 break; 00251 case EC_RO_CONNECTED_WITH_WRITES_PENDING: 00252 state = EC_RO_CONNECTED; 00253 watchReadEvents(m_notifyReads); 00254 watchWriteEvents(false); 00255 break; 00256 case EC_DISCONNECTING_WITH_WRITES_PENDING: 00257 state = EC_DISCONNECTED; 00258 watchReadEvents(false); 00259 watchWriteEvents(false); 00260 try { 00261 fd.close(); 00262 } catch (const SystemException &e) { 00263 emitSystemErrorEvent(e.brief(), e.code()); 00264 } 00265 emitEvent(onDisconnect); 00266 break; 00267 default: 00268 // Should never be reached. 00269 abort(); 00270 } 00271 } else { 00272 switch (state) { 00273 case EC_CONNECTED: 00274 if (outboxTooLarge()) { 00275 // If we have way too much stuff in the outbox then 00276 // suspend reading until we've sent out the entire outbox. 00277 state = EC_TOO_MANY_WRITES_PENDING; 00278 watchReadEvents(false); 00279 watchWriteEvents(true); 00280 } else { 00281 state = EC_WRITES_PENDING; 00282 watchReadEvents(m_notifyReads); 00283 watchWriteEvents(true); 00284 } 00285 break; 00286 case EC_RO_CONNECTED: 00287 fprintf(stderr, "BUG: when outbox is non-empty the state should never be EC_RO_CONNECTED!\n"); 00288 abort(); 00289 break; 00290 case EC_WRITES_PENDING: 00291 case EC_RO_CONNECTED_WITH_WRITES_PENDING: 00292 watchReadEvents(m_notifyReads); 00293 watchWriteEvents(true); 00294 break; 00295 case EC_TOO_MANY_WRITES_PENDING: 00296 case EC_DISCONNECTING_WITH_WRITES_PENDING: 00297 watchReadEvents(false); 00298 watchWriteEvents(true); 00299 break; 00300 default: 00301 // Should never be reached. 00302 abort(); 00303 } 00304 } 00305 } 00306 00307 void watchReadEvents(bool enable = true) { 00308 if (readWatcher.is_active() && !enable) { 00309 readWatcher.stop(); 00310 } else if (!readWatcher.is_active() && enable) { 00311 readWatcher.start(); 00312 } 00313 } 00314 00315 void watchWriteEvents(bool enable = true) { 00316 if (writeWatcher.is_active() && !enable) { 00317 writeWatcher.stop(); 00318 } else if (!writeWatcher.is_active() && enable) { 00319 writeWatcher.start(); 00320 } 00321 } 00322 00323 void emitEvent(Callback callback) { 00324 if (callback != NULL) { 00325 callback(this); 00326 } 00327 } 00328 00329 void emitSystemErrorEvent(const string &message, int code) { 00330 if (onSystemError != NULL) { 00331 onSystemError(this, message, code); 00332 } 00333 } 00334 00335 public: 00336 /** The client's file descriptor. Could be -1: see <tt>ioAllowed()</tt>. */ 00337 FileDescriptor fd; 00338 00339 /** Controls what to do when a write error is encountered. */ 00340 enum { 00341 /** Forcefully disconnect the client. */ 00342 DISCONNECT_FULL, 00343 /** Close the writer side of the connection, but continue allowing reading. */ 00344 DISCONNECT_WRITE 00345 } writeErrorAction; 00346 00347 /** 00348 * Called when the file descriptor becomes readable and read notifications 00349 * are enabled (see <tt>notifyRead()</tt>). When there's too much pending 00350 * outgoing data, readability notifications are temporarily disabled; see 00351 * <tt>write()</tt> for details. 00352 */ 00353 Callback onReadable; 00354 00355 /** 00356 * Called when the client is disconnected. This happens either immediately 00357 * when <tt>disconnect()</tt> is called, or a short amount of time later. 00358 * See the documentation for that function for details. 00359 * 00360 * Please note that destroying an EventedClient object does *not* cause 00361 * this callback to be called. 00362 */ 00363 Callback onDisconnect; 00364 00365 /** 00366 * Called when <tt>detach()</tt> is called for the first time. 00367 */ 00368 Callback onDetach; 00369 00370 /** 00371 * Called after all pending outgoing data have been written out. 00372 * If <tt>write()</tt> can be completed immediately without scheduling 00373 * data for later, then <tt>write()</tt> will call this callback 00374 * immediately after writing. 00375 */ 00376 Callback onPendingDataFlushed; 00377 00378 /** 00379 * System call errors are reported with this callback. 00380 */ 00381 SystemErrorCallback onSystemError; 00382 00383 /** 00384 * EventedClient doesn't do anything with this. Set it to whatever you want. 00385 */ 00386 void *userData; 00387 00388 /** 00389 * Creates a new EventedClient with the given libev loop and file descriptor. 00390 * The initial reference count is 1. 00391 */ 00392 EventedClient(struct ev_loop *loop, const FileDescriptor &_fd) 00393 : readWatcher(loop), 00394 writeWatcher(loop), 00395 fd(_fd) 00396 { 00397 state = EC_CONNECTED; 00398 refcount = 1; 00399 m_notifyReads = false; 00400 outboxLimit = 1024 * 32; 00401 writeErrorAction = DISCONNECT_FULL; 00402 onReadable = NULL; 00403 onDisconnect = NULL; 00404 onDetach = NULL; 00405 onPendingDataFlushed = NULL; 00406 onSystemError = NULL; 00407 userData = NULL; 00408 readWatcher.set(fd, ev::READ); 00409 readWatcher.set<EventedClient, &EventedClient::_onReadable>(this); 00410 writeWatcher.set<EventedClient, &EventedClient::onWritable>(this); 00411 writeWatcher.set(fd, ev::WRITE); 00412 } 00413 00414 virtual ~EventedClient() { 00415 // Unregister file descriptor from the event loop poller before 00416 // closing the file descriptor. 00417 watchReadEvents(false); 00418 watchWriteEvents(false); 00419 } 00420 00421 /** 00422 * Increase reference count. 00423 */ 00424 void ref() { 00425 refcount++; 00426 } 00427 00428 /** 00429 * Decrease reference count. Upon reaching 0, this EventedClient object 00430 * will be destroyed. 00431 */ 00432 void unref() { 00433 refcount--; 00434 assert(refcount >= 0); 00435 if (refcount == 0) { 00436 delete this; 00437 } 00438 } 00439 00440 /** 00441 * Returns whether it is allowed to perform some kind of I/O with 00442 * this client, either reading or writing. 00443 * Usually true, and false when the client is either being disconnected 00444 * or has been disconnected. A return value of false indicates that 00445 * <tt>fd</tt> might be -1, but even when it isn't -1 you shouldn't 00446 * access <tt>fd</tt> anymore. 00447 * When the connection is half-closed (e.g. after closeWrite() has 00448 * been called) the return value is still be true. Only when I/O of any 00449 * kind is disallowed will this function return false. 00450 */ 00451 bool ioAllowed() const { 00452 return state != EC_DISCONNECTING_WITH_WRITES_PENDING 00453 && state != EC_DISCONNECTED; 00454 } 00455 00456 /** 00457 * Returns whether it is allowed to write data to the client. 00458 * Usually true, and false when the client is either being disconnected 00459 * or has been disconnected or when the writer side of the client 00460 * connection has been closed. write() will do nothing if this function 00461 * returns false. 00462 */ 00463 bool writeAllowed() const { 00464 return state == EC_CONNECTED 00465 || state == EC_WRITES_PENDING 00466 || state == EC_TOO_MANY_WRITES_PENDING 00467 || state == EC_RO_CONNECTED_WITH_WRITES_PENDING; 00468 } 00469 00470 /** Used by unit tests. */ 00471 bool readWatcherActive() const { 00472 return readWatcher.is_active(); 00473 } 00474 00475 /** 00476 * Returns the number of bytes that are scheduled to be sent to the 00477 * client at a later time. 00478 * 00479 * @see write() 00480 */ 00481 size_t pendingWrites() const { 00482 return outbox.size(); 00483 } 00484 00485 /** 00486 * Sets whether you're interested in read events. This will start or 00487 * stop the input readiness watcher appropriately according to the 00488 * current state. 00489 * 00490 * If the client connection is already being closed or has already 00491 * been closed then this method does nothing. 00492 */ 00493 void notifyReads(bool enable) { 00494 if (!ioAllowed()) { 00495 return; 00496 } 00497 00498 this_thread::disable_interruption di; 00499 this_thread::disable_syscall_interruption dsi; 00500 m_notifyReads = enable; 00501 updateWatcherStates(); 00502 } 00503 00504 /** 00505 * Sets a limit on the client outbox. The outbox is where data is stored 00506 * that could not be immediately sent to the client, e.g. because of 00507 * network congestion. Whenver the outbox's size grows past this limit, 00508 * EventedClient will enter a state in which it will stop listening for 00509 * read events and instead concentrate on sending out all pending data. 00510 * 00511 * Setting this to 0 means that the outbox has an unlimited size. Please 00512 * note however that this also means that the outbox's memory could grow 00513 * unbounded if the client is too slow at receiving data. 00514 * 00515 * The default value is some non-zero value. 00516 * 00517 * If the client connection is already being closed or has already 00518 * been closed then this method does nothing. 00519 */ 00520 void setOutboxLimit(unsigned int size) { 00521 if (!ioAllowed()) { 00522 return; 00523 } 00524 00525 this_thread::disable_interruption di; 00526 this_thread::disable_syscall_interruption dsi; 00527 outboxLimit = size; 00528 updateWatcherStates(); 00529 } 00530 00531 void write(const StaticString &data) { 00532 write(&data, 1); 00533 } 00534 00535 /** 00536 * Sends data to this client. This method will try to send the data 00537 * immediately (in which no intermediate copies of the data will be made), 00538 * but if the client is not yet ready to receive data (e.g. because of 00539 * network congestion) then the data will be buffered and scheduled for 00540 * sending later. 00541 * 00542 * If an I/O error was encountered then the action taken depends on the 00543 * value of <em>writeActionError</em>. By default it is DISCONNECT_FULL, 00544 * meaning the client connection will be closed by calling 00545 * <tt>disconnect(true)</tt>. This means this method could potentially 00546 * call the <tt>onDisconnect</tt> callback. 00547 * 00548 * If the client connection is already being closed, has already 00549 * been closed or if the writer side is closed, then this method does 00550 * nothing. 00551 * 00552 * The <tt>onPendingDataFlushed</tt> callback will be called after 00553 * this data and whatever existing pending data have been written 00554 * out. That may either be immediately or after a short period of 00555 * of time. 00556 */ 00557 void write(const StaticString data[], unsigned int count) { 00558 if (!writeAllowed()) { 00559 return; 00560 } 00561 00562 ssize_t ret; 00563 this_thread::disable_interruption di; 00564 this_thread::disable_syscall_interruption dsi; 00565 00566 ret = gatheredWrite(fd, data, count, outbox); 00567 if (ret == -1) { 00568 int e = errno; 00569 if (writeErrorAction == DISCONNECT_FULL) { 00570 disconnect(true); 00571 } else { 00572 closeWrite(); 00573 } 00574 emitSystemErrorEvent("Cannot write data to client", e); 00575 } else { 00576 updateWatcherStates(); 00577 if (outbox.empty()) { 00578 emitEvent(onPendingDataFlushed); 00579 } 00580 } 00581 } 00582 00583 /** 00584 * Close only the writer side of the client connection. 00585 * After calling this method, subsequent write() calls won't do anything 00586 * anymore. Any pending outgoing data will be sent out whenever the 00587 * opportunity arises. 00588 * 00589 * This function does nothing if the client is being disconnected, 00590 * already disconnected or if only the writer side is closed. 00591 */ 00592 void closeWrite() { 00593 this_thread::disable_syscall_interruption dsi; 00594 00595 switch (state) { 00596 case EC_CONNECTED: 00597 assert(outbox.empty()); 00598 state = EC_RO_CONNECTED; 00599 if (syscalls::shutdown(fd, SHUT_WR) == -1) { 00600 int e = errno; 00601 emitSystemErrorEvent( 00602 "Cannot shutdown writer half of the client socket", 00603 e); 00604 } 00605 break; 00606 case EC_WRITES_PENDING: 00607 case EC_TOO_MANY_WRITES_PENDING: 00608 state = EC_RO_CONNECTED_WITH_WRITES_PENDING; 00609 break; 00610 default: 00611 break; 00612 } 00613 updateWatcherStates(); 00614 } 00615 00616 /** 00617 * Disconnects the client. This actually closes the underlying file 00618 * descriptor, even if the FileDescriptor object still has references. 00619 * 00620 * If <em>force</em> is true then the client will be disconnected 00621 * immediately, and any pending outgoing data will be discarded. 00622 * Otherwise the client will be disconnected after all pending 00623 * outgoing data have been sent; in the mean time no new data can be 00624 * received from or sent to the client. 00625 * 00626 * After the client has actually been disconnected (which may be either 00627 * immediately or after a short period of time), a disconnect event will 00628 * be emitted. 00629 * 00630 * If the client connection has already been closed then this method 00631 * does nothing. If the client connection is being closed (because 00632 * there's pending outgoing data) then the behavior depends on the 00633 * <tt>force</tt> argument: if true then the connection is closed 00634 * immediately and the pending data is discarded, otherwise this 00635 * method does nothing. 00636 * 00637 * The <tt>onDisconnect</tt> callback will be called after the file 00638 * descriptor is closed, which is either immediately or after all 00639 * pending data has been sent out. 00640 */ 00641 void disconnect(bool force = false) { 00642 if (!ioAllowed() && !(state == EC_DISCONNECTING_WITH_WRITES_PENDING && force)) { 00643 return; 00644 } 00645 00646 this_thread::disable_interruption di; 00647 this_thread::disable_syscall_interruption dsi; 00648 00649 if (state == EC_CONNECTED || state == EC_RO_CONNECTED || force) { 00650 state = EC_DISCONNECTED; 00651 watchReadEvents(false); 00652 watchWriteEvents(false); 00653 try { 00654 fd.close(); 00655 } catch (const SystemException &e) { 00656 emitSystemErrorEvent(e.brief(), e.code()); 00657 } 00658 emitEvent(onDisconnect); 00659 } else { 00660 state = EC_DISCONNECTING_WITH_WRITES_PENDING; 00661 watchReadEvents(false); 00662 watchWriteEvents(true); 00663 if (syscalls::shutdown(fd, SHUT_RD) == -1) { 00664 int e = errno; 00665 emitSystemErrorEvent( 00666 "Cannot shutdown reader half of the client socket", 00667 e); 00668 } 00669 } 00670 } 00671 00672 /** 00673 * Detaches the client file descriptor so that this EventedClient no longer 00674 * has any control over it. Any EventedClient I/O watchers on the client file 00675 * descriptor will be stopped and further I/O on the file descriptor via 00676 * EventedClient will become impossible. Any pending outgoing data will be 00677 * discarded. The original client file descriptor is returned and 00678 * <tt>onDetach</tt> is called. Subsequent calls to this function will 00679 * return -1 and will no longer call <tt>onDetach</tt>. 00680 * 00681 * @post !ioAllowed() 00682 * @post fd == -1 00683 */ 00684 FileDescriptor detach() { 00685 if (state == EC_DISCONNECTED) { 00686 return fd; 00687 } else { 00688 FileDescriptor oldFd = fd; 00689 state = EC_DISCONNECTED; 00690 watchReadEvents(false); 00691 watchWriteEvents(false); 00692 fd = -1; 00693 emitEvent(onDetach); 00694 return oldFd; 00695 } 00696 } 00697 }; 00698 00699 00700 } // namespace Passenger 00701 00702 #endif /* _PASSENGER_EVENTED_CLIENT_H_ */