michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #ifndef BASE_MESSAGE_PUMP_LIBEVENT_H_ michael@0: #define BASE_MESSAGE_PUMP_LIBEVENT_H_ michael@0: michael@0: #include "base/message_pump.h" michael@0: #include "base/time.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: // Declare structs we need from libevent.h rather than including it michael@0: struct event_base; michael@0: struct event; michael@0: michael@0: class nsDependentCSubstring; michael@0: michael@0: namespace base { michael@0: michael@0: // Class to monitor sockets and issue callbacks when sockets are ready for I/O michael@0: // TODO(dkegel): add support for background file IO somehow michael@0: class MessagePumpLibevent : public MessagePump { michael@0: public: michael@0: michael@0: // Object returned by WatchFileDescriptor to manage further watching. michael@0: class FileDescriptorWatcher { michael@0: public: michael@0: FileDescriptorWatcher(); michael@0: ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor. michael@0: michael@0: // NOTE: These methods aren't called StartWatching()/StopWatching() to michael@0: // avoid confusion with the win32 ObjectWatcher class. michael@0: michael@0: // Stop watching the FD, always safe to call. No-op if there's nothing michael@0: // to do. michael@0: bool StopWatchingFileDescriptor(); michael@0: michael@0: private: michael@0: // Called by MessagePumpLibevent, ownership of |e| is transferred to this michael@0: // object. michael@0: void Init(event* e, bool is_persistent); michael@0: michael@0: // Used by MessagePumpLibevent to take ownership of event_. michael@0: event *ReleaseEvent(); michael@0: friend class MessagePumpLibevent; michael@0: michael@0: private: michael@0: bool is_persistent_; // false if this event is one-shot. michael@0: event* event_; michael@0: DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher); michael@0: }; michael@0: michael@0: // Used with WatchFileDescptor to asynchronously monitor the I/O readiness of michael@0: // a File Descriptor. michael@0: class Watcher { michael@0: public: michael@0: virtual ~Watcher() {} michael@0: // Called from MessageLoop::Run when an FD can be read from/written to michael@0: // without blocking michael@0: virtual void OnFileCanReadWithoutBlocking(int fd) = 0; michael@0: virtual void OnFileCanWriteWithoutBlocking(int fd) = 0; michael@0: }; michael@0: michael@0: MessagePumpLibevent(); michael@0: virtual ~MessagePumpLibevent(); michael@0: michael@0: enum Mode { michael@0: WATCH_READ = 1 << 0, michael@0: WATCH_WRITE = 1 << 1, michael@0: WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE michael@0: }; michael@0: michael@0: // Have the current thread's message loop watch for a a situation in which michael@0: // reading/writing to the FD can be performed without Blocking. michael@0: // Callers must provide a preallocated FileDescriptorWatcher object which michael@0: // can later be used to manage the Lifetime of this event. michael@0: // If a FileDescriptorWatcher is passed in which is already attached to michael@0: // an event, then the effect is cumulative i.e. after the call |controller| michael@0: // will watch both the previous event and the new one. michael@0: // If an error occurs while calling this method in a cumulative fashion, the michael@0: // event previously attached to |controller| is aborted. michael@0: // Returns true on success. michael@0: // TODO(dkegel): switch to edge-triggered readiness notification michael@0: bool WatchFileDescriptor(int fd, michael@0: bool persistent, michael@0: Mode mode, michael@0: FileDescriptorWatcher *controller, michael@0: Watcher *delegate); michael@0: michael@0: michael@0: // This is analagous to FileDescriptorWatcher above, which really is michael@0: // just a wrapper around libevent's |struct event|. This class acts michael@0: // as a sort of "scoped event watcher" in that it guarantees that michael@0: // when this class is out of scope, the signal-event it wraps is michael@0: // removed from libevent's guts. michael@0: // michael@0: // XXX/cjones: this isn't my favorite API, but preserving it in michael@0: // order to match code above michael@0: class SignalEvent { michael@0: friend class MessagePumpLibevent; michael@0: michael@0: public: michael@0: SignalEvent(); michael@0: ~SignalEvent(); // implicitly calls StopCatching() michael@0: michael@0: // Have libevent forget this event. michael@0: bool StopCatching(); michael@0: michael@0: private: michael@0: void Init(event* e); michael@0: event* ReleaseEvent(); michael@0: michael@0: event* event_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(SignalEvent); michael@0: }; michael@0: michael@0: class SignalWatcher { michael@0: public: michael@0: virtual ~SignalWatcher() {} michael@0: // Called from MessageLoop::Run when |sig| has been delivered to michael@0: // this process michael@0: virtual void OnSignal(int sig) = 0; michael@0: }; michael@0: michael@0: // Have the current thread's message loop catch the signal |sig|. michael@0: // Multiple watchers can catch the same signal; they're all notified michael@0: // upon its delivery. Callers must provide a preallocated michael@0: // SignalEvent object which can be used to manage the lifetime of michael@0: // this event. Returns true on success. michael@0: bool CatchSignal(int sig, michael@0: SignalEvent* sigevent, michael@0: SignalWatcher* delegate); michael@0: michael@0: michael@0: // MessagePump methods: michael@0: virtual void Run(Delegate* delegate); michael@0: virtual void Quit(); michael@0: virtual void ScheduleWork(); michael@0: virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); michael@0: michael@0: private: michael@0: michael@0: // Risky part of constructor. Returns true on success. michael@0: bool Init(); michael@0: michael@0: // This flag is set to false when Run should return. michael@0: bool keep_running_; michael@0: michael@0: // This flag is set when inside Run. michael@0: bool in_run_; michael@0: michael@0: // The time at which we should call DoDelayedWork. michael@0: TimeTicks delayed_work_time_; michael@0: michael@0: // Libevent dispatcher. Watches all sockets registered with it, and sends michael@0: // readiness callbacks when a socket is ready for I/O. michael@0: event_base* event_base_; michael@0: michael@0: // Called by libevent to tell us a registered FD can be read/written to. michael@0: static void OnLibeventNotification(int fd, short flags, michael@0: void* context); michael@0: michael@0: // Called by libevent upon receiving a signal michael@0: static void OnLibeventSignalNotification(int sig, short flags, michael@0: void* context); michael@0: michael@0: // Unix pipe used to implement ScheduleWork() michael@0: // ... callback; called by libevent inside Run() when pipe is ready to read michael@0: static void OnWakeup(int socket, short flags, void* context); michael@0: // ... write end; ScheduleWork() writes a single byte to it michael@0: int wakeup_pipe_in_; michael@0: // ... read end; OnWakeup reads it and then breaks Run() out of its sleep michael@0: int wakeup_pipe_out_; michael@0: // ... libevent wrapper for read end michael@0: event* wakeup_event_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent); michael@0: }; michael@0: michael@0: /** michael@0: * LineWatcher overrides OnFileCanReadWithoutBlocking. It separates the read michael@0: * data by mTerminator and passes each line to OnLineRead. michael@0: */ michael@0: class LineWatcher : public MessagePumpLibevent::Watcher michael@0: { michael@0: public: michael@0: LineWatcher(char aTerminator, int aBufferSize) : mReceivedIndex(0), michael@0: mBufferSize(aBufferSize), michael@0: mTerminator(aTerminator) michael@0: { michael@0: mReceiveBuffer = new char[mBufferSize]; michael@0: } michael@0: michael@0: ~LineWatcher() {} michael@0: michael@0: protected: michael@0: /** michael@0: * OnError will be called when |read| returns error. Derived class should michael@0: * implement this function to handle error cases when needed. michael@0: */ michael@0: virtual void OnError() {} michael@0: virtual void OnLineRead(int aFd, nsDependentCSubstring& aMessage) = 0; michael@0: virtual void OnFileCanWriteWithoutBlocking(int /* aFd */) {} michael@0: private: michael@0: virtual void OnFileCanReadWithoutBlocking(int aFd) MOZ_FINAL; michael@0: michael@0: nsAutoPtr mReceiveBuffer; michael@0: int mReceivedIndex; michael@0: int mBufferSize; michael@0: char mTerminator; michael@0: }; michael@0: } // namespace base michael@0: michael@0: #endif // BASE_MESSAGE_PUMP_LIBEVENT_H_