Linux kernel datastructures - linux

I am new to module writing and need a circular buffer[1] and a vector. Since the Linux kernel apparently provides some data structures (lib) (list, trees), I was wondering if there is a vector equivalent?
While I think I am well capable to write my own, I prefer libraries for such things to prevent the duplication of code and to avoid errors.
[1] Found while composing the question, kfifo, also Queues in the Linux Kernel might be of interest.

As far as I know there is no implementation of vectors till 4.1 Linux kernel. And it does not make any sense to have one as vectors can be designed with the basic data structures whose implementation is already provided in Linux kernel.

I'm a bit late, but if anyone needs to implement a generalized vector in C, it can be done using void pointers and a sizeof operator on initialization. It would look something like this:
struct MyVec {
void *data;
size_t stored_sizeof;
size_t size;
size_t allocated_size
};
#define STARTING_ALLOCATED_SIZE 10
void MyVec_init(struct MyVec *self, size_t sizeof_data_type) {
self->stored_sizeof = sizeof_data_type;
self->data = malloc(self->stored_sizeof * STARTING_ALLOCATED_SIZE);
self->size = 0;
self->allocated_size = STARTING_ALLOCATED_SIZE;
}
void MyVec_deinit(struct MyVec *self) {
free(self->data);
}
bool MyVec_at(struct MyVec *self, size_t index, void *to_fill) {
if (index >= size)
return false;
memcpy(to_fill, self->data + (index * self->stored_sizeof), self->stored_sizeof);
return true;
}
and all the other methods that you might need, just being sure to use stored_sizeof whenever indexing, adding/removing elements, and the like.

Related

Confused use of memcpy in ext2fs library

I am reading the source code implementation of libext2fs, which is part of the project e2fsprogs used to debug Ext2/3/4 file systems. And came across one confused point about the use of method memcpy as follows.
In the library, it maintains one function ext2fs_get_mem which is used to allocate dynamic memories:
_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
{
void *pp;
pp = malloc(size);
if (!pp)
return EXT2_ET_NO_MEMORY;
memcpy(ptr, &pp, sizeof (pp));
return 0;
}
The caller will call it like:
retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs)
In the above case, variable fs is just type of struct struct_ext2_filsys, all right.
The confused point is why the function ext2fs_get_mem need to call memcpy and what's the purpose? Why not directly allocate memory to the pointer void *ptr by malloc?

LPNMITEMACTIVATE and code analysis (C26462)

