« Return to Thread: [future] Early draft of wait for multiple futures interface

Re: [future] Early draft of wait for multiple futures interface

by Johan Torp :: Rate this Message:

Reply to Author | View in Thread

After thinking more carefully on the problem I've realized it is implementable using condition variables. Here is a revised proposal, which - for now - excludes barriers.

----------------------------   Proposal  ----------------------------  

Add class future_expression that basically is a function which depend on futures values.
- A typed evaluation function (some work) is associated with each future dependency
--- This evaluation can result in that the future_expressions becomes ready
- A future dependency is a parent child relationship. This forms a graph where "promise-futures" are leafs.
--- You can wait() on any future in the graph
- When a future is completed it tells it's parents they need to re-evaluate via a future-complete callback
--- The future_expression that needs re-evaluation will:
--- 1. Schedule the associated evaluation to be run on the client thread on next wait or is_ready is called
--- 2. Notifies it's condition in case the client thread was waiting on this particular node
--- 3. Signals it's parents they too need to re-evaluate  
------ This way, the notifications propagate upwards to the root future
------ Some future_expression might be scheduled for re-evaluation even though
- When a future expression becomes ready it drops it's shared ownership to all depending childs
--- They are no longer needed and can die
--- This is done as a part of the pending evaluations on the client thread
- For now this is not a concurrent object, neither are the futures formed from it
--- All evaluation code can thus be single threaded
--- We can change this so that the work of evaluating a future is done by the first thread waiting on it.
     
Note:
- The callback is ONLY usable by future_exprs. - No user code can be executed by promise fulfilling code
- Other abstractions build on future_expressions rather than the dangerous complete-callback


----------------------------  Part of interface  ----------------------------

template<class T>
struct future_result
{
  T running_result_;
  bool value_is_final_;
  optional<exception> exception_;
  ...
};

///  A representation of a function which depends on futures.
///
template <class T>
class future_expression
{
  /// Default value if no dependencies are added
  future_expression(T starting_value);

  /// Add a future alongside with an evaluation function which will be called lazily by waiting threads
  template<class U>
  void add_dependency<U> (shared_future<U> f,
                                      function<void(U future_value, future_result<T>& result_setter)> on_ready);

  /// Perform pending evaluations. If none sets an exception or result wait on the internal condition
  void wait();

  /// If not ready, perform pending evaluations. If none sets an exception or result, return false
  bool is_ready();
};  

----------------------------  Example code  ----------------------------

void set_if_true(bool b, result& result_setter)
{
  if (b)
  {
    result_setter.running_value_ = true;
    result_setter.value_is_final_ = true;
  }
}

template<class T>
future<bool> operator||(future<bool> lhs, future<bool> rhs)
{
  future_expression<R> exp(false);
  exp.add_dependency(a1, &set_if_true);
  exp.add_dependency(a2, &set_if_true);
  return future<R>(exp);
}

template<class T>
void running_add(T value, result& result_setter)
{
  result_setter.running_value_ += value;
}

template<class T>
future<T> sum(vector<future<T>> futures)
{
  future_expression<T> exp(value_initialized<T>());

  for f in futures
     exp.add_dependency(f, &running_add);

  return future<T>(exp);
}

----------------------------  Conclusions  ----------------------------
 
This mechanism would solve all the issues we've found. Most importantly it allows making composite futures while prohibiting execution of user code while fulfilling promises.

Unfortunately, I feel it might be a little cumbersome to use. Also, since we need wait_for_many support it needs to get accepted alongside with the first version of the future library. I was hoping this version could be very lightweight.
 
What do you think? Any quick thoughts?

Johan

 « Return to Thread: [future] Early draft of wait for multiple futures interface