[libcamera-devel,v2,06/11] libcamera: Add event notification infrastructure

Message ID 20190107231151.23291-7-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: Add event notification and timers
Related show

Commit Message

Laurent Pinchart Jan. 7, 2019, 11:11 p.m. UTC
Add three new classes, EventDispatcher, EventNotifier and Timer, that
define APIs for file descriptor event notification and timers. The
implementation of the EventDispatcher is meant to be provided to
libcamera by the application.

The event dispatcher is integrated twith the camera manager to implement
automatic registration of timers and events.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
Changes since v1:

- Rename EventNotifier::isEnabled() to EventNotifier::enabled()
- Fix typo in documentation
---
 include/libcamera/camera_manager.h   |   7 ++
 include/libcamera/event_dispatcher.h |  33 ++++++++
 include/libcamera/event_notifier.h   |  42 ++++++++++
 include/libcamera/libcamera.h        |   1 +
 include/libcamera/meson.build        |   3 +
 include/libcamera/timer.h            |  37 ++++++++
 src/libcamera/camera_manager.cpp     |  42 +++++++++-
 src/libcamera/event_dispatcher.cpp   | 103 +++++++++++++++++++++++
 src/libcamera/event_notifier.cpp     | 121 +++++++++++++++++++++++++++
 src/libcamera/meson.build            |   3 +
 src/libcamera/timer.cpp              | 105 +++++++++++++++++++++++
 11 files changed, 496 insertions(+), 1 deletion(-)
 create mode 100644 include/libcamera/event_dispatcher.h
 create mode 100644 include/libcamera/event_notifier.h
 create mode 100644 include/libcamera/timer.h
 create mode 100644 src/libcamera/event_dispatcher.cpp
 create mode 100644 src/libcamera/event_notifier.cpp
 create mode 100644 src/libcamera/timer.cpp

Comments

Jacopo Mondi Jan. 8, 2019, 9:43 a.m. UTC | #1
Hi Laurent,
   a few minor things on comments, otherwise

Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

