Date: Wed, 16 Aug 2000 12:19:51 -0400 (EDT) From: Peter Dufault <dufault@hda.com> To: hackers@FreeBSD.ORG Subject: Re: IPC, shared memory, syncronization AND threads... Message-ID: <200008161619.MAA43175@hda.hda.com> In-Reply-To: <200008161223.IAA42181@hda.hda.com> from Peter Dufault at "Aug 16, 2000 08:23:49 am"
next in thread | previous in thread | raw e-mail | index | archive | help
Here's the kind of thing I have in mind, wrapped around the pthreads
mutexes. This replaces default pthread mutexes (those with no special
attributes) with possibly fast ones. I haven't done any real timing but
I've verified that a program I have works and runs a lot faster with
these wrappers. Obviously you have to use -include mutex.h and various
-D flags to try this out.
Header:
#ifndef _MUTEX_H_
#define _MUTEX_H_
#include <pthread.h>
struct _pthread_mutex;
typedef struct _pthread_mutex *_pthread_mutex_t;
int _pthread_mutex_init(_pthread_mutex_t *, const pthread_mutexattr_t *);
int _pthread_mutex_lock(_pthread_mutex_t *);
int _pthread_mutex_unlock(_pthread_mutex_t *);
#endif /* _MUTEX_H_ */
Wrappers:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
typedef unsigned long castype; /* Data type you can CAS */
extern int cas(volatile castype *, const castype, const castype);
struct _pthread_mutex {
castype lock;
struct pthread_mutex *strong_lock;
struct pthread_cond *done_unlocking;
struct pthread_mutex *unlock_lock;
int unlocking;
};
#include "mutex.h"
/*
* _PTHREAD_MUTEX_INITIALIZER would have to be something like:
*/
#define _PTHREAD_MUTEX_INITIALIZER { \
0,\
PTHREAD_MUTEX_INITIALIZER,\
PTHREAD_COND_INITIALIZER,\
PTHREAD_MUTEX_INITIALIZER,\
0}
/* cas: Compare and swap. Return 1 if it succeeds, zero if it
* doesn't.
*/
int
cas(volatile castype *ptr, const castype o, const castype n)
{
volatile int result = 0;
__asm __volatile(
"cmpxchg%L3 %3,%1; jne 0f; inc%L0 %0; 0:"
: "=m"(result)
: "m"(*ptr), "a"(o), "r"(n)
);
return result;
}
static void
oops(const char *s)
{
fprintf(stderr, "_pthread_mutex: %s.\n", s);
}
/* Here's the init. If there are any non-standard attributes use a default.
*/
int
_pthread_mutex_init(_pthread_mutex_t *mp, const pthread_mutexattr_t *attr)
{
struct _pthread_mutex *m = (struct _pthread_mutex *)calloc(1, sizeof(*m));
if (attr) {
m->lock = 4;
return pthread_mutex_init(&m->strong_lock, attr);
}
*mp = m;
return 0;
}
/* mutex_lock:
* First try to go from 0 to 1. If that works, we have the lock.
*
* Then see if it is 4. That means it is a non-default lock,
* just call the standard locker. These two steps could be published
* in the header along with the structure to make things inlineable.
*
* Finally go into a loop, doing the first step again, then:
*
* If it fails, set it from 1 to 2 to let the unlock know someone
* is waiting and then call the existing lock.
* If it is already at 2 just call the existing lock.
*
* The only thing left is that it is being unlocked, that is, it must be
* 3. Wait for the unlocker to do its thing then repeat.
*/
int _pthread_mutex_lock(_pthread_mutex_t *mp)
{
struct _pthread_mutex *m = *mp;
if (cas(&m->lock, 0, 1)) /* Try for the fast lock. */
return 0;
if (m->lock == 4)
return pthread_mutex_lock(&m->strong_lock); /* Not default lock */
while (1) {
if (cas(&m->lock, 0, 1)) /* Try for the fast lock. */
break;
if (cas(&m->lock, 1, 2) || cas(&m->lock, 2, 2))
return pthread_mutex_lock(&m->strong_lock);
pthread_mutex_lock(&m->unlock_lock);
/* It is being unlocked, which can take a long time.
* Wait for the unlocker
*/
while (m->unlocking) {
pthread_cond_wait(&m->done_unlocking, &m->unlock_lock);
}
pthread_mutex_unlock(&m->unlock_lock);
}
return 0;
}
/* unlock: First try to do a fast unlock and also handle the special
* attributes case.
* Then we must be the unlocker (there should only be one), so set the
* flag so people know we're unlocking.
*
* Do the unlock and then signal anyone waiting.
*/
int _pthread_mutex_unlock(_pthread_mutex_t *mp)
{
struct _pthread_mutex *m = *mp;
if (cas(&m->lock, 1, 0)) /* Try for the fast unlock */
return 0;
if (m->lock == 4)
return pthread_mutex_unlock(&m->strong_lock); /* Not default lock */
/* It has to be 2 or there are multiple unlocks going on.
*/
if (!cas(&m->lock, 2, 3))
oops("multiple unlocks?");
pthread_mutex_lock(&m->unlock_lock);
m->unlocking = 1;
pthread_mutex_unlock(&m->strong_lock);
m->unlocking = 0;
pthread_mutex_unlock(&m->unlock_lock);
pthread_cond_signal(&m->done_unlocking);
return 0;
}
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200008161619.MAA43175>
