Re: [boost] Asynchronous library now in Boost Library Incubator
boost.asynchronous looks to have many of the same features/functionality as HPX. Can I ask why you chose to reimplement futures/lightweight thread pools/parallel STL/etc, rather than working on improving HPX to suit your needs?
I don't think the libraries have the same goals (or at least it seems to me so). Asynchronous is first of all an architecture tool focusing on organizing a whole application into thread worlds. The libraries also do not have the same design / tradeoffs. Asynchronous can make use of futures but encourages callbacks and tries to make these as safe as possible. The goal is to be as asynchronous (meaning non-blocking) as possible. It is no coincidence that I'm also the author of Boost.MSM. The encouraged design is a whole lot of state machines, each in its own thread (possibly sharing a thread), sending tasks to threadpools, TCP connections, etc. and waiting for callbacks. Future-based designs do not mix well with state machines' run-to-completion. The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both.
JB Disclaimer : I have contributed o HPX, but I'm not one of the "German HPC Guys" :) .
No hard feeling. I'm grateful for participation and the chance to explain the underlying ideas of the library. Thanks, Christophe
On Dienstag, 29. November 2016 20:41:58 CET Christophe Henry wrote:
boost.asynchronous looks to have many of the same features/functionality
as HPX.
Can I ask why you chose to reimplement futures/lightweight thread
pools/parallel STL/etc,
rather than working on improving HPX to suit your needs?
I don't think the libraries have the same goals (or at least it seems to me so). Asynchronous is first of all an architecture tool focusing on organizing a whole application into thread worlds.
Can you please elaborate a little on what is meant with "thread worlds"?
The libraries also do not have the same design / tradeoffs. Asynchronous can make use of futures but encourages callbacks and tries to make these as safe as possible. The goal is to be as asynchronous (meaning non-blocking) as possible. It is no coincidence that I'm also the author of Boost.MSM. The encouraged design is a whole lot of state machines, each in its own thread (possibly sharing a thread), sending tasks to threadpools, TCP connections, etc. and waiting for callbacks. Future-based designs do not mix well with state machines' run-to-completion.
The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both.
Does this mean that Boost.Asynchronous provides realtime worst case execution time guarantees? Can you point me to the pages in the docs where those techniques are described in detail?
JB Disclaimer : I have contributed o HPX, but I'm not one of the "German HPC
Guys" :) .
No hard feeling. I'm grateful for participation and the chance to explain the underlying ideas of the library.
Thanks, Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 11/29/2016 09:40 PM, Thomas Heller wrote:
On Dienstag, 29. November 2016 20:41:58 CET Christophe Henry wrote:
boost.asynchronous looks to have many of the same features/functionality
as HPX.
Can I ask why you chose to reimplement futures/lightweight thread
pools/parallel STL/etc,
rather than working on improving HPX to suit your needs?
I don't think the libraries have the same goals (or at least it seems to me so). Asynchronous is first of all an architecture tool focusing on organizing a whole application into thread worlds.
Can you please elaborate a little on what is meant with "thread worlds"?
You might want to read the concepts parts. I'll elaborate a little more. I will also add more to the doc. A "thread world" is a world defined by a (single threaded) scheduler and all the objects which have been created, are living and destroyed within this context. It is usually agreed on that objects and threads do not mix well. Class diagrams fail to display both as these are orthogonal concepts. Asynchronous solves this by organizing objects into worlds, each living within a thread. This way, life cycles issues and the question of thread access to objects is solved. It is similar to the Active Object pattern, but with n Objects living within a thread.
The libraries also do not have the same design / tradeoffs. Asynchronous can make use of futures but encourages callbacks and tries to make these as safe as possible. The goal is to be as asynchronous (meaning non-blocking) as possible. It is no coincidence that I'm also the author of Boost.MSM. The encouraged design is a whole lot of state machines, each in its own thread (possibly sharing a thread), sending tasks to threadpools, TCP connections, etc. and waiting for callbacks. Future-based designs do not mix well with state machines' run-to-completion.
The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both.
Does this mean that Boost.Asynchronous provides realtime worst case execution time guarantees?
I did not state this. This is not a real-time library. However what the library offers might or might not be sufficient for your needs. Automotive usually has more stringent needs as other industries. A way to handle hard real-time (meaning an answer has to be given latest at a precise time), is to write manager objects like state machines (no matter real state machines or own implementation) living within a thread world and reacting to events. A timer event would be one of them. When the event is emitted, the manager can react. The library ensures communication between worlds using queues with different priority. Giving the highest priority to the timer event will ensure it will be handled next. In theory a run to completion executes in 0 time unit. But as this is never the case, there is a delay due to user code and thread context switching, so it is not a perfect hard real-time. To help with soft real-time (throughput), the library provides threadpools and parallelization mechanisms. Christophe
On Mittwoch, 30. November 2016 20:31:58 CET Christophe Henry wrote:
On 11/29/2016 09:40 PM, Thomas Heller wrote:
On Dienstag, 29. November 2016 20:41:58 CET Christophe Henry wrote:
boost.asynchronous looks to have many of the same features/functionality
as HPX.
Can I ask why you chose to reimplement futures/lightweight thread
pools/parallel STL/etc,
rather than working on improving HPX to suit your needs?
I don't think the libraries have the same goals (or at least it seems to me so). Asynchronous is first of all an architecture tool focusing on organizing a whole application into thread worlds.
Can you please elaborate a little on what is meant with "thread worlds"?
You might want to read the concepts parts. I'll elaborate a little more. I will also add more to the doc. A "thread world" is a world defined by a (single threaded) scheduler and all the objects which have been created, are living and destroyed within this context. It is usually agreed on that objects and threads do not mix well. Class diagrams fail to display both as these are orthogonal concepts. Asynchronous solves this by organizing objects into worlds, each living within a thread. This way, life cycles issues and the question of thread access to objects is solved. It is similar to the Active Object pattern, but with n Objects living within a thread.
Thanks, this makes it a bit clearer. So essentially, a thread world, defines the scope of your object? Which class is modeling the concept of the thread world now, the scheduler? I think this information is missing from the documentation. Is there also a default thread world?
The libraries also do not have the same design / tradeoffs. Asynchronous can make use of futures but encourages callbacks and tries to make these as safe as possible. The goal is to be as asynchronous (meaning non-blocking) as possible. It is no coincidence that I'm also the author of Boost.MSM. The encouraged design is a whole lot of state machines, each in its own thread (possibly sharing a thread), sending tasks to threadpools, TCP connections, etc. and waiting for callbacks. Future-based designs do not mix well with state machines' run-to-completion.
The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both.
Does this mean that Boost.Asynchronous provides realtime worst case execution time guarantees?
I did not state this. This is not a real-time library.
No, you never stated that explicitly. It was my interpretation of the sentences above. Reading through the documentation etc. one might indeed get the feeling that Boost.Asynchronous was designed for real-time applications. Which is probably very hard to justify given the different memory allocations, exceptions etc.
However what the library offers might or might not be sufficient for your needs. Automotive usually has more stringent needs as other industries. A way to handle hard real-time (meaning an answer has to be given latest at a precise time), is to write manager objects like state machines (no matter real state machines or own implementation) living within a thread world and reacting to events. A timer event would be one of them. When the event is emitted, the manager can react. The library ensures communication between worlds using queues with different priority. Giving the highest priority to the timer event will ensure it will be handled next. In theory a run to completion executes in 0 time unit. But as this is never the case, there is a delay due to user code and thread context switching, so it is not a perfect hard real-time. To help with soft real-time (throughput), the library provides threadpools and parallelization mechanisms.
So we are speaking about soft realtime here, with a focus on high throughput, correct? That makes more sense and is more aligned with the research I am aware of about multi-core real time systems. For a moment, I was hoping you found a way to solve that problem, which would have been awesome.
Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
<snip>
Thanks, this makes it a bit clearer. So essentially, a thread world, defines the scope of your object? Which class is modeling the concept of the thread world now, the scheduler? I think this information is missing from the documentation. Is there also a default thread world?
Definition of a thread world (which I just learned is called Appartment in the Microsoft world, thanks to Bjorn Reese for pointing this out): https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blob... The scheduler is modeling the thread world in which the objects live. There is no default thread world, one can create as many as needed. <snip>
Does this mean that Boost.Asynchronous provides realtime worst case execution time guarantees?
I did not state this. This is not a real-time library.
No, you never stated that explicitly. It was my interpretation of the sentences above. Reading through the documentation etc. one might indeed get the feeling that Boost.Asynchronous was designed for real-time applications. Which is probably very hard to justify given the different memory allocations, exceptions etc.
It was designed for industrial applications. The real-time constraints are less strict, so that the delay caused by memory allocations, thread switching etc. is negligible.
However what the library offers might or might not be sufficient for your needs. Automotive usually has more stringent needs as other industries. A way to handle hard real-time (meaning an answer has to be given latest at a precise time), is to write manager objects like state machines (no matter real state machines or own implementation) living within a thread world and reacting to events. A timer event would be one of them. When the event is emitted, the manager can react. The library ensures communication between worlds using queues with different priority. Giving the highest priority to the timer event will ensure it will be handled next. In theory a run to completion executes in 0 time unit. But as this is never the case, there is a delay due to user code and thread context switching, so it is not a perfect hard real-time. To help with soft real-time (throughput), the library provides threadpools and parallelization mechanisms.
So we are speaking about soft realtime here, with a focus on high throughput, correct? That makes more sense and is more aligned with the research I am aware of about multi-core real time systems. For a moment, I was hoping you found a way to solve that problem, which would have been awesome.
Sorry, no ;-) Its focus is on soft real-time, though as written above, the hard real-time constraints necessary for a production line are easily achievable. All one needs is defining high priority for urgent events. The reaction time is then the longest task a scheduler has to execute with a lesser priority. Taking as an example an object reacting to events and executing no long task, living withing a scheduler with 2 queues, one low prio and one high prio, if a timer event is sent to the high prio queue, the max. reaction time is the task currently executing on this scheduler + one context switch. Not perfect real-time but good enough for many needs. The model the very fast employee of the restaurant in my introduction (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blob...). Cheers, Christophe
Does this mean that Boost.Asynchronous provides realtime worst case execution time guarantees?
I did not state this. This is not a real-time library.
No, you never stated that explicitly. It was my interpretation of the sentences above. Reading through the documentation etc. one might indeed get the feeling that Boost.Asynchronous was designed for real-time applications. Which is probably very hard to justify given the different memory allocations, exceptions etc.
It was designed for industrial applications. The real-time constraints are less strict, so that the delay caused by memory allocations, thread switching etc. is negligible.
I still fail to see the connection between industrial applications and the absolute need to use Boost.Asynchronous. Can you elaborate please? The docs are very complicated and I have not managed to put together the full picture in my head.
However what the library offers might or might not be sufficient for your needs. Automotive usually has more stringent needs as other industries. A way to handle hard real-time (meaning an answer has to be given latest at a precise time), is to write manager objects like state machines (no matter real state machines or own implementation) living within a thread world and reacting to events. A timer event would be one of them. When the event is emitted, the manager can react. The library ensures communication between worlds using queues with different priority. Giving the highest priority to the timer event will ensure it will be handled next. In theory a run to completion executes in 0 time unit. But as this is never the case, there is a delay due to user code and thread context switching, so it is not a perfect hard real-time. To help with soft real-time (throughput), the library provides threadpools and parallelization mechanisms.
So we are speaking about soft realtime here, with a focus on high throughput, correct? That makes more sense and is more aligned with the research I am aware of about multi-core real time systems. For a moment, I was hoping you found a way to solve that problem, which would have been awesome.
Sorry, no ;-) Its focus is on soft real-time, though as written above, the hard real-time constraints necessary for a production line are easily achievable. All one needs is defining high priority for urgent events. The reaction time is then the longest task a scheduler has to execute with a lesser priority. Taking as an example an object reacting to events and executing no long task, living withing a scheduler with 2 queues, one low prio and one high prio, if a timer event is sent to the high prio queue, the max. reaction time is the task currently executing on this scheduler + one context switch. Not perfect real-time but good enough for many needs.
IIUC, this means that Boost.Asynchronous provides no real-time guarantees at all but relies on the programmer to make sure that no long-running tasks are active. Is that correct? Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
On 12/04/2016 04:02 PM, Hartmut Kaiser wrote:
Does this mean that Boost.Asynchronous provides realtime worst case execution time guarantees?
I did not state this. This is not a real-time library.
No, you never stated that explicitly. It was my interpretation of the sentences above. Reading through the documentation etc. one might indeed get the feeling that Boost.Asynchronous was designed for real-time applications. Which is probably very hard to justify given the different memory allocations, exceptions etc.
It was designed for industrial applications. The real-time constraints are less strict, so that the delay caused by memory allocations, thread switching etc. is negligible.
I still fail to see the connection between industrial applications and the absolute need to use Boost.Asynchronous. Can you elaborate please? The docs are very complicated and I have not managed to put together the full picture in my head.
Please do not make me say what I did not. Where did I talk about absolute need? As I answered in another thread, let me quote: "It can be made of tasks, state machines, communications, controlling of motors, using external libraries made of threads you cannot control and much more". This kind of application is made of a great number of different aspects. One does not even get to control all of them as a fair number of external libraries is used which themselves use their own thread to call a callback. Parallelization is an aspect, which Asynchronous manages quite well (see Boost.Sort mini review), but there are more. Writing thread-safe code with all these aspects is a challenge. Worst, such applications imply a huge cost when they fail. Stopping a production line is a disaster. No client will be happy to know that the software produced a core file but it's great because developers can fix their race. So, how do we solve this? It turns out that good testing (unit, regression, and good old testers) will find most "normal" bugs but no good testing finds all races. Actively avoiding races by compartmentalizing code into thread worlds with safe callbacks go a long way. Communicating between worlds or with external libraries can be made with make_safe_callback as in this example of a classical layered application (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blob...).
However what the library offers might or might not be sufficient for your needs. Automotive usually has more stringent needs as other industries. A way to handle hard real-time (meaning an answer has to be given latest at a precise time), is to write manager objects like state machines (no matter real state machines or own implementation) living within a thread world and reacting to events. A timer event would be one of them. When the event is emitted, the manager can react. The library ensures communication between worlds using queues with different priority. Giving the highest priority to the timer event will ensure it will be handled next. In theory a run to completion executes in 0 time unit. But as this is never the case, there is a delay due to user code and thread context switching, so it is not a perfect hard real-time. To help with soft real-time (throughput), the library provides threadpools and parallelization mechanisms.
So we are speaking about soft realtime here, with a focus on high throughput, correct? That makes more sense and is more aligned with the research I am aware of about multi-core real time systems. For a moment, I was hoping you found a way to solve that problem, which would have been awesome.
Sorry, no ;-) Its focus is on soft real-time, though as written above, the hard real-time constraints necessary for a production line are easily achievable. All one needs is defining high priority for urgent events. The reaction time is then the longest task a scheduler has to execute with a lesser priority. Taking as an example an object reacting to events and executing no long task, living withing a scheduler with 2 queues, one low prio and one high prio, if a timer event is sent to the high prio queue, the max. reaction time is the task currently executing on this scheduler + one context switch. Not perfect real-time but good enough for many needs.
IIUC, this means that Boost.Asynchronous provides no real-time guarantees at all but relies on the programmer to make sure that no long-running tasks are active. Is that correct?
Again, yes. The aim of the library is that users write short tasks in the single thread worlds and use threadpools for long tasks. The library also provides very good diagnostics (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blob...) to detect long tasks. Developers are surprisingly bad at guessing where their code is slow, so it is very helpful. Regards, Christophe
Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Christophe
I don't think the libraries have the same goals (or at least it seems to me so). Asynchronous is first of all an architecture tool focusing on organizing a whole application into thread worlds. < If I understand this correctly, then I'd say that that is what HPX does too. (I usually explain to others that OpenMP creates 'parallel regions', where code operates in tasks, whereas the whole of an HPX application is one enormous parallel region). I presume this is what you mean by 'thread worlds'
The libraries also do not have the same design / tradeoffs. Asynchronous can make use of futures but encourages callbacks and tries to make these as safe as possible. The goal is to be as asynchronous (meaning non-blocking) as possible. < A future .then() or continuation is really just a type of callback too (broadly speaking).
Future-based designs do not mix well with state machines' run-to-completion. < Could you elaborate a little on that comment please? I have a had a couple of instances where I'm manipulating 'state' and transitioning from one to another - I would welcome some ideas on how to make this a bit cleaner.
The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both. < I would say that non-blocking behaviour is one of the central goals of HPX. Continuations are only triggered when futures become ready and therefore blocking is avoided - so I don't think there's any disagreement there! Races are a problem, but a functional style of programming goes a long way to avoiding them. Thanks for the reply - I'll be going through your repo looking for good ideas (and borrowing them). Cheers JB
On 11/30/2016 08:10 AM, Biddiscombe, John A. wrote:
Christophe
I don't think the libraries have the same goals (or at least it seems to me so). Asynchronous is first of all an architecture tool focusing on organizing a whole application into thread worlds. <
If I understand this correctly, then I'd say that that is what HPX does too. (I usually explain to others that OpenMP creates 'parallel regions', where code operates in tasks, whereas the whole of an HPX application is one enormous parallel region). I presume this is what you mean by 'thread worlds' Not sure if I understand. Can you point me to some documentation or examples?
Disclaimer: my knowledge of HPX is limited to a quick glance at the doc. Feel free to correct my mistakes. I understood HPX as a library returning futures to calls to asynchronous functions, somewhat similar to std::async. In that case, the main region with all the futures would be a thread world for asynchronous. In huge applications, this does not solve the problem of organizing thousands of objects within threads.
The libraries also do not have the same design / tradeoffs. Asynchronous can make use of futures but encourages callbacks and tries to make these as safe as possible. The goal is to be as asynchronous (meaning non-blocking) as possible. <
A future .then() or continuation is really just a type of callback too (broadly speaking).
Not really, if I understand correctly (http://en.cppreference.com/w/cpp/experimental/future/then). The functor object passed to then() is executed in an unspecified thread of execution, which makes it hard for the caller to (safely) pass data which might or might not be valid or relevant at the time the then functor executes. I however could not find out whether this also applied to HPX. This simple example, called within a state machine living within a thread world will help me show the difference: post_callback( // task running in threadpool [](){/* long lasting task */}, // callback [this](boost::asynchronous::expected<void>) { // if this is called, it is guaranteed that: // this executes within the thread context of the calling object // the calling object is still alive //----> we now decide what to do next } ); As the callback executes within the calling object's thread context, we can choose at the callback execution time if we still want to call the code which we would call in the then(), and if we call, we do it with data relevant at this time, not at the time of the first call.
Future-based designs do not mix well with state machines' run-to-completion. <
Could you elaborate a little on that comment please? I have a had a couple of instances where I'm manipulating 'state' and transitioning from one to another - I would welcome some ideas on how to make this a bit cleaner.
A future.then() returns another future so isn't it just delaying the fact that I have to decide at some point what to do with the future object? Do I poll it, get() it or ignore it? Polling is tedious, ignore usually wrong, and get() means blocking. As a state machine run-to-completion is supposed to execute in no time, this is not a good option. Run-to-completion means: event is sent, transition is executed. Until it completes, the state machine is in an unstable state. This has to be kept as short as possible.
The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both. <
I would say that non-blocking behaviour is one of the central goals of HPX. Continuations are only triggered when futures become ready and therefore blocking is avoided - so I don't think there's any disagreement there! Races are a problem, but a functional style of programming goes a long way to avoiding them.
I agree that HPX likely solves well the race problem, which is a great achievement. I feel both libraries solve quite different problems. Asynchronous is focusing on organizing objects into threads and building everything else on top of that. In that context, callbacks within these object's thread contexts make more sense than a fire-and-forget of a future.then(); Which does not mean a fundamental disagreement with HPX, simply a different focus.
Thanks for the reply - I'll be going through your repo looking for good ideas (and borrowing them).
I'd love it! Thanks a lot for the feedback, I now see what things doc is missing (explanation of thread worlds, reasons for callbacks)
Cheers
JB
Cheers, Christophe
If I understand this correctly, then I'd say that that is what HPX does too. (I usually explain to others that OpenMP creates 'parallel regions', where code operates in tasks, whereas the whole of an HPX application is one enormous parallel region). I presume this is what you mean by 'thread worlds' Not sure if I understand. Can you point me to some documentation or examples? <<
For you and any others reading this, the main hpx docs are here http://stellar-group.github.io/hpx/docs/html/ but there is useful additional material in the form of tutorial slides https://stellar-group.github.io/tutorials/#1 and they link to a number of examples - though the main hpx repo has an examples directory with many more of them.
I understood HPX as a library returning futures to calls to asynchronous functions, somewhat similar to std::async. In that case, the main region with all the futures would be a thread world for asynchronous. In huge applications, this does not solve the problem of organizing thousands of objects within threads. << Yes, I suppose that the whole application would be a single thread world using that analogy. Objects are not 'bound' to a particular thread, they can be accessed freely from any thread and the user is responsible for protecting them - or preferably writing functional-like code to avoid collisions etc.
A future .then() or continuation is really just a type of callback too (broadly speaking).
Not really, if I understand correctly (http://en.cppreference.com/w/cpp/experimental/future/then). The functor object passed to then() is executed in an unspecified thread of execution, which makes it hard for the caller to (safely) pass data which might or might not be valid or relevant at the time the then functor executes. I however could not find out whether this also applied to HPX. << In HPX there are launch policies that can be used to control thread execution, the one that would come closest to what you are looking for would be future<thing> = hpx::async(do_something_interesting()).then(hpx::launch::sync, do_another_interesting_thing()); This tells the .then() continuation to execute the next function in the same context as the thread that completes the initial future. Note that launch::policies control certain aspects of 'how' a thread should be launched, but executors control where (and are a powerful abstraction used extensively throughout HPX)
A future.then() returns another future so isn't it just delaying the fact that I have to decide at some point what to do with the future object? Do I poll it, get() it or ignore it? Polling is tedious, ignore usually wrong, and get() means blocking. As a state machine run-to-completion is supposed to execute in no time, this is not a good option. Run-to-completion means: event is sent, transition is executed. Until it completes, the state machine is in an unstable state. This has to be kept as short as possible. <<
In general yes, there needs to be a .get() at the end of a chain of futures - but fortunately, you can just launch tasks with continuations and 'throw away' the final future - and never call get(). When each task completes, it will trigger it's continuation, and if nobody is 'using' the final value returned from a chain of futures, it will just clean itself up on completion. I didn't quite understand the state machine explanation - but I'll continue looking through the boost.asynchronous material until I am happier. yours JB
On 12/01/2016 03:04 PM, Biddiscombe, John A. wrote:
For you and any others reading this, the main hpx docs are here http://stellar-group.github.io/hpx/docs/html/ but there is useful additional material in the form of tutorial slides https://stellar-group.github.io/tutorials/#1 and they link to a number of examples - though the main hpx repo has an examples directory with many more of them.
Thanks.
I understood HPX as a library returning futures to calls to asynchronous functions, somewhat similar to std::async. In that case, the main region with all the futures would be a thread world for asynchronous. In huge applications, this does not solve the problem of organizing thousands of objects within threads. << Yes, I suppose that the whole application would be a single thread world using that analogy. Objects are not 'bound' to a particular thread, they can be accessed freely from any thread and the user is responsible for protecting them - or preferably writing functional-like code to avoid collisions etc.
A future .then() or continuation is really just a type of callback too (broadly speaking).
Not really, if I understand correctly (http://en.cppreference.com/w/cpp/experimental/future/then). The functor object passed to then() is executed in an unspecified thread of execution, which makes it hard for the caller to (safely) pass data which might or might not be valid or relevant at the time the then functor executes. I however could not find out whether this also applied to HPX. <<
In HPX there are launch policies that can be used to control thread execution, the one that would come closest to what you are looking for would be
future<thing> = hpx::async(do_something_interesting()).then(hpx::launch::sync, do_another_interesting_thing());
This tells the .then() continuation to execute the next function in the same context as the thread that completes the initial future.
Does it mean a threadpool thread or the thread which started the asynchronous task? <snip>
I didn't quite understand the state machine explanation - but I'll continue looking through the boost.asynchronous material until I am happier.
I tried to add an example and the underlying rationale (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blob...). The Manager is a very simplified state machine but I hope you get the idea. Cheers, Christophe
I understood HPX as a library returning futures to calls to asynchronous functions, somewhat similar to std::async. In that case, the main region with all the futures would be a thread world for asynchronous. In huge applications, this does not solve the problem of organizing thousands of objects within threads. <<
Why not? You can easily (and I mean it) create asynchronous applications solely using futures for defining the necessary dependencies between tasks, running millions of tasks.
Yes, I suppose that the whole application would be a single thread world using that analogy. Objects are not 'bound' to a particular thread, they can be accessed freely from any thread and the user is responsible for protecting them - or preferably writing functional-like code to avoid
collisions etc.
HPX uses executors to fine-control where which task is executed. Every function which (potentially) creates a new task can optionally be used with an instance of an executor (async(), future::then(), etc.)
A future .then() or continuation is really just a type of callback too (broadly speaking).
Not really, if I understand correctly (http://en.cppreference.com/w/cpp/experimental/future/then). The functor object passed to then() is executed in an unspecified thread of execution, which makes it hard for the caller to (safely) pass data which might or might not be valid or relevant at the time the then functor executes.
By default in HPX the continuation is executed on the thread which has made the future ready.
I however could not find out whether this also applied to HPX. <<
In HPX there are launch policies that can be used to control thread execution, the one that would come closest to what you are looking for would be future<thing> = hpx::async(do_something_interesting()).then(hpx::launch::sync, do_another_interesting_thing());
This tells the .then() continuation to execute the next function in the same context as the thread that completes the initial future.
Does it mean a threadpool thread or the thread which started the asynchronous task?
The thread which makes the future ready. But executors can be used to control the concrete thread (see above). Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
On 12/04/2016 03:56 PM, Hartmut Kaiser wrote:
I understood HPX as a library returning futures to calls to asynchronous functions, somewhat similar to std::async. In that case, the main region with all the futures would be a thread world for asynchronous. In huge applications, this does not solve the problem of organizing thousands of objects within threads. <<
Why not? You can easily (and I mean it) create asynchronous applications solely using futures for defining the necessary dependencies between tasks, running millions of tasks.
Probably because not every application is solely made of tasks. It can be made of tasks, state machines, communications, controlling of motors, using external libraries made of threads you cannot control and much more. This is what I meant about industry. I don't think one can do it only with futures created from a main which only needs to wait for the end of millions of tasks. If you disagree, it's ok for me. You just do not have the same problems to solve as I do. Great, but I offer the library for review for all those who happen to have the same problems to solve as I do.
Yes, I suppose that the whole application would be a single thread world using that analogy. Objects are not 'bound' to a particular thread, they can be accessed freely from any thread and the user is responsible for protecting them - or preferably writing functional-like code to avoid
collisions etc.
HPX uses executors to fine-control where which task is executed. Every function which (potentially) creates a new task can optionally be used with an instance of an executor (async(), future::then(), etc.)
Are we reviewing HPX? Sorry Hartmut if I gave the impression of comparing Asynchronous to HPX. IIUC it is a library written by a private company and not intended for boost inclusion. I mostly care about std::async and the proposed Standard extensions for futures. I'm not the one who brought HPX to discussion.
A future .then() or continuation is really just a type of callback too (broadly speaking).
Not really, if I understand correctly (http://en.cppreference.com/w/cpp/experimental/future/then). The functor object passed to then() is executed in an unspecified thread of execution, which makes it hard for the caller to (safely) pass data which might or might not be valid or relevant at the time the then functor executes.
By default in HPX the continuation is executed on the thread which has made the future ready.
Which would be like std::async IIUC, in which case I regret that it is not what I would want. If you again read the example (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blob...), The Manager gets a new information coming from another thread. This information makes the Manager change his mind. We have at least 3 threads participating. With a continuation executed by a thread of the threadpool, reading the Manager information would be a race. Solving this problem without race and mutex is what Asynchronous provides. BTW, Asynchronous also provides continuations, future-based and most of all not future-based. There is just not one solution for every problem.
I however could not find out whether this also applied to HPX. <<
In HPX there are launch policies that can be used to control thread execution, the one that would come closest to what you are looking for would be future<thing> = hpx::async(do_something_interesting()).then(hpx::launch::sync, do_another_interesting_thing());
This tells the .then() continuation to execute the next function in the same context as the thread that completes the initial future.
Does it mean a threadpool thread or the thread which started the asynchronous task?
The thread which makes the future ready. But executors can be used to control the concrete thread (see above).
No idea what this means but I feel this might be out of scope of the discussion. Regards, Christophe
Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Yes, I suppose that the whole application would be a single thread world using that analogy. Objects are not 'bound' to a particular thread, they can be accessed freely from any thread and the user is responsible for protecting them - or preferably writing functional-like code to avoid
collisions etc.
HPX uses executors to fine-control where which task is executed. Every function which (potentially) creates a new task can optionally be used with an instance of an executor (async(), future::then(), etc.)
Are we reviewing HPX? Sorry Hartmut if I gave the impression of comparing Asynchronous to HPX.
I was not under the impression that this was a formal review of Boost.Async. I rather was trying to understand the specifics of Boost.Async and therefor I was comparing it to something I know ;)
IIUC it is a library written by a private company and not intended for boost inclusion.
Just to rectify this: HPX is not a product of a private company. It's just a large open source project distributed under the Boost license with many (very capable) people contributing, which because of its scale and the produced code quality might have created your impression of it having a commercial background. The reason why it's not considered for Boost inclusion is simple. Nobody has stepped forward to make that happen. We'd be happy to support anybody interested in pushing such an avenue.
I mostly care about std::async and the proposed Standard extensions for futures. I'm not the one who brought HPX to discussion.
That was John B. IIRC, he was asking why you have decided to create your own library instead of joining forces by contributing to HPX based on his impression that Boost.Async provides services very similar to HPX. Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
On Donnerstag, 1. Dezember 2016 21:45:52 CET Christophe Henry wrote:
<snip>
I didn't quite understand the state machine explanation - but I'll continue looking through the boost.asynchronous material until I am happier. I tried to add an example and the underlying rationale (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blo b/master/libs/asynchronous/doc/asynchronous.html#d0e6437).
The Manager is a very simplified state machine but I hope you get the idea.
When looking at the example code, doesn't it contain a race on Manager::needs_second_task member. If not, how is it avoided?
Cheers, Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Dienstag, 6. Dezember 2016 15:44:39 CET you wrote:
On Donnerstag, 1. Dezember 2016 21:45:52 CET Christophe Henry wrote:
<snip>
I didn't quite understand the state machine explanation - but I'll continue looking through the boost.asynchronous material until I am happier.
I tried to add an example and the underlying rationale (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/b lo b/master/libs/asynchronous/doc/asynchronous.html#d0e6437).
The Manager is a very simplified state machine but I hope you get the idea.
When looking at the example code, doesn't it contain a race on Manager::needs_second_task member. If not, how is it avoided?
So in this example, the race is avoided because it uses a single thread scheduler as the "thread world", right? What happens if I'd change it to run on multiple threads?
Cheers, Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Dienstag, 6. Dezember 2016 15:55:14 CET you wrote:
On Dienstag, 6. Dezember 2016 15:44:39 CET you wrote:
On Donnerstag, 1. Dezember 2016 21:45:52 CET Christophe Henry wrote:
<snip>
I didn't quite understand the state machine explanation - but I'll continue looking through the boost.asynchronous material until I am happier.
I tried to add an example and the underlying rationale (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous /b lo b/master/libs/asynchronous/doc/asynchronous.html#d0e6437).
The Manager is a very simplified state machine but I hope you get the idea.
When looking at the example code, doesn't it contain a race on Manager::needs_second_task member. If not, how is it avoided?
So in this example, the race is avoided because it uses a single thread scheduler as the "thread world", right? What happens if I'd change it to run on multiple threads?
Just out of curiosity, I implemented your example just with executors and future::then: https://gist.github.com/sithhell/42eec1e183df206556086cfbf20918ac 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?
Cheers, Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
<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.
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. - 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. 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. 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/asynchronous/blob... Regards, Christophe
Am 06.12.2016 10:14 nachm. schrieb "Christophe Henry" < christophe.j.henry@gmail.com>: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
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.
Then replace "main thread" by "another thread than the one which created the object".
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 ;)
:) Because they're not. Please have a look at the definitions. I think your executors would be more a scheduler or a scheduler shared proxy (which shuts down a scheduler when no more needed). A thread world is the scheduler, hem sorry executor, and the objects living inside. A thread world enforces compartmentalizing objects into schedulers/executors.
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.
I think they are your friends ;-) Mine are callbacks. Blocking futures are my enemies. I'm still unsure whether there is such a thing as a non-blocking future.
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 about that. One of the reasons for starting discussing the library before the review is that my team and I are using the library since so long that we're not seeing any more what others do not understand. Thank you for your help pointing me the difficulties. I clearly have to make this clearer in the doc. A scheduler = I suppose, roughly your executor. The next two: see Active Object Pattern Gang of Four. The names are chosen as to correspond to the pattern. A servant = an thread-unsafe object like the manager of our example, living into a single-thread-scheduler. A servant proxy = A thread-safe object behaving like the servant it represents, for usage by outside threads.
On 12/06/2016 11:44 PM, Christophe Henry wrote:
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.
I think they are your friends ;-) Mine are callbacks. Blocking futures are my enemies. I'm still unsure whether there is such a thing as a non-blocking future.
What exactly do you mean by blocking? Logical blocking as in P0296R0? Suspending the execution context for waiting until the result is ready? Possible suspension of the running threads due to contention in the synchronization with the shared state? If it is the suspension until the asynchronous task returned the result, then yes, this is exactly what the Concurrency TS is trying to solve. By allowing to attach continuations to futures. The future that is passed to the continuation is guaranteed to be ready. The future in the continuation (attached with .then) behaves exactly as your expected. Something like your continuation construct is perfectly doable without blocking (that is suspending until the result is set) on futures. co_await nicely integrates with futures such that you never actually need to suspend and code using continuations/callbacks gets much cleaner. callback based mechanisms, be it either through .then or your mechanism, will never, ever be as performant or elegant without co_await. Anyway ... using futures or not is not really the point of your library. It's just a matter of how you skin your cat. Of course, a current std::future can not meet those requirements. boost::future can (and many others already mentioned in this thread ;)). A future, in the end, is just a mechanism to transport the result of an asynchronous operation. You chose to solve that in a different manner, so be it. We can agree to disagree on how to skin the cat ;)
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 about that. One of the reasons for starting discussing the library before the review is that my team and I are using the library since so long that we're not seeing any more what others do not understand. Thank you for your help pointing me the difficulties. I clearly have to make this clearer in the doc.
A scheduler = I suppose, roughly your executor.
The next two: see Active Object Pattern Gang of Four. The names are chosen as to correspond to the pattern.
A servant = an thread-unsafe object like the manager of our example, living into a single-thread-scheduler. A servant proxy = A thread-safe object behaving like the servant it represents, for usage by outside threads.
Ok, it all starts to get clearer now, and the purpose of the library is starting to materialize in my head. I understand the motivation better now. I am not sure yet if I like that concept ;) Thanks for the clarifications. NB: You should consider using github pages to serve your documentation instead of this htmlpreview.github.io thingy. Works much smoother.
On 12/07/2016 03:59 PM, Thomas Heller wrote:
On 12/06/2016 11:44 PM, Christophe Henry wrote:
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.
I think they are your friends ;-) Mine are callbacks. Blocking futures are my enemies. I'm still unsure whether there is such a thing as a non-blocking future.
What exactly do you mean by blocking? Logical blocking as in P0296R0? Suspending the execution context for waiting until the result is ready? Possible suspension of the running threads due to contention in the synchronization with the shared state?
I mean longer waiting due to future not being set.
If it is the suspension until the asynchronous task returned the result, then yes, this is exactly what the Concurrency TS is trying to solve. By allowing to attach continuations to futures. The future that is passed to the continuation is guaranteed to be ready. The future in the continuation (attached with .then) behaves exactly as your expected.
Something like your continuation construct is perfectly doable without blocking (that is suspending until the result is set) on futures. co_await nicely integrates with futures such that you never actually need to suspend and code using continuations/callbacks gets much cleaner. callback based mechanisms, be it either through .then or your mechanism, will never, ever be as performant or elegant without co_await.
Anyway ... using futures or not is not really the point of your library. It's just a matter of how you skin your cat. Of course, a current std::future can not meet those requirements. boost::future can (and many others already mentioned in this thread ;)). A future, in the end, is just a mechanism to transport the result of an asynchronous operation. You chose to solve that in a different manner, so be it. We can agree to disagree on how to skin the cat ;)
Yes. We both follow the same goal, removing programmers out of harm way through direct thread access.
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 about that. One of the reasons for starting discussing the library before the review is that my team and I are using the library since so long that we're not seeing any more what others do not understand. Thank you for your help pointing me the difficulties. I clearly have to make this clearer in the doc.
A scheduler = I suppose, roughly your executor.
The next two: see Active Object Pattern Gang of Four. The names are chosen as to correspond to the pattern.
A servant = an thread-unsafe object like the manager of our example, living into a single-thread-scheduler. A servant proxy = A thread-safe object behaving like the servant it represents, for usage by outside threads.
Ok, it all starts to get clearer now, and the purpose of the library is starting to materialize in my head. I understand the motivation better now. I am not sure yet if I like that concept ;) Thanks for the clarifications.
I will improve the doc and examples in the next days to make it clearer. BTW I just improved our first example (the task in 2 parts) by making some more threads access the proxy. It shows there is no life issue, the last thread holding the last proxy to an object from a thread world will shut the thread world down. I think it is a powerful design feature. Software is usually designed in components, which one tries to make reusable. This is usually difficult because of thread issues (in which thread context did we use this thing in the last project? Its own? Another one? Who shut it down?). This problem is now gone. The component represented by one (or several) proxy is safe, completely reusable and its thread limits are well defined (and irrelevant for the software using the component). This turned out in real code to be a life saver.
NB: You should consider using github pages to serve your documentation instead of this htmlpreview.github.io thingy. Works much smoother.
Yes, I'll probably follow your advice, I'm not happy about github.io either. Thanks for the interesting discussion.
On 12/06/2016 03:55 PM, Thomas Heller wrote:
On Dienstag, 6. Dezember 2016 15:44:39 CET you wrote:
On Donnerstag, 1. Dezember 2016 21:45:52 CET Christophe Henry wrote:
<snip>
I didn't quite understand the state machine explanation - but I'll continue looking through the boost.asynchronous material until I am happier.
I tried to add an example and the underlying rationale (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/b lo b/master/libs/asynchronous/doc/asynchronous.html#d0e6437).
The Manager is a very simplified state machine but I hope you get the idea.
When looking at the example code, doesn't it contain a race on Manager::needs_second_task member. If not, how is it avoided?
So in this example, the race is avoided because it uses a single thread scheduler as the "thread world", right?
And because you use a proxy and post_callback.
What happens if I'd change it to run on multiple threads?
In this case you'd have to synchronize and it would make little sense, which is why servants are usually used within a single thread world.
On 12/06/2016 03:44 PM, Thomas Heller wrote:
On Donnerstag, 1. Dezember 2016 21:45:52 CET Christophe Henry wrote:
<snip>
I didn't quite understand the state machine explanation - but I'll continue looking through the boost.asynchronous material until I am happier. I tried to add an example and the underlying rationale (https://htmlpreview.github.io/?https://github.com/henry-ch/asynchronous/blo b/master/libs/asynchronous/doc/asynchronous.html#d0e6437).
The Manager is a very simplified state machine but I hope you get the idea.
When looking at the example code, doesn't it contain a race on Manager::needs_second_task member. If not, how is it avoided?
No it doesn't. This is the point. We have at least three threads in the game, no mutex and no race. It is avoided because main thread does not access the servant and its attributes directly but through a proxy object, and because the callback is posted into the servant thread.
Future-based designs do not mix well with state machines' run-to- completion. <
Could you elaborate a little on that comment please? I have a had a couple of instances where I'm manipulating 'state' and transitioning from one to another - I would welcome some ideas on how to make this a bit cleaner.
A future.then() returns another future so isn't it just delaying the fact that I have to decide at some point what to do with the future object? Do I poll it, get() it or ignore it? Polling is tedious, ignore usually wrong, and get() means blocking. As a state machine run-to-completion is supposed to execute in no time, this is not a good option. Run-to-completion means: event is sent, transition is executed. Until it completes, the state machine is in an unstable state. This has to be kept as short as possible.
HPX uses futures as explicit means to express dependencies between tasks which allows for asynchronous execution without barriers. Surely, at some point you need to call get() on the top-most future representing the whole execution tree, but this is usually not an issue as most (if not all) applications have natural synchronization points imposed by their implemented logic. These future objects are a very useful construct allowing to express various kind of asynchrony in an application. But I'm sure you agree to that as more than half of the documentation of Boost.Asynchronous explains concepts based on using futures as a concept.
The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both.
Using futures, even if you have to call future::get() does not imply blocking. It implies possible suspension, which is a completely different beast. Calling future::get() on one task does not 'block' the kernel-thread running the task as it is now free to run other tasks.
I would say that non-blocking behaviour is one of the central goals of HPX. Continuations are only triggered when futures become ready and therefore blocking is avoided - so I don't think there's any disagreement there! Races are a problem, but a functional style of programming goes a long way to avoiding them.
I agree that HPX likely solves well the race problem, which is a great achievement. I feel both libraries solve quite different problems. Asynchronous is focusing on organizing objects into threads and building everything else on top of that. In that context, callbacks within these object's thread contexts make more sense than a fire-and-forget of a future.then();
Why do you think future::then() is fire& forget? Future::then is a means of turning a future into a callback, while the opposite can be achieved for instance by using a packaged task. So I'd say both concepts are equivalent in terms of provided functionality. The benefit of a future based approach (in my experience) is that it leads to a very functional style of programming, while encouraging value semantics. Both things naturally support race-free programming... The other benefit is that futures very much hide the notion of threads altogether, thus exposing higher level abstractions freeing the programmer's mind from tracking thousands (or more) execution states at the same time. Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
On 12/04/2016 04:15 PM, Hartmut Kaiser wrote:
Future-based designs do not mix well with state machines' run-to- completion. <
Could you elaborate a little on that comment please? I have a had a couple of instances where I'm manipulating 'state' and transitioning from one to another - I would welcome some ideas on how to make this a bit cleaner.
A future.then() returns another future so isn't it just delaying the fact that I have to decide at some point what to do with the future object? Do I poll it, get() it or ignore it? Polling is tedious, ignore usually wrong, and get() means blocking. As a state machine run-to-completion is supposed to execute in no time, this is not a good option. Run-to-completion means: event is sent, transition is executed. Until it completes, the state machine is in an unstable state. This has to be kept as short as possible.
HPX uses futures as explicit means to express dependencies between tasks
Hartmut, can we leave HPX aside and talk about the library discussed here?
which allows for asynchronous execution without barriers. Surely, at some point you need to call get() on the top-most future representing the whole execution tree, but this is usually not an issue as most (if not all) applications have natural synchronization points imposed by their implemented logic. These future objects are a very useful construct allowing to express various kind of asynchrony in an application. But I'm sure you agree to that as more than half of the documentation of Boost.Asynchronous explains concepts based on using futures as a concept.
Not at all! The first examples are using futures to show that the library supports futures and as transition to the other, more powerful parts of the library. The future-based continuations are also provided for cases where no other possibility is available (external libraries returning futures).
The reason for the non-blocking focus is that I'm using the library at work in an industrial 24/7 application. When working on the production line, blocking is a disaster. If an algorithm goes astray, or simply takes longer than usually, blocking the production line results in huge costs. Crashes due to races also. Asynchronous provides ways to avoid both.
Using futures, even if you have to call future::get() does not imply blocking. It implies possible suspension, which is a completely different beast. Calling future::get() on one task does not 'block' the kernel-thread running the task as it is now free to run other tasks.
It implies blocking the current thread of execution, at least std::future. I'm not talking about HPX ones, as I do not know the library and would prefer to comment only asynchronous and std. Coming back to my other favorite subject, if a state machine is waiting on a get(), its run to completion is incorrect and it cannot react to events.
I would say that non-blocking behaviour is one of the central goals of HPX. Continuations are only triggered when futures become ready and therefore blocking is avoided - so I don't think there's any disagreement there! Races are a problem, but a functional style of programming goes a long way to avoiding them.
I agree that HPX likely solves well the race problem, which is a great achievement. I feel both libraries solve quite different problems. Asynchronous is focusing on organizing objects into threads and building everything else on top of that. In that context, callbacks within these object's thread contexts make more sense than a fire-and-forget of a future.then();
Why do you think future::then() is fire& forget?
This is what I understand from reading the description of std experimental. If future::then is executed by a threadpool thread, it is not done by the single thread world and a race risk is given in order to not execute the then part. Am I missing something?
Future::then is a means of turning a future into a callback, while the opposite can be achieved for instance by using a packaged task. So I'd say both concepts are equivalent in terms of provided functionality.
The benefit of a future based approach (in my experience) is that it leads to a very functional style of programming, while encouraging value semantics. Both things naturally support race-free programming... The other benefit is that futures very much hide the notion of threads altogether, thus exposing higher level abstractions freeing the programmer's mind from tracking thousands (or more) execution states at the same time.
In my experience, not everything is functional and not only futures encourage value semantics. By using a threadpool, one is automatically encouraged to do so: move into the pool, move back from it. Asynchronous simply takes another way to these goals. It aims at race-free programming and hides threads just to a certain point: know where they are and how many, do not access them directly. I suppose, some will prefer HPX or std::async style, others Asynchronous. There are probably many ways to the same goal. Regards, Christophe
Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (4)
-
Biddiscombe, John A.
-
Christophe Henry
-
Hartmut Kaiser
-
Thomas Heller