Why is it that in the source code in the SDK for LPNMITEMACTIVATE it is defined with the asterix to the left?
typedef struct tagNMITEMACTIVATE
{
NMHDR hdr;
int iItem;
int iSubItem;
UINT uNewState;
UINT uOldState;
UINT uChanged;
POINT ptAction;
LPARAM lParam;
UINT uKeyFlags;
} NMITEMACTIVATE, *LPNMITEMACTIVATE;
I am always used to the pointer being on the right. Either way, code like:
const LPNMITEMACTIVATE pNMItem = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
Will still flag a const (C26462) warning:
If I change the code to:
const NMITEMACTIVATE* pNMItem = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
The warning will go away.
I tried this with Visual Studio 2022, first of all, warning C26462 was not enabled by default. Perhaps you are using an earlier release, or there is something odd with my installation.
After manually enabling the warning, I could make that warning go away by assigning pNMItem more than once:
LPNMITEMACTIVATE pNMItem = nullptr;
pNMItem = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
How is this useful?
Or it can be fixed as suggested in other answers. But you may have additional problem because pNMHDR was probably declared as LPNMHDR, so you have to rewrite more lines:
NMHDR hdr = { 0 };
const NMHDR* pNMHDR = reinterpret_cast<NMHDR*>(&hdr);
const NMITEMACTIVATE* pNMItem = reinterpret_cast<const NMITEMACTIVATE*>(pNMHDR);
This can be a big waste of time. Note, the extra compliance is recommended if you are writing code that's supposed to run on any system. But MFC is tied to Windows so this isn't really an issue. MFC and Windows are still using that "long pointer" crap that's left over from 16-bit Windows, they are not compliant themselves, so consider turning off some of these warnings.
This is standard C/C++
Like in this (not runnable) code snippet:
typedef int *LPINT;
// typedef int* LPINT; // you could write this, it's exactly the
// the same as above
int main()
{
LPINT pint;
int* pint2;
*pint = *pint2;
}
pint and pint2 are both pointers to int. BTW this is hiding a pointer type behind a typedef, which is a bad idea (but was considered as a good idea in old MS days), but lots of Microsoft headers still have these typedef sometype *LPsometype; typedefs for compatibility reasons.
Another example which is closer to the MS header you're refering to:
This:
typedef struct tagNMITEMACTIVATE
{
int hdr;
int iItem;
} NMITEMACTIVATE, *LPNMITEMACTIVATE;
is equivalent to this:
typedef struct tagNMITEMACTIVATE
{
int hdr;
int iItem;
} NMITEMACTIVATE;
typedef struct tagNMITEMACTIVATE *LPNMITEMACTIVATE;
For pointer const can be applied to the type the pointer points at:
const NMITEMACTIVATE* p;
or
NMITEMACTIVATE const* p;
Or it can be applied to the pointer variable itself:
NMITEMACTIVATE* const p;
Now if you have typedef:
typedef NMITEMACTIVATE *PNMITEMACTIVATE;
The const would not apply to the type being pointed at. Either way it is the pointer itself is constant:
const PNMITEMACTIVATE p;
PNMITEMACTIVATE const p;
To avoid this confusion, prefer not to use raw pointer typedefs (and not to define them).

A portable, std::string-like string class which is not std::string

For various (C++11 std::string-change-related) reasons I would like to use a non-std::string string class in some places in my C++11-ish code, which need to be agreeable with several versions of several C++ compilers.
Now, I don't really mind whether my strings behave in pre-C++11-style or post-C++11 way; and I don't care about performance either. I just want a nice reliable string class which under no circumstances gives me errors involving std::__cxx11::string and other such voodoo.
What are my options? Can I/should I replicate some single compiler's implementation in my own header files? Is GCC's vstring an option? Something else?
template<class T>
struct crappy_string {
mutable std::vector<T> buffer;
T const* c_str() const {
if (buffer.empty() || buffer.back())
buffer.push_back(0);
return buffer.data();
}
friend bool operator<( crappy_string const& lhs, crappy_string const& rhs ) {
lhs.c_str(); rhs.c_str(); // ensure null terminator in both
return lhs.buffer < rhs.buffer;
}
};
This satisfies the minimal requirements you gave in the comments.

Initializing empty polymorphic Singleton type without magic statics

