Boost C++ Libraries Home Libraries People FAQ More

Futures

Anthony Williams

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


Table of Contents

Overview
Creating asynchronous values
Wait Callbacks and Lazy Futures
Reference
state enum
unique_future class template
Default Constructor
Destructor
Move Constructor
Move Assignment Operator
Member function swap()
Member function get()
Member function wait()
Member function timed_wait()
Member function timed_wait()
Member function is_ready()
Member function has_value()
Member function has_exception()
Member function get_state()
shared_future class template
Default Constructor
Member function get()
Member function wait()
Member function timed_wait()
Member function timed_wait()
Member function is_ready()
Member function has_value()
Member function has_exception()
Member function get_state()
promise class template
Default Constructor
Move Constructor
Move Assignment Operator
Destructor
Member Function get_future()
Member Function set_value()
Member Function set_exception()
Member Function set_wait_callback()
packaged_task class template
Task Constructor
Move Constructor
Move Assignment Operator
Destructor
Member Function get_future()
Member Function operator()()
Member Function set_wait_callback()
Non-member function wait_for_any()
Non-member function wait_for_all()

The futures library provides a means of handling synchronous future values, whether those values are generated by another thread, or on a single thread in response to external stimuli, or on-demand.

This is done through the provision of four class templates: jss::unique_future and jss::shared_future which are used to retrieve the asynchronous results, and jss::promise and jss::packaged_task which are used to generate the asynchronous results.

An instance of jss::unique_future holds the one and only reference to a result. Ownership can be transferred between instances using the move constructor or move-assignment operator, but at most one instance holds a reference to a given asynchronous result. When the result is ready, it is returned from jss::unique_future<R>::get() by rvalue-reference to allow the result to be moved or copied as appropriate for the type.

On the other hand, many instances of jss::shared_future may reference the same result. Instances can be freely copied and assigned, and jss::shared_future<R>::get() returns a const reference so that multiple calls to jss::shared_future<R>::get() are safe. You can move an instance of jss::unique_future into an instance of jss::shared_future, thus transferring ownership of the associated asynchronous result, but not vice-versa.

You can wait for futures either individually or with one of the jss::wait_for_any() and jss::wait_for_all() functions.

You can set the value in a future with either a jss::promise or a jss::packaged_task. A jss::packaged_task is a callable object that wraps a function or callable object. When the packaged task is invoked, it invokes the contained function in turn, and populates a future with the return value. This is an answer to the perennial question: "how do I return a value from a thread?": package the function you wish to run as a jss::packaged_task and pass the packaged task to the thread constructor. The future retrieved from the packaged task can then be used to obtain the return value. If the function throws an exception, that is stored in the future in place of the return value.

int calculate_the_answer_to_life_the_universe_and_everything()
{
    return 42;
}

jss::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything);
jss::unique_future<int> fi=pt.get_future();

boost::thread task(boost::move(pt)); // launch task on a thread

fi.wait(); // wait for it to finish
    
assert(fi.is_ready());
assert(fi.has_value());
assert(!fi.has_exception());
assert(fi.get_state()==jss::future_state::ready);
assert(fi.get()==42);

A jss::promise is a bit more low level: it just provides explicit functions to store a value or an exception in the associated future. A promise can therefore be used where the value may come from more than one possible source, or where a single operation may produce multiple values.

jss::promise<int> pi;
jss::unique_future<int> fi;
fi=pi.get_future();

pi.set_value(42);

assert(fi.is_ready());
assert(fi.has_value());
assert(!fi.has_exception());
assert(fi.get_state()==jss::future_state::ready);
assert(fi.get()==42);

Both jss::promise and jss::packaged_task support wait callbacks that are invoked when a thread blocks in a call to wait() or timed_wait() on a future that is waiting for the result from the jss::promise or jss::packaged_task, in the thread that is doing the waiting. These can be set using the set_wait_callback() member function on the jss::promise or jss::packaged_task in question.