On Tue, Jan 08, 2019 at 01:11:46AM +0200, Laurent Pinchart wrote:
> Add three new classes, EventDispatcher, EventNotifier and Timer, that
> define APIs for file descriptor event notification and timers. The
> implementation of the EventDispatcher is meant to be provided to
> libcamera by the application.
>
> The event dispatcher is integrated twith the camera manager to implement
> automatic registration of timers and events.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
> Changes since v1:
>
> - Rename EventNotifier::isEnabled() to EventNotifier::enabled()
> - Fix typo in documentation
> ---
>  include/libcamera/camera_manager.h   |   7 ++
>  include/libcamera/event_dispatcher.h |  33 ++++++++
>  include/libcamera/event_notifier.h   |  42 ++++++++++
>  include/libcamera/libcamera.h        |   1 +
>  include/libcamera/meson.build        |   3 +
>  include/libcamera/timer.h            |  37 ++++++++
>  src/libcamera/camera_manager.cpp     |  42 +++++++++-
>  src/libcamera/event_dispatcher.cpp   | 103 +++++++++++++++++++++++
>  src/libcamera/event_notifier.cpp     | 121 +++++++++++++++++++++++++++
>  src/libcamera/meson.build            |   3 +
>  src/libcamera/timer.cpp              | 105 +++++++++++++++++++++++
>  11 files changed, 496 insertions(+), 1 deletion(-)
>  create mode 100644 include/libcamera/event_dispatcher.h
>  create mode 100644 include/libcamera/event_notifier.h
>  create mode 100644 include/libcamera/timer.h
>  create mode 100644 src/libcamera/event_dispatcher.cpp
>  create mode 100644 src/libcamera/event_notifier.cpp
>  create mode 100644 src/libcamera/timer.cpp
>
> diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h
> index e14da0f893b3..15e7c162032a 100644
> --- a/include/libcamera/camera_manager.h
> +++ b/include/libcamera/camera_manager.h
> @@ -14,6 +14,7 @@ namespace libcamera {
>
>  class Camera;
>  class DeviceEnumerator;
> +class EventDispatcher;
>  class PipelineHandler;
>
>  class CameraManager
> @@ -27,13 +28,19 @@ public:
>
>  	static CameraManager *instance();
>
> +	void setEventDispatcher(EventDispatcher *dispatcher);
> +	EventDispatcher *eventDispatcher();
> +
>  private:
>  	CameraManager();
>  	CameraManager(const CameraManager &) = delete;
>  	void operator=(const CameraManager &) = delete;
> +	~CameraManager();
>
>  	DeviceEnumerator *enumerator_;
>  	std::vector<PipelineHandler *> pipes_;
> +
> +	EventDispatcher *dispatcher_;
>  };
>
>  } /* namespace libcamera */
> diff --git a/include/libcamera/event_dispatcher.h b/include/libcamera/event_dispatcher.h
> new file mode 100644
> index 000000000000..c20518c6866d
> --- /dev/null
> +++ b/include/libcamera/event_dispatcher.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * event_dispatcher.h - Event dispatcher
> + */
> +#ifndef __LIBCAMERA_EVENT_DISPATCHER_H__
> +#define __LIBCAMERA_EVENT_DISPATCHER_H__
> +
> +#include <vector>
> +
> +namespace libcamera {
> +
> +class EventNotifier;
> +class Timer;
> +
> +class EventDispatcher
> +{
> +public:
> +	virtual ~EventDispatcher();
> +
> +	virtual void registerEventNotifier(EventNotifier *notifier) = 0;
> +	virtual void unregisterEventNotifier(EventNotifier *notifier) = 0;
> +
> +	virtual void registerTimer(Timer *timer) = 0;
> +	virtual void unregisterTimer(Timer *timer) = 0;
> +
> +	virtual void processEvents() = 0;
> +};
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_EVENT_DISPATCHER_H__ */
> diff --git a/include/libcamera/event_notifier.h b/include/libcamera/event_notifier.h
> new file mode 100644
> index 000000000000..1e9b6da1340c
> --- /dev/null
> +++ b/include/libcamera/event_notifier.h
> @@ -0,0 +1,42 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * event_notifier.h - File descriptor event notifier
> + */
> +#ifndef __LIBCAMERA_EVENT_NOTIFIER_H__
> +#define __LIBCAMERA_EVENT_NOTIFIER_H__
> +
> +#include <libcamera/signal.h>
> +
> +namespace libcamera {
> +
> +class EventNotifier
> +{
> +public:
> +	enum Type {
> +		Read,
> +		Write,
> +		Exception,
> +	};
> +
> +	EventNotifier(int fd, Type type);
> +	virtual ~EventNotifier();
> +
> +	Type type() const { return type_; }
> +	int fd() const { return fd_; }
> +
> +	bool enabled() const { return enabled_; }
> +	void setEnabled(bool enable);
> +
> +	Signal<EventNotifier *> activated;
> +
> +private:
> +	int fd_;
> +	Type type_;
> +	bool enabled_;
> +};
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_EVENT_NOTIFIER_H__ */
> diff --git a/include/libcamera/libcamera.h b/include/libcamera/libcamera.h
> index f9556a8bce62..785babefa1c8 100644
> --- a/include/libcamera/libcamera.h
> +++ b/include/libcamera/libcamera.h
> @@ -9,5 +9,6 @@
>
>  #include <libcamera/camera.h>
>  #include <libcamera/camera_manager.h>
> +#include <libcamera/event_dispatcher.h>
>
>  #endif /* __LIBCAMERA_LIBCAMERA_H__ */
> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> index 6f87689ea528..d7cb55ba4a49 100644
> --- a/include/libcamera/meson.build
> +++ b/include/libcamera/meson.build
> @@ -1,8 +1,11 @@
>  libcamera_api = files([
>      'camera.h',
>      'camera_manager.h',
> +    'event_dispatcher.h',
> +    'event_notifier.h',
>      'libcamera.h',
>      'signal.h',
> +    'timer.h',
>  ])
>
>  install_headers(libcamera_api,
> diff --git a/include/libcamera/timer.h b/include/libcamera/timer.h
> new file mode 100644
> index 000000000000..97dcc01f493d
> --- /dev/null
> +++ b/include/libcamera/timer.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * timer.h - Generic timer
> + */
> +#ifndef __LIBCAMERA_TIMER_H__
> +#define __LIBCAMERA_TIMER_H__
> +
> +#include <cstdint>
> +
> +#include <libcamera/signal.h>
> +
> +namespace libcamera {
> +
> +class Timer
> +{
> +public:
> +	Timer();
> +
> +	void start(unsigned int msec);
> +	void stop();
> +	bool isRunning() const;
> +
> +	unsigned int interval() const { return interval_; }
> +	uint64_t deadline() const { return deadline_; }
> +
> +	Signal<Timer *> timeout;
> +
> +private:
> +	unsigned int interval_;
> +	uint64_t deadline_;
> +};
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_TIMER_H__ */
> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
> index 1a9d2f38e3b9..cbfd977f1e3c 100644
> --- a/src/libcamera/camera_manager.cpp
> +++ b/src/libcamera/camera_manager.cpp
> @@ -6,8 +6,10 @@
>   */
>
>  #include <libcamera/camera_manager.h>
> +#include <libcamera/event_dispatcher.h>
>
>  #include "device_enumerator.h"
> +#include "log.h"
>  #include "pipeline_handler.h"
>
>  /**
> @@ -37,10 +39,15 @@
>  namespace libcamera {
>
>  CameraManager::CameraManager()
> -	: enumerator_(nullptr)
> +	: enumerator_(nullptr), dispatcher_(nullptr)
>  {
>  }
>
> +CameraManager::~CameraManager()
> +{
> +	delete dispatcher_;
> +}
> +
>  /**
>   * \brief Start the camera manager
>   *
> @@ -176,4 +183,37 @@ CameraManager *CameraManager::instance()
>  	return &manager;
>  }
>
> +/**
> + * \brief Set the event dispatcher
> + * \param dispatcher Pointer to the event dispatcher
> + *
> + * libcamera requires an event dispatcher to integrate event notification and
> + * timers with the application event loop. Applications shall call this function
> + * once and only once before the camera manager is started with start() to set
> + * the event dispatcher.
> + *
> + * The CameraManager takes ownership of the event dispatcher and will delete it
> + * when the application terminates.
> + */
> +void CameraManager::setEventDispatcher(EventDispatcher *dispatcher)
> +{
> +	if (dispatcher_) {
> +		LOG(Warning) << "Event dispatcher is already set";
> +		return;
> +	}
> +
> +	dispatcher_ = dispatcher;
> +}
> +
> +/**
> + * \fn CameraManager::eventDispatcher()

Not needed, as the comment applies to the function just after the
comment block.

> + * \brief Retrieve the event dispatcher
> + * \return Pointer to the event dispatcher, or nullptr if no event dispatcher
> + * has been set
> + */
> +EventDispatcher *CameraManager::eventDispatcher()
> +{
> +	return dispatcher_;
> +}
> +
>  } /* namespace libcamera */
> diff --git a/src/libcamera/event_dispatcher.cpp b/src/libcamera/event_dispatcher.cpp
> new file mode 100644
> index 000000000000..b893ab046985
> --- /dev/null
> +++ b/src/libcamera/event_dispatcher.cpp
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * event_dispatcher.cpp - Event dispatcher
> + */
> +
> +#include <libcamera/event_dispatcher.h>
> +
> +/**
> + * \file event_dispatcher.h
> + */
> +
> +namespace libcamera {
> +
> +/**
> + * \class EventDispatcher
> + * \brief Interface to manage the libcamera events and timers
> + *
> + * The EventDispatcher class allows the integration of the application event
> + * loop with libcamera by abstracting how events and timers are managed and
> + * processed.
> + *
> + * To listen to events, libcamera creates EventNotifier instances and register

s/register/registers ?

> + * them with the dispatcher with registerEventNotifier(). The event notifier
> + * \ref EventNotifier::activated signal is then emitted by the dispatcher
> + * whenever the event is detected.
> + *
> + * To set timers, libcamera creates Timer instances and register them with the

s/register/registers ?

> + * dispatcher with registerTimer(). The timer \ref Timer::timeout signal is then
> + * emitted by the dispatcher when the timer times out.
> + */
> +
> +EventDispatcher::~EventDispatcher()
> +{
> +}
> +
> +/**
> + * \fn EventDispatcher::registerEventNotifier()
> + * \brief Register an event notifier
> + * \param notifier The event notifier to register
> + *
> + * Once the \a notifier is registered with the dispatcher, the dispatcher will
> + * emit the notifier \ref EventNotifier::activated signal whenever a
> + * corresponding event is detected on the notifier's file descriptor. The event
> + * is monitored until the notifier is unregistered with
> + * unregisterEventNotifier().
> + *
> + * Registering multiple notifiers for the same file descriptor and event type is
> + * not allowed and results in undefined behaviour.
> + */
> +
> +/**
> + * \fn EventDispatcher::unregisterEventNotifier()
> + * \brief Unregister an event notifier
> + * \param notifier The event notifier to unregister
> + *
> + * After this function returns the \a notifier is guaranteed not to emit the
> + * \ref EventNotifier::activated signal.
> + *
> + * If the notifier isn't registered, this function performs no operation.
> + */
> +
> +/**
> + * \fn EventDispatcher::registerTimer()
> + * \brief Register a timer
> + * \param timer The timer to register
> + *
> + * Once the \a timer is registered with the dispatcher, the dispatcher will emit
> + * the timer \ref Timer::timeout signal when the timer times out. The timer can
> + * be unregistered with unregisterTimer() before it times out, in which case the
> + * signal will not be emitted.
> + *
> + * When the \a timer times out, it is automatically unregistered by the
> + * dispatcher and can be registered back as early as from the \ref Timer::timeout
> + * signal handlers.
> + *
> + * Registering the same timer multiple times is not allowed and results in
> + * undefined behaviour.
> + */
> +
> +/**
> + * \fn EventDispatcher::unregisterTimer()
> + * \brief Unregister a timer
> + * \param timer The timer to unregister
> + *
> + * After this function returns the \a timer is guaranteed not to emit the
> + * \ref Timer::timeout signal.
> + *
> + * If the timer isn't registered, this function performs no operation.
> + */
> +
> +/**
> + * \fn EventDispatcher::processEvents()
> + * \brief Wait for and process pending events
> + *
> + * This function processes all pending events associated with registered event
> + * notifiers and timers and signals the corresponding EventNotifier and Timer
> + * objects. If no events are pending, it waits for the first event and processes
> + * it before returning.
> + */
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/event_notifier.cpp b/src/libcamera/event_notifier.cpp
> new file mode 100644
> index 000000000000..8dc45a546a11
> --- /dev/null
> +++ b/src/libcamera/event_notifier.cpp
> @@ -0,0 +1,121 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * event_notifier.cpp - File descriptor event notifier
> + */
> +
> +#include <libcamera/camera_manager.h>
> +#include <libcamera/event_dispatcher.h>
> +#include <libcamera/event_notifier.h>
> +
> +/**
> + * \file event_notifier.h
> + * \brief Event notifier

Event notifier for file descriptor events ?

> + */
> +
> +namespace libcamera {
> +
> +/**
> + * \class EventNotifier
> + * \brief Notify of activity on a file descriptor
> + *
> + * The EventNotifier models a file descriptor event source that can be
> + * monitored. It is created with the file descriptor to be monitored and the
> + * type of event, and is enabled by default. It will emit the \ref activated
> + * signal whenever an event of the monitored type occurs on the file descriptor.
> + *
> + * Supported type of events are EventNotifier::Read, EventNotifier::Write and
> + * EventNotifier::Exception. The type is specified when constructing the
> + * notifier, and can be retrieved using the type() function. To listen to
> + * multiple event types on the same file descriptor multiple notifiers must be
> + * created.
> + *
> + * The notifier can be disabled with the setEnable() function. When the notifier
> + * is disabled it ignores events and does not emit the \ref activated signal.
> + * The notifier can then be re-enabled with the setEnable() function.
> + *
> + * Creating multiple notifiers of the same type for the same file descriptor is
> + * not allowed and results in undefined behaviour.
> + *
> + * Notifier events are detected and dispatched from the
> + * EventDispatcher::processEvents() function.
> + */
> +
> +/**
> + * \enum EventNotifier::Type
> + * Type of file descriptor event to listen for.
> + * \var EventNotifier::Read
> + * Data is available to be read from the file descriptor
> + * \var EventNotifier::Write
> + * Data can be written to the file descriptor
> + * \var EventNotifier::Exception
> + * An exception has occurred on the file descriptor
> + */
> +
> +/**
> + * \brief Construct an event notifier with a file descriptor and event type
> + * \param fd The file descriptor to monitor
> + * \param type The event type to monitor
> + */
> +EventNotifier::EventNotifier(int fd, Type type)
> +	: fd_(fd), type_(type), enabled_(false)
> +{
> +	setEnabled(true);
> +}
> +
> +EventNotifier::~EventNotifier()
> +{
> +	setEnabled(false);
> +}
> +
> +/**
> + * \fn EventNotifier::type()
> + * \brief Retrive the type of the event being monitored
> + * \return The type of the event
> + */
> +
> +/**
> + * \fn EventNotifier::fd()
> + * \brief Retrive the file descriptor being monitored
> + * \return The file descriptor
> + */
> +
> +/**
> + * \fn EventNotifier::enabled()
> + * \brief Retrieve the notifier state
> + * \return true if the notifier is enabled, or false otherwise

s/true/True ?

> + * \sa setEnable()
> + */
> +
> +/**
> + * \brief Enable or disable the notifier
> + * \param enable true to enable the notifier, false to disable it

s/true/True ?

> + *
> + * This function enables or disables the notifier. A disabled notifier ignores
> + * events and does not emit the \ref activated signal.
> + */
> +void EventNotifier::setEnabled(bool enable)
> +{
> +	if (enabled_ == enable)
> +		return;
> +
> +	enabled_ = enable;
> +
> +	EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher();
> +	if (enable)
> +		dispatcher->registerEventNotifier(this);
> +	else
> +		dispatcher->unregisterEventNotifier(this);
> +}
> +
> +/**
> + * \var EventNotifier::activated
> + * \brief Signal emitted when the event occurs
> + *
> + * This signal is emitted when the event \ref type() occurs on the file
> + * descriptor monitored by the notifier. The notifier pointer is passed as a
> + * parameter.
> + */
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 3ec86e75b57e..61fb93205b34 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -2,11 +2,14 @@ libcamera_sources = files([
>      'camera.cpp',
>      'camera_manager.cpp',
>      'device_enumerator.cpp',
> +    'event_dispatcher.cpp',
> +    'event_notifier.cpp',
>      'log.cpp',
>      'media_device.cpp',
>      'media_object.cpp',
>      'pipeline_handler.cpp',
>      'signal.cpp',
> +    'timer.cpp',
>  ])
>
>  libcamera_headers = files([
> diff --git a/src/libcamera/timer.cpp b/src/libcamera/timer.cpp
> new file mode 100644
> index 000000000000..57c49aa2ef36
> --- /dev/null
> +++ b/src/libcamera/timer.cpp
> @@ -0,0 +1,105 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * timer.cpp - Generic timer
> + */
> +
> +#include <time.h>
> +
> +#include <libcamera/camera_manager.h>
> +#include <libcamera/event_dispatcher.h>
> +#include <libcamera/timer.h>
> +
> +#include "log.h"
> +
> +/**
> + * \file timer.h
> + * \brief Generic timer
> + */
> +
> +namespace libcamera {
> +
> +/**
> + * \class Timer
> + * \brief Single-shot timer interface
> + *
> + * The Timer class models a single-shot timer that is started with start() and
> + * emits the \ref timeout signal when it times out.
> + *
> + * Once started the timer will run until it times out. It can be stopped with
> + * stop(), and once it times out or is stopped, can be started again with
> + * start().
> + */
> +
> +/**
> + * \brief Construct a timer
> + */
> +Timer::Timer()
> +{
> +}
> +
> +/**
> + * \brief Start or restart the timer with a timeout of \a msec
> + * \param msec The timer duration in milliseconds
> + *
> + * If the timer is already running it will be stopped and restarted.
> + */
> +void Timer::start(unsigned int msec)
> +{
> +	struct timespec tp;
> +	clock_gettime(CLOCK_MONOTONIC, &tp);
> +
> +	interval_ = msec;
> +	deadline_ = tp.tv_sec * 1000000000ULL + tp.tv_nsec + msec * 1000000;
> +
> +	LOG(Debug) << "Starting timer " << this << " with interval " << msec
> +		   << ": deadline " << deadline_;
> +
> +	CameraManager::instance()->eventDispatcher()->registerTimer(this);
> +}
> +
> +/**
> + * \brief Stop the timer
> + *
> + * After this function returns the timer is guaranteed not to emit the
> + * \ref timeout signal.
> + *
> + * If the timer is not running this function performs no operation.
> + */
> +void Timer::stop()
> +{
> +	CameraManager::instance()->eventDispatcher()->unregisterTimer(this);
> +
> +	deadline_ = 0;
> +}
> +
> +/**
> + * \brief

Empty brief. I know, there's not much to say on this function...

> + * \return true if the timer is running, false otherwise

s/true/True ?

> + */
> +bool Timer::isRunning() const
> +{
> +	return deadline_ != 0;
> +}
> +
> +/**
> + * \fn Timer::interval()
> + * \brief Retrieve the timer interval
> + * \return The timer interval in milliseconds
> + */
> +
> +/**
> + * \fn Timer::deadline()
> + * \brief Retrieve the timer deadline
> + * \return The timer deadline in nanoseconds
> + */
> +
> +/**
> + * \var Timer::timeout
> + * \brief Signal emitted when the timer times out
> + *
> + * The timer pointer is passed as a parameter.
> + */
> +
> +} /* namespace libcamera */

Thanks
  j

> --
> Regards,
>
> Laurent Pinchart
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h
index e14da0f893b3..15e7c162032a 100644
--- a/include/libcamera/camera_manager.h
+++ b/include/libcamera/camera_manager.h
@@ -14,6 +14,7 @@  namespace libcamera {
 
 class Camera;
 class DeviceEnumerator;
+class EventDispatcher;
 class PipelineHandler;
 
 class CameraManager
@@ -27,13 +28,19 @@  public:
 
 	static CameraManager *instance();
 
+	void setEventDispatcher(EventDispatcher *dispatcher);
+	EventDispatcher *eventDispatcher();
+
 private:
 	CameraManager();
 	CameraManager(const CameraManager &) = delete;
 	void operator=(const CameraManager &) = delete;
+	~CameraManager();
 
 	DeviceEnumerator *enumerator_;
 	std::vector<PipelineHandler *> pipes_;
+
+	EventDispatcher *dispatcher_;
 };
 
 } /* namespace libcamera */
diff --git a/include/libcamera/event_dispatcher.h b/include/libcamera/event_dispatcher.h
new file mode 100644
index 000000000000..c20518c6866d
--- /dev/null
+++ b/include/libcamera/event_dispatcher.h
@@ -0,0 +1,33 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_dispatcher.h - Event dispatcher
+ */
+#ifndef __LIBCAMERA_EVENT_DISPATCHER_H__
+#define __LIBCAMERA_EVENT_DISPATCHER_H__
+
+#include <vector>
+
+namespace libcamera {
+
+class EventNotifier;
+class Timer;
+
+class EventDispatcher
+{
+public:
+	virtual ~EventDispatcher();
+
+	virtual void registerEventNotifier(EventNotifier *notifier) = 0;
+	virtual void unregisterEventNotifier(EventNotifier *notifier) = 0;
+
+	virtual void registerTimer(Timer *timer) = 0;
+	virtual void unregisterTimer(Timer *timer) = 0;
+
+	virtual void processEvents() = 0;
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_EVENT_DISPATCHER_H__ */
diff --git a/include/libcamera/event_notifier.h b/include/libcamera/event_notifier.h
new file mode 100644
index 000000000000..1e9b6da1340c
--- /dev/null
+++ b/include/libcamera/event_notifier.h
@@ -0,0 +1,42 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_notifier.h - File descriptor event notifier
+ */
+#ifndef __LIBCAMERA_EVENT_NOTIFIER_H__
+#define __LIBCAMERA_EVENT_NOTIFIER_H__
+
+#include <libcamera/signal.h>
+
+namespace libcamera {
+
+class EventNotifier
+{
+public:
+	enum Type {
+		Read,
+		Write,
+		Exception,
+	};
+
+	EventNotifier(int fd, Type type);
+	virtual ~EventNotifier();
+
+	Type type() const { return type_; }
+	int fd() const { return fd_; }
+
+	bool enabled() const { return enabled_; }
+	void setEnabled(bool enable);
+
+	Signal<EventNotifier *> activated;
+
+private:
+	int fd_;
+	Type type_;
+	bool enabled_;
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_EVENT_NOTIFIER_H__ */
diff --git a/include/libcamera/libcamera.h b/include/libcamera/libcamera.h
index f9556a8bce62..785babefa1c8 100644
--- a/include/libcamera/libcamera.h
+++ b/include/libcamera/libcamera.h
@@ -9,5 +9,6 @@ 
 
 #include <libcamera/camera.h>
 #include <libcamera/camera_manager.h>
+#include <libcamera/event_dispatcher.h>
 
 #endif /* __LIBCAMERA_LIBCAMERA_H__ */
diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
index 6f87689ea528..d7cb55ba4a49 100644
--- a/include/libcamera/meson.build
+++ b/include/libcamera/meson.build
@@ -1,8 +1,11 @@ 
 libcamera_api = files([
     'camera.h',
     'camera_manager.h',
+    'event_dispatcher.h',
+    'event_notifier.h',
     'libcamera.h',
     'signal.h',
+    'timer.h',
 ])
 
 install_headers(libcamera_api,
diff --git a/include/libcamera/timer.h b/include/libcamera/timer.h
new file mode 100644
index 000000000000..97dcc01f493d
--- /dev/null
+++ b/include/libcamera/timer.h
@@ -0,0 +1,37 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * timer.h - Generic timer
+ */
+#ifndef __LIBCAMERA_TIMER_H__
+#define __LIBCAMERA_TIMER_H__
+
+#include <cstdint>
+
+#include <libcamera/signal.h>
+
+namespace libcamera {
+
+class Timer
+{
+public:
+	Timer();
+
+	void start(unsigned int msec);
+	void stop();
+	bool isRunning() const;
+
+	unsigned int interval() const { return interval_; }
+	uint64_t deadline() const { return deadline_; }
+
+	Signal<Timer *> timeout;
+
+private:
+	unsigned int interval_;
+	uint64_t deadline_;
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_TIMER_H__ */
diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
index 1a9d2f38e3b9..cbfd977f1e3c 100644
--- a/src/libcamera/camera_manager.cpp
+++ b/src/libcamera/camera_manager.cpp
@@ -6,8 +6,10 @@ 
  */
 
 #include <libcamera/camera_manager.h>
+#include <libcamera/event_dispatcher.h>
 
 #include "device_enumerator.h"
+#include "log.h"
 #include "pipeline_handler.h"
 
 /**
@@ -37,10 +39,15 @@ 
 namespace libcamera {
 
 CameraManager::CameraManager()
-	: enumerator_(nullptr)
+	: enumerator_(nullptr), dispatcher_(nullptr)
 {
 }
 
+CameraManager::~CameraManager()
+{
+	delete dispatcher_;
+}
+
 /**
  * \brief Start the camera manager
  *
@@ -176,4 +183,37 @@  CameraManager *CameraManager::instance()
 	return &manager;
 }
 
+/**
+ * \brief Set the event dispatcher
+ * \param dispatcher Pointer to the event dispatcher
+ *
+ * libcamera requires an event dispatcher to integrate event notification and
+ * timers with the application event loop. Applications shall call this function
+ * once and only once before the camera manager is started with start() to set
+ * the event dispatcher.
+ *
+ * The CameraManager takes ownership of the event dispatcher and will delete it
+ * when the application terminates.
+ */
+void CameraManager::setEventDispatcher(EventDispatcher *dispatcher)
+{
+	if (dispatcher_) {
+		LOG(Warning) << "Event dispatcher is already set";
+		return;
+	}
+
+	dispatcher_ = dispatcher;
+}
+
+/**
+ * \fn CameraManager::eventDispatcher()
+ * \brief Retrieve the event dispatcher
+ * \return Pointer to the event dispatcher, or nullptr if no event dispatcher
+ * has been set
+ */
+EventDispatcher *CameraManager::eventDispatcher()
+{
+	return dispatcher_;
+}
+
 } /* namespace libcamera */
diff --git a/src/libcamera/event_dispatcher.cpp b/src/libcamera/event_dispatcher.cpp
new file mode 100644
index 000000000000..b893ab046985
--- /dev/null
+++ b/src/libcamera/event_dispatcher.cpp
@@ -0,0 +1,103 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_dispatcher.cpp - Event dispatcher
+ */
+
+#include <libcamera/event_dispatcher.h>
+
+/**
+ * \file event_dispatcher.h
+ */
+
+namespace libcamera {
+
+/**
+ * \class EventDispatcher
+ * \brief Interface to manage the libcamera events and timers
+ *
+ * The EventDispatcher class allows the integration of the application event
+ * loop with libcamera by abstracting how events and timers are managed and
+ * processed.
+ *
+ * To listen to events, libcamera creates EventNotifier instances and register
+ * them with the dispatcher with registerEventNotifier(). The event notifier
+ * \ref EventNotifier::activated signal is then emitted by the dispatcher
+ * whenever the event is detected.
+ *
+ * To set timers, libcamera creates Timer instances and register them with the
+ * dispatcher with registerTimer(). The timer \ref Timer::timeout signal is then
+ * emitted by the dispatcher when the timer times out.
+ */
+
+EventDispatcher::~EventDispatcher()
+{
+}
+
+/**
+ * \fn EventDispatcher::registerEventNotifier()
+ * \brief Register an event notifier
+ * \param notifier The event notifier to register
+ *
+ * Once the \a notifier is registered with the dispatcher, the dispatcher will
+ * emit the notifier \ref EventNotifier::activated signal whenever a
+ * corresponding event is detected on the notifier's file descriptor. The event
+ * is monitored until the notifier is unregistered with
+ * unregisterEventNotifier().
+ *
+ * Registering multiple notifiers for the same file descriptor and event type is
+ * not allowed and results in undefined behaviour.
+ */
+
+/**
+ * \fn EventDispatcher::unregisterEventNotifier()
+ * \brief Unregister an event notifier
+ * \param notifier The event notifier to unregister
+ *
+ * After this function returns the \a notifier is guaranteed not to emit the
+ * \ref EventNotifier::activated signal.
+ *
+ * If the notifier isn't registered, this function performs no operation.
+ */
+
+/**
+ * \fn EventDispatcher::registerTimer()
+ * \brief Register a timer
+ * \param timer The timer to register
+ *
+ * Once the \a timer is registered with the dispatcher, the dispatcher will emit
+ * the timer \ref Timer::timeout signal when the timer times out. The timer can
+ * be unregistered with unregisterTimer() before it times out, in which case the
+ * signal will not be emitted.
+ *
+ * When the \a timer times out, it is automatically unregistered by the
+ * dispatcher and can be registered back as early as from the \ref Timer::timeout
+ * signal handlers.
+ *
+ * Registering the same timer multiple times is not allowed and results in
+ * undefined behaviour.
+ */
+
+/**
+ * \fn EventDispatcher::unregisterTimer()
+ * \brief Unregister a timer
+ * \param timer The timer to unregister
+ *
+ * After this function returns the \a timer is guaranteed not to emit the
+ * \ref Timer::timeout signal.
+ *
+ * If the timer isn't registered, this function performs no operation.
+ */
+
+/**
+ * \fn EventDispatcher::processEvents()
+ * \brief Wait for and process pending events
+ *
+ * This function processes all pending events associated with registered event
+ * notifiers and timers and signals the corresponding EventNotifier and Timer
+ * objects. If no events are pending, it waits for the first event and processes
+ * it before returning.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/event_notifier.cpp b/src/libcamera/event_notifier.cpp
new file mode 100644
index 000000000000..8dc45a546a11
--- /dev/null
+++ b/src/libcamera/event_notifier.cpp
@@ -0,0 +1,121 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_notifier.cpp - File descriptor event notifier
+ */
+
+#include <libcamera/camera_manager.h>
+#include <libcamera/event_dispatcher.h>
+#include <libcamera/event_notifier.h>
+
+/**
+ * \file event_notifier.h
+ * \brief Event notifier
+ */
+
+namespace libcamera {
+
+/**
+ * \class EventNotifier
+ * \brief Notify of activity on a file descriptor
+ *
+ * The EventNotifier models a file descriptor event source that can be
+ * monitored. It is created with the file descriptor to be monitored and the
+ * type of event, and is enabled by default. It will emit the \ref activated
+ * signal whenever an event of the monitored type occurs on the file descriptor.
+ *
+ * Supported type of events are EventNotifier::Read, EventNotifier::Write and
+ * EventNotifier::Exception. The type is specified when constructing the
+ * notifier, and can be retrieved using the type() function. To listen to
+ * multiple event types on the same file descriptor multiple notifiers must be
+ * created.
+ *
+ * The notifier can be disabled with the setEnable() function. When the notifier
+ * is disabled it ignores events and does not emit the \ref activated signal.
+ * The notifier can then be re-enabled with the setEnable() function.
+ *
+ * Creating multiple notifiers of the same type for the same file descriptor is
+ * not allowed and results in undefined behaviour.
+ *
+ * Notifier events are detected and dispatched from the
+ * EventDispatcher::processEvents() function.
+ */
+
+/**
+ * \enum EventNotifier::Type
+ * Type of file descriptor event to listen for.
+ * \var EventNotifier::Read
+ * Data is available to be read from the file descriptor
+ * \var EventNotifier::Write
+ * Data can be written to the file descriptor
+ * \var EventNotifier::Exception
+ * An exception has occurred on the file descriptor
+ */
+
+/**
+ * \brief Construct an event notifier with a file descriptor and event type
+ * \param fd The file descriptor to monitor
+ * \param type The event type to monitor
+ */
+EventNotifier::EventNotifier(int fd, Type type)
+	: fd_(fd), type_(type), enabled_(false)
+{
+	setEnabled(true);
+}
+
+EventNotifier::~EventNotifier()
+{
+	setEnabled(false);
+}
+
+/**
+ * \fn EventNotifier::type()
+ * \brief Retrive the type of the event being monitored
+ * \return The type of the event
+ */
+
+/**
+ * \fn EventNotifier::fd()
+ * \brief Retrive the file descriptor being monitored
+ * \return The file descriptor
+ */
+
+/**
+ * \fn EventNotifier::enabled()
+ * \brief Retrieve the notifier state
+ * \return true if the notifier is enabled, or false otherwise
+ * \sa setEnable()
+ */
+
+/**
+ * \brief Enable or disable the notifier
+ * \param enable true to enable the notifier, false to disable it
+ *
+ * This function enables or disables the notifier. A disabled notifier ignores
+ * events and does not emit the \ref activated signal.
+ */
+void EventNotifier::setEnabled(bool enable)
+{
+	if (enabled_ == enable)
+		return;
+
+	enabled_ = enable;
+
+	EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher();
+	if (enable)
+		dispatcher->registerEventNotifier(this);
+	else
+		dispatcher->unregisterEventNotifier(this);
+}
+
+/**
+ * \var EventNotifier::activated
+ * \brief Signal emitted when the event occurs
+ *
+ * This signal is emitted when the event \ref type() occurs on the file
+ * descriptor monitored by the notifier. The notifier pointer is passed as a
+ * parameter.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 3ec86e75b57e..61fb93205b34 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -2,11 +2,14 @@  libcamera_sources = files([
     'camera.cpp',
     'camera_manager.cpp',
     'device_enumerator.cpp',
+    'event_dispatcher.cpp',
+    'event_notifier.cpp',
     'log.cpp',
     'media_device.cpp',
     'media_object.cpp',
     'pipeline_handler.cpp',
     'signal.cpp',
+    'timer.cpp',
 ])
 
 libcamera_headers = files([
diff --git a/src/libcamera/timer.cpp b/src/libcamera/timer.cpp
new file mode 100644
index 000000000000..57c49aa2ef36
--- /dev/null
+++ b/src/libcamera/timer.cpp
@@ -0,0 +1,105 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * timer.cpp - Generic timer
+ */
+
+#include <time.h>
+
+#include <libcamera/camera_manager.h>
+#include <libcamera/event_dispatcher.h>
+#include <libcamera/timer.h>
+
+#include "log.h"
+
+/**
+ * \file timer.h
+ * \brief Generic timer
+ */
+
+namespace libcamera {
+
+/**
+ * \class Timer
+ * \brief Single-shot timer interface
+ *
+ * The Timer class models a single-shot timer that is started with start() and
+ * emits the \ref timeout signal when it times out.
+ *
+ * Once started the timer will run until it times out. It can be stopped with
+ * stop(), and once it times out or is stopped, can be started again with
+ * start().
+ */
+
+/**
+ * \brief Construct a timer
+ */
+Timer::Timer()
+{
+}
+
+/**
+ * \brief Start or restart the timer with a timeout of \a msec
+ * \param msec The timer duration in milliseconds
+ *
+ * If the timer is already running it will be stopped and restarted.
+ */
+void Timer::start(unsigned int msec)
+{
+	struct timespec tp;
+	clock_gettime(CLOCK_MONOTONIC, &tp);
+
+	interval_ = msec;
+	deadline_ = tp.tv_sec * 1000000000ULL + tp.tv_nsec + msec * 1000000;
+
+	LOG(Debug) << "Starting timer " << this << " with interval " << msec
+		   << ": deadline " << deadline_;
+
+	CameraManager::instance()->eventDispatcher()->registerTimer(this);
+}
+
+/**
+ * \brief Stop the timer
+ *
+ * After this function returns the timer is guaranteed not to emit the
+ * \ref timeout signal.
+ *
+ * If the timer is not running this function performs no operation.
+ */
+void Timer::stop()
+{
+	CameraManager::instance()->eventDispatcher()->unregisterTimer(this);
+
+	deadline_ = 0;
+}
+
+/**
+ * \brief
+ * \return true if the timer is running, false otherwise
+ */
+bool Timer::isRunning() const
+{
+	return deadline_ != 0;
+}
+
+/**
+ * \fn Timer::interval()
+ * \brief Retrieve the timer interval
+ * \return The timer interval in milliseconds
+ */
+
+/**
+ * \fn Timer::deadline()
+ * \brief Retrieve the timer deadline
+ * \return The timer deadline in nanoseconds
+ */
+
+/**
+ * \var Timer::timeout
+ * \brief Signal emitted when the timer times out
+ *
+ * The timer pointer is passed as a parameter.
+ */
+
+} /* namespace libcamera */