patterncppMinor
lockbox (a boost-like container)
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,
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
Update: As mentioned by Loki in the comments you can use
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
box1andbox2and 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 = box2in order to assign the contents ofbox2tobox1
- Thread B executes:
box2 = box1in order to assign the contents ofbox1tobox2
- Now if the timing is right the following could happen:
- Thread A executes the assignment operator and locks the right hand side first so
box2is 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
box1is now locked.
- Thread B tries to lock the left hand side which is
box1but that's already locked so it waits
- Thread A picks up again and now tries to lock the left hand side which is
box2but 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.