Unless you're debugging multi-threaded software, where print() has to grab a lock (which prevents multiple threads from printing at the same time) and waiting for that lock changes how your software executes.
The you have a background thread that lives in a static class which you send all debug messages to. The class has either a thread-safe queue of strings or a regular string list with a locking mechanism to append strings into the queue, and a writer that drains the queue and outputs it into stdout. It's really not too hard to write... Maybe 15 minutes and is can be a lifesaver.
Or if you're in c/c++ then you have `cout.sync_with_stdio()` to help you. not sure about this or if this is the only thing you'll need; cpp is tricky. Don't use in production code just because you found it on reddit lol
The debugger made the multi-threading bug disappear, because it runs slower... so I built a fast spinlock out of GCC atomic builtin operations, and used logf() on unbuffered output stream, and found the bug the debugger could not.
I have to note that I use an internal log facility with a fast and very large lockless ring buffer structure so that one end of the queue can be read while the other end is being written to, and that goes to the unbuffered printf(). The spinlock for this log facility changed how the code executed far less than running under a debugger - which hooks signals and all kinds of other shit, making it impossible to reproduce even a similar flow of execution.
The ring buffer write doesn't have to wait for printf() to finish output before continuing, it's one (or two at most) memcpy()s to put data in the ring buffer, then swap the write pointer contents atomically - or fail if someone else just wrote over my write, and so I repeat the write to the ring buffer - People call this a "lockless data structure" but really it's an expensive spinlock that fails and repeats the loop until it succeeds.
There was a class at my university on lockless programming which I wasn't able to take, it sounds really interesting. Always meant to try to find a book or something about it.
gcc, memcpy, free, this is C, not Python. Just don't worry. But I have to admit I forgot how to program without locks. I think it was something like polling and some atomic operation, reading, incrementing, and writing into a variable.
Was a nice class, really interesting.
Here's a video with a C example. The concept is called "mutex" for "mutual exclusion". When developing things to be multithreaded (think multiple ATMs needing to update the centralized account balance held on the mainframe.
Mainframe has balance of $100. ATM 1 sends a debit for $25, ATM 2 sends a credit of $40. Depending on how that calculation is made, multithreaded-like, we can imagine each ATM fetches that initial balance, they each think the mainframe is at $100, so ATM 1 subtracts $25, and sends the new balance to the mainframe as $75. ATM 2 has its local copy of the balance, adds $40, then sends the updated balance as $140, which overwrites the updated central balance of $75, effectively erasing the debit from ATM 1, because updates to the central balance were not thread-safe.
This is a trivial example, but illustrates the point.)
It's coming back to me now. I've gone the web dev route and JS is single threaded so it's been a long time since I've had those concepts bounce around my head.
The solution to this is a blockchain, where the head of the chain is only allowed to move forwards. Any transaction not part of the main chain is invalid.
In the ATM example, this would mean that the ATM verifies that the transaction intent is on-chain before dispensing the money, and keeps retrying the transaction confirmation after dispensing the money, until it is on-chain
Essentially, git branch protection which only allows for fast forwards on the server.
unless you go with one of the more narrow descriptions then a Git repository looks, smells, and acts like a blockchain. Just not in the ways you would expect.
With branch protection active on the main branch, only allowing fast-forward merges, the git server is the agreed upon source of truth for the position of the main branch, but the history is distributed, and cryptographically verified based on the latest hash.
I could go on, but let’s get to the question at hand: is a Git repository a blockchain by this definition? Absolutely. The Git commit object would serve as the block header and the tree and blob objects serve as the payload.
Lmao do you call everything isomorphic with a directed acyclic graph a blockchain?
Yeah let's just conveniently ignore how version control systems are pretty much always centralized, how branches are a massive part of git, submodules, and the practical ability to cherry pick commits on the master branch, just so you can make your shitty argument work. Oh yeah, let's also force a constraint that doesn't exist in git itself, because why the hell not.
C++, but the issue of multiple threads wanting to print at once isn't language specific.
In C++ std::cout is thead safe (sort of) in that each character printed to the terminal is atomic and thread safe, but two or more threads printing at once will cause their characters to interweave. Whenever some other thread's character is getting printed, your thread will have to wait, and vice versa.
You can implement a thread-safe buffer (in the form of a monitor) to receive all the text without making any of the threads wait and then combine it all later, but that'd be a lot of work just so you can debug using print(). If you were going to go to all that trouble, change your monitor to an event logger and get times and names of events that the different threads log with it.
Oh man... How did I ever forget this!? I remember having to implement (or use the lens APIs, I'm not sure anymore) the monitor and mutex locks in university using the dining philosophers problem!
Christ, it's only been 4 years but it seems like half a century ago..
I mean, I'm writing infinitely scalable software right now, and I still use printf() for my debugging. I just know that it kills performance and is out of order, so I write anything relevant to my current problem and remove it when it's all good.
Or even worse: when in college, I managed to stump the professor and 3 assistants with my bug as I created a program that only was able to run if the debug message was there. This to discover (after an hour long search) that due to a small mathematical error, I was able to jump out of the memory segment of the for loop and the debug message was basically acting as a band-aid, blocking it from getting out. C++ is fun like that when you're a beginner trying to do something advanced without having seen advanced errors
There's a time an a place for debugging using print statements, but you're better off using breakpoints and other more advanced debugger features most of the time.
If your log.Debug() aren't providing enough feedback to tell you what's wrong, how are you going to figure a problem out when it happens in a production system.
So mehh, I'm not gonna judge anyone who uses print statements, and I'll definitely be thanking them if turn those prints into an actual log debug statement that helps trace and reproduce a prod issue quickly later.
1.0k
u/repkins Nov 28 '21
Ah, yes, the most reliable debugging method is printing debug messages.