Boost logo

Boost :

Subject: Re: [boost] Asynchronous library now in Boost Library Incubator
From: Thomas Heller (thom.heller_at_[hidden])
Date: 2016-12-06 16:43:51


Am 06.12.2016 10:14 nachm. schrieb "Christophe Henry" <
christophe.j.henry_at_[hidden]>:

<snip>

Just out of curiosity, I implemented your example just with executors and
> future::then:
> https://gist.github.com/sithhell/42eec1e183df206556086cfbf20918ac
>

Oh we are back to HPX...
Well, if it's out of curiosity... I can't promise to give a correct answer
due to my little knowledge of HPX. Please excuse my possibly incorrect
interpretations.

This is not really HPX specific. You can do the same with Boost.Thread, the
concurrency TS, executors, coroutines. All basically speaking the same
language.

 Granted, it doesn't bind the lifetime of the manager to the object, but
> that
> should be a trivial, for example with an intrusive pointer instead of raw
> this
> in the continuations. And it misses the proxy object which
> could be implemented to hide this lifetime tracking in a similar way that
> boost.async, although, the destructor of the executor waits until all
> threads
> have been processed (implicitly keeping the object alive long enough).
> Other
> than that, it has the same race freedom guarantees, due to only ever
> manipulating the object on a single thread of execution. It only blocks on
> the
> future returned from start(). The location on where to execute the
> callback/
> continuation is fixed. The "thread world" is defined by the executor,
> which is a
> very generic and powerful abstraction and properly aligned with the current
> development in the C++ Standards Committee. The layered example can be
> done in
> a similar fashion, each object containing its own executor.
> What do you think?
>

>From what I understand, I don't think both examples are equivalent (yet):
- the lifetime issues are not visible in this example. There is a single
object and it is living in main's thread, which removes quite some salt. It
has no interaction whatsoever with other threads or objects. My example
shows interaction with the main thread.

This does too.

- as you wrote, there is no proxy. If the manager object lived in another
thread than main, it would be non-trivial to avoid races. Calls to members
would be a race. Destructor would also become an issue.
A key point of my example is that the object is now complete and reusable
in a different thread context. I could extend the example at will with more
threads and continue to have no thread issue. As it is, your manager object
is still thread-unsafe if used in another context.

Of course it can be implemented, it is already done, in servant_proxy.

- how many threads are there? I'm unsure if one or two. In my example,
there are 3 (main, servant thread, threadpool), though I'm sure your
example can be extended to have the same number.
It looks like task and callback are executed in the same thread (executor).
Correct? My example executed the long tasks in the threadpool. I'm quite
sure your example can also be extended to do the same.

What I was trying to say was: the concept you introduce is completely
orthogonal: you can easily implemented it on top of such things. If I could
reimplement everything you did on an afternoon, that would be great ;)

Regarding the threads: yes, i was not really aware of your three threads
active. That shouldn't be important. And yes, it can be extended. What's
important are the semantics, and I'm confused again...
In a single thread world can two member functions be called concurrently on
an object living in that world? It seems it can, the servant and
threadpool? I'm sorry, the more I think about it, the more confusing it
gets.

It would be great if we'd speak the same language as well, why not call
your thread worlds executors and open a whole new world of possibilities ;)

I think it's great. We are just scratching the surface but we are coming to
the interesting stuff.
In the example, the manager object is created on the stack, is alone with
no interaction with other objects or libraries, even less with external
threads.

But if we imagine lots of thread worlds, each with hundreds of servants and
proxies, things start becoming interesting. One simply cannot define all of
the application objects in main and life issues appear quickly.
I'll try to think of a realistic example showing such a thing.

You might want to have a look at the next example in the doc, the layers,
which makes things even more interesting by introducing safe callbacks,
which can be called from any thread (asynchronous thread worlds or whatever
thread the application has). I hope it will then be clear that a single
object on a stack is not a general solution and that an object living and
being accessed in a clearly defined thread with no outside possibility to
use it incorrectly is a better one.

Don't get me wrong, I'm not really trying to say that libraries like hpx,
or boost.thread or folly or ppl solved your problem. I was trying to
demonstrate that futures, async and friends are not your enemies, on the
contrary.

Regarding the layered example, I'm still trying to understand the semantics
of your different APIs. There are a lot of things doing the same from a
different perspective (proxy, servant, scheduler).

Sorry, it looks like generating the doc broke the links.
Here the link to the named examples:

https://htmlpreview.github.io/?https://github.com/henry-ch/a
synchronous/blob/master/libs/asynchronous/doc/asynchronous.html#d0e6434

Regards,

Christophe

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman
/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk