Passenger: FileDescriptor.h Source File

- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List

# FileDescriptor.h

    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_FILE_DESCRIPTOR_H_
    00026 #define _PASSENGER_FILE_DESCRIPTOR_H_
    00027
    00028 #include <boost/shared_ptr.hpp>
    00029 #include <oxt/system_calls.hpp>
    00030
    00031 #include <unistd.h>
    00032 #include <cerrno>
    00033
    00034 #include "MessageChannel.h"
    00035 #include "Exceptions.h"
    00036
    00037 namespace Passenger {
    00038
    00039 using namespace boost;
    00040 using namespace oxt;
    00041
    00042
    00043 /**
    00044  * Wrapper class around a file descriptor integer, for RAII behavior.
    00045  *
    00046  * A FileDescriptor object behaves just like an int, so that you can pass it to
    00047  * system calls such as read(). It performs reference counting. When the last
    00048  * copy of a FileDescriptor has been destroyed, the underlying file descriptor
    00049  * will be automatically closed. In this case, any close() system call errors
    00050  * are silently ignored. If you are interested in whether the close() system
    00051  * call succeeded, then you should call FileDescriptor::close().
    00052  *
    00053  * This class is *not* thread-safe. It is safe to call system calls on the
    00054  * underlying file descriptor from multiple threads, but it's not safe to
    00055  * call FileDescriptor::close() from multiple threads if all those
    00056  * FileDescriptor objects point to the same underlying file descriptor.
    00057  */
    00058 class FileDescriptor {
    00059 private:
    00060         struct SharedData {
    00061                 int fd;
    00062
    00063                 SharedData(int fd) {
    00064                         this->fd = fd;
    00065                 }
    00066
    00067                 ~SharedData() {
    00068                         if (fd >= 0) {
    00069                                 this_thread::disable_syscall_interruption dsi;
    00070                                 syscalls::close(fd);
    00071                         }
    00072                 }
    00073
    00074                 void close() {
    00075                         if (fd >= 0) {
    00076                                 this_thread::disable_syscall_interruption dsi;
    00077                                 int theFd = fd;
    00078                                 fd = -1;
    00079                                 if (syscalls::close(theFd) == -1 && errno != ENOTCONN) {
    00080                                         int e = errno;
    00081                                         throw SystemException("Cannot close file descriptor", e);
    00082                                 }
    00083                         }
    00084                 }
    00085         };
    00086
    00087         /** Shared pointer for reference counting on this file descriptor */
    00088         shared_ptr<SharedData> data;
    00089
    00090 public:
    00091         /**
    00092          * Creates a new empty FileDescriptor instance that has no underlying
    00093          * file descriptor.
    00094          *
    00095          * @post *this == -1
    00096          */
    00097         FileDescriptor() { }
    00098
    00099         /**
    00100          * Creates a new FileDescriptor instance with the given fd as a handle.
    00101          *
    00102          * @post *this == fd
    00103          */
    00104         FileDescriptor(int fd) {
    00105                 if (fd >= 0) {
    00106                         /* Make sure that the 'new' operator doesn't overwrite
    00107                          * errno so that we can write code like this:
    00108                          *
    00109                          *    FileDescriptor fd = open(...);
    00110                          *    if (fd == -1) {
    00111                          *       print_error(errno);
    00112                          *    }
    00113                          */
    00114                         int e = errno;
    00115                         data.reset(new SharedData(fd));
    00116                         errno = e;
    00117                 }
    00118         }
    00119
    00120         /**
    00121          * Close the underlying file descriptor. If it was already closed, then
    00122          * nothing will happen. If there are multiple copies of this FileDescriptor
    00123          * then the underlying file descriptor will be closed for every one of them.
    00124          *
    00125          * @throws SystemException Something went wrong while closing
    00126          *                         the file descriptor.
    00127          * @post *this == -1
    00128          */
    00129         void close() {
    00130                 if (data != NULL) {
    00131                         data->close();
    00132                         data.reset();
    00133                 }
    00134         }
    00135
    00136         /**
    00137          * Overloads the integer cast operator so that it will return the underlying
    00138          * file descriptor handle as an integer.
    00139          *
    00140          * Returns -1 if FileDescriptor::close() was called.
    00141          */
    00142         operator int () const {
    00143                 if (data == NULL) {
    00144                         return -1;
    00145                 } else {
    00146                         return data->fd;
    00147                 }
    00148         }
    00149
    00150         FileDescriptor &operator=(int fd) {
    00151                 /* Make sure that the 'new' and 'delete' operators don't
    00152                  * overwrite errno so that we can write code like this:
    00153                  *
    00154                  *    FileDescriptor fd;
    00155                  *    fd = open(...);
    00156                  *    if (fd == -1) {
    00157                  *       print_error(errno);
    00158                  *    }
    00159                  */
    00160                 int e = errno;
    00161                 if (fd >= 0) {
    00162                         data.reset(new SharedData(fd));
    00163                 } else {
    00164                         data.reset();
    00165                 }
    00166                 errno = e;
    00167                 return *this;
    00168         }
    00169
    00170         FileDescriptor &operator=(const FileDescriptor &other) {
    00171                 /* Make sure that the 'delete' operator implicitly invoked by
    00172                  * shared_ptr doesn't overwrite errno so that we can write code
    00173                  * like this:
    00174                  *
    00175                  *    FileDescriptor fd;
    00176                  *    fd = other_file_descriptor_object;
    00177                  *    if (fd == -1) {
    00178                  *       print_error(errno);
    00179                  *    }
    00180                  */
    00181                 int e = errno;
    00182                 data = other.data;
    00183                 errno = e;
    00184                 return *this;
    00185         }
    00186 };
    00187
    00188 /**
    00189  * A synchronization mechanism that's implemented with file descriptors,
    00190  * and as such can be used in combination with select() and friends.
    00191  *
    00192  * One can wait for an event on an EventFd by select()ing it on read events.
    00193  * Another thread can signal the EventFd by calling notify().
    00194  */
    00195 class EventFd {
    00196 private:
    00197         int reader;
    00198         int writer;
    00199
    00200 public:
    00201         EventFd() {
    00202                 int fds[2];
    00203
    00204                 if (syscalls::pipe(fds) == -1) {
    00205                         int e = errno;
    00206                         throw SystemException("Cannot create a pipe", e);
    00207                 }
    00208                 reader = fds[0];
    00209                 writer = fds[1];
    00210         }
    00211
    00212         ~EventFd() {
    00213                 this_thread::disable_syscall_interruption dsi;
    00214                 syscalls::close(reader);
    00215                 syscalls::close(writer);
    00216         }
    00217
    00218         void notify() {
    00219                 MessageChannel(writer).writeRaw("x", 1);
    00220         }
    00221
    00222         int fd() const {
    00223                 return reader;
    00224         }
    00225 };
    00226
    00227 } // namespace Passenger
    00228
    00229 #endif /* _PASSENGER_FILE_DESCRIPTOR_H_ */