Networking TS + Beast, NEW Tutorials, Read this to learn std::net !!!
Fellow C++, Boost, and WG21 Enthusiasts, lend me your ear! I write to inform you about exciting developments in C++ networking. First, a bit of background. Networking comes in three flavors: * Networking TS https://cplusplus.github.io/networking-ts/draft.pdf * Boost.Asio https://www.boost.org/doc/libs/1_69_0/doc/html/boost_asio.html * Standalone Asio https://github.com/chriskohlhoff/asio These three are for the most part identical, except that Asio flavors have additional features like ssl::stream and signal_set which are not in the TS, but will very likely appear in a future update or version. We've had Asio for over a decade now, but there is a shortage of experts. Some people believe this shortage is because Asio in particular (and thus, Networking TS since they have identical interfaces) is "difficult to use." I believe it is wrong to blame Asio for this. Concurrent programs in general are hard to write. This is applicable: "Unfortunately, today's reality is that only thoughtful experts can write explicitly concurrent programs that are correct and efficient. This is because today's programming models for concurrency are subtle, intricate, and fraught with pitfalls that easily (and frequently) result in unforeseen races (i.e., program corruption) deadlocks (i.e., program lockup) and performance cliffs (e.g., priority inversion, convoying, and sometimes complete loss of parallelism and/or even worse performance than a single-threaded program). And even when a correct and efficient concurrent program is written, it takes great care to maintain — it's usually brittle and difficult to maintain correctly because current programming models set a very high bar of expertise required to reason reliably about the operation of concurrent programs, so that apparently innocuous changes to a working concurrent program can (and commonly do, in practice) render it entirely or intermittently nonworking in unintended and unexpected ways. Because getting it right and keeping it right is so difficult, at many major software companies there is a veritable priesthood of gurus who write and maintain the core concurrent code." - Herb Sutter, "The Trouble with Locks", http://www.drdobbs.com/cpp/the-trouble-with-locks/184401930 Although this was written in 2005 it is still relevant today. It is understandable that Asio will be the first target of anger and frustration when writing concurrent programs, since it is on the "front line" so to speak. There has also been a distinct shortage of *good* tutorials and examples for Asio. Articles or blog posts which teach you step by step, explaining everything, and giving example code which demonstrates best practices. Boost.Beast is my low-level HTTP/WebSocket library which builds on Boost.Asio: https://github.com/boostorg/beast In the original release of Beast, the documentation stated "prior understanding of Boost.Asio is required." However, field experience has shown that users ignore that requirement and attempt to write complex, concurrent programs as their first-time introduction to both Beast and Asio. Based on feedback from committee members, and to serve users better, the scope of Beast has been enlarged to include first-time users of networking. The upcoming Boost 1.70 release reflects this new scope and I am excited to announce some very nice things which you can access today. First of all, Beast documentation and examples no longer use the "boost::asio" namespace, they the namespace alias "net::". While this is cosmetic, it reinforces the notion when inspecting code that it is equally applicable to Boost.Asio, Asio, and Networking TS (identifiers which are not in the TS, such as signal_set, are still qualified with boost::asio). A new documentation page explains the three flavors of networking: https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io.htm... This is also explained in my 2018 CppCon presentation: https://youtu.be/7FQwAjELMek?t=444 I have added a "Networking Refresher", a complete overview of networking from soup to nuts. No prior knowledge or understanding of networking is required, everything is explained in detail so if you want to learn this is the place to start. I also kept it short, but it is loaded with hyperlinks for further learning: https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/asi... There was a recent paper in Kona, P1269R0 ("Three Years with the Networking TS") about difficulty of implementing timeouts. To address this, Beast now has a stream class which implements configurable timeouts for you, and callers no longer have to fiddle with timers manually anymore. Everything "Just Works." It achieves the P1269R0 author's goal of having timeouts "built-in to asynchronous operations", but in a way that fits in with the design of the TS: https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/tim... I feel that this `beast::basic_stream` serves as an existence proof that the current design of Networking TS is sound - the TS offers a flexible toolbox which lets you build your own framework the way that you want it, without making odd choices for you. We are still discovering ways of leveraging it to maximum use. The beast::websocket::stream also has built-in timeouts, but they are enhanced to support "idle pings" (keeping client connections alive) and everything is fully configurable: https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_websoc... All you need to do to get sensible, suggested websocket timeouts is add one line of code after creating your stream: ws.set_option(websocket::stream_base::timeout::suggested( beast::role_type::server)); To address the cumbersome boilerplate of writing composed operations (specifically the need to propagate the associated allocator and associated executor, and to avoid invoking the completion handler from within the initiating function when the operation would complete immediately) Beast adds two new utility base classes, with plentiful documentation and examples throughout: https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/wri... There are two well-rounded examples which show you step by step how to write these things in a safe way: https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/wri... https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/wri... I have a big, new open source project which implements a server, that uses the `system_context`, taking full advantage of native Windows and Mac OS system-level execution context features. To support this use case and industry feedback, the examples in Beast now default to being always thread-safe. All examples use a "strand", and leverage P1322R0 ("Networking TS enhancement to enable custom I/O executors"). Yes, this paper which was approved in Kona, is now implemented in both Boost.Beast, and Boost.Asio, including all of the Beast examples, so if you pick up Boost 1.70 (or the master branches from github) you can start playing with this as soon as you're done reading this message!! http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html We have an active #beast channel in the C++ Slack (https://slack.cpp.al) where experienced users can help, no question is too small! I hope you will join me and the rest of the Beast and Asio community in exploring what the very powerful Networking TS and Asio libraries have to offer, and build something great together! Regards P.S. Don't forget to star the repository! https://github.com/boostorg/beast
Hi Vinnie, I have followed your stuff since you were on the Juce Forum (audio plugin developer here) and I was wondering if you ever took a look at https://github.com/mefyl/elle It's a network library that use coroutine and boost asio I was quite impressed when I saw a demo of it at a French c++ user Group. just my 2 cents. Le 14/03/2019 à 17:48, Vinnie Falco via Boost-users a écrit :
Fellow C++, Boost, and WG21 Enthusiasts, lend me your ear!
I write to inform you about exciting developments in C++ networking.
First, a bit of background. Networking comes in three flavors:
* Networking TS https://cplusplus.github.io/networking-ts/draft.pdf * Boost.Asio https://www.boost.org/doc/libs/1_69_0/doc/html/boost_asio.html * Standalone Asio https://github.com/chriskohlhoff/asio
These three are for the most part identical, except that Asio flavors have additional features like ssl::stream and signal_set which are not in the TS, but will very likely appear in a future update or version. We've had Asio for over a decade now, but there is a shortage of experts. Some people believe this shortage is because Asio in particular (and thus, Networking TS since they have identical interfaces) is "difficult to use." I believe it is wrong to blame Asio for this. Concurrent programs in general are hard to write. This is applicable:
"Unfortunately, today's reality is that only thoughtful experts can write explicitly concurrent programs that are correct and efficient. This is because today's programming models for concurrency are subtle, intricate, and fraught with pitfalls that easily (and frequently) result in unforeseen races (i.e., program corruption) deadlocks (i.e., program lockup) and performance cliffs (e.g., priority inversion, convoying, and sometimes complete loss of parallelism and/or even worse performance than a single-threaded program). And even when a correct and efficient concurrent program is written, it takes great care to maintain — it's usually brittle and difficult to maintain correctly because current programming models set a very high bar of expertise required to reason reliably about the operation of concurrent programs, so that apparently innocuous changes to a working concurrent program can (and commonly do, in practice) render it entirely or intermittently nonworking in unintended and unexpected ways. Because getting it right and keeping it right is so difficult, at many major software companies there is a veritable priesthood of gurus who write and maintain the core concurrent code." - Herb Sutter, "The Trouble with Locks", http://www.drdobbs.com/cpp/the-trouble-with-locks/184401930
Although this was written in 2005 it is still relevant today. It is understandable that Asio will be the first target of anger and frustration when writing concurrent programs, since it is on the "front line" so to speak. There has also been a distinct shortage of *good* tutorials and examples for Asio. Articles or blog posts which teach you step by step, explaining everything, and giving example code which demonstrates best practices.
Boost.Beast is my low-level HTTP/WebSocket library which builds on Boost.Asio: https://github.com/boostorg/beast
In the original release of Beast, the documentation stated "prior understanding of Boost.Asio is required." However, field experience has shown that users ignore that requirement and attempt to write complex, concurrent programs as their first-time introduction to both Beast and Asio. Based on feedback from committee members, and to serve users better, the scope of Beast has been enlarged to include first-time users of networking. The upcoming Boost 1.70 release reflects this new scope and I am excited to announce some very nice things which you can access today.
First of all, Beast documentation and examples no longer use the "boost::asio" namespace, they the namespace alias "net::". While this is cosmetic, it reinforces the notion when inspecting code that it is equally applicable to Boost.Asio, Asio, and Networking TS (identifiers which are not in the TS, such as signal_set, are still qualified with boost::asio).
A new documentation page explains the three flavors of networking:
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io.htm...
This is also explained in my 2018 CppCon presentation:
https://youtu.be/7FQwAjELMek?t=444
I have added a "Networking Refresher", a complete overview of networking from soup to nuts. No prior knowledge or understanding of networking is required, everything is explained in detail so if you want to learn this is the place to start. I also kept it short, but it is loaded with hyperlinks for further learning:
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/asi...
There was a recent paper in Kona, P1269R0 ("Three Years with the Networking TS") about difficulty of implementing timeouts. To address this, Beast now has a stream class which implements configurable timeouts for you, and callers no longer have to fiddle with timers manually anymore. Everything "Just Works." It achieves the P1269R0 author's goal of having timeouts "built-in to asynchronous operations", but in a way that fits in with the design of the TS:
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/tim...
I feel that this `beast::basic_stream` serves as an existence proof that the current design of Networking TS is sound - the TS offers a flexible toolbox which lets you build your own framework the way that you want it, without making odd choices for you. We are still discovering ways of leveraging it to maximum use. The beast::websocket::stream also has built-in timeouts, but they are enhanced to support "idle pings" (keeping client connections alive) and everything is fully configurable:
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_websoc...
All you need to do to get sensible, suggested websocket timeouts is add one line of code after creating your stream:
ws.set_option(websocket::stream_base::timeout::suggested( beast::role_type::server));
To address the cumbersome boilerplate of writing composed operations (specifically the need to propagate the associated allocator and associated executor, and to avoid invoking the completion handler from within the initiating function when the operation would complete immediately) Beast adds two new utility base classes, with plentiful documentation and examples throughout:
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/wri...
There are two well-rounded examples which show you step by step how to write these things in a safe way:
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/wri...
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/wri...
I have a big, new open source project which implements a server, that uses the `system_context`, taking full advantage of native Windows and Mac OS system-level execution context features. To support this use case and industry feedback, the examples in Beast now default to being always thread-safe. All examples use a "strand", and leverage P1322R0 ("Networking TS enhancement to enable custom I/O executors"). Yes, this paper which was approved in Kona, is now implemented in both Boost.Beast, and Boost.Asio, including all of the Beast examples, so if you pick up Boost 1.70 (or the master branches from github) you can start playing with this as soon as you're done reading this message!!
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
We have an active #beast channel in the C++ Slack (https://slack.cpp.al) where experienced users can help, no question is too small! I hope you will join me and the rest of the Beast and Asio community in exploring what the very powerful Networking TS and Asio libraries have to offer, and build something great together!
Regards
P.S. Don't forget to star the repository! https://github.com/boostorg/beast _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Olivier Tristan Research & Development www.uvi.net
On Thu, Mar 14, 2019 at 4:49 PM Vinnie Falco via Boost-users < boost-users@lists.boost.org> wrote:
Fellow C++, Boost, and WG21 Enthusiasts, lend me your ear!
I write to inform you about exciting developments in C++ networking.
There was a recent paper in Kona, P1269R0 ("Three Years with the Networking TS") about difficulty of implementing timeouts. To address this, Beast now has a stream class which implements configurable timeouts for you, and callers no longer have to fiddle with timers manually anymore. Everything "Just Works." It achieves the P1269R0 author's goal of having timeouts "built-in to asynchronous operations", but in a way that fits in with the design of the TS:
< https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/tim...
I feel that this `beast::basic_stream` serves as an existence proof that the current design of Networking TS is sound - the TS offers a flexible toolbox which lets you build your own framework the way that you want it, without making odd choices for you. We are still discovering ways of leveraging it to maximum use. The beast::websocket::stream also has built-in timeouts, but they are enhanced to support "idle pings" (keeping client connections alive) and everything is fully configurable:
This is excellent news. Timeouts are ASIO's biggest downfall. I'll definitely check it out. If it's low level, and performant enough, then it paves the way for ASIO-ed connectors for things like Redis and ZeroMQ.
On Thu, Mar 14, 2019 at 10:50 AM james
This is excellent news. Timeouts are ASIO's biggest downfall. I'll definitely check it out. If it's low level, and performant enough, then it paves the way for ASIO-ed connectors for things like Redis and ZeroMQ.
Thanks! And we are very happy to answer questions, feel free to open an issue or come to the #beast Slack channel. Regards
Hi,
Am 14.03.2019 um 17:48 schrieb Vinnie Falco via Boost-users
On Thu, 14 Mar 2019 at 16:49, Vinnie Falco via Boost-users
There was a recent paper in Kona, P1269R0 ("Three Years with the Networking TS") about difficulty of implementing timeouts. To address this, Beast now has a stream class which implements configurable timeouts for you, and callers no longer have to fiddle with timers manually anymore. Everything "Just Works." It achieves the P1269R0 author's goal of having timeouts "built-in to asynchronous operations", but in a way that fits in with the design of the TS:
https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/tim...
I have not used the synchronous API at all. But section 3.1 of P1269R0 seems to make a good point about the lack of timeouts in synchronous calls, doesn't it? beast::basic_stream makes using timeouts in the async API way simpler, but is the sync API really basically unusable because of the lack of timeouts thing?
On Fri, Mar 15, 2019 at 9:45 AM Cristian Morales Vega via Boost-users < boost-users@lists.boost.org> wrote:
On Thu, 14 Mar 2019 at 16:49, Vinnie Falco via Boost-users
wrote: There was a recent paper in Kona, P1269R0 ("Three Years with the Networking TS") about difficulty of implementing timeouts. To address this, Beast now has a stream class which implements configurable timeouts for you, and callers no longer have to fiddle with timers manually anymore. Everything "Just Works." It achieves the P1269R0 author's goal of having timeouts "built-in to asynchronous operations", but in a way that fits in with the design of the TS:
< https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_io/tim...
I have not used the synchronous API at all. But section 3.1 of P1269R0 seems to make a good point about the lack of timeouts in synchronous calls, doesn't it? beast::basic_stream makes using timeouts in the async API way simpler, but is the sync API really basically unusable because of the lack of timeouts thing?
Yes, for anything other than a toytown application at least.
On Fri, Mar 15, 2019 at 2:45 AM Cristian Morales Vega
I have not used the synchronous API at all. But section 3.1 of P1269R0 seems to make a good point about the lack of timeouts in synchronous calls, doesn't it? beast::basic_stream makes using timeouts in the async API way simpler, but is the sync API really basically unusable because of the lack of timeouts thing?
Synchronous APIs are perfectly fine. Asio defines SyncReadStream and SyncWriteStream named requirements, allowing for generic algorithms to be implemented. Not everything is a socket (for example, a serial port). A synchronous stream represents the reliable, in-order delivery of bytes using functions that block, nothing more and nothing less. For example, serial file I/O can be represented using a synchronous stream. If the synchronous stream requirements mandated timeouts, it would eliminate a broad set of implementations (including sockets on some platforms).
Sigh. A few comments from real-world experience.
We've had Asio for over a decade now, but there is a shortage of experts. Some people believe this shortage is because Asio in
particular (and thus, Networking TS since they have identical interfaces) is "difficult to use."
I believe it is wrong to blame Asio for this.
I disagree. Recently, I've been coding parallel, including networked & asynchronous, programs in C# and Java, and the experience has been a JOY. You get threads, executors, synchronization primitives, tasks, cancellation, monadic futures and concurrent (blocking and non-blocking) data structures out of the box with the platform, plus a myriad of extensions as libraries. As for executors, you don't need to be bothered with them unless you really want to for some reason. In the beginning the C# program had 2 bugs (premature exit and deadlock) both of which were easy to find fix. This program has now hundreds of hours of heavy usage w/o bugs. It was easy and a joy to build thanks to the platform facilities. In the java program I'm developing now, UI must be updated from "the UI thread". It took me whole *half an hour* to figure out how to write an executor that delegates execution to the UI thread and how to continue a completed future there (the flow is asynchronous network thread -> ui thread). All that just using the official JDK documentation, no external tutorials needed. Compare the documentation for Vertx (vertx.io) or netty (both Java toolkits for writing asynchronous reactive programs) with that of asio. On another C++ project, I had to roll my own.. basically everything built upon thread/mutex/cv and _unfortunately_ I decided to use asio for networking and serial ports. I feel dread every time I have to visit asio code and I regret I simply didn't use native Win32 APIs with direct callbacks. There are still native bits in there because Win32 natively supports cancellation of _synchronous_ blocking operations. Once I attempted to write an asio service, tried to understand the simple example from the documentation, and I gave up. I used a thread, blocking call and CancelSynchronousIO (and I consider myself fortunate to develop for Windows only that has it). Asio _is_ a relatively nice wrapper around socket and serial port APIs, but that's about it, IMO. On the other hand, I could have written the same wrappers around native APIs in two days and not haul along what I consider the baggage of technical debt that is asio in the codebase. So now I'm regrettably stuck with asio for the forseeable future for two reasons: 1) laziness (i.e., writing the said socket/serial wrappers) and 2) need for timeouts and cancellation (just as well served by thread per client and CancelSynchronousIO). Lesson learned the hard way 😞 Conclusion, if any: many people just want/need thread per client and synchronous IO for simplicity, but until asio/networking TS provide for timeouts and cancellation of synchronous requests, it's a wrong tool for those people, me included. -- Stian
As a followup and a concrete example, here's what C++ is competing against; this piece of C# code starts an asynchronous read of a file and waits for it to complete up to some timeout value; only platform facilities are used here (i.e., no external libraries). How does ReadAsync complete? Executor? Thread? OS-callback? Don't know, don't care, it works. I have an idea of how to accomplish the same in C++, and it's not pleasant -- worker thread, promise/future, blocking queue and CancelSynchronousIO. Cannot even use std::async because CancelSynchronousIO needs a target thread ID. try { int bytesRead = 0; var operationStart = DateTime.Now; var vt = sourceFile.ReadAsync(writeTask.Data); if (vt.IsCompletedSuccessfully) { bytesRead = vt.Result; } else { var t = vt.AsTask(); if (!t.Wait(State.TransferTimeoutSeconds * 1000)) throw new AggregateException(new TimeoutException("Reader timed out.")); bytesRead = t.Result; } writeTask.Data = writeTask.Data.Slice(0, bytesRead); var operationDuration = (DateTime.Now - operationStart); volumeHealthReport.ReadStatistics.UpdateBandwidth(bytesRead, operationDuration); return bytesRead; } catch (AggregateException e) { if (e.InnerException != null) writeTask.ReaderException = e.InnerException; else writeTask.ReaderException = e; volumeHealthReport.ReadStatistics.IncrementErrorCount(); return 0; }
On Fri, Mar 15, 2019 at 7:38 AM Stian Zeljko Vrba
I have an idea of how to accomplish the same in C++, and it's not pleasant -- worker thread, promise/future, blocking queue and CancelSynchronousIO. Cannot even use std::async because CancelSynchronousIO needs a target thread ID.
Yes, yes! I see now...your ideas on how to implement it in C++ convince me more than anything that the documentation is the problem. There's no need for any of that stuff, the code you provided could be implemented in C++ using asio without too much fuss, and there are actually several ways to do it all of which are succinct and will work wonderfully.
I see now...your ideas on how to implement it in C++
convince me more than anything that the documentation is the problem.
The documentation definitely is a problem, but consider the following trade-offs:
- It took me half an hour to write the function in C# based on simple platform-provided and easily discoverable abstractions (IntelliSense lists ReadAsync just by side with Read; from there on it's easy to discover the remaining bits and pieces, and there's not much more to understand for the simplest use cases like that one).
- It'd probably take me 2-3 hours to implement the said thread/queue algorithm. It'd be boring, "obviously correct" code, built on simple concurrency primitives, understandable and maintainable by others. It'd also have simple failure modes.
- It'd take me ?? hours to understand the myriad of underlying asio concepts and their interplay, ?? hours to implement it, ?? hours to test it suitably (or become convinced of correctness in another way) and it'd have more complex failure modes (as anything else asynchronous). Also it would probably be non-understandable "black magic" to others who haven't spent at least ?? hours understanding the same bits of asio as well.
I have taken a look at your tutorial on writing composed operations -- which is very well written -- but I already see that the time required to read and understand it is longer than the time I used to implement the pasted C# snippet. (Yes, it demonstrates a different use-case, but the point stands I believe.)
I'm not disputing that asio is built on solid abstractions and concepts, what I'm trying to say is that "something" should exist that would allow me to write the same functionality in 30 minutes in C++ the only prior knowledge being "there exists primitive for asynchronously reading from a file" (the same prior knowledge in the case of C#).
Whether that "something" is "documentation for dummies", a "book of recipes", high-level utility library, … ?, I do not know.
I only know that I'm 2-3X more productive in Java/C# than in C++ (collusion of different factors; another story), and the gap is increasing. Working code needs to be delivered, customers are waiting and there's only so much time one can dedicate to studying arcane details of solidly designed but on the first sight impenetrable libraries.
-- Stian
________________________________
From: Vinnie Falco
I have an idea of how to accomplish the same in C++, and it's not pleasant -- worker thread, promise/future, blocking queue and CancelSynchronousIO. Cannot even use std::async because CancelSynchronousIO needs a target thread ID.
Yes, yes! I see now...your ideas on how to implement it in C++ convince me more than anything that the documentation is the problem. There's no need for any of that stuff, the code you provided could be implemented in C++ using asio without too much fuss, and there are actually several ways to do it all of which are succinct and will work wonderfully.
I think you largely underestimate how many (often conflicting) requirements and user expectations a high-quality C++ library has to deal with. While efficiency in the C# world is no doubt important, it's worth remembering that a high-quality C++ library needs to be precisely specified and be very specific with its requirements and guarantees. This is why there's so many concepts required to grasp - ASIO has to introduce the necessary vocabulary to be able to provide an exact specification. Few, if any, C# programmers need to worry where a library gets their memory from or whether said library has customization points that allow the user to plug any homegrown async model they use in their project into the library (which is why ASIO has `async_result` and allows you to customize memory allocations via allocators where applicable). Additionally, judging by the API you mention, few C# users have a need to do efficient scatter-gather using non-contigous buffers. Remember, C++ is often used where no other high-level language has gone before. I could go on and put features from both ASIO and the C# async library side-by-side, but I think you get the picture. C++ libraries aren't complex because library developers like to masturbate while watching listings of their code - the complexity is there for a purpose (of course, sometimes it's not justifiable, mistakes happen). Library authors in higher-level languages can often make a lot of arbitrary decisions in a library, likely, because their users don't mind for the most part. In C++, a component that makes arbitrary decisions without any way to change them or to customize, most likely doesn't get used (anyone remember std::async() ?). On Fri, Mar 15, 2019 at 4:50 PM Stian Zeljko Vrba via Boost-users < boost-users@lists.boost.org> wrote:
I see now...your ideas on how to implement it in C++ convince me more than anything that the documentation is the problem.
The documentation definitely is a problem, but consider the following trade-offs:
- It took me half an hour to write the function in C# based on simple platform-provided and easily discoverable abstractions (IntelliSense lists ReadAsync just by side with Read; from there on it's easy to discover the remaining bits and pieces, and there's not much more to understand for the simplest use cases like that one).
- It'd probably take me 2-3 hours to implement the said thread/queue algorithm. It'd be boring, "obviously correct" code, built on simple concurrency primitives, understandable and maintainable by others. It'd also have simple failure modes.
- It'd take me ?? hours to understand the myriad of underlying asio concepts and their interplay, ?? hours to implement it, ?? hours to test it suitably (or become convinced of correctness in another way) and it'd have more complex failure modes (as anything else asynchronous). Also it would probably be non-understandable "black magic" to others who haven't spent at least ?? hours understanding the same bits of asio as well.
I have taken a look at your tutorial on writing composed operations -- which is very well written -- but I already see that the time required to read and understand it is longer than the time I used to implement the pasted C# snippet. (Yes, it demonstrates a different use-case, but the point stands I believe.)
I'm not disputing that asio is built on solid abstractions and concepts, what I'm trying to say is that "something" should exist that would allow me to write the same functionality in 30 minutes in C++ the only prior knowledge being "there exists primitive for asynchronously reading from a file" (the same prior knowledge in the case of C#).
Whether that "something" is "documentation for dummies", a "book of recipes", high-level utility library, … ?, I do not know.
I only know that I'm 2-3X more productive in Java/C# than in C++ (collusion of different factors; another story), and the gap is increasing. Working code needs to be delivered, customers are waiting and there's only so much time one can dedicate to studying arcane details of solidly designed but on the first sight impenetrable libraries.
-- Stian
------------------------------ *From:* Vinnie Falco
*Sent:* Friday, March 15, 2019 3:46:07 PM *To:* Stian Zeljko Vrba *Cc:* boost@lists.boost.org List; boost-users@lists.boost.org *Subject:* Re: [Boost-users] Networking TS + Beast, NEW Tutorials, Read this to learn std::net !!! On Fri, Mar 15, 2019 at 7:38 AM Stian Zeljko Vrba
wrote: I have an idea of how to accomplish the same in C++, and it's not pleasant -- worker thread, promise/future, blocking queue and CancelSynchronousIO. Cannot even use std::async because CancelSynchronousIO needs a target thread ID.
Yes, yes! I see now...your ideas on how to implement it in C++ convince me more than anything that the documentation is the problem. There's no need for any of that stuff, the code you provided could be implemented in C++ using asio without too much fuss, and there are actually several ways to do it all of which are succinct and will work wonderfully. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
Remember, C++ is often used where no other high-level language has gone before.
I'm well aware of that. However, how is that an argument for also not providing user-friendly, "instant productivity" high-level wrappers? By not doing that, developers having simple needs with reasonable [1] defaults are doomed to chase the rabbit-hole of complex specifications and reinvent the wheel again and again. Countless hours of programmer productivity wasted.
[1] Yes, what is reasonable? Look towards C# or Java. The wheel has already been invented.
Also, when it's easier (at least to me) to grasp raw Win32/Linux APIs than to study asio concepts and how they fit together, then the whole purpose of the standard library is defeated. Because: given an option of A: use time to learn concepts valid in C++ world only; option B: use (less) time to learn and use general OS mechanisms and concepts.. I know which route I'm going to choose and which route gives more long-term and more reusable knowledge. And probably a faster way towards working code, which is what matters in the end.
-- Stian
________________________________
From: Damian Jarek
I see now...your ideas on how to implement it in C++
convince me more than anything that the documentation is the problem.
The documentation definitely is a problem, but consider the following trade-offs:
- It took me half an hour to write the function in C# based on simple platform-provided and easily discoverable abstractions (IntelliSense lists ReadAsync just by side with Read; from there on it's easy to discover the remaining bits and pieces, and there's not much more to understand for the simplest use cases like that one).
- It'd probably take me 2-3 hours to implement the said thread/queue algorithm. It'd be boring, "obviously correct" code, built on simple concurrency primitives, understandable and maintainable by others. It'd also have simple failure modes.
- It'd take me ?? hours to understand the myriad of underlying asio concepts and their interplay, ?? hours to implement it, ?? hours to test it suitably (or become convinced of correctness in another way) and it'd have more complex failure modes (as anything else asynchronous). Also it would probably be non-understandable "black magic" to others who haven't spent at least ?? hours understanding the same bits of asio as well.
I have taken a look at your tutorial on writing composed operations -- which is very well written -- but I already see that the time required to read and understand it is longer than the time I used to implement the pasted C# snippet. (Yes, it demonstrates a different use-case, but the point stands I believe.)
I'm not disputing that asio is built on solid abstractions and concepts, what I'm trying to say is that "something" should exist that would allow me to write the same functionality in 30 minutes in C++ the only prior knowledge being "there exists primitive for asynchronously reading from a file" (the same prior knowledge in the case of C#).
Whether that "something" is "documentation for dummies", a "book of recipes", high-level utility library, … ?, I do not know.
I only know that I'm 2-3X more productive in Java/C# than in C++ (collusion of different factors; another story), and the gap is increasing. Working code needs to be delivered, customers are waiting and there's only so much time one can dedicate to studying arcane details of solidly designed but on the first sight impenetrable libraries.
-- Stian
________________________________
From: Vinnie Falco
I have an idea of how to accomplish the same in C++, and it's not pleasant -- worker thread, promise/future, blocking queue and CancelSynchronousIO. Cannot even use std::async because CancelSynchronousIO needs a target thread ID.
Yes, yes! I see now...your ideas on how to implement it in C++ convince me more than anything that the documentation is the problem. There's no need for any of that stuff, the code you provided could be implemented in C++ using asio without too much fuss, and there are actually several ways to do it all of which are succinct and will work wonderfully. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.orgmailto:Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
On Fri, Mar 15, 2019 at 11:58 AM Stian Zeljko Vrba
However, how is that an argument for also not providing user-friendly, "instant productivity" high-level wrappers? By not doing that, developers having simple needs with reasonable [1] defaults are doomed to chase the rabbit-hole of complex specifications and reinvent the wheel again and again. Countless hours of programmer productivity wasted.
So your complaint is not that Asio is bad, but that the author stopped when he should have kept going and writing more code? Nothing stops you or anyone else from supplying that missing code. As I have done with Beast.
However, how is that an argument for also not providing user-friendly, "instant productivity" high-level wrappers? By not doing that, developers having simple needs with reasonable [1] defaults are doomed to chase the rabbit-hole of complex specifications and reinvent the wheel again and again. Countless hours of programmer productivity wasted.
So your complaint is not that Asio is bad, but that the author stopped when he should have kept going and writing more code? Nothing stops you or anyone else from supplying that missing code. As I have done with Beast.
That's not how I read the OP's point. He seemed to me to be pointing out that .NET APIs provide a stupid-simple API on top of lots of complexity which itself sits on top of the Win32 winsock API. And that I find a very fair criticism of the Networking TS, and one shared by a ton of people on WG21. Networking ought to be some stupid-simple coroutinised Ranges i/o API on the top. Then stuff like strands, i/o contexts, completion tokens et al in a distinct, held separate, API layer that you never see from the stupid simple API layer, unless you ask. And then very low level almost-raw-sockets in a third, distinct, held separate API at the bottom, that you ought to never see unless you really have to. That's the ideal in a lot of people's opinion, but nobody has the time to do it right. So we standardise what's common practice, not what people would prefer common practice ought to be. And that's entirely right and proper in my book. Standards ought to be about existing practice not about innovation, and this is one reason I find the current trajectory of one half of the Networking TS into pure innovation land (i.e. Executors, which don't exist anywhere yet in the proposed form) worrying. Me personally, I'd really like to see the bottom level part of Networking minus all the concurrency stuff standardised separately as soon as possible. We understand that part well. But, up to the TS editors and champions in the end, and they've not decided to do that. Niall
On Fri, Mar 15, 2019 at 12:19 PM Niall Douglas via Boost-users
That's not how I read the OP's point. He seemed to me to be pointing out that .NET APIs provide a stupid-simple API on top of lots of complexity which itself sits on top of the Win32 winsock API.
Well, I think you read it incorrectly.
And that I find a very fair criticism of the Networking TS
You're doing the same thing that the OP did, which is blaming Networking TS for not doing enough. Let me see if I can explain it "stupid-simple" for you: We have this lower layer N (which is Networking TS) but users want SS (stupid-simple high level interface). SS can be implemented in terms of N, and you criticize Networking TS because it is N and not SS. It sounds like I have perfectly characterized the point of both the OP and yourself. I agree that we need SS but that doesn't mean we should not have N. Then there's B (for Beast) which uses N, and can be used to write SS but that's a whole different discussion :) Here's a diagram to make it easy: SS Stupid/Simple <--- You want this B Beast <--- We have this now N Networking TS <-- We've had this Please show me on the diagram why the availability of N and/or B are obstacles to implement SS? Thanks
And that I find a very fair criticism of the Networking TS
You're doing the same thing that the OP did, which is blaming Networking TS for not doing enough.
That would be a mischaracterisation. What I said is that there is an ideal design which lots of people would like if infinite development resources were available. The kind of resourcing which .NET had, for example. But we don't and didn't have that, so we don't get the stupid-simple API part. We stop short in the complex intermediate API, with the layers all smooshed together and indistinct. And not separated cleanly.
Please show me on the diagram why the availability of N and/or B are obstacles to implement SS?
Beast is an extreme example of a design wholly dependent on that of an external library. As ASIO changes, you'll have to spent a lot of time keeping up using resources which you'd prefer to invest elsewhere. This is a big reason why people don't choose Boost, and instead do poor man's local clones of Boost facilities. It guarantees no coupling on externally moving targets. For the same reason, it takes a lot of resources to build stupid simple high level APIs on top of shifting lower level APIs. I'm not sure it can be done by people unless full time employed to do so. And that's been historically very hard in C++, only a very few parts of the standard library were developed by people employed to solely do that and nothing else. So tldr, it's not an engineering problem, it's a resourcing problem, including the enormous resourcing requirement to build a consensus at WG21 and get it through plenary. Look at the vast resourcing Ranges or Coroutines sucked down, for example. High level stupid simple APIs are hideously expensive to standardise, even for a big multinational corporation like Microsoft (which largely sponsored both). Niall
On Fri, Mar 15, 2019 at 8:19 PM Niall Douglas via Boost-users
Me personally, I'd really like to see the bottom level part of Networking minus all the concurrency stuff standardised separately as soon as possible. We understand that part well. But, up to the TS editors and champions in the end, and they've not decided to do that.
I think a lot of people, myself included, would have been happy with just having access to a low-level standard sockets API; and then building on top of it later on. Cheers, Miguel
Nothing stops you or anyone else from supplying that missing code.
?! As I explained in the previous mail, (lack of) TIME stops me. I'm an "ordinary programmer" who *needs* networking/serial/async, but I don't need ridiculously high performance, am not networking expert and have no desire to become one. I have even less desire to spend 5X or more time developing software that will have unnecessarily high performance (!) compared to 1X development effort for "satisfactory performance" (i.e., "it works for the use case"). And I believe that I'm not alone there.
That's not how I read the OP's point. He seemed to me to be pointing out that .NET APIs provide a stupid-simple API on top of lots of complexity which itself sits on top of the Win32 winsock API.
That's the correct interpretation.
Networking ought to be some stupid-simple coroutinised Ranges i/o API on the top. […]
This. -- Stian
Networking TS is great and wished (by me anyway) feature. Though I have no full picture of whole design, I need/like it, and I have chance to dig into it in the future. The current TS is current progress only, and it will be simplificated for understanding I think, but for that needs some time. [my shorten history of learning the CPP (and I am not alone I thinking)] 0) after reading few pages of Stroustrup's C++ book: hmmm, it looks cool ! 1) acquaintance to STL: that is magic ! 2) acquaintance to template programming: wtf ? I have cognitive dissonance ! 3) few weeks later: I done it ! I can do it ! :) 4) after reading few examples of the boost libraries: hmmm, it looks cool ! 5) ... [/end history] To Vinnie and other devs: thanks a lot for your job ! -- the best regards
Networking ought to be some stupid-simple coroutinised Ranges i/o API on the top. […]
This.
We *may* get this, if everything works out. Eric Niebler has a fairly complete concept of what a high level Ranges i/o ought to look like. Dalton Woodard is working on a generic mechanism for Ranges i/o to discover any low level scatter-gather i/o implementation, such that Ranges can "just work" with whatever you feed it. Elias Kosunen is working on an iostreams << replacement matching the >> replacement which is fmt, just standardised into C++ 20. Zach Laine and others are working on Unicode string support. I'm working on a generic low level i/o library API, and have just internally distributed the first draft of an enhanced C++ memory and object model which has first class support for exchanging representations of objects between multiple C++ programs. I'll be releasing draft 2 to SG12 next week, I'll be speaking on the topic at ACCU in April followed by attending WG14 in May, all building towards generating a head of steam for progress within WG21 over Cologne-Belfast-Prague-Bulgaria. We'll see how things go. Niall
On Sat, Mar 16, 2019 at 3:05 PM Niall Douglas via Boost-users < boost-users@lists.boost.org> wrote:
Networking ought to be some stupid-simple coroutinised Ranges i/o API on the top. […]
This.
We *may* get this, if everything works out.
Eric Niebler has a fairly complete concept of what a high level Ranges i/o ought to look like.
Dalton Woodard is working on a generic mechanism for Ranges i/o to discover any low level scatter-gather i/o implementation, such that Ranges can "just work" with whatever you feed it.
Elias Kosunen is working on an iostreams << replacement matching the >> replacement which is fmt, just standardised into C++ 20.
Zach Laine and others are working on Unicode string support.
I'm working on a generic low level i/o library API, and have just internally distributed the first draft of an enhanced C++ memory and object model which has first class support for exchanging representations of objects between multiple C++ programs. I'll be releasing draft 2 to SG12 next week, I'll be speaking on the topic at ACCU in April followed by attending WG14 in May, all building towards generating a head of steam for progress within WG21 over Cologne-Belfast-Prague-Bulgaria.
I don't see how it is a good thing for things that haven't been adopted to make it straight into the standard. What's wrong with publishing libraries, then standardizing the ones that are already de-facto standard? Github makes things super easy, publish stuff, and in a few years we know what's what and then standardization is a simple nod from the committee, reflecting the reality of what's already used in practice.
On 3/16/19 3:37 PM, Emil Dotchevski via Boost-users wrote:
On Sat, Mar 16, 2019 at 3:05 PM Niall Douglas via Boost-users
mailto:boost-users@lists.boost.org> wrote: Networking ought to be some stupid-simple coroutinised Ranges i/o API on the top. […]
This.
We *may* get this, if everything works out.
Eric Niebler has a fairly complete concept of what a high level Ranges i/o ought to look like.
Dalton Woodard is working on a generic mechanism for Ranges i/o to discover any low level scatter-gather i/o implementation, such that Ranges can "just work" with whatever you feed it.
Elias Kosunen is working on an iostreams << replacement matching the >> replacement which is fmt, just standardised into C++ 20.
Zach Laine and others are working on Unicode string support.
I'm working on a generic low level i/o library API, and have just internally distributed the first draft of an enhanced C++ memory and object model which has first class support for exchanging representations of objects between multiple C++ programs. I'll be releasing draft 2 to SG12 next week, I'll be speaking on the topic at ACCU in April followed by attending WG14 in May, all building towards generating a head of steam for progress within WG21 over Cologne-Belfast-Prague-Bulgaria.
I don't see how it is a good thing for things that haven't been adopted to make it straight into the standard. What's wrong with publishing libraries, then standardizing the ones that are already de-facto standard? Github makes things super easy, publish stuff, and in a few years we know what's what and then standardization is a simple nod from the committee, reflecting the reality of what's already used in practice.
+1 Lot's people are saying this. But no one is listening. Robert Ramey
I don't see how it is a good thing for things that haven't been adopted to make it straight into the standard. What's wrong with publishing libraries, then standardizing the ones that are already de-facto standard? Github makes things super easy, publish stuff, and in a few years we know what's what and then standardization is a simple nod from the committee, reflecting the reality of what's already used in practice.
I don't know where you're getting this from. Ranges has multiple implementations, and has been on github for years. Dalton's been putting together his framework in public on github, seeking to bridge LLFIO/ASIO/etc with plenty of stakeholder feedback. Elias has had his library on github for yonks now with plenty of users. It's not as old as fmt, true, it was designed to do the inverse of fmt in a similar way. Zach has had his Unicode library on github for years now, indeed it's even in Boost format with a fairly complete set of Boost format docs. LLFIO has been in public view since 2013, underwent a review here in 2015, was completely rearchitected based on that feedback and has since gained a good few number of hardcore users. Now, I'll grant you that none of these are ASIO-type popular libraries. But it's not like they lack implementation and userbase experience, either. Most of these are multi-year old codebases with real world users using them in production, and giving ample feedback rounding off the rough edges over time. I'll put this another way: there are plenty of standard library proposals before the committee with no reference implementation library whatsoever even attempted. There are lots more where the reference library was clearly thrown together so there was one at all, and nobody actually uses the proposed library in production. Nobody can say that about any of the libraries above, bar perhaps Dalton's, and that's only because he's implementing a recently begun feature discovery bridge between Ranges i/o and everything else. I would agree that there is insufficient user experience of these libraries yes. They could do with more. But I'd also argue they're in the top 5% of standard library enhancements being currently proposed for standardisation in terms of real world use experience. They're good, relative to what else is being proposed. We live in the world we are in. Niall
On Sat, Mar 16, 2019 at 4:55 PM Niall Douglas via Boost-users < boost-users@lists.boost.org> wrote:
I don't see how it is a good thing for things that haven't been adopted to make it straight into the standard. What's wrong with publishing libraries, then standardizing the ones that are already de-facto standard? Github makes things super easy, publish stuff, and in a few years we know what's what and then standardization is a simple nod from the committee, reflecting the reality of what's already used in
practice.
I don't know where you're getting this from.
Ranges has multiple implementations, and has been on github for years.
Dalton's been putting together his framework in public on github, seeking to bridge LLFIO/ASIO/etc with plenty of stakeholder feedback.
Elias has had his library on github for yonks now with plenty of users. It's not as old as fmt, true, it was designed to do the inverse of fmt in a similar way.
Zach has had his Unicode library on github for years now, indeed it's even in Boost format with a fairly complete set of Boost format docs.
LLFIO has been in public view since 2013, underwent a review here in 2015, was completely rearchitected based on that feedback and has since gained a good few number of hardcore users.
Now, I'll grant you that none of these are ASIO-type popular libraries. But it's not like they lack implementation and userbase experience, either. Most of these are multi-year old codebases with real world users using them in production, and giving ample feedback rounding off the rough edges over time.
There are many libraries that pass this rather low bar. You are not saying that all such libraries qualify for standardization, are you? What, ASIO is way too good, we can't expect this sort of excellence from any other library poised to become standardized? How about this bar: for a library to qualify for standardization, it must have retained its dominant position for at least two years without any interface changes. To argue that this is unreasonable is equivalent to stating that there is nothing wrong with changing the interface after standardization.
I'll put this another way: there are plenty of standard library proposals before the committee with no reference implementation library whatsoever even attempted. There are lots more where the reference library was clearly thrown together so there was one at all, and nobody actually uses the proposed library in production.
Yes I know. Total lunacy.
They're good, relative to what else is being proposed.
Nobody is arguing that they are not good. There is no reason to standardize every good library out there. We should only standardize the ones that have already become THE standard in their respective domain.
I try to avoid saying that something must be in Boost before being accepted in std, because I don’t want it to sound like a gate keeper, but there simply is no close second to the Boost libraries for exposure and user base. I don’t want to cast aspersions on these particular libraries (particularly since I consider some of these authors to be personal friends), nor to I want to belittle this:
On Sat, Mar 16, 2019 at 4:55 PM Niall Douglas via Boost-users
mailto:boost-users@lists.boost.org> wrote: But it's not like they lack implementation and userbase experience, either. Most of these are multi-year old codebases with real world users using them in production, and giving ample feedback rounding off the rough edges over time.
But as Emil said:
There are many libraries that pass this rather low bar.
Of any individual C++ library author today, my guess is that Eric would have the best chance of attracting a large user base for his personal GitHub, but I would still consider that exposure as below the bar of what I’d like to see for a library that we are going to put into the standard (given that, once in, we’ll never be able to drop support or modify the ABI). Even if a library is being used by a bajillion developers at MegaCorp, I’m not willing to waiver on this. All those developers are unlikely to be using the library with as diverse a set of applications and environments as will be done by Boost users. I know that getting a library into Boost is no small task (Niall may know that better than anyone on the planet), but if it is worth putting in std then it is worth putting in Boost first. If it isn’t worth the effort to put it into Boost, then it isn’t ready to become part of the library that we have to live with forever. If there is no time to put it into Boost first, then it is definitely being rushed. The feedback that authors receive during the review process and from being deployed to tens of thousands of users is simply unavailable any other way. The insight gained by maintaining a library over a few release cycles for use by thousands of users in thousands of diverse applications on every conceivable platform and configuration will effect the API for the better in ways that cannot be foreseen and are likely to be under-appreciated. Put it Boost first. (Or any other library with over 100K users and on almost every platform in modern use.) Jon
On Sat, Mar 16, 2019 at 6:57 PM Jon Kalb via Boost-users < boost-users@lists.boost.org> wrote:
Of any individual C++ library author today, my guess is that Eric would have the best chance of attracting a large user base for his personal GitHub, but I would still consider that exposure as below the bar of what I’d like to see for a library that we are going to put into the standard (given that, once in, we’ll never be able to drop support or modify the ABI).
I know that getting a library into Boost is no small task (Niall may know
It appears that, depending on whom you know, it might be easier to get a library in the C++ standard compared to getting it into Boost. This speaks volumes, if not about the quality of the libraries being standardized, at least about the different mindset of the people involved. that better than anyone on the planet), but if it is worth putting in std then it is worth putting in Boost first. It appears that he has learned his lesson and won't repeat that mistake. :)
On Mar 16, 2019, at 7:42 PM, Emil Dotchevski via Boost-users
mailto:boost-users@lists.boost.org> wrote: On Sat, Mar 16, 2019 at 6:57 PM Jon Kalb via Boost-users
mailto:boost-users@lists.boost.org> wrote: Of any individual C++ library author today, my guess is that Eric would have the best chance of attracting a large user base for his personal GitHub, but I would still consider that exposure as below the bar of what I’d like to see for a library that we are going to put into the standard (given that, once in, we’ll never be able to drop support or modify the ABI).
It appears that, depending on whom you know, it might be easier to get a library in the C++ standard compared to getting it into Boost.
I have heard this and I’m afraid that it might be true.
This speaks volumes, if not about the quality of the libraries being standardized, at least about the different mindset of the people involved.
Agreed. I think there is a great deal of unfounded arrogance in people that think that we can design an API that we’ll be pleased to live with forever sans extensive implementation experience.
I know that getting a library into Boost is no small task (Niall may know that better than anyone on the planet), but if it is worth putting in std then it is worth putting in Boost first.
It appears that he has learned his lesson and won't repeat that mistake. :)
I hope not. This is a “mistake” that bears repeating. Jon
It appears that, depending on whom you know, it might be easier to get a library in the C++ standard compared to getting it into Boost.
I have heard this and I’m afraid that it might be true.
I'd say Chris right now would probably disagree. Beman probably would disagree, Filesystem took far longer at WG21 than anyone reasonable would have expected, and that's after it had gone through the Boost process multiple times. Beman told me it took him between twenty and twenty-five years of effort, depending on how you counted it. He wasn't sure if he would do it again, if he could have travelled back in time to tell his younger self. I've heard Eric in the past say it is equally hard to get a library into Boost as into WG21, and they are both quite different processes with different needs and requirements. And he's said on more than one occasion that he only has enough in him to get Ranges past either Boost, or WG21. He doesn't have enough in him to do both. I'd echo Eric's sentiments on this completely. I don't have it in me to ever get a fundamentals library into Boost again. Besides, I'd likely end up getting divorced and my children no longer speaking to me. It's not worth it, personally speaking.
I know that getting a library into Boost is no small task (Niall may know that better than anyone on the planet), but if it is worth putting in std then it is worth putting in Boost first.
It appears that he has learned his lesson and won't repeat that mistake. :)
I hope not. This is a “mistake” that bears repeating.
I can see me doing a small niche library one day. But never again for a fundamentals library. I would also say that the Boost review process is a poor fit for niche libraries. There isn't a sufficient critical mass of domain experts for a good review, it ends up becoming a popularity vote by the inexperienced-in-that-very-specific-topic. In reviews in the past I've been dismayed to see the review of someone I know is a domain expert in a library being weighted equally to reviews by those who were not. But there is little you can do about it, when a mere five people are reviewing, there isn't the numbers to weigh one much heavier than the others. So a library ends up passing, despite that the domain expert found severe flaws in the proposed design. I'm all for more libraries heading to Boost first, where Boost is a good fit for them. Boost was great for Outcome, precisely because Outcome had so much review feedback available. Fundamental libraries have that. But niche libraries don't fit Boost well. For example, I don't personally think that the Graphics proposal would fit a Boost review well. The bikeshedding would be enormous, the topic is quite niche, and the true correct design is probably three separate Graphics libraries, the same way as XML processing has three entirely separate ideal ways of library support. There also several big, well established, dominant API libraries none of which have a snowball's chance at WG21 due to using ancient C++, or C. Niall
On Sun, Mar 17, 2019 at 1:43 AM Niall Douglas via Boost-users < boost-users@lists.boost.org> wrote:
It appears that, depending on whom you know, it might be easier to get a library in the C++ standard compared to getting it into Boost.
I have heard this and I’m afraid that it might be true.
I'd say Chris right now would probably disagree. Beman probably would disagree, Filesystem took far longer at WG21 than anyone reasonable would have expected, and that's after it had gone through the Boost process multiple times. Beman told me it took him between twenty and twenty-five years of effort, depending on how you counted it. He wasn't sure if he would do it again, if he could have travelled back in time to tell his younger self.
Of course WG21 could have simply accepted the library after it went through years of development and reviews, rather than trying to "fix" it. This is precisely why WG21 should not be involved in innovation and design, because then acceptance becomes a matter of opinion and politics, rather than a simple acknowledgement of an interface that is already successful.
I'd echo Eric's sentiments on this completely. I don't have it in me to ever get a fundamentals library into Boost again. Besides, I'd likely end up getting divorced and my children no longer speaking to me. It's not worth it, personally speaking.
You leave out the other possibility, to leave the library out of the standard, where most libraries, including good libraries, belong.
Of course WG21 could have simply accepted the library after it went through years of development and reviews, rather than trying to "fix" it. This is precisely why WG21 should not be involved in innovation and design, because then acceptance becomes a matter of opinion and politics, rather than a simple acknowledgement of an interface that is already successful.
Like Boost, they declare changes which must be made before acceptance. For some features e.g. Coroutines, it ends up being an awful lot of discussion but ultimately very few actual changes other than naming. For others e.g. Filesystem, probably Networking, it becomes how long is a piece of string. Back when Gor started down the standards route for Coroutines, I remember warning him over a burger on a park bench in Aspen just how long a piece of string he could end up upon. He ended up proving me wrong, I am glad to say, just seven years approx start to finish. But it looked at the time to be a decade plus effort to reach consensus for his proposal. For any reasonably disruptive change, you've got to sign on to investing a decade plus of your life to reach standards fruition. Given that we all get maybe thirty to forty years of professional career, that's a very big claim on your productive years.
I'd echo Eric's sentiments on this completely. I don't have it in me to ever get a fundamentals library into Boost again. Besides, I'd likely end up getting divorced and my children no longer speaking to me. It's not worth it, personally speaking.
You leave out the other possibility, to leave the library out of the standard, where most libraries, including good libraries, belong.
The same argument would then apply to Boost by this logic. I'm not sure that I agree with that. The usual counterargument to standardising ever more libraries generally involves a decent centralised package ecosystem for C++, and sure, I get that that would avoid much over-eager standardisation. However Python has world class centralised package management (albeit with a few forks), yet in many people's opinion continues to aggregate too much inferior functionality into its standard library given the extremely high quality of substitutes in its package repos. In particular, some people feel that an inferior library gets into Python's standard library, when a much superior alternative does not. And that's because the inferior library had a champion, and the superior library did not. Ultimately it comes down to committee perception of champions. If someone is willing to champion a library into the standard, in C++ and Python it generally tends to happen eventually given enough application of will by the champion. The people behind the core standard essentially assume that those motivated enough to champion a proposal for as many years/decades as needed deserve to eventually win, irrespective of the fundamental utility of the standard library proposal. Contrast that with C, where the default answer to proposals for new library features is "no, unless you can *prove* its utter necessity for the survival of the language/ecosystem". The former attitude invites large committees, and scalability problems with sheer numbers. The latter attitude dissuades people such that WG14 attendance is approx 20-30 people nowadays, down from 60+ plus a decade ago. The correct attitude is somewhere in between these two approaches. But it's very hard to aim down the middle. I've sat in WG21 watching a library proposal which I personally felt didn't add enough value to be worth the committee time to pass it. But equally, it's not a bad proposal, I personally like the proposer, and nobody else in the room objects because they're all probably thinking the same as me. So it goes through, and displaces time which would have have been more productively spent elsewhere. Personally speaking, I would prefer to have problems of scalability due to growth and numbers rather than problems of lack of enthusiasm. So I vote for what WG21 is doing, rather than what WG14 is doing. Ultimately ISO SC22 (Programming languages) is 99.9% volunteer-led, so you've got to do whatever keeps bringing in the volunteers. If that means over-standardisation in order to keep them coming, I think that's not a terrible tradeoff given the alternative: people just stop caring to bother at all. Niall
On Sun, Mar 17, 2019 at 6:19 PM Niall Douglas via Boost-users < boost-users@lists.boost.org> wrote:
Of course WG21 could have simply accepted the library after it went through years of development and reviews, rather than trying to "fix" it. This is precisely why WG21 should not be involved in innovation and design, because then acceptance becomes a matter of opinion and politics, rather than a simple acknowledgement of an interface that is already successful.
Like Boost, they declare changes which must be made before acceptance.
If a library is successful, popular, and its interface hasn't changed for years, it makes no sense to change it and then immediately standardize it.
I'd echo Eric's sentiments on this completely. I don't have it in me to ever get a fundamentals library into Boost again. Besides, I'd likely end up getting divorced and my children no longer speaking to me. It's not worth it, personally speaking.
You leave out the other possibility, to leave the library out of the standard, where most libraries, including good libraries, belong.
The same argument would then apply to Boost by this logic. I'm not sure that I agree with that.
It absolutely does apply to Boost. To think that it does not is equivalent to thinking that all non-Boost libraries are crap.
The usual counterargument to standardising ever more libraries generally involves a decent centralised package ecosystem for C++, and sure, I get that that would avoid much over-eager standardisation.
That's not the argument I'm making. From this point of view, the lack of centralized package ecosystem is not an excuse. But maybe all that needs to happen to avoid the obvious problems is slow down the process: put the library in experimental and standardize it only after/if its interface has remained unchanged for several years. Wasn't this what happened with Filesystem anyway?
There are many libraries that pass this rather low bar. You are not saying that all such libraries qualify for standardization, are you? What, ASIO is way too good, we can't expect this sort of excellence from any other library poised to become standardized?> How about this bar: for a library to qualify for standardization, it must have retained its dominant position for at least two years without any interface changes. To argue that this is unreasonable is equivalent to stating that there is nothing wrong with changing the interface after standardization.
I'm not unsympathetic to this viewpoint. I've often said that nobody should be standardising libraries which the committee invents out of thin air, yet the committee keeps doing exactly that. But I also think that "dominant position" is very hard nowadays. Most of the libraries which are dominant are designed around ancient C++. They have zero chance of standardisation. There is also the hard reality that even if your library has been stable for years, even decades, the committee is going to force you to completely redesign it into something totally innovative anyway. This is exactly what has happened to ASIO, the Networking TS will be getting close to a brand new design, a whole new API by the time its done. Or, better worded, it was redesigned by committee through invention out of thin air. That suggests to not go too far down the market share route first if your end goal is standardisation. That's simply facts on the ground, that's the playing field, and you have to play the game that is being played.
Nobody is arguing that they are not good. There is no reason to standardize every good library out there. We should only standardize the ones that have already become THE standard in their respective domain.
Equally, I think most would agree that iostreams needs an overhaul for modern C++. There is a ton of inefficiency in there, and moreover because the language lacks understanding of stuff like i/o, the optimiser mostly gives up when faced with trivially obvious i/o optimisation opportunities. This is very low hanging fruit, and it is stuff WG14 are not against also changing for C. POSIX is also keen. It just needs someone to do the work, coordinate the various parties, and get a common proposal over the line. I agree that it would be far better if multinationals sponsored this kind of deep in-out refactoring through prototype implementation. But they see no commercial advantage. For example, Microsoft is very well aware that Linux file i/o is up to 80x faster than Windows' now. It's getting embarrassing the difference. But Microsoft management can't see how closing the gap turns into extra dollars, so they won't authorise the works needed. They take the view that the customer will adapt if they need to. Meanwhile, all Windows users make do with awful git performance, and much slower compile times. That lack of corporate sponsorship of meaningful change leaves open only the approach of change by committee invention out of thin air. Nobody prefers this. But what else can be done? The fact WG14 and POSIX are warm to doing committee invention in the area of i/o shows just how much frustration with the status quo has built up. Niall
On 16/03/2019 03:38, Stian Zeljko Vrba wrote:
As a followup and a concrete example, here's what C++ is competing against; this piece of C# code starts an asynchronous read of a file and waits for it to complete up to some timeout value; only platform facilities are used here (i.e., no external libraries). [...] var vt = sourceFile.ReadAsync(writeTask.Data); if (vt.IsCompletedSuccessfully) { bytesRead = vt.Result; } else { var t = vt.AsTask(); if (!t.Wait(State.TransferTimeoutSeconds * 1000)) throw new AggregateException(new TimeoutException("Reader timed out."));
FWIW, using Task.Wait is in general not recommended. It's relatively safe on a worker thread that is only doing one async thing (but not always safe if there are multiple async operations). It's pretty much never safe to call it on the UI thread. In fact async-and-then-wait is an anti-pattern, since the code is not actually asynchronous at all. You should either make it actually asynchronous or use the synchronous read API instead. (As it is, the task actually continues running even when your "timeout" occurs, because you're not using task cancellation correctly.)
Oh, I know about possible caveats of Task.Wait. It's on its own worker thread, and that thread also arranges that only one async operation is in flight. And in case of timeout, the whole (composite) operation on that file is aborted, so if the task is still hanging somewhere and waiting for completion... it doesn't matter; the whole operation will have been marked as failed by the time it's completed and the thread will have moved on to the next file.
Task is MS's weird name for a future (TaskCompletionSource being the promise part), and the language (ab)uses Task<T> return value to transform async/await into state machines. THIS -- in combination with async/await (that also includes UI) -- is the situation when most (all?) caveats of waiting on a task arise, and THAT is an anti-pattern. This is not the case in my code. (Even that "anti-pattern" can be made safe with ConfigureAwait.)
Even the official Microsoft documentation (tutorial) teaches how to use Wait: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view... ; indeed Wait is essential for fork-join parallelism or other asynchronous computations.
As for using synchronous API: no-go. Timeout is most likely due to network disk having disappeared, which puts the thread (on Linux, possibly OSX as well) into uninterruptible sleep, so I'd have to manage my own thread pool... and then the future/thread/blocking-queue solution I hinted at becomes even more complicated.
Because of uninterruptible sleep, not even the async Task may be cancellable, so I just let it hang and let the thread pool take care of spawning a new thread if necessary (assuming that async IO completion needs a dedicated thread on the platform.)
Thanks for the input though.
-----Original Message-----
From: Boost-users
As a followup and a concrete example, here's what C++ is competing against; this piece of C# code starts an asynchronous read of a file and waits for it to complete up to some timeout value; only platform facilities are used here (i.e., no external libraries). [...] var vt = sourceFile.ReadAsync(writeTask.Data); if (vt.IsCompletedSuccessfully) { bytesRead = vt.Result; } else { var t = vt.AsTask(); if (!t.Wait(State.TransferTimeoutSeconds * 1000)) throw new AggregateException(new TimeoutException("Reader timed out."));
FWIW, using Task.Wait is in general not recommended. It's relatively safe on a worker thread that is only doing one async thing (but not always safe if there are multiple async operations). It's pretty much never safe to call it on the UI thread. In fact async-and-then-wait is an anti-pattern, since the code is not actually asynchronous at all. You should either make it actually asynchronous or use the synchronous read API instead. (As it is, the task actually continues running even when your "timeout" occurs, because you're not using task cancellation correctly.) _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
On Fri, Mar 15, 2019 at 7:19 AM Stian Zeljko Vrba
I believe it is wrong to blame Asio for this.
I disagree. Recently, I've been coding parallel, including networked & asynchronous, programs in C# and Java, and the experience has been a JOY. You get threads, executors, synchronization primitives, tasks, cancellation, monadic futures and concurrent (blocking and non-blocking) data structures out of the box with the platform, plus a myriad of extensions as libraries. As for executors, you don't need to be bothered with them unless you really want to for some reason.
If you were to compare POSIX or BSD sockets to C# and Java, you would say exactly the same things. Asio (like Beast) is a low-level, portable abstraction of I/O. You can BUILD "threads", "executors", "synchronization primitives", "tasks", "cancellation", "monadic futures", and "concurrent data structures" with it. Does this mean OS-level sockets are the wrong abstraction? Of course not. The reason C# seems "ahead" of C++ is two-fold: 1. WG21 goofed up by delaying networking 2. C# is not developed through the International Standards Process These mistakes are being fixed though, and you will see library support in C++ catch up by leaps and bounds. And when we finally get these things (built on top of Asio/Networking TS, which has proven over and over to be the correct low-level abstraction) it will be better than C#, Java, or Rust. Compare the documentation for Vertx (vertx.io) or netty (both Java toolkits
for writing asynchronous reactive programs) with that of asio.
Yes I agree, there is a lack of documentation and especially tutorials for beginners (which I am trying to fix). This has nothing to do with whether Asio is the right portable, low-level abstraction (it is) but the lack of guidance from the author could lead you to believe incorrect things about the lib. Once I attempted to write an asio service, tried to understand the simple
example from the documentation, and I gave up. I used a thread, blocking call and CancelSynchronousIO (and I consider myself fortunate to develop for Windows only that has it).
The documentation on services is particularly scant. There is no explanation of what they are for, how to use them, best practices, or even "Frequently Asked Questions." I was able to figure it out though, but I am able to focus all of my time on it.
Asio _is_ a relatively nice wrapper around socket and serial port APIs, but that's about it
Yes now you're getting close to an accurate description. More correct would be to say that Asio provides robust abstractions of buffers, dynamic buffers, synchronous I/O, and asynchronous I/O. Networking is a subset of that (but not the only part, see signals and serial ports, and soon file I/O).
IMO. On the other hand, I could have written the same wrappers around native APIs in two days and not haul along what I consider the baggage of technical debt that is asio in the codebase.
There is no API technical debt. That is to say, that the interfaces are modern and up-to-date. You could argue that particular implementations such as Boost.Asio have extra code to provide C++03 compatibility, but this has nothing to do with the soundness of the design. As these interfaces are part of a TS, there will be implementations in gcc and clang appearing shortly (if not already so). These will be written from scratch so there will be no support for older C++.
Conclusion, if any: many people just want/need thread per client and synchronous IO for simplicity, but until asio/networking TS provide for timeouts and cancellation of synchronous requests, it's a wrong tool for those people, me included.
If you define struct sync_socket { net::io_context ioc_; net::ip::tcp::socket_; net::steady_timer timer_; ... template<class Buffers> std::size_t write_some (Buffers const& b); }; You can easily implement timeouts in `write_some` thusly: template<class Buffers> std::size_t sync_socket:: write_some (Buffers const& b, error_code& ec) { std::size_t n; timer_.expires_after(seconds(30)); timer_.async_wait( [&](error_code ec) { socket_.cancel(ec): }); socket_.async_write_some(b, [&](error_code ec_, std::size_t n_) { ec = ec_; n = n_; }); ioc_.run(); return n; } Now, you can use `sync_socket` with all the usual sync write operations like `net::write`, and get timeouts. Thanks
On Fri, 15 Mar 2019 at 14:44, Vinnie Falco via Boost-users
The documentation on services is particularly scant. There is no explanation of what they are for, how to use them, best practices, or even "Frequently Asked Questions." I was able to figure it out though, but I am able to focus all of my time on it.
How much time I did lose looking at "services" when I actually only needed a "composed operation"... (at that point the ASIO docs had one example of a service, but none of a "composed operation"; now it has of both, so it's improving).
If you define
struct sync_socket { net::io_context ioc_; net::ip::tcp::socket_; net::steady_timer timer_; ... template<class Buffers> std::size_t write_some (Buffers const& b); };
You can easily implement timeouts in `write_some` thusly:
template<class Buffers> std::size_t sync_socket:: write_some (Buffers const& b, error_code& ec) { std::size_t n; timer_.expires_after(seconds(30)); timer_.async_wait( [&](error_code ec) { socket_.cancel(ec): }); socket_.async_write_some(b, [&](error_code ec_, std::size_t n_) { ec = ec_; n = n_; }); ioc_.run(); return n; }
Now, you can use `sync_socket` with all the usual sync write operations like `net::write`, and get timeouts.
When you explain it it actually sounds obvious... and I feel silly for not seeing it. You do that a lot to me.
participants (14)
-
Cristian Morales Vega
-
Damian Jarek
-
Dmitrij V
-
Emil Dotchevski
-
Gavin Lambert
-
james
-
Jon Kalb
-
Klebsch, Mario
-
Miguel Ojeda
-
Niall Douglas
-
Olivier Tristan
-
Robert Ramey
-
Stian Zeljko Vrba
-
Vinnie Falco