|
With a background thread they might not even need to use a future; just do the blocking op and wait, that’s a big part of what threads are for!
|
# ? Oct 5, 2023 00:44 |
|
|
# ? Jun 3, 2024 10:48 |
|
Rocko Bonaparte posted:I think the main motivation for a promise is to have something run after something else finishes and has a value to spit out, but I have never used them in C++. It's kind of strange here! I figure you'd pass a function to the promise and "chain" it. A promise-future pair are just the two ends of a communication channel that you may then pass around your code. One piece of code can write a value to the promise, which a completely unrelated piece of code somewhere else may then retrieve by reading from the future. If you just want to poll the status of the future then std::future::wait_for(0s) != std::future_status::timeout and similar works. std::future::is_ready() is in the works (and exists in std::experimental). It has been in the works for a decade; not entirely sure what's going on with it. std::future::wait_for(0s) may still technically block, but I believe it's lock-free in the major implementations if atomic_flag is lock free. Xerophyte fucked around with this message at 01:23 on Oct 5, 2023 |
# ? Oct 5, 2023 00:46 |
|
In Java you have a ListenableFuture that allows you to get a callback on a particular executor when the value becomes available, which means you're not burning an entire os thread for every single future you're waiting on. It looks like C++ was considering standardising a future::then() method which would allow much the same thing, but it doesn't seem to have happened for some reason? Maybe they figured that coroutines were a better way to accomplish that goal?
|
# ? Oct 5, 2023 01:19 |
|
Yeah I'm more looking for the "here's a chunk of code to run when the future's ready" version. Currently we can end up with dozens of threads all blocked. Possibly unbounded, actually, I'd have to double-check. And there's certainly other ways to stop doing that, I'm hoping this one is the least painful Thanks!
|
# ? Oct 5, 2023 01:19 |
|
Is this more of an async/await situation than a parallelism situation? Like you have a bunch of small computations to do before and after blocking operations and you want something to schedule in a smart way? https://en.cppreference.com/w/cpp/thread/async looks to be pretty recently added, though…
|
# ? Oct 5, 2023 01:31 |
|
It's not really clear what you're asking for - you want something to happen after something else completes, but on what thread? If you want it to happen on a specific thread, that waits until that specific thing is done, you want a std::future/promise. If you want it to happen on a specific thread or pool of threads, that does various pieces of work after various events, you want a dispatcher sort of pattern, where the thing that completes the work pushes a lambda (or some other sort of object indicating the work to be done) onto a queue that wakes up a thread to consume from the queue if one is blocked, or if all the threads are busy then the action will wait 'til one of those threads is done with what it's already doing. If you want it to do the action on the same thread as the one that was doing the thing you were waiting for to complete, you probably just want to have that thread call a callback when it's done. I don't think there's a standard library for dispatcher-queue, but it's pretty simple, you guard a deque with a mutex and a condition variable, lock-pushback-signal-release to add work, lock-popfront-release to consume work, if there's nothing to popfront you wait for the condition variable and try again, plus you have to figure out your own termination condition and termination behavior.
|
# ? Oct 5, 2023 01:40 |
|
std::future is missing then(), which is the thing that actually makes futures interesting and useful and it's kinda pointless as a result. It's relatively easy to roll your own future type which actually is useful.
|
# ? Oct 5, 2023 02:26 |
|
Yep more async/await. It's a pile of C++ that an Android and an iOS app both call into. That pile of C++ in turn can call back out to the platform for database and network operations. A call sequence might be code:
I'd like to change the URL session, database, and topmost (in this case, logIn()) calls to return futures/be coroutines/something. The URL session or database will complete the future on some arbitrary thread and that's ok with me, they've got their own dispatchers and thread pools, I just don't want to park a thread waiting for them to finish.
|
# ? Oct 5, 2023 02:34 |
|
Jabor posted:In Java you have a ListenableFuture that allows you to get a callback on a particular executor when the value becomes available, which means you're not burning an entire os thread for every single future you're waiting on. Plorkyeran posted:std::future is missing then(), which is the thing that actually makes futures interesting and useful and it's kinda pointless as a result. It's relatively easy to roll your own future type which actually is useful. Woops, missed these. "Where is std::future::then()" is the concise way to ask what I'm going for. Seems like coroutines are the closest? The platform barrier is handled by Djinni and it has a future that works at that barrier, but I didn't necessarily want to spread Djinni stuff all throughout the C++ code. It seems like a fairly generic future type with a then() though, so maybe it's the right thing to use.
|
# ? Oct 5, 2023 02:44 |
|
Yeah I was thinking of the JavaScript version, specifically: https://promisesaplus.com/ I think I have seen this acknowledged and used elsewhere. Python's Future class is a future, but let's you attach a callback to run something when the value is filled. I was surprised C++ was cooking something else.
|
# ? Oct 5, 2023 04:40 |
|
Rocko Bonaparte posted:Yeah I was thinking of the JavaScript version, specifically: 1. assign the 'then' action to a thread that is waiting for it (that's what it effectively does already) 2. do the 'then' action on the thread that's completing the future (this is just a callback, you don't need a future/promise at all for this, just pass in a std::function and name it "then") 3. post the 'then' action to a threadpool or something (which requires a whole dispatcher setup that doesn't by default exist) 4. start a whole new thread just to handle the 'then' action. 1 is what it already is, 2 doesn't need a future at all, 3 subtly requires a bunch of prerequisites, and 4 is a performance disaster. I guess the thing that we're familiar with from Javascript-style 'then' is that there's just one thread and at some point it's in a "not doing anything" state, and that's when any async-resolved 'then' actions get resolved. But C++ doesn't have a "not doing anything" state by default, so there's no common place to resolve these things.
|
# ? Oct 5, 2023 05:07 |
|
The whole point of an abstraction is to abstract things, saying "you don't need the abstraction to handle this scenario because you can just reach in and do it manually" makes you wonder why anyone would bother with the abstraction you've defined instead of picking one that actually works. If you have 2, you can implement 3 on top of it by having the code that's executed directly on the completing thread just post the actual continuation to whatever dispatcher you happen to be using, so while it would be nice to solve 3 it's not really a requirement for the thing to be useful. Jabor fucked around with this message at 05:31 on Oct 5, 2023 |
# ? Oct 5, 2023 05:29 |
|
roomforthetuna posted:I found this absence of 'then' surprising too when I first encountered C++ promises/futures, but now that I'm comfortable with them I don't understand what it is that you'd expect a future-with-then to do - it really has to do one of: I think its just people getting confused at how other languages handle this stuff. What they actually want is a packaged way to throw a chunk of code that sets a promise into its own thread that, once it completes, then executes a callback in that same thread. In JS you'll have async APIs that return a Promise that you can then bind a callback to with .then(). The goal is to throw that code off into its own thread and forget about it. The .then() is supplied to take the results of that Promise and choose how to handle them at the time of completion. You don't care about when all of this happens. There is nothing built into the STL to handle this, but you could design your own system to do this using std::promise and std::future. Nalin fucked around with this message at 05:39 on Oct 5, 2023 |
# ? Oct 5, 2023 05:36 |
|
If you were in (sufficiently updated) Windows Land you could use Microsoft's Concurrency Runtime, which includes a template library with then support.
|
# ? Oct 5, 2023 05:45 |
|
then() also returns another future for the result of running the closure passed to then(), so it lets you get away from increasingly indented callbacks. I agree it doesn't look super useful with a single call. And in my case, I have a future type with then() that translates between C++ and Java/Objective-C, so I figure if I can use something similar within C++ it'll be overall easier to follow. (I can't add similarly translatable callbacks without a ton of work.)
|
# ? Oct 5, 2023 06:03 |
roomforthetuna posted:2. do the 'then' action on the thread that's completing the future (this is just a callback, you don't need a future/promise at all for this, just pass in a std::function and name it "then") The other issue with this is, what if the promise has already been filled when you call 'then'? Which thread does the 'then' then resolve on? It can't be the thread that filled the promise, it has probably already moved on to some other work, or maybe simply exited. It can't be the thread calling 'then' on the future because then you might suddenly be blocking in places you don't want to block. The 'then' really has to be supplied at promise-future pair creation time to be able to guarantee it running on the promise-filling thread. And I just want to note that I personally found std::promise/future a very convenient way to express waiting for a thread to produce a result and then consume it, without needing to all the logic with lower level primitives.
|
|
# ? Oct 5, 2023 07:26 |
|
It just happens in the thread calling then(). Why would you be blocking if the promise has already been filled? If it's been filled then by definition the value is already available. You do need to write the code inside the callback under the presumption that it could be called in either place though.
|
# ? Oct 5, 2023 08:06 |
|
In charge of putting ci/cd for a bunch of C/C++ mixed projects. Anyone have any experience with linters? I really want to do something like force every public function to have doxygen style comments, and enforce some level of style but that can be adapted per-project.
|
# ? Oct 5, 2023 12:33 |
|
Caveat Emptor: std::async basically spawns a new pthread in every stdlib implementation I worked with. We sped up a microservice platform by several orders of magnitude because 80% of the CPU time was spent on std::async's loving pthread_create. It was a good eye opener as to why most C++ shops seem to use or roll their own thread pool lib.
|
# ? Oct 5, 2023 13:21 |
|
go play outside Skyler posted:In charge of putting ci/cd for a bunch of C/C++ mixed projects. Anyone have any experience with linters? I feel like a style check would be the absolute last thing on my list, c/c++ users are super finicky about that poo poo ime and will fight it tooth and nail I think if you can get a build+test+artifact store happy per target and make it easy to run compiler upgrade tests, platform changes, etc that is amazing and much better than the crap I regularly deal with
|
# ? Oct 5, 2023 14:07 |
|
Beef posted:Caveat Emptor: std::async basically spawns a new pthread in every stdlib implementation I worked with. We sped up a microservice platform by several orders of magnitude because 80% of the CPU time was spent on std::async's loving pthread_create. It was a good eye opener as to why most C++ shops seem to use or roll their own thread pool lib. It is literally standardized to do that. Only MSVC's std::async is useful and that's because they break standard there.
|
# ? Oct 5, 2023 14:46 |
|
std::execution aka "senders and receivers" looks neat, but I’ll probably be in retirement before it's released https://www.youtube.com/watch?v=hLbhNTRKafo
|
# ? Oct 5, 2023 15:04 |
|
nielsm posted:And I just want to note that I personally found std::promise/future a very convenient way to express waiting for a thread to produce a result and then consume it, without needing to all the logic with lower level primitives. Likewise. My thread worker pool has a business end of C++ code:
I can see how, for promises, having a lambda you run on promise::set_value would be useful, but it seems quite easy to implement in a custom promise/future pair that wraps the std ones.
|
# ? Oct 5, 2023 15:41 |
|
Xarn posted:It is literally standardized to do that. Only MSVC's std::async is useful and that's because they break standard there. std::launch::async is allowed to be implemented however the implementation wants, as long as it doesn't run it on the same thread
|
# ? Oct 6, 2023 04:22 |
I'm going to implement it by taking out an ad on a Times Square screen with a QR code with a link to the async code recompiled to js, and whenever someone hopefully completes it they upload the result somewhere I can poll. No wait, that will make shared memory and such really annoying. Never mind.
|
|
# ? Oct 6, 2023 05:23 |
|
nielsm posted:I'm going to implement it by taking out an ad on a Times Square screen with a QR code with a link to the async code recompiled to js, and whenever someone hopefully completes it they upload the result somewhere I can poll. What, no messenger pigeons?
|
# ? Oct 6, 2023 09:14 |
The pigeons can be trained to direct passerbys' attention to the ad.
|
|
# ? Oct 6, 2023 09:42 |
|
b0lt posted:std::launch::async is allowed to be implemented however the implementation wants, as long as it doesn't run it on the same thread quote:If launch::async is set in policy, calls invoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...) ([func.invoke], [thread.thread.constr]) as if in a new thread of execution represented by a thread object with the values produced by auto being materialized ([conv.rval]) in the thread that called async. The thread has to be "fresh" e.g. in regards to thread local state. An implementation could in theory do a ton of work to clean up existing threads in a thread pool, but this is a significant limitation on what the implementation can do
|
# ? Oct 6, 2023 14:36 |
Is that only your own reading of "as if in a new thread of execution", or has that been confirmed/expanded upon by someone involved in writing that text? Are thread-local variables in new threads defined to be zeroed memory, or undefined value? Edit: And regardless of the answer to that (well I looked it up, they are initialized at thread startup), what is more costly? Launching a new OS thread and initializing thread local variables in it, or re-using an existing OS thread and initializing thread local variables in it? What other state does a thread have? Besides potentially not-cleaned-up ownership of OS objects, which imo is a bug in the program. nielsm fucked around with this message at 17:11 on Oct 6, 2023 |
|
# ? Oct 6, 2023 17:05 |
|
Just statically determine if the called function reads TLS before writing, and if not (which will always be the case because it can’t assume there’s anything there!) skip that part. Optimization is just not getting caught cheating after all.
|
# ? Oct 6, 2023 17:19 |
|
Jabor posted:It just happens in the thread calling then(). Why would you be blocking if the promise has already been filled? If it's been filled then by definition the value is already available. For whatever reason you're not ready to set up Y immediately. So your main thread sets up a promise for X and launches a thread to do X. Then later you do ".then(Y)" in the main thread. If X already being filled causes you to run Y on the main thread, then your entire program will freeze for the duration of Y.
|
# ? Oct 7, 2023 02:53 |
|
That is in fact a scenario where then() is not appropriate. That doesn't make it not useful for other things.
|
# ? Oct 7, 2023 03:12 |
|
Yeah, I'm pretty sure these primitives are for you to set up a digraph of tasks now that either starts working immediately or that you can dispatch later, and the runtime will then manage load-balancing on them for you.
|
# ? Oct 7, 2023 04:40 |
|
What are the downsides of statically linking libstdc++ or libgcc? I'd appreciate an authoritative source on static vs dynamic linking of these libraries in general, because I interact with tons of tools that have very questionable build processes, frequently including statically linking glibc, libgcc, and libstdc++ with -static, -static-libgcc, or -static-libstdc++. Does anyone have any textbooks and/or posts they'd recommend on this subject? I know that statically linking glibc is a bad idea because it will still need to load the system's glibc anyway, and it's possible to get weird failures. I've had a decent track record of getting small improvement PRs accepted to these kinds of tools, and I'm wondering if I should focus my efforts only on dynamically linking glibc, or if it's worth trying to find a portable way to dynamically link libcc and libstdc++ too? The best thoughts I've found on this are a decade old Stack Overflow discussion that links a blog post from 2005: https://stackoverflow.com/questions/13636513/linking-libstdc-statically-any-gotchas. As much as I would like efforts to move towards shipping your dependencies as dynamic libraries and using LD_LIBRARY_PATH to use them, I feel like baby steps are the easiest place to start. Hell, I also have started seeing more and more Docker containers that just have statically linked binaries in them, but that's a slightly different thing. Edit: Related, I've never really worked with RPATHs or been the one to set them or think about them, but that behavior mentioned where you can set RPATH to $ORIGIN to make a binary search for .sos next to it sounds like something that I'd want most of the time. I've always envied how Windows handles .dlls compared to having to use environment variables to manage finding libraries on Linux. Twerk from Home fucked around with this message at 19:53 on Oct 27, 2023 |
# ? Oct 27, 2023 19:43 |
|
I'm working on a project that would benefit from having a good function call trace system, after reading up on stuff around the internet it seems like this is a format that can do the job:code:
code:
|
# ? Nov 1, 2023 02:34 |
|
Well, you screwed up your examples, it should be do { } while (0)
|
# ? Nov 1, 2023 02:40 |
|
PDP-1 posted:I'm working on a project that would benefit from having a good function call trace system, after reading up on stuff around the internet it seems like this is a format that can do the job: well... I think more typically you do this: code:
code:
code:
|
# ? Nov 1, 2023 02:42 |
|
I think the more salient explanation comes less from "where should the semicolon be inserted" and more from wanting your TRACE() macro to always expand to an expression statement. First, convince yourself that do { a; b; c; } while (0) is exactly the same thing as a; b; c;; the while test will fail and we'll never return to the top of the loop, so the body is executed just once. OK, so: Consider a more generalized version of your TRACE macro: maybe we want to be more or less verbose depending on some parameter, so each macro will actually be an if-statement: will the following program compile without error or warning...? C code:
code:
C code:
C code:
C code:
edit: now a question for you, OP: If we need the body of the macro to be isolated in its own scope, what's the problem with simply wrapping it in its own block, like so? C code:
Dijkstracula fucked around with this message at 03:41 on Nov 1, 2023 |
# ? Nov 1, 2023 03:30 |
|
That's a much better explanation, thanks!
|
# ? Nov 1, 2023 03:38 |
|
|
# ? Jun 3, 2024 10:48 |
|
Dijkstracula posted:Iedit: now a question for you, OP: If we need the body of the macro to be isolated in its own scope, what's the problem with simply wrapping it in its own block, like so? This has the same problem as including a semicolon at the end of a statement-like macro: users can leave off the semicolon when calling it. AND THEY WILL
|
# ? Nov 1, 2023 09:38 |