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 <boost/make_shared.hpp>
    00030 #include <oxt/system_calls.hpp>
    00031
    00032 #include <utility>
    00033 #include <unistd.h>
    00034 #include <cerrno>
    00035
    00036 #include <Exceptions.h>
    00037
    00038 namespace Passenger {
    00039
    00040 using namespace std;
    00041 using namespace boost;
    00042 using namespace oxt;
    00043
    00044
    00045 void safelyClose(int fd);
    00046
    00047
    00048 /**
    00049  * Wrapper class around a file descriptor integer, for RAII behavior.
    00050  *
    00051  * A FileDescriptor object behaves just like an int, so that you can pass it to
    00052  * system calls such as read(). It performs reference counting. When the last
    00053  * copy of a FileDescriptor has been destroyed, the underlying file descriptor
    00054  * will be automatically closed. In this case, any close() system call errors
    00055  * are silently ignored. If you are interested in whether the close() system
    00056  * call succeeded, then you should call FileDescriptor::close().
    00057  *
    00058  * This class is *not* thread-safe. It is safe to call system calls on the
    00059  * underlying file descriptor from multiple threads, but it's not safe to
    00060  * call FileDescriptor::close() from multiple threads if all those
    00061  * FileDescriptor objects point to the same underlying file descriptor.
    00062  */
    00063 class FileDescriptor {
    00064 private:
    00065         struct SharedData {
    00066                 int fd;
    00067
    00068                 SharedData(int fd) {
    00069                         this->fd = fd;
    00070                 }
    00071
    00072                 ~SharedData() {
    00073                         if (fd >= 0) {
    00074                                 this_thread::disable_syscall_interruption dsi;
    00075                                 syscalls::close(fd);
    00076                         }
    00077                 }
    00078
    00079                 void close(bool checkErrors = true) {
    00080                         if (fd >= 0) {
    00081                                 this_thread::disable_syscall_interruption dsi;
    00082                                 int theFd = fd;
    00083                                 fd = -1;
    00084                                 if (checkErrors) {
    00085                                         safelyClose(theFd);
    00086                                 } else {
    00087                                         syscalls::close(fd);
    00088                                 }
    00089                         }
    00090                 }
    00091
    00092                 void detach() {
    00093                         fd = -1;
    00094                 }
    00095         };
    00096
    00097         /** Shared pointer for reference counting on this file descriptor */
    00098         shared_ptr<SharedData> data;
    00099
    00100 public:
    00101         /**
    00102          * Creates a new empty FileDescriptor instance that has no underlying
    00103          * file descriptor.
    00104          *
    00105          * @post *this == -1
    00106          */
    00107         FileDescriptor() { }
    00108
    00109         /**
    00110          * Creates a new FileDescriptor instance with the given fd as a handle.
    00111          *
    00112          * @post *this == fd
    00113          */
    00114         FileDescriptor(int fd) {
    00115                 if (fd >= 0) {
    00116                         /* Make sure that the 'new' operator doesn't overwrite
    00117                          * errno so that we can write code like this:
    00118                          *
    00119                          *    FileDescriptor fd = open(...);
    00120                          *    if (fd == -1) {
    00121                          *       print_error(errno);
    00122                          *    }
    00123                          */
    00124                         int e = errno;
    00125                         data = make_shared<SharedData>(fd);
    00126                         errno = e;
    00127                 }
    00128         }
    00129
    00130         /**
    00131          * Close the underlying file descriptor. If it was already closed, then
    00132          * nothing will happen. If there are multiple copies of this FileDescriptor
    00133          * then the underlying file descriptor will be closed for every one of them.
    00134          *
    00135          * @params checkErrors Whether a SystemException should be thrown in case
    00136          *                     closing the file descriptor fails. If false, errors
    00137          *                     are silently ignored.
    00138          * @throws SystemException Something went wrong while closing
    00139          *                         the file descriptor. Only thrown if
    00140          *                         checkErrors is true.
    00141          * @post *this == -1
    00142          */
    00143         void close(bool checkErrors = true) {
    00144                 if (data != NULL) {
    00145                         data->close(checkErrors);
    00146                         data.reset();
    00147                 }
    00148         }
    00149
    00150         /**
    00151          * Detach from the underlying file descriptor without closing it.
    00152          * This FileDescriptor and all copies will no longer affect the
    00153          * underlying file descriptors.
    00154          *
    00155          * @return The underlying file descriptor, or -1 if already closed.
    00156          * @post *this == -1
    00157          */
    00158         int detach() {
    00159                 if (data != NULL) {
    00160                         int fd = data->fd;
    00161                         data->detach();
    00162                         data.reset();
    00163                         return fd;
    00164                 } else {
    00165                         return -1;
    00166                 }
    00167         }
    00168
    00169         /**
    00170          * Overloads the integer cast operator so that it will return the underlying
    00171          * file descriptor handle as an integer.
    00172          *
    00173          * Returns -1 if FileDescriptor::close() was called.
    00174          */
    00175         operator int () const {
    00176                 if (data == NULL) {
    00177                         return -1;
    00178                 } else {
    00179                         return data->fd;
    00180                 }
    00181         }
    00182
    00183         FileDescriptor &operator=(int fd) {
    00184                 /* Make sure that the 'new' and 'delete' operators don't
    00185                  * overwrite errno so that we can write code like this:
    00186                  *
    00187                  *    FileDescriptor fd;
    00188                  *    fd = open(...);
    00189                  *    if (fd == -1) {
    00190                  *       print_error(errno);
    00191                  *    }
    00192                  */
    00193                 int e = errno;
    00194                 if (fd >= 0) {
    00195                         data = make_shared<SharedData>(fd);
    00196                 } else {
    00197                         data.reset();
    00198                 }
    00199                 errno = e;
    00200                 return *this;
    00201         }
    00202
    00203         FileDescriptor &operator=(const FileDescriptor &other) {
    00204                 /* Make sure that the 'delete' operator implicitly invoked by
    00205                  * shared_ptr doesn't overwrite errno so that we can write code
    00206                  * like this:
    00207                  *
    00208                  *    FileDescriptor fd;
    00209                  *    fd = other_file_descriptor_object;
    00210                  *    if (fd == -1) {
    00211                  *       print_error(errno);
    00212                  *    }
    00213                  */
    00214                 int e = errno;
    00215                 data = other.data;
    00216                 errno = e;
    00217                 return *this;
    00218         }
    00219 };
    00220
    00221 /**
    00222  * A structure containing two FileDescriptor objects. Behaves like a pair
    00223  * and like a two-element array.
    00224  */
    00225 class FileDescriptorPair: public pair<FileDescriptor, FileDescriptor> {
    00226 public:
    00227         FileDescriptorPair() { }
    00228
    00229         FileDescriptorPair(const FileDescriptor &a, const FileDescriptor &b)
    00230                 : pair<FileDescriptor, FileDescriptor>(a, b)
    00231                 { }
    00232
    00233         FileDescriptor &operator[](int index) {
    00234                 if (index == 0) {
    00235                         return first;
    00236                 } else if (index == 1) {
    00237                         return second;
    00238                 } else {
    00239                         throw ArgumentException("Index must be either 0 of 1");
    00240                 }
    00241         }
    00242 };
    00243
    00244 // Convenience aliases.
    00245 typedef FileDescriptorPair Pipe;
    00246 typedef FileDescriptorPair SocketPair;
    00247
    00248 /**
    00249  * A synchronization mechanism that's implemented with file descriptors,
    00250  * and as such can be used in combination with select() and friends.
    00251  *
    00252  * One can wait for an event on an EventFd by select()ing it on read events.
    00253  * Another thread can signal the EventFd by calling notify().
    00254  */
    00255 class EventFd {
    00256 private:
    00257         int reader;
    00258         int writer;
    00259
    00260 public:
    00261         EventFd() {
    00262                 int fds[2];
    00263
    00264                 if (syscalls::pipe(fds) == -1) {
    00265                         int e = errno;
    00266                         throw SystemException("Cannot create a pipe", e);
    00267                 }
    00268                 reader = fds[0];
    00269                 writer = fds[1];
    00270         }
    00271
    00272         ~EventFd() {
    00273                 this_thread::disable_syscall_interruption dsi;
    00274                 syscalls::close(reader);
    00275                 syscalls::close(writer);
    00276         }
    00277
    00278         void notify() {
    00279                 ssize_t ret = syscalls::write(writer, "x", 1);
    00280                 if (ret == -1 && errno != EAGAIN) {
    00281                         int e = errno;
    00282                         throw SystemException("Cannot write notification data", e);
    00283                 }
    00284         }
    00285
    00286         int fd() const {
    00287                 return reader;
    00288         }
    00289 };
    00290
    00291 } // namespace Passenger
    00292
    00293 #endif /* _PASSENGER_FILE_DESCRIPTOR_H_ */