I’m working on a project in C that involves creating a user-level thread library by overriding pthread.h. I’m currently working on the mutex functions.
In my implementation, I store mutexes as a linked list of structs. What I would like to do in pthread_mutex_init is store the pointer to the element that is the newly created mutex in the pthread_mutex_t mutex variable. Is this possible, though? Here’s some of my code so you get an idea of what I’m trying to do:
typedef struct mutexList
{
int mid;
struct mutexList *prevMutex;
struct mutexList *nextMutex;
int locked;
struct queueItem *owner;
struct blockedThread *blockedHead;
struct blockedThread *blockedTail;
} mutexElement;
int mutexCounter = 0;
mutexElement *mutexHead = NULL;
mutexElement *mutexTail = NULL;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
{
mutexElement *newMutex = (mutexElement *) malloc(sizeof(mutexElement));
fprintf(stdout, "***LOG: Creating new mutex.\n");
if(newMutex == NULL)
return ENOMEM;
newMutex->mid = mutexCounter;
mutexCounter++;
newMutex->locked = 0;
newMutex->nextMutex = NULL;
newMutex->blockedHead = NULL;
newMutex->blockedTail = NULL;
if(mutexHead == NULL)
mutexHead = newMutex;
if(mutexTail != NULL)
mutexTail->nextMutex = newMutex;
mutexTail = newMutex;
mutex = (&newMutex);
return 0;
}
I think your general idea can work, but there are at least a few of problems with your particular example:
mutexCounter,mutexHeadandmutexTailitems aren’t handled in a thread-safe manner (an important property for this kind of library)mutex = (&newMutex);is pointless –mutexis a parameter that is a copy of the user’s argument. When your function returns,mutexsimply ceases to exist; it doesn’t get returned to the caller.&newMutexto the user in the first place is a problem:newMutexis a local variable that will expire when the function returns. Storing a pointer to it in something that’s supposed to live past the current function call’s duration won’t work.Note that I’m not saying that the above is an exhaustive list of problems – they’re just ones that jumped out off the bat.
You could make the
pthread_mutex_ttype a typedef for astruct mutexList*and place the value ofnewMutex(not the address ofnewMutex) in*mutexsincemutexwould be astruct mutexList**. The user’spthread_mutex_tvariable would contain a pointer to the structure that’s on your linked list.If the user neglects to call
pthread_mutex_destroy()and lets hispthread_mutex_tvariable go out of scope, then thestruct mutexListelement that was allocated would never be freed, but that’s a user error that you can really do nothing about.As far as mutex initialization goes, there’s another big problem that you’d still need to think about – static initialization. The user is allowed to do something like:
In this case, the user doesn’t need to call
pthread_mutex_init()– in fact, it would be an error to do so. Your library will need to detect and perform whatever appropriate dynamic initialization is needed for these statically initialized mutexes on first use (or something equivalent). And you have to do it in a thread safe manner – the first use is not only allowed to be done on arbitrary threads, one of the main use cases for these static mutexes is to permit contention on the mutex right off the bat (for example to perform more complex initialization of other static data by the first thread that happens to get to it).To summarize, I think the general approach of keeping a container of private mutex data structures and having the user’s
pthread_mutex_tvariables be essentially pointers (or some other indirect reference) to the appropriate item in the container is fine. However, there are a lot of complex details – especially making sure your functions are thread safe – that need to be considered and designed for.The kinds of problems that existed in the example indicate that the design so far hasn’t been given nearly enough thought. That might be OK – maybe you just started on it; just don’t underestimate the attention to detail you’ll need to give these things.