Lots of Heads-Down Coding, and Trying to Understand Boost
Today has been a very hard day where I've been trying to get on top of boost and it's asio package as well as threads and serialization. It's a lot, I know, but the point of this project I'm on right now is to stop using all these home-grown classes and start to work in something like boost which will be a part of the standard moving forward. It'll make bringing up the next guy a lot easier if it's all using boost and then they only need to focus on the usage of boost, not the low-level threading, socket, and serialization libraries. It's an admirable goal, but it's a ton of work.
So today has been trying to figure out threading. I can appreciate the model boost has for threading, but I'd also like something that looks a little more like the Java Thread object - where you subclass it, have a method you implement and then can start this instance and away it goes. It's just targeted at different market - you could do the same thing, in theory, by having a method in a class the launch point for the thread, and then have everything in that one method.
Possible, but there will be times that a simple class like this will be handy - so I wrote it.
Next, I needed to deal with the atomic integers. Boost has the 16-bit versions, which is nice, but there were already applications of an atomic integer in the code (in the transfer statistics class) where I needed to have an unsigned 32-bit integer. So boost was not going to get me where I needed to be. Additionally, I really haven't liked the very C-like usage of a lot of these atomic operations on non-atomic data types. Why not just make a class that is an atomic 16-bit (and 32-bit) unsigned integer? So I did that as well.
What I ended up using were the GCC primitives which did everything I really needed - but it took some thinking to figure out. For example, with only:
T __sync_fetch_and_add (T *ptr, T value, ...); T __sync_fetch_and_sub (T *ptr, T value, ...); T __sync_fetch_and_or (T *ptr, T value, ...); T __sync_fetch_and_and (T *ptr, T value, ...); T __sync_fetch_and_xor (T *ptr, T value, ...); T __sync_fetch_and_nand (T *ptr, T value, ...);
how do you do the "atomic set"? It struggled with this for a while until I also saw:
bool __sync_bool_compare_and_swap (T *ptr, T oldValue, T newValue, ...);
and then it hit me: I just want to make sure that it always compares as "equal". Easy:
auint32_t & auint32_t::operator=( const uint32_t aValue ) { __sync_bool_compare_and_swap(&mValue, mValue, aValue); }
So I'm using the idea that the existing ivar is always going to equal itself, and because of that, we'll have a swap that puts the new value into place. Hey, it's not rocket science, but it wasn't clearly laid out, either.
In the end, I had two really nice atomic unsigned integer classes with all the nice operators overloaded so you can do the prefix "++" and the postfix as well, and the sum and difference. It's all there. They look and act like their non-atomic counterparts, but they are thread-safe without using any locks.