$Revision$
$Date$
Locking Interface
Why use it ?
The main reason in creating it was to have a single transparent
interface to various locking methods. For example right now SER
uses the following locking methods, depending on their availability
on the target system.
FAST_LOCK
Fast inline assembly locks, defined in
fast_lock.h. They are currently
available for x86, sparc64, strong-arm (amv4l) and ppc
(external untested contributed code). In general if the
assembly code exists for a given architecture and the
compiler knows inline assembly (for example sun cc does
not) FAST_LOCK is preferred. The main advantage of using
FAST_LOCK is very low memory overhead and extremely fast
lock/unlock operations (like 20 times faster than SYSV
semaphores on linux & 40 times on solaris). The only thing
that comes close to them are pthread mutexes (which are
about 3-4 times slower).
PTHREAD_MUTEX
Uses pthread_mutex_lock/unlock. They are quite fast but
they work between processes only on some systems (they do
not work on linux).
POSIX_SEM
Uses posix semaphores
(sem_wait/sem_post). They
are slower than the previous methods but still way faster
then SYSV semaphores. Unfortunately they also do not work
on all the systems (e.g. linux).
SYSV_SEM
This is the most portable but also the slowest locking
method. Another problem is that the number of semaphores
that can be allocated by a process is limited. One also has
to free them before exiting.
How to use it ?
First of all you have to include
locking.h. Then when compiling the code one or
all of FAST_LOCK, USE_PTHREAD_MUTEX, USE_PTHREAD_SEM or
USE_SYSV_SEM must be defined (the ser
Makefile.defs takes care of this, you should
need to change it only for new architectures or
compilers). locking.h defines 2 new types:
gen_lock_t and
lock_set_t.
Simple Locks
The simple locks are simple mutexes. The type is
gen_lock_t.
Do not make any assumptions on
gen_lock_t base type, it does not have
to be always an int.
Allocation And Initialization
The locks are allocated with: gen_lock_t*
lock_alloc() and initialized with
gen_lock_t* lock_init(gen_lock_t*
lock). Both functions return 0 on failure. The
locks must be initialized before use. A proper alloc/init
sequence looks like:
gen_lock_t* lock;
lock=lock_alloc();
if (lock==0) goto error;
if (lock_init(lock)==0){
lock_dealloc(lock);
goto error; /* could not init lock*/
}
...
Lock allocation can be skipped in some cases: if the lock is
already in shared memory you don't need to allocate it again,
you can initialize it directly, but keep in mind that the lock
MUST be in shared memory.
Example:
struct s {
int foo;
gen_lock_t lock;
} bar;
bar=shm_malloc(sizeof struct s); /* we allocate it in the shared memory */
if (lock_init(&bar->lock)==0){
/* error initializing the lock */
...
}
Destroying And Deallocating the Locks
void lock_destroy
gen_lock_t* lock
void lock_dealloc
gen_lock_t* lock
The lock_destroy function must be called
first. It removes the resources associated with the lock, but
it does not also free the lock shared memory part. Think of
sysv rmid. Please don't forget to call this
function, or you can leave allocated resources in some cases
(e.g sysv semaphores). Be careful to call it in your module
destroy function if you use any global module locks.
Example:
lock_destroy(lock);
lock_dealloc(lock);
Of course you don't need to call
lock_dealloc if your lock was not
allocated with lock_alloc.
Locking And Unlocking
void lock_get
gen_lock_t* lock
void lock_release
gen_lock_t* lock
Lock Sets
The lock sets are kind of sysv semaphore sets equivalent. The
type is lock_set_t. Use them when you
need a lot of mutexes. In some cases they waste less system
resources than arrays of gen_lock_t
(e.g. sys v semaphores).
Allocating And Initializing
lock_set_t* lock_set_alloc
int no
lock_set_t* lock_set_init
lock_set_t* set
Both functions return 0 on failure.
Expect the allocation function to fail for large
numbers. It depends on the locking method used and the
system available resources (again the sysv semaphores
example).
Example:
lock_set_t *lock_set;
lock_set=lock_set_alloc(100);
if (lock_set==0) goto error;
if (lock_set_init(lock_set)==0){
lock_set_dealloc(lock_set);
goto error;
}
Destroying And Deallocating
void lock_set_destroy
lock_set_t* s
void lock_set_dealloc
lock_set_t* s
Again don't forget to "destroy" the locks.
Locking And Unlocking
void lock_set_get
lock_set_t* s
int i
void lock_set_release
lock_set_t* s
int i
Example:
lock_set_get(lock_set, 2);
/* do something */
lock_set_release(lock_set, 2);