9 ms·
are you an AI programmed to be annoying? I gave you great information on how to make your queues thread safe again. I don't know where this expectation comes
by CyberDildonics 1mo ago
are you an AI programmed to be annoying?
I gave you great information on how to make your queues thread safe again.
I don't know where this expectation comes from that you can reply to me and I can't reply to you. If you don't want to continue you don't have to reply.
This is totally irrelevant because if you paid attention, the problem wasn't even threads. It was concurrency, more abstractly. Iterator invalidation based on the "manifested" order of execution.
This is really just mixing terms. You aren't going to notice all your concurrency bugs without threads. If you're holding a raw pointer to an internal resource of a data structure while other threads can modify it, you aren't going to see all your bugs until multiple threads are modifying and reading from the queue.
If the threads have unpredictable lifetimes, then yes, some form of reference counting is indicated.
It isn't about threads having unpredictable lifetimes, they could all be running at the same time and have predictable lifetimes.
In some cases your data structures might even be so messy that you end up with cycles.
Then don't do that.
Thanks for the pointers to what is probably 5K lines of C++ boilerplate.
Lots of people get a lot of good out of them.
But I have written half a dozen concurrent queues myself,
You might want to benchmark and test those bad boys thoroughly if you think you can hold a raw pointer into
a data structure that can change from other threads. If you use a template you won't have to rewrite them over and over.
Also don't forget that allocations can lock and that your double allocations of the struct and data in a data structure can amplify that.
If you want an approachable source that leaves out the fluff,
Thanks, but I haven't made the same assumptions about raw pointers in concurrent data structures then blamed the STL, so I haven't had the bugs that you're talking about here.
- jstimpfle 1mo ago> I gave you great information "Great" is quite debatable. In any case, nothing I hadn't already known. > You aren't going to notice all your concurrency bugs without threads. True, but my problem was neither proper locking / thread safety, nor reference counting. You still felt the need to explain to me because you don't realize the problem isn't that I don't understand what you say. The problem is that you don't understand / don't want to accept what I say, and you prefer assuming I'm talking out of my ass. > Lots of people get a lot of good out of them. Well if they don't want to create and understand their own but instead prefer to invite tons of unnecessary boilerplate to the point where you can't find the actual functionality -- good for them. > You might want to benchmark and test those bad boys thoroughly if you think you can hold a raw pointer into a data structure that can change from other threads I DO NOT THINK THAT. Why do you keep implying that my thinking is wrong? That is so arrogant of you. Reference counting (how you keep something alive) is completely orthogonal to the queue's functionality. In my case, the queue was used as a "global" kind of object, so no reference counting needed. > Also don't forget that allocations can lock and that your double allocations of the struct and data in a data structure can amplify that. In general I avoid unnecessary allocations, where did I imply making "double allocations"? What I argued is that indirection may not be as bad as you think, may in fact be the correct way to make your program both more maintainable and more performant. I try to organize memory allocation upfront to keep memory local to subsystems, which reduces or avoids contention in many cases (for example there might be only a single thread doing allocations for a subsystem at a time). > Thanks, but I haven't made the same assumptions about raw pointers in concurrent data structures then blamed the STL, so I haven't had the bugs that you're talking about here You're arguing all the time for just buying into stuff as a cargo cult, I'm only trying to describe how much weight all this ceremony introduces, which makes it more painful to maintain, makes it more likely to introduce bugs, and harder to find bugs. Don't explain basic C++ stuff to me. I understand it. What I'm saying is that this is not the best way to write things at all. There's a lot of "abstraction" slop that brings more downsides than upsides. But I'm sure you never run into this type of problem... Good for you!
- CyberDildonics 1mo agoIn any case, nothing I hadn't already known. I at least showed you cppreference so you can look up the data structures and their guarantees. True, but my problem was neither proper locking / thread safety, nor reference counting. But you did blame the STL for concurrency bugs so there must have been something. prefer to invite tons of unnecessary boilerplate I'm not sure a heavily tested and fast lock free queue library is boilerplate. You used C++'s dequeue, wouldn't that be boilerplate by this bizarre definition? Wouldn't everything? I DO NOT THINK THAT. Why do you keep implying that my thinking is wrong? That is so arrogant of you. That's good, I must have misunderstood since you were blaming concurrency bugs on the standard library data structures. In my case, the queue was used as a "global" kind of object, so no reference counting needed. I think you might have misunderstood that the reference counting is for anything returned from a data structure so that it can see that something is being used and not modify it. The reference counts of the returned object are actually pointers to the internal reference counts in the data structure, like checking out a library book. This is not how I would do a queue though and not how the queues I linked work. They copy data in and out and are best used for small data. Large amounts of data can be handled in a different way by a different structure. where did I imply making "double allocations"? The C style allocation of structs to pointers then allocation of the underlying data is two allocations and double indirection. This isn't good for multi-threading because allocations have their price, just a heads up. You're arguing all the time for just buying into stuff as a cargo cult I don't think so, I've made a lot of stuff that works. Don't explain basic C++ stuff to me. I understand it. Well.. we all get bit by standard library assumptions from time to time and need to read the docs, but it just isn't a concurrency problem with the STL. There's a lot of "abstraction" slop that brings more downsides than upsides. Claims without evidence unfortunately. The fast concurrent queues I linked are great and using destructors to keep track of reference counts is great. Both are minimal. I would say inserting resource management manually into every function is boilerplate.
- jstimpfle 1mo ago> I at least showed you cppreference so you can look up the data structures and their guarantees. Why do you show this to me??? Don't you think I know it? > But you did blame the STL for concurrency bugs so there must have been something. I explained the problem at your request: I pointed out that this was a bug I introduced myself, but yes, I blamed it on STL (and its complexity). Again, it was abstractly a "concurrency" problem, and it did manifest when using multiple threads, but the problem was not due to missing mutex nor reference counting. Instead it was because of a kind of iterator invalidation that I had not expected at the time of banging out some shitty iterator code. The uniform iterator abstraction made it arguably way easier to miss. > You used C++'s dequeue, wouldn't that be boilerplate by this bizarre definition? yes absolutely, std::deque is a super bad offender, in many ways. I advise against using it. More than against using STL in general, although I don't recommend that either. > I think you might have misunderstood that the reference counting is for anything returned from a data structure so that it can see that something is being used and not modify it. The reference counts of the returned object are actually pointers to the internal reference counts in the data structure, like checking out a library book. No I have not misunderstood anything. Again you're coming back to your arrogant pattern. There are many ways to implement reference counting. When doing it manually instead of with e.g. std::shared_ptr, it's quite common to embed the count inside the object, not make it a separate allocation. > This is not how I would do a queue though and not how the queues I linked work. They copy data in and out and are best used for small data. Large amounts of data can be handled in a different way by a different structure. There are many types of queues. There are queues that buffer two elements, there are queues that buffer millions of elements. There are queues that get persisted (like a database). There are queues that are ephemeral. There are queues that have multiple produces and/or consumers, there are queues with only a single producer/consumer. There are queues that get locked. There are queues that get accessed with atomics only. There are queues that store elements directly. There are queues that store elements buffered in chunks or packets... "Queue" typically implies FIFO but not always. > The C style allocation of structs to pointers then allocation of the underlying data is two allocations and double indirection. But I rarely don't do that. And that's not implied by "C style" at all. And importantly, structure (pointer indirection) doesn't imply allocation strategy. > Well.. we all get bit by standard library assumptions from time to time and need to read the docs, but it just isn't a concurrency problem with the STL. I have explained the issue at length. So please stop repeating made-up contradictions. > Claims without evidence unfortunately. The fast concurrent queues I linked are great and using destructors to keep track of reference counts is great. Both are minimal. Claims without evidence unfortunately... Except, it's quite evident that there is a lot of code in them and it's hard to find out how anything works because of that. How would I even evaluate if the queue is doing what I need? That queue functionality implemented here should probably be a tenth of that code (!). > I would say inserting resource management manually into every function is boilerplate. Good, because I don't do that at all. And I criticize that RAII is a system that sneaks in resource management _implicitly_ everywhere, which is not visible in the source code. That's why I prefer C-style: making it explicit, allowing me to find the optimal structure that avoids unnecessary fluff in the first place.