From owner-freebsd-hackers Wed Aug 16 9:18:48 2000 Delivered-To: freebsd-hackers@freebsd.org Received: from hda.hda.com (host65.hda.com [63.104.68.65]) by hub.freebsd.org (Postfix) with ESMTP id 0C3A437BF17 for ; Wed, 16 Aug 2000 09:18:37 -0700 (PDT) (envelope-from dufault@hda.hda.com) Received: (from dufault@localhost) by hda.hda.com (8.9.3/8.9.3) id MAA43175 for hackers@FreeBSD.ORG; Wed, 16 Aug 2000 12:19:52 -0400 (EDT) (envelope-from dufault) From: Peter Dufault Message-Id: <200008161619.MAA43175@hda.hda.com> Subject: Re: IPC, shared memory, syncronization AND threads... In-Reply-To: <200008161223.IAA42181@hda.hda.com> from Peter Dufault at "Aug 16, 2000 08:23:49 am" To: hackers@FreeBSD.ORG Date: Wed, 16 Aug 2000 12:19:51 -0400 (EDT) X-Mailer: ELM [version 2.4ME+ PL61 (25)] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG 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 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 #include #include 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