Hi, I wanted to ask what is the best solution for the following problem. (explained below)
I have following memory library code (simplified):
// struct is opaque to callee
struct memory {
void *ptr;
size_t size;
pthread_mutex_t mutex;
};
size_t memory_size(memory *self)
{
if (self == NULL) {
return 0;
}
{
size_t size = 0;
if (pthread_mutex_lock(self->mutex) == 0) {
size = self->size;
(void)pthread_mutex_unlock(self->mutex);
}
return size;
}
}
void *memory_beginAccess(memory *self)
{
if (self == NULL) {
return NULL;
}
if (pthread_mutex_lock(self->mutex) == 0) {
return self->ptr;
}
return NULL;
}
void memory_endAccess(memory *self)
{
if (self == NULL) {
return;
}
(void)pthread_mutex_unlock(self->mutex);
}
The problem:
// ....
memory *target = memory_alloc(100);
// ....
{
void *ptr = memory_beginAccess(target);
// ^- implicit lock of internal mutex
operationThatNeedsSize(ptr, memory_size(target));
// ^- implicit lock of internal mutex causes a deadlock (with fastmutexes)
memory_endAccess(target);
// ^- implicit unlock of internal mutex (never reached)
}
So, I thought of three possible solutions:
1.) Use a recursive mutex. (but I heard this is bad practice and should be avoided whenever possible).
2.) Use different function names or a flag parameter:
memory_sizeLocked()
memory_size()
memory_size(TRUE) memory_size(FALSE)
3.) Catch if pthread_mutex_t returns EDEADLK and increment a deadlock counter (and decrement on unlock) (Same as recursive mutex?)
So is there another solution for this problem? Or is one of the three solutions above "good enough" ?
Thanks for any help in advance
Use two versions of the same function, one that locks and the other that doesn't. This way you will have to modify the least amount of code. It is also logically correct since, you must know when you are in a critical part of the code or not.
Related
I've been reading through and attempting to apply Tyler Hoffman's C/C++ unit testing strategies.
He offers the following as a way to fake a mutex:
#define NUM_MUTEXES 256
typedef struct Mutex {
uint8_t lock_count;
} Mutex;
static Mutex s_mutexes[NUM_MUTEXES];
static uint32_t s_mutex_index;
// Fake Helpers
void fake_mutex_init(void) {
memset(s_mutexes, 0, sizeof(s_mutexes));
}
bool fake_mutex_all_unlocked(void) {
for (int i = 0; i < NUM_MUTEXES; i++) {
if (s_mutexes[i].lock_count > 0) {
return false;
}
}
return true;
}
// Implementation
Mutex *mutex_create(void) {
assert(s_mutex_index < NUM_MUTEXES);
return &s_mutexes[s_mutex_index++];
}
void mutex_lock(Mutex *mutex) {
mutex->lock_count++;
}
void mutex_unlock(Mutex *mutex) {
mutex->lock_count--;
}
For a module that has functions like:
#include "mutex/mutex.h"
static Mutex *s_mutex;
void kv_store_init(lfs_t *lfs) {
...
s_mutex = mutex_create();
}
bool kv_store_write(const char *key, const void *val, uint32_t len) {
mutex_lock(s_mutex); // New
...
mutex_unlock(s_mutex); // New
analytics_inc(kSettingsFileWrite);
return (rv == len);
}
After being setup in a test like:
TEST_GROUP(TestKvStore) {
void setup() {
fake_mutex_init();
...
}
...
}
I'm confused about a couple things:
How does fake_mutex_init() cause methods using mutex_lock and mutex_unlock to use the fake lock and unlock?
How does this actually fake mutex locking? Can I produce deadlocks with these fakes? Or, should I just be checking the lock count in my tests?
How does fake_mutex_init() cause methods using mutex_lock and mutex_unlock to use the fake lock and unlock?
It doesn't. It's unrelated.
In the tutorial, the tests are linked with one of the implementations. In the case of this specific test, it is linked with this fake mutex implementation.
How does this actually fake mutex locking?
It just increments some integers inside an array. There is no "locking" involved in any way.
Can I produce deadlocks with these fakes?
No, because there is locking, there is no waiting, so there are no deadlocks, which occur when two threads wait for each other.
Or, should I just be checking the lock count in my tests?
Not, in "tests" - for tests mutex is an abstract thing.
Adding assert(mutex->lock_count > 0) to unlock to check if your tests do not unlock a mutex twice seems like an obvious improvement.
I'm a college student learning how to deal with threads and databases.
Overall, I'm trying to make a function that will take a list of locks, see if the current lock the program is handling is in the list, and mutex lock that lock.
Currently, I am having issues initializing the *locks, but every time I do so, I get a segmentation error (core dump).
I already try using the different ways of initializing the mutex lock:
&locks->lock = PTHREAD_MUTEX_INITIALIZER;
as well as using : pthread_mutex_init(&locks->lock, NULL);
on the .h file, it contains
typedef struct {
char *table;
pthrad_mutex_t lock;} TableLock;
main file:
static pthread_mutex_t lock_on_locks;
static int active_tables = 0;
static TableLock *locks = NULL;
// Table locking functions
void sudba_lock(char *table) {
sleep(2);
if (locks == NULL) {
my_realloc(locks, sizeof(TableLock));
}
pthread_mutex_lock(&lock_on_locks);
char table_name[strlen(table) + 1];
table_name[strlen(table)] = '\0';
sprintf(table_name, "%s", table);
if (active_tables == 0) {
pthread_mutex_init(&locks->lock, NULL);
pthread_mutex_lock(&locks->lock);
locks[active_tables].table = table_name;
active_tables++;
}
the my_realloc function is this:
void *my_realloc(void *ptr, size_t size) {
void *result = malloc(size);
if(!result) abort();
return result
}
Any help is appreciated
Your crash has nothing to do with pthread_mutex_lock; it's just that you're passing a null pointer to it because you didn't save the result of realloc. Where you have:
my_realloc(locks, sizeof(TableLock));
it should be:
locks = my_realloc(locks, sizeof(TableLock));
But I'm not clear why you're allocating it anyway since this looks like a single-instance lock. Normally locks either have static storage duration or exist inside some structure you're allocating (whose contents they'll protect). Allocating an individual lock by itself is a code smell.
There are a lot of other things that look wrong with your code too, independent of the crash.
I'm having a problem with my C code where I declare a static int variable (as a flag), then initialize it to -1 in init() which is only called once, then when I try to update the value to 0 or 1 later on, it keeps reverting back to -1.
Does anyone know what the problem can be?
I don't have any local variables with the same identifier so I'm really lost.
Thanks!
static int previousState;
void init()
{
previousState = -1;
}
void moveForward(int currentState)
{
if (previousState == -1)
previousState = currentState;
if (previousState != currentState)
{
/* do stuff */
/* PROBLEM: it never goes into here, because previousState is always -1! */
}
/* other code */
}
void main()
{
init();
if (fork() == 0)
{
/* do stuff */
moveForward(1);
exit();
}
/* more forks */
moveForward(0);
exit();
}
Each process calls moveForward just once. Processes do not share static data!
Use threads, or use shared memory. Also use mutex or semaphore for concurrent access of shared data . Preferably switch to a language better suited for parallel prosessing...
/*language C code*/
#include "windows.h"
typedef struct object_s
{
SRWLOCK lock;
int data;
} object_t, *object_p; /*own and pointer type*/
void thread(object_p x)
{
AcquireSRWLockExclusive(&x->lock);
//...do something that could probably change x->data value to 0
if(x->data==0)
free(x);
else
ReleaseSRWLockExclusive(&x->lock);
}
void main()
{
int i;
object_p object=(object_p)malloc(sizeof(object_t));
InitializeSRWLock(&object->lock);
for(i=0;i<3;i++)
CreateThread(0,0,thread,object,0);
}
As you can figure out in the codes above, what I have to accomplish is to let one thread conditionally free the object on which the other two may block. Codes above are obviously flawed because if object is set free along with the lock, all blocking threads give us nowhere but wrong.
A solution below
/*language C code*/
#include "windows.h"
typedef struct object_s
{
/*change: move lock to stack in main()*/
int data;
} object_t, *object_p; /*own and pointer type*/
void thread(void * x)
{
struct {
PSRWLOCK l;
object_p o;
} * _x=x;
AcquireSRWLockExclusive(_x->l);
//...do something that could probably change x->data value to 0
if(_x->o->data==0)
free(_x->o);
ReleaseSRWLockExclusive(&x->lock);
}
void main()
{
int i;
SRWLOCK lock; /*lock over here*/
object_p object=(object_p)malloc(sizeof(object_t));
InitializeSRWLock(&lock);
/*pack for thread context*/
struct
{
PSRWLOCK l;
object_p o;
} context={&lock, object};
for(i=0;i<3;i++)
CreateThread(0,0,thread,&context,0);
}
works in this case but not applicable however, in my final project because there is actually a dynamic linked list of objects. By applying this solution it means that there must be a list of locks accordingly, each lock for an object and moreover, when a certain object is set free, its lock must be set free at the same time. There is nothing new compared with the first code section.
Now I wonder if there is an alternative solution to this. Thank you very much!
The solution is to not allocate the lock together with the data. I would suggest that you move the data out of that struct and replace it with a pointer to the data. Your linked list can then free the data first, and then the node, without any problems. Here's some pseudo code:
typedef struct
{
lock_t lock;
int* data_ptr;
} something_t;
void init_something (something_t* thing, ...)
{
thing->lock = init_lock();
thing->data_ptr = malloc(...); // whatever the data is supposed to be
}
void free_something (somthing_t* thing)
{
lock(thing->lock);
free(thing->data_ptr);
thing->data_ptr = NULL;
unlock(thing->lock);
}
...
void linked_list_delete_node (...)
{
free_something(node_to_delete->thing);
free(node_to_delete);
}
...
void thread (void* x)
{
lock(x->lock);
//...do something that could probably change x->data_ptr->data... to 0
if(x->data_ptr->data == 0)
{
free_something(x->data_ptr->data);
}
unlock(x->lock);
}
AcquireSRWLockExclusive(lock);
if(_x->o->data==0)
free(_x);
ReleaseSRWLockExclusive(lock);
As a sidenote, a C program for Windows can never return void. A hosted C program must always return int. Your program will not compile on a C compiler.
Also, CreateThread() expects a function pointer to a function returning a 32-bit value and taking a void pointer as parameter. You pass a different kind of function pointer, function pointer casts aren't allowed in C, nor am I sure what sort of madness Windows will execute if it gets a different function pointer than what it expects. You invoke undefined behavior. This can cause your program to crash or behave in unexpected or random ways.
You need to change your thread function to DWORD WINAPI thread (LPVOID param);
gcc 4.4.4 c89
I have always thought of using malloc for the life of the project for being the scope.
But I am just wondering if my idea is the best practice. For example, I initalize an instance of the struct in main. And create 2 functions for creating and destroying. I am just wondering if this is the right thing to do.
I have some skeleton code below.
Many thanks for any advice,
typedef struct Network_dev_t {
size_t id;
char *name;
} Network_dev;
Network_dev* create_network_device(Network_dev *network)
{
network = malloc(sizeof *network);
if(network == NULL) {
return NULL;
}
return network;
}
void do_something(Network_dev *network)
{
/* Do something with the network device */
}
void destroy_network_device(Network_dev *network)
{
free(network);
}
int main(void)
{
Network_dev *network = NULL;
network = create_network_device(network);
/* Do something with the network device */
do_something(network);
destroy_network_device(network);
return 0;
}
Looks good.
I have a point or 2 about create_network_device
Network_dev* create_network_device(Network_dev *network)
no need to pass in a pointer; I'd rather have Network_dev* create_network_device(void)
{
network = malloc(sizeof *network);
the if is not really necessary; if malloc failed the return network at the end of the function is the same as return NULL.
if(network == NULL) {
return NULL;
}
If the allocation succeeded you might want to insure the struct members are in a know state here
/* if (network) { */
/* id = 0; */
/* name = NULL; */
/* } */
return network;
}
This code looks fine to me. I agree with pmg that your create_network_device could use a little work. Just to pull together what he said and make things clearer, here is exactly how I would write the function:
Network_dev *create_network_device()
{
Network_dev *network = malloc(sizeof(*network));
if (network) {
network->id = 0;
network->name = NULL;
}
return network;
}
It is best to allocate memory and free memory in the same function. Just like you open and close files in the same function. You did this by creating and destroying a Network_dev in the main() function, which is good. This makes it easy to confirm that all malloced locations are also freed.
It is best to malloc() something as late as possible and free() it as soon as possible. That is, hold the memory for as short as possible. If your program's job is to do something with Network_dev, you did all right. If your program does a lot of other things, you should do them before malloc() or after free().