This allows lazy futures where the result is not actually computed until it is needed by some thread. In the example below, the call to f.get() invokes the callback invoke_lazy_task, which runs the task to set the value. If you remove the call to f.get(), the task is not ever run.

int calculate_the_answer_to_life_the_universe_and_everything()
{
    return 42;
}

void invoke_lazy_task(jss::packaged_task<int>& task)
{
    try
    {
        task();
    }
    catch(jss::task_already_started&)
    {}
}

int main()
{
    jss::packaged_task<int> task(calculate_the_answer_to_life_the_universe_and_everything);
    task.set_wait_callback(invoke_lazy_task);
    jss::unique_future<int> f(task.get_future());

    assert(f.get()==42);
}
namespace future_state
{
    enum state {uninitialized, waiting, ready};
}
template <typename R>
class unique_future
{
    unique_future(unique_future & rhs);// = delete;
    unique_future& operator=(unique_future& rhs);// = delete;

public:
    typedef future_state::state state;

    unique_future();
    ~unique_future();

    // move support
    unique_future(unique_future && other);
    unique_future& operator=(unique_future && other);

    void swap(unique_future& other);

    // retrieving the value
    R&& get();        

    // functions to check state
    state get_state() const;
    bool is_ready() const;        
    bool has_exception() const;        
    bool has_value() const;        

    // waiting for the result to be ready
    void wait() const;        
    template<typename Duration>
    bool timed_wait(Duration const& rel_time) const;
    bool timed_wait_until(boost::system_time const& abs_time) const;
};
unique_future();

Effects:

Constructs an uninitialized future.

Postconditions:

this->is_ready returns false. this->get_state() returns jss::future_state::uninitialized.

Throws:

Nothing.

~unique_future();

Effects:

Destroys *this.

Throws:

Nothing.

unique_future(unique_future && other);

Effects:

Constructs a new future, and transfers ownership of the asynchronous result associated with other to *this.

Postconditions:

this->get_state() returns the value of other->get_state() prior to the call. other->get_state() returns jss::future_state::uninitialized. If other was associated with an asynchronous result, that result is now associated with *this. other is not associated with any asynchronous result.

Throws:

Nothing.

Notes:

If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.

unique_future& operator=(unique_future && other);

Effects:

Transfers ownership of the asynchronous result associated with other to *this.

Postconditions:

this->get_state() returns the value of other->get_state() prior to the call. other->get_state() returns jss::future_state::uninitialized. If other was associated with an asynchronous result, that result is now associated with *this. other is not associated with any asynchronous result. If *this was associated with an asynchronous result prior to the call, that result no longer has an associated jss::unique_future instance.

Throws:

Nothing.

Notes:

If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.

void swap(unique_future & other);

Effects:

Swaps ownership of the asynchronous results associated with other and *this.

Postconditions:

this->get_state() returns the value of other->get_state() prior to the call. other->get_state() returns the value of this->get_state() prior to the call. If other was associated with an asynchronous result, that result is now associated with *this, otherwise *this has no associated result. If *this was associated with an asynchronous result, that result is now associated with other, otherwise other has no associated result.

Throws:

Nothing.

R&& get();
R& unique_future<R&>::get();
void unique_future<void>::get();

Effects:

If *this is associated with an asynchronous result, waits until the result is ready as-if by a call to jss::unique_future<R>::wait(), and retrieves the result (whether that is a value or an exception).

Returns:

If the result type R is a reference, returns the stored reference. If R is void, there is no return value. Otherwise, returns an rvalue-reference to the value stored in the asynchronous result.

Postconditions:

this->is_ready() returns true. this->get_state() returns jss::future_state::ready.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted. Any exception stored in the asynchronous result in place of a value.

Notes:

get() is an interruption point.

void wait();

Effects:

If *this is associated with an asynchronous result, waits until the result is ready. If the result is not ready on entry, and the result has a wait callback set, that callback is invoked prior to waiting.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the wait callback if such a callback is called.

