Next Previous Up Top


5 Examples

5.2 Asynchronous Calls and Transparent Futures

5.2.1 - Threads
5.2.2 - Futures

5.2 Asynchronous Calls and Transparent Futures


This second and more sophisticated example of europa standard demonstrates that it is actually possible to build a library class that achieves asynchronous calls with a transparent futures semantics on the results of those calls. In order to obtain the asynchronous execution of member functions thanks to threads, the library user will just inherit from the reified class AThreaded:

namespace EC
{
	class AThreaded: public EC_Reflect
	{
	 public:
		Future_P* get_future (EC_Any obj);
	} ;
}

All the calls made on instances of AThreaded derived classes will be executed asynchronously and potentially concurrently by a pool of threads. A special function, get_future, applied to the result of an asynchronous call, returns a future accessor (a proxy) that provides for explicitly testing its status; its returns null when apply on other objects. The Future_P interface is defined as follows:

namespace EC
{
	class Future_P
	{
	 public:
		void Wait(); // Wait until the future is known
		bool Awaited(); // Test if future is known
		EC_Any value(); // Wait, and returns the value
	} ;
}

Using the class C of the echo example, the class AThreaded can be used in the following way:

class AC: public C, public AThreaded
{
public:
	AC(P1 pl): C(pl) {}
	AC(P1 p2): C(p2) {}
} ;

The class AC now features asynchronous calls in a transparent manner:

void main()
{
	P1 pl;
	Res* vl,v2;
	AC* ac = ec_new ((), AC,(pl));
	vl = ac->vfoo(...); // Asynchronous call
	v2 = ac->foo (...); // Asynchronous call
	vl->bar(); // Automatically triggers a wait until the
	v2->bar(); // value is actually returned from a thread
	// The only constraint being that function bar has to be virtual
	C* c = ac; // Polymorphic assignment
	...
	vl = c->vfoo(...); // Asynchronous call
}

If the user needs to check the results availibility, to avoid being blocked on an unknown future, he can use the following scheme:

void main()
{
	AC* ac;
	vl = ac->vfoo(...); // Asynchronous call
	v2 = ac->foo (...); // Asynchronous call
	Future_P* fl=ac->get_future (EC_Any(vl));
	Future_P* f2=ac->get_future (EC_Any(v2));
	while (fl->Awaited() && f2->Awaited() )
         ... do something else ...
}

5.2.1 Threads

We now present the implementation of such a library. First, the class AThreaded is defined as follows:

namespace EC
{
	class AThreaded: public EC_Reflect
	{
	 private:
		AThreaded_P* ec_proxy();
	 public:
		Future_P* get_future (EC_Any obj)
		{
			EC_Proxy* p = ec_proxy(); // Needs to use ec_proxy(), such that the
			// current function is not reified
			EC_Proxy* pr = obj.is_reified();
			if (pr)
				return (dynamic_cast<Future_P*> (pr));
			else
			... error or build a proxy anyway
		}
	} ;
}

The proxy itself being imlemented by:

namespace EC
{
	class AThreaded_P: public EC_Proxy
	{
	 public:
		void ec_reify (EC_Call* c)
		{
			EC_Class rt = c->mb()->return_type();
			// Get result type of mb fct being called
			Future_P* fp = new Future_P() // Create a future proxy
			EC_Any fut = rt->new_reified_object(fp);
			// Creates a reified object of the return type with a
			// future proxy behaviour
			t->enqueue (c,obj,fut);
			// Add the call, the object, and the future to the list
			// of pending calls to be executed by the threads
			c->res = fut; // Returns the future
		}

		// Constructor
		AThreaded_P(EC_Class cl, EC_Call* c)
		{
			obj = cl->new_obj(c);
			// Create the object to be called asynchronously
			t = ... get threads one way or another ...
		}

	 protected:
		EC_Any obj; // Reified object: called asynchronously
		My_threads t; // Pool of threads
	} ;
}

The ec_reify function constitutes the new and the most interesting part of this definition. Each time member function is called, a reified object of the result type is created with a future behaviour. Then, the future together with the reified call are enqueued in the list of pending calls; the result is set as the future.

Later on, the call will be executed by one thread with the following code:

... get a pending call c, the object obj, and its future fut ...
fut->set_value(c->execute(obj));

At this point, an actual implementation would have some extra mechanism to resume the waiting objects -- it could be implemented within the set_value member.

5.2.2 Futures

The classes that program the future behaviour are the following. First, we have to program the reified class that allows to get the future behaviour from standard classes. This class was used in the AThreaded_P proxy class in order to build a future for any user return type (new_reified_object(ec_cid(Future))).

namespace EC
{
	class Future: public EC_Reflect
	{
	virtual void Wait() {} // Reified
	virtual bool Awaited()
	{ // Not Reified
		return (ec_proxy()->Awaited());
	}
	protected:
		Future_P* ec_proxy(); // Future proxy
	} ;
}

In that case, we can notice that the reified class Future has two member functions that are specific to such a class: Wait and Awaited. Defining the functions in the reified class allows to add them to classes that will be defined inheriting from Future. Note that Awaited accesses the proxy, and as such is not reified, while Wait does and as a consequence is reified (this is the simpliest way to define it, but for efficiency purpose it could be defined without reification by ec_proxy()-> Wait()).

The proxy future class that implements the future behaviour is as follows:

namespace EC
{
	class Future_P: public EC_Proxy
	{
	 public:
		void ec_reify (EC_Call* c)
		{
			Wait(); // Wait until the future is known
			c->res=c->execute(obj); // Do the call
		}

		void Wait() { if (obj==O) wait_obj(); }
		bool Awaited () { return(obj!=O); }
		EC_Any value (){ Wait(); return (obj); }
		void set_value (EC_Any r) { obj=r; }
		// Constructor
		Future_P ()
		{
			obj=O; // Set the object to unknown
		}
	 protected:
		EC_Any obj; // Reified object to be accessed through the future
	} ;
}

Copyright 1997 EUROPA WG

Last updated: 26 Nov 1997