HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

lockbox (a boost-like container)

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
lockboxcontainerboostlike

Problem

So I introduced myself to templates in C++11 and I have to say it's really confusing, but also very fascinating. I just need to get my head around what happens at compile time so that I don't wind up making one line of driver code amount to a megabyte of asm...

Anyway, to teach myself I wrote a little container class. It's a container that locks and unlocks a mutex on each read/write operation on a group of data. There's still a few features I want to add to it, but that will take a lot of forced templating beyond compile-time recursion (more on this at the end).

Here is the code (updated 11/5):

```
#include
#include
#include
#include "pack_size.hpp"
#include "pack_size_index.hpp"
#include "pod_test.hpp"

template class lockbox {
static_assert(sizeof...(item_t) > 0, "empty lockboxes are not permitted.");
private:
pod_test _test; //No data, tests to make sure item_t parameters are pod-only
public: char _items[pack_size::value];
std::mutex _mutex;

template
static inline const void set_all(char* dest, const type_t& value, const args&... values) {
type_t align = (type_t)&dest[pack_size_index::value];

*align = value;

set_all (dest, values...);
}

template
static inline const void set_all(char* dest, const type_t& value) {
type_t align = (type_t)&dest[pack_size_index::value];

*align = value;
}

template
static inline const void get_all(char* src, type_t& value, args&... values) {
type_t align = (type_t)&src[pack_size_index::value];

value = *align;

get_all (src, values...);
}

template
static inline const void get_all(char* src, type_t& value) {
type_t align = (type_t)&src[pack_size_index::value];

value = *align;
}

template
static inline const void set_indices(char* dest,
typename std::tuple_element >::type& value,

Solution

I might be misreading things so correct me if I'm wrong but Ithink your assignment operator can deadlock. A lockbox only makes sense when accessed from multiple threads, doesn't it (otherwise what would be the point)?

  • Let's say we have two boxes box1 and box2 and two threads A and B.



  • Both threads get a reference to both boxes.



  • Each thread tries to copy the contents of one box into the other for some reason, so



  • Thread A executes: box1 = box2 in order to assign the contents of box2 to box1



  • Thread B executes: box2 = box1 in order to assign the contents of box1 to box2



  • Now if the timing is right the following could happen:



  • Thread A executes the assignment operator and locks the right hand side first so box2 is now locked.



  • Thread A gets interrupted or Thread B just manages to sneak in the next step on another CPU core



  • Thread B executes the assignment operator and locks the right hand side first so box1 is now locked.



  • Thread B tries to lock the left hand side which is box1 but that's already locked so it waits



  • Thread A picks up again and now tries to lock the left hand side which is box2 but that's locked so it waits



  • Kaputt



  • It's probably pretty rare but deadlocks often have the habit of being rare and non-determinsistic



Update: As mentioned by Loki in the comments you can use std::lock to obtain multiple locks. It's employing a deadlock avoidance algorithm (I guess effectively something like: "try to lock all objects, if one failed, unlock all obtained locks and try again until you succeed").

Context

StackExchange Code Review Q#33731, answer score: 2

Revisions (0)

No revisions yet.