Postconditions:

this->is_ready() returns true. this->get_state() returns jss::future_state::ready.

Notes:

wait() is an interruption point.

template<typename Duration>
bool timed_wait(Duration const& wait_duration);

Effects:

If *this is associated with an asynchronous result, waits until the result is ready, or the time specified by wait_duration has elapsed. If the result is not ready on entry, and the result has a wait callback set, that callback is invoked prior to waiting.

Returns:

true if *this is associated with an asynchronous result, and that result is ready before the specified time has elapsed, false otherwise.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the wait callback if such a callback is called.

Postconditions:

If this call returned true, then this->is_ready() returns true and this->get_state() returns jss::future_state::ready.

Notes:

timed_wait() is an interruption point. Duration must be a type that meets the Boost.DateTime time duration requirements.

bool timed_wait(boost::system_time const& wait_timeout);

Effects:

If *this is associated with an asynchronous result, waits until the result is ready, or the time point specified by wait_timeout has passed. If the result is not ready on entry, and the result has a wait callback set, that callback is invoked prior to waiting.

Returns:

true if *this is associated with an asynchronous result, and that result is ready before the specified time has passed, false otherwise.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the wait callback if such a callback is called.

Postconditions:

If this call returned true, then this->is_ready() returns true and this->get_state() returns jss::future_state::ready.

Notes:

timed_wait() is an interruption point.

bool is_ready();

Effects:

Checks to see if the asynchronous result associated with *this is set.

Returns:

true if *this is associated with an asynchronous result, and that result is ready for retrieval, false otherwise.

Throws:

Nothing.

bool has_value();

Effects:

Checks to see if the asynchronous result associated with *this is set with a value rather than an exception.

Returns:

true if *this is associated with an asynchronous result, that result is ready for retrieval, and the result is a stored value, false otherwise.

Throws:

Nothing.

bool has_exception();

Effects:

Checks to see if the asynchronous result associated with *this is set with an exception rather than a value.

Returns:

true if *this is associated with an asynchronous result, that result is ready for retrieval, and the result is a stored exception, false otherwise.

Throws:

Nothing.

future_state::state get_state();

Effects:

Determine the state of the asynchronous result associated with *this, if any.

Returns:

jss::future_state::uninitialized if *this is not associated with an asynchronous result. jss::future_state::ready if the asynchronous result associated with *this is ready for retrieval, jss::future_state::waiting otherwise.

Throws:

Nothing.

template <typename R>
class shared_future
{
public:
    typedef future_state::state state;

    shared_future();
    ~shared_future();

    // copy support
    shared_future(shared_future const& other);
    shared_future& operator=(shared_future const& other);

    // move support
    shared_future(shared_future && other);
    shared_future(unique_future<R> && other);
    shared_future& operator=(shared_future && other);
    shared_future& operator=(unique_future<R> && other);

    void swap(shared_future& other);

    // retrieving the value
    R get();
    
    // functions to check state, and wait for ready
    state get_state() const;
    bool is_ready() const;
    bool has_exception() const;
    bool has_value() const;

    // waiting for the result to be ready
    void wait() const;        
    template<typename Duration>
    bool timed_wait(Duration const& rel_time) const;
    bool timed_wait_until(boost::system_time const& abs_time) const;        
};
shared_future();

Effects:

Constructs an uninitialized future.

Postconditions:

this->is_ready returns false. this->get_state() returns jss::future_state::uninitialized.

Throws:

Nothing.

const R& get();

Effects:

If *this is associated with an asynchronous result, waits until the result is ready as-if by a call to jss::shared_future<R>::wait(), and returns a const reference to the result.

Returns:

If the result type R is a reference, returns the stored reference. If R is void, there is no return value. Otherwise, returns a const reference to the value stored in the asynchronous result.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted.

Notes:

get() is an interruption point.

