Documentation

Documentation.Development-Manual-API-Locking-3-2 History

Hide minor edits - Show changes to markup

February 18, 2022, at 10:06 AM by razvancrainea -
Changed line 139 from:

gen_lock_set_t *lock_set_alloc(void);

to:

gen_lock_set_t *lock_set_alloc(int n);

December 11, 2019, at 06:55 PM by liviu -
Added lines 1-307:
Documentation -> Development Manual 3.2 -> Locking API

This page has been visited 730 times. (:title OpenSIPS Development - Locking API:)


(:allVersions Development-Manual-API-Locking 3.2:)


Locking API

(:toc-float Table of Content:)

OpenSIPS has it's own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the usage case and the menuconfig provided compilation flags, the OpenSIPS generic locks can be converted either to busy locks, futexes, SysV locks, etc.
The Locking API offers two distinct functionalities, one for using single lock elements, and another for operating on entire sets of locks.

Single Lock API

The API can be used by including “locking.h” . The OpenSIPS generic lock is defined by the gen_lock_t structure.
Allocating a new lock is done by calling lock_alloc : (:source lang=C -link -getcode :) /* Returns :

      A shared memory allocated lock, or NULL in case of an error.
  • /

gen_lock_t *lock_alloc(void); (:sourceend:)
Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock separately - if the lock is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock, it must be initialized : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock instance to be initialized

Returns :

      The initialized lock in case of success, or NULL in case of error.
  • /

gen_lock_t* lock_init(gen_lock_t* lock); (:sourceend:)
In order to acquire a lock, one must use the lock_get function : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
  • /

void lock_get(gen_lock_t *lock); (:sourceend:)
The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_release should be used : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be released.
  • /

void lock_release(gen_lock_t *lock); (:sourceend:)
Once a lock is no longer needed, one must first destroy the lock, and then the lock can be safely deallocated. (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be destroyed
  • /

void lock_destroy(gen_lock_t *lock); /* Parameters :

      lock - the lock to be deallocated
  • /

void lock_dealloc(gen_lock_t *lock); (:sourceend:)
Here is a code snippet showing the typical code used when dealing with single lock instances : (:source lang=C -link -getcode :)

gen_lock_t *my_lock;

int init_function(void) {

	/* … */

	my_lock = lock_alloc();
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock \n”);
		return -1;
	}

	if (lock_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */
	lock_get(my_lock)


	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_release(my_lock)
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_destroy(my_lock);
	lock_dealloc(my_lock);

	/* … */

} (:sourceend:)

Lock Set API

Operating on an entire array of locks can become very useful when dealing with structures like hashes, where you would need a lock per each hash entry.
The API can be used by including locking.h . The OpenSIPS generic array of locks is defined by the gen_lock_set_t structure, and working with it is very similar in concept to operating a single lock entry.
Allocating a new lock set is done by calling lock_set_alloc : (:source lang=C -link -getcode :) /* Returns :

      A shared memory allocated lock set, or NULL in case of an error.
  • /

gen_lock_set_t *lock_set_alloc(void); (:sourceend:)
Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock set separately - if the lock set is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock set, it must be initialized : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock set instance to be initialized

Returns :

      The initialized lock in case of success, or NULL in case of error.
  • /

gen_lock_set_t* lock_set_init(gen_lock_set_t* lock); (:sourceend:)
In order to acquire a lock in a lock set, one must use the lock_set_get function : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
      entry - the entry in the lock set that needs to be acquired
  • /

void lock_set_get(gen_lock_set_t *lock,int entry); (:sourceend:)
The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_set_release should be used : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be released.
      entry - the entry in the lock set that needs to be released
  • /

void lock_set_release(gen_lock_set_t *lock,int entry); (:sourceend:)
Once a lock set is no longer needed, one must first destroy the lock set, and then the lock set can be safely deallocated. (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock set to be destroyed
  • /

void lock_set_destroy(gen_lock_set_t *lock); /* Parameters :

      lock - the lock set to be deallocated
  • /

void lock_set_dealloc(gen_lock_set_t *lock); (:sourceend:)
Here is a code snippet showing the typical code used when dealing with single lock instances : (:source lang=C -link -getcode :) gen_lock_set_t *my_lock;

int init_function(void) {

	/* … */

        /* allocate lock set with 32 entries */
	my_lock = lock_set_alloc(32);
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock set \n”);
		return -1;
	}

	if (lock_set_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock set \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */

	/* acquire entry 5 in the lock set */
	lock_set_get(my_lock,5)

	/* also acquire entry 21 in the lock set */
	lock_set_get(my_lock,21);

	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_set_release(my_lock,21);
	lock_set_release(my_lock,5);
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_set_destroy(my_lock);
	lock_set_dealloc(my_lock);

	/* … */

} (:sourceend:)

Readers-Writers Locking API

A readers-writer lock is like a mutex, in that it controls access to a shared resource, allowing concurrent access to multiple threads for reading but restricting access to a single thread for writes (or other changes) to the resource.
This can prove very useful when having an use case where all the OpenSIPS processes need read-only access to a resource, but you need to have an MI command to reload that resource ( eg. from a database ). In such scenarios, using a readers-writers lock can improve performance by a considerable margin.
The API can be used by including “rw_locking.h” . The OpenSIPS generic lock is defined by the rw_lock_t structure.

Allocating a new readers-writers lock into shared memory and initializing it is done by calling lock_init_rw :

(:source lang=C -link -getcode :) /* Returns :

      A shared memory allocated rw lock, or NULL in case of an error.
  • /

inline static rw_lock_t * lock_init_rw(void); (:sourceend:)
In order to acquire the lock for reading purpose, one should use lock_start_read : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
  • /

void lock_start_read(rw_lock_t * lock); (:sourceend:)
In case there is currently a write access ongoing, the lock will block until the write is done. Otherwise, the lock will be acquired immediately.
After the read is finished, you must call lock_stop_read : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be released
  • /

void lock_stop_read(rw_lock_t * lock); (:sourceend:)
For requesting a write access, you can use lock_start_write : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
  • /

void lock_start_write(rw_lock_t * lock); (:sourceend:)
In case there are other ongoing writes, the op will block until the other writes complete. Otherwise, the lock will block until all the existing readers finish reading.
After the write operation is finished, call lock_stop_write : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be release
  • /

void lock_stop_write(rw_lock_t * lock); (:sourceend:)
Upon calling the above function, readers will again be allowed in the critical section.
For destroying and deallocating a rw lock, use lock_destroy_rw : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be destroyed
  • /

void lock_destroy_rw(rw_lock_t * lock); (:sourceend:)


Page last modified on February 18, 2022, at 10:06 AM