|
Wordle 352 4/6
⬛⬛⬛⬛⬛
🟨🟨⬛⬛⬛
⬛🟩🟩🟩🟩
🟩🟩🟩🟩🟩
|
|
|
|
|
Wordle 352 4/6
⬛⬛⬛🟩⬛
⬛⬛⬛🟩🟩
⬛🟨⬛🟩🟩
🟩🟩🟩🟩🟩
Get me coffee and no one gets hurt!
|
|
|
|
|
Wordle 352 5/6*
⬜⬜⬜⬜⬜
🟨⬜🟨⬜⬜
🟨⬜🟨⬜⬜
⬜🟩🟩🟩🟩
🟩🟩🟩🟩🟩
|
|
|
|
|
Wordle 351 6/6
⬜🟨⬜🟨⬜
🟩⬜🟨⬜⬜
🟩🟩⬜⬜🟨
🟩🟩🟨⬜⬜
🟩🟩⬜🟩⬜
🟩🟩🟩🟩🟩
Just managed it.
|
|
|
|
|
Wordle 351 3/6
🟨⬜⬜🟨⬜
⬜🟨⬜⬜🟨
🟩🟩🟩🟩🟩 I think I was lucky rather than skilled today!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Wordle 351 4/6
⬜⬜⬜⬜🟨
⬜⬜⬜🟩🟩
⬜🟩⬜🟩🟩
🟩🟩🟩🟩🟩
Software rusts. Simon Stephenson, ca 1994. So does this signature. me, 2012
|
|
|
|
|
Wordle 351 3/6
⬛🟨⬛⬛🟨
🟨🟩🟩⬛🟨
🟩🟩🟩🟩🟩
|
|
|
|
|
Wordle 351 4/6
⬛⬛⬛⬛🟨
🟨⬛⬛🟨🟨
🟨🟨⬛⬛🟨
🟩🟩🟩🟩🟩
|
|
|
|
|
Wordle 351 4/6
⬛⬛⬛⬛⬛
🟩🟩⬛🟩⬛
🟩🟩⬛🟩⬛
🟩🟩🟩🟩🟩
Get me coffee and no one gets hurt!
|
|
|
|
|
Wordle 351 5/6
🟨⬜⬜🟩⬜
⬜⬜⬜⬜🟨
⬜⬜⬜⬜⬜
🟨🟨⬜⬜⬜
🟩🟩🟩🟩🟩
|
|
|
|
|
I'm in the middle of--I hope it's the middle, not still near the beginning--of targeting software for Linux.
One test involves creating a new thread that furiously generates work to load the system. So I enter the command to initiate it, and my system generates a log saying that the thread has been deleted. It has barely been created, so what's going on?!
I also have a thread that performs a brief task when the system starts up, and then exits. Debugging reveals that the pthread library immediately reused the identifier of the thread that exited for the new thread that I was creating! I would've given 100:1 odds against this, but evidently no one who worked on this library had experience with anything other than TOY SYSTEMS!
|
|
|
|
|
|
If the identifier corresponds to a block in LIFO pool, I can see it happening. But why not just a global unsigned int that gets incremented on each thread creation? They should definitely have known better.
The issue also exists in distributed systems. Send a request from something identified by A, free A, reallocate A, and the new user of A gets a spurious reply to the original request. Unless you're sure that A won't get reallocated for a while, it has to be supplemented with a sequence number to distinguish incarnations.
|
|
|
|
|
Greg Utas wrote: But why not just a global unsigned int that gets incremented on each thread creation? If you are allowed to use non-posix functions you should have a look at pthread_getunique_np which does exactly that on some implementations. You could alternatively use pthread_setname_np to name your threads with a GUID.
|
|
|
|
|
Interesting, thanks. Ubuntu's <pthread.h> has no pthread_getunique_np but does have pthread_getname_np and pthread_getname_np . That would put the burden on me to generate unique names, which is OK. But I use the pthread_t from pthread_self as a key into a std::map , and having to replace that with a string is gross.
|
|
|
|
|
Have looked at std::thread[^]? It has an id member (without any promise of uniqueness) but you could derive your own class and make a unique id.
Mircea
|
|
|
|
|
I did indeed look at <thread> and almost decided to wrap it, which would have been a fairly straightforward evolution. But I decided not to:
- It wouldn't eliminate much code. (I'd started before C++11, so porting to Linux finally did get me to convert to
<mutex> , <condition_variable> , <chrono> , and <filesystem> instead of writing Linux targets for what I use from them.) - It doesn't let you specify the stack size. Most platforms have a default stack size that is far larger than what makes sense to me.
- It doesn't let you create a thread that is already detached. Not a big deal, but I create them detached.
thread::id is opaque. I want an identifier that I can see and use as a std::map key. Actually, it supports stream operator<< and std::map , but it annoyed me. Figuring out how to support <chrono> in my static analysis tool, given its dependence on <ratio> and its rat's nest of template metaprogramming, already had me in a pissy mood.thread::native_handle_type is platform-specific.- If you wrap a simple thread with a
Thread object that provides lots more capabilities, a new thread can start to run before its Thread object has been fully constructed. This, not to understate it, is a Very Bad Thing, and I didn't want to end up solving it again if <thread> broke my solution.
|
|
|
|
|
Couldn't agree more.
FWIW, for the last point you raised (thread starting), you could keep a pointer to a std::thread inside your object and actually create it in a method like start() or run() . Not that this would make the code any smaller. Otherwise, I agree with you that starting the thread in constructor was not a wise decision.
Mircea
|
|
|
|
|
I start a native thread in my Thread constructor too, so maybe I'm not wise either! I need to think about your suggestion. Which is, if I understand correctly, to create the Thread object first and then invoke a Thread::run function to actually create the native thread. That sounds appealing, even though I dislike two-phase construction. There would also be a window in which the native thread could start to run before its handle got written into the Thread object. You'd think that window would be very tiny, but given how vigorously Windows and Linux context switch, I'd want to close it.
|
|
|
|
|
I was thinking more that the user of Thread object invokes the Thread::start function when the thread is needed. This is a nice way of passing the buck to the user if he started the thread at an inappropriate time
If you want to take a look at my complicated way of starting threads you can see it here mlib/thread.cpp - neacsum/mlib[^] (it's Windows only).
The steps are:
1. Creator makes new thread with static function as entry point. Passes *this as argument.
2. New thread starts suspended to avoid any race conditions (existed in older Windows versions - haven't checked in Win10).
3. Creator resumes suspended thread and waits for a "created" event.
4. New thread signals the created event and waits for the "started" event.
5. Code in creator thread invokes thread::start() function. This one signals the "created"event and new thread runs.
If you want to see it in action, you can take a look at the code it this article: Producer/Consumer Queues in C++[^]
Mircea
|
|
|
|
|
My approach is similar but has no concept of creating a thread until it needs to run, so start would always be invoked immediately. Almost all the threads are daemons.
|
|
|
|
|
Greg Utas wrote: I use the pthread_t from pthread_self as a key into a std::map, and having to replace that with a string is gross I didn't notice this before. A GUID is the same width as a 128-bit integral type. You don't need to use a std::string as your key.
Check to see if your compiler supports std::map<__int128>
|
|
|
|
|
The signatures for these are
extern int pthread_getname_np(pthread_t __target_thread, char *__buf, size_t __buflen);
extern int pthread_setname_np(pthread_t __target_thread, const char *__name); No GUIDs, unfortunately.
EDIT: I did manage to fix the problem without much difficulty, so things are progressing. But soon I'll be testing stuff that relies on -fnon-call-exceptions, which could lead to the whole thing getting binned. 🤞
|
|
|
|
|
You will be amused to learn that I'm hoping to run all threads at the same priority. I think it will work, with only a minor drawback. The current design is overly defensive given how vigorously both Windows and Linux context switch and has largely become a case of "it's always been done that way."
|
|
|
|
|
Greg Utas wrote: You will be amused to learn that I'm hoping to run all threads at the same priority. Yes I know, I've been spying paying attention to your latest endeavors.
It crossed my mind that you could use pthread_setname_np() to name your threads Randor so that you can have the satisfaction of terminating me while you write your Linux libraries.
|
|
|
|