void wait();

Effects:

If *this is associated with an asynchronous result, waits until the result is ready. If the result is not ready on entry, and the result has a wait callback set, that callback is invoked prior to waiting.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the wait callback if such a callback is called.

Postconditions:

this->is_ready() returns true. this->get_state() returns jss::future_state::ready.

Notes:

wait() is an interruption point.

template<typename Duration>
bool timed_wait(Duration const& wait_duration);

Effects:

If *this is associated with an asynchronous result, waits until the result is ready, or the time specified by wait_duration has elapsed. If the result is not ready on entry, and the result has a wait callback set, that callback is invoked prior to waiting.

Returns:

true if *this is associated with an asynchronous result, and that result is ready before the specified time has elapsed, false otherwise.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the wait callback if such a callback is called.

Postconditions:

If this call returned true, then this->is_ready() returns true and this->get_state() returns jss::future_state::ready.

Notes:

timed_wait() is an interruption point. Duration must be a type that meets the Boost.DateTime time duration requirements.

bool timed_wait(boost::system_time const& wait_timeout);

Effects:

If *this is associated with an asynchronous result, waits until the result is ready, or the time point specified by wait_timeout has passed. If the result is not ready on entry, and the result has a wait callback set, that callback is invoked prior to waiting.

Returns:

true if *this is associated with an asynchronous result, and that result is ready before the specified time has passed, false otherwise.

Throws:

jss::future_uninitialized if *this is not associated with an asynchronous result. boost::thread_interrupted if the result associated with *this is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the wait callback if such a callback is called.

Postconditions:

If this call returned true, then this->is_ready() returns true and this->get_state() returns jss::future_state::ready.

Notes:

timed_wait() is an interruption point.

bool is_ready();

Effects:

Checks to see if the asynchronous result associated with *this is set.

Returns:

true if *this is associated with an asynchronous result, and that result is ready for retrieval, false otherwise.

Throws:

Nothing.

bool has_value();

Effects:

Checks to see if the asynchronous result associated with *this is set with a value rather than an exception.

Returns:

true if *this is associated with an asynchronous result, that result is ready for retrieval, and the result is a stored value, false otherwise.

Throws:

Nothing.

bool has_exception();

Effects:

Checks to see if the asynchronous result associated with *this is set with an exception rather than a value.

Returns:

true if *this is associated with an asynchronous result, that result is ready for retrieval, and the result is a stored exception, false otherwise.

Throws:

Nothing.

future_state::state get_state();

Effects:

Determine the state of the asynchronous result associated with *this, if any.

Returns:

jss::future_state::uninitialized if *this is not associated with an asynchronous result. jss::future_state::ready if the asynchronous result associated with *this is ready for retrieval, jss::future_state::waiting otherwise.

Throws:

Nothing.

template <typename R>
class promise
{
    promise(promise & rhs);// = delete;
    promise & operator=(promise & rhs);// = delete;
public:
    // template <class Allocator> explicit promise(Allocator a);

    promise();
    ~promise();

    // Move support
    promise(promise && rhs);
    promise & operator=(promise&& rhs);
    
    void swap(promise& other);
    // Result retrieval
    unique_future<R> get_future();

    // Set the value
    void set_value(R& r);
    void set_value(R&& r);
    void set_exception(boost::exception_ptr e);

    template<typename F>
    void set_wait_callback(F f);        
};
promise();

Effects:

Constructs a new jss::promise with no associated result.

Throws:

Nothing.

promise(promise && other);

Effects:

Constructs a new jss::promise, and transfers ownership of the result associated with other to *this, leaving other with no associated result.

Throws:

Nothing.

Notes:

If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.

promise& operator=(promise && other);

Effects:

Transfers ownership of the result associated with other to *this, leaving other with no associated result. If there was already a result associated with *this, and that result was not ready, sets any futures associated with that result to ready with a jss::broken_promise exception as the result.

Throws:

Nothing.