Suppose you had a polymorphic Singleton type (in our case a custom std::error_category type). The type is stateless, so no data members, but it does have a couple of virtual functions. The problem arises when instantiating this type in a multithreaded environment.
The easiest way to achieve this would be to use C++11's magic statics:
my_type const& instantiate() {
static const my_type instance;
return instance;
}
Unfortunately, one of our compilers (VC11) does not support this feature.
Should I expect that this will explode in a multithreaded environment? I'm quite certain that as far as the standard goes, all bets are off. But given that the type does not contain any data members and only virtual functions, what kind of errors should I expect from a mainstream implementation like VC11? For example, neither Boost.System nor VC seem to take any precautions against this in their implementation of error_category. Are they just being careless or is it unreasonably paranoid to worry about races here?
What would be the best way to get rid of the data race in a standard-compliant way? Since the type in this case is an error_category I want to avoid doing a heap allocation if possible. Keep in mind that the Singleton semantics are vital in this case, since equality of error categories is determined by pointer-comparison. This means that for example thread-local storage is not an option.
Here is a possibly simpler version of Casey's answer, which uses an atomic spinlock to guard a normal static declaration.
my_type const& instantiate()
{
static std::atomic_int flag;
while (flag != 2)
{
int expected = 0;
if (flag.compare_exchange_weak(expected, 1))
break;
}
try
{
static my_type instance = whatever; // <--- normal static decl and init
flag = 2;
return instance;
}
catch (...)
{
flag = 0;
throw;
}
}
This code is also easier to turn into three macro's for reuse, which are easily #defined to nothing on platforms which support magic statics.
my_type const& instantiate()
{
MY_MAGIC_STATIC_PRE;
static my_type instance = whatever; // <--- normal static decl and init
MY_MAGIC_STATIC_POST;
return instance;
MY_MAGIC_STATIC_SCOPE_END;
}
Attempt #2b: Implement your own equivalent of std::once_flag, with an atomic<int> (Live at Rextester):
my_type const& instantiate() {
static std::aligned_storage<sizeof(my_type), __alignof(my_type)>::type storage;
static std::atomic_int flag;
while (flag < 2) {
// all threads spin until the object is properly initialized
int expected = 0;
if (flag.compare_exchange_weak(expected, 1)) {
// only one thread succeeds at the compare_exchange.
try {
::new (&storage) my_type;
} catch(...) {
// Initialization failed. Let another thread try.
flag = 0;
throw;
}
// Success!
if (!std::is_trivially_destructible<my_type>::value) {
std::atexit([] {
reinterpret_cast<my_type&>(storage).~my_type();
});
}
flag = 2;
}
}
return reinterpret_cast<my_type&>(storage);
}
This only relies on the compiler to correctly zero-initialize all static storage duration objects, and also uses the nonstandard extension __alignof(<type>) to properly align storage since Microsoft's compiler team can't be bothered add the keyword without the two underscores.
Attempt#1: Use std::call_once in conjunction with a std::once_flag (Live demo at Coliru):
my_type const& instantiate() {
struct empty {};
union storage_t {
empty e;
my_type instance;
constexpr storage_t() : e{} {}
~storage_t() {}
};
static std::once_flag flag;
static storage_t storage;
std::call_once(flag, []{
::new (&storage.instance) my_type;
std::atexit([]{
storage.instance.~my_type();
});
});
return storage.instance;
}
The default constructor for std::once_flag is constexpr, so it's guaranteed to be constructed during constant initialization. I am under the impression [citation needed] that VC correctly performs constant initialization. EDIT: Unfortunately, MSVC up through VS12 still doesn't support constexpr, so this technique has some undefined behavior. I'll try again.
The standard is silent on the question of how statics are constructed when the function is called on multiple threads.
gcc uses locks to make function level statics threadsafe (can be disabled by a flag). Most (all?) versions of Visual C++ do NOT have threadsafe function level statics.
It is recommended to use a lock around the variable declaration to guarantee thread-safety.

Question about pthreads & pointers

Here is an example of thread creation code that is often seen. pthread_create uses a lot of pointers/addresses and I was wondering why this is so.
pthread_t threads[NUM_THREADS];
long t;
for(t=0; t<NUM_THREADS; t++){
rc = pthread_create(&threads[t], NULL, &someMethod, (void *)t);
}
Is there a major advantage or difference for using the '&' to refer to the variable array 'threads' as well as 'someMethod' (as opposed to just 'threads' and just 'someMethod')? And also, why is 't' usually passed as a void pointer instead of just 't'?
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg);
You need to pass a pointer to a pthread_t variable to pthread_create. &threads[t] and threads+t achieve this. threads[t] does not. pthread_create requires a pointer so it can return a value through it.
someMethod is a suitable expression for the third argument, since it's the address of the function. I think &someMethod is redundantly equivalent, but I'm not sure.
You are casting t to void * in order to jam a long into a void *. I don't think a long is guaranteed to fit in a void *. It's definitely a suboptimal solution even if the guarantee exists. You should be passing a pointer to t (&t, no cast required) for clarity and to ensure compatibility with the expected void *. Don't forget to adjust someMethod accordingly.
pthread_t threads[NUM_THREADS];
long t;
for (t=0; t<NUM_THREADS; t++) {
rc = pthread_create(&threads[t], NULL, someMethod, &t);
}

Resources