Notes:

If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.

~promise();

Effects:

Destroys *this. If there was a result associated with *this, and that result is not ready, sets any futures associated with that task to ready with a jss::broken_promise exception as the result.

Throws:

Nothing.

unique_future<R> get_future();

Effects:

If *this was not associated with a result, allocate storage for a new asynchronous result and associate it with *this. Returns a jss::unique_future associated with the result associated with *this.

Throws:

jss::future_already_retrieved if the future associated with the task has already been retrieved. std::bad_alloc if any memory necessary could not be allocated.

void set_value(R&& r);
void set_value(const R& r);
void promise<R&>::set_value(R& r);
void promise<void>::set_value();

Effects:

If *this was not associated with a result, allocate storage for a new asynchronous result and associate it with *this. Store the value r in the asynchronous result associated with *this. Any threads blocked waiting for the asynchronous result are woken.

Postconditions:

All futures waiting on the asynchronous result are ready and jss::unique_future<R>::has_value() or jss::shared_future<R>::has_value() for those futures shall return true.

Throws:

jss::promise_already_satisfied if the result associated with *this is already ready. std::bad_alloc if the memory required for storage of the result cannot be allocated. Any exception thrown by the copy or move-constructor of R.

void set_exception(boost::exception_ptr e);

Effects:

If *this was not associated with a result, allocate storage for a new asynchronous result and associate it with *this. Store the exception e in the asynchronous result associated with *this. Any threads blocked waiting for the asynchronous result are woken.

Postconditions:

All futures waiting on the asynchronous result are ready and jss::unique_future<R>::has_exception() or jss::shared_future<R>::has_exception() for those futures shall return true.

Throws:

jss::promise_already_satisfied if the result associated with *this is already ready. std::bad_alloc if the memory required for storage of the result cannot be allocated.

template<typename F>
void set_wait_callback(F f);

Preconditions:

The expression f(t) where t is a lvalue of type jss::packaged_task shall be well-formed. Invoking a copy of f shall have the same effect as invoking f

Effects:

Store a copy of f with the asynchronous result associated with *this as a wait callback. This will replace any existing wait callback store alongside that result. If a thread subsequently calls one of the wait functions on a jss::unique_future or jss::shared_future associated with this result, and the result is not ready, f(*this) shall be invoked.

Throws:

std::bad_alloc if memory cannot be allocated for the required storage.

template<typename R>
class packaged_task
{
    packaged_task(packaged_task&);// = delete;
    packaged_task& operator=(packaged_task&);// = delete;
    
public:
    // construction and destruction
    template <class F>
    explicit packaged_task(F const& f);

    explicit packaged_task(R(*f)());
    
    template <class F>
    explicit packaged_task(F&& f);

    // template <class F, class Allocator>
    // explicit packaged_task(F const& f, Allocator a);
    // template <class F, class Allocator>
    // explicit packaged_task(F&& f, Allocator a);

    ~packaged_task()
    {}

    // move support
    packaged_task(packaged_task&& other);
    packaged_task& operator=(packaged_task&& other);

    void swap(packaged_task& other);
    // result retrieval
    unique_future<R> get_future();        

    // execution
    void operator()();

    template<typename F>
    void set_wait_callback(F f);        
};
template<typename F>
packaged_task(F const &f);

packaged_task(R(*f)());

template<typename F>
packaged_task(F&&f);

Preconditions:

f() is a valid expression with a return type convertible to R. Invoking a copy of f shall behave the same as invoking f.

Effects:

Constructs a new jss::packaged_task with a copy of f stored as the associated task.

Throws:

Any exceptions thrown by the copy (or move) constructor of f. std::bad_alloc if memory for the internal data structures could not be allocated.

packaged_task(packaged_task && other);

Effects:

Constructs a new jss::packaged_task, and transfers ownership of the task associated with other to *this, leaving other with no associated task.

Throws:

Nothing.

Notes:

If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.

packaged_task& operator=(packaged_task && other);

Effects:

Transfers ownership of the task associated with other to *this, leaving other with no associated task. If there was already a task associated with *this, and that task has not been invoked, sets any futures associated with that task to ready with a jss::broken_promise exception as the result.

Throws:

Nothing.

Notes:

If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.

~packaged_task();

Effects:

Destroys *this. If there was a task associated with *this, and that task has not been invoked, sets any futures associated with that task to ready with a jss::broken_promise exception as the result.

Throws:

Nothing.

unique_future<R> get_future();

Effects:

Returns a jss::unique_future associated with the result of the task associated with *this.

Throws:

jss::task_moved if ownership of the task associated with *this has been moved to another instance of jss::packaged_task. jss::future_already_retrieved if the future associated with the task has already been retrieved.

void operator()();

Effects:

Invoke the task associated with *this and store the result in the corresponding future. If the task returns normally, the return value is stored as the asynchronous result, otherwise the exception thrown is stored. Any threads blocked waiting for the asynchronous result associated with this task are woken.

Postconditions:

All futures waiting on the asynchronous result are ready

Throws:

jss::task_moved if ownership of the task associated with *this has been moved to another instance of jss::packaged_task. jss::task_already_started if the task has already been invoked.

template<typename F>
void set_wait_callback(F f);

Preconditions:

The expression f(t) where t is a lvalue of type jss::packaged_task shall be well-formed. Invoking a copy of f shall have the same effect as invoking f

Effects:

Store a copy of f with the task associated with *this as a wait callback. This will replace any existing wait callback store alongside that task. If a thread subsequently calls one of the wait functions on a jss::unique_future or jss::shared_future associated with this task, and the result of the task is not ready, f(*this) shall be invoked.

Throws:

jss::task_moved if ownership of the task associated with *this has been moved to another instance of jss::packaged_task.

template<typename Iterator>
Iterator wait_for_any(Iterator begin,Iterator end);

template<typename F1,typename F2>
unsigned wait_for_any(F1& f1,F2& f2);

template<typename F1,typename F2,typename F3>
unsigned wait_for_any(F1& f1,F2& f2,F3& f3);

template<typename F1,typename F2,typename F3,typename F4>
unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4);

template<typename F1,typename F2,typename F3,typename F4,typename F5>
unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5);

Preconditions:

The types Fn shall be specializations of jss::unique_future or jss::shared_future, and Iterator shall be a forward iterator with a value_type which is a specialization of jss::unique_future or jss::shared_future.

Effects:

Waits until at least one of the specified futures is ready.

Returns:

The range-based overload returns an Iterator identifying the first future in the range that was detected as ready. The remaining overloads return the zero-based index of the first future that was detected as ready (first parameter => 0, second parameter => 1, etc.).

Throws:

boost::thread_interrupted if the current thread is interrupted. Any exception thrown by the wait callback associated with any of the futures being waited for. std::bad_alloc if memory could not be allocated for the internal wait structures.

Notes:

wait_for_any() is an interruption point.

template<typename Iterator>
void wait_for_all(Iterator begin,Iterator end);

template<typename F1,typename F2>
void wait_for_all(F1& f1,F2& f2);

template<typename F1,typename F2,typename F3>
void wait_for_all(F1& f1,F2& f2,F3& f3);

template<typename F1,typename F2,typename F3,typename F4>
void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4);

template<typename F1,typename F2,typename F3,typename F4,typename F5>
void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5);

Preconditions:

The types Fn shall be specializations of jss::unique_future or jss::shared_future, and Iterator shall be a forward iterator with a value_type which is a specialization of jss::unique_future or jss::shared_future.

Effects:

Waits until all of the specified futures are ready.

Throws:

Any exceptions thrown by a call to wait() on the specified futures.

Notes:

wait_for_all() is an interruption point.

Last revised: May 30, 2008 at 09:30:07 GMT