Fri Nov 12 12:02:45 2010

Asterisk developer's documentation


func_lock.c File Reference

Dialplan mutexes. More...

#include "asterisk.h"
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/astobj2.h"
#include "asterisk/utils.h"
Include dependency graph for func_lock.c:

Go to the source code of this file.

Data Structures

struct  channel_lock_frame
struct  lock_frame
struct  locklist

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int get_lock (struct ast_channel *chan, char *lockname, int try)
static int load_module (void)
static void * lock_broker (void *unused)
static void lock_fixup (void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
static void lock_free (void *data)
static int lock_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int null_cmp_cb (void *obj, void *arg, int flags)
static int null_hash_cb (const void *obj, const int flags)
static int trylock_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int unload_module (void)
static int unlock_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialplan mutexes" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, }
static struct ast_module_infoast_module_info = &__mod_info
static pthread_t broker_tid = AST_PTHREADT_NULL
static struct ast_custom_function lock_function
static struct ast_datastore_info lock_info
static struct ast_custom_function trylock_function
static int unloading = 0
static struct ast_custom_function unlock_function

Detailed Description

Dialplan mutexes.

Author:
Tilghman Lesher <func_lock_2007@the-tilghman.com>

Definition in file func_lock.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 494 of file func_lock.c.

static void __unreg_module ( void   )  [static]

Definition at line 494 of file func_lock.c.

static int get_lock ( struct ast_channel chan,
char *  lockname,
int  try 
) [static]

Definition at line 211 of file func_lock.c.

References ao2_alloc, ao2_container_alloc, ao2_link, ao2_ref, ao2_unlink, ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_cond_destroy(), ast_cond_init(), ast_cond_timedwait(), ast_datastore_alloc, ast_datastore_free(), ast_debug, ast_free, AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), channel_lock_frame::channel, lock_frame::cond, lock_frame::count, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, LOG_ERROR, lock_frame::mutex, lock_frame::name, ast_channel::name, null_cmp_cb(), null_hash_cb(), lock_frame::owner, and lock_frame::requesters.

Referenced by lock_read(), and trylock_read().

00212 {
00213    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00214    struct lock_frame *current;
00215    struct channel_lock_frame *clframe = NULL;
00216    AST_LIST_HEAD(, channel_lock_frame) *list;
00217    int res = 0, *link;
00218    struct timespec three_seconds = { .tv_sec = 3 };
00219 
00220    if (!lock_store) {
00221       ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
00222       lock_store = ast_datastore_alloc(&lock_info, NULL);
00223       if (!lock_store) {
00224          ast_log(LOG_ERROR, "Unable to allocate new datastore.  No locks will be obtained.\n");
00225          return -1;
00226       }
00227 
00228       list = ast_calloc(1, sizeof(*list));
00229       if (!list) {
00230          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %sLOCK will fail.\n", try ? "TRY" : "");
00231          ast_datastore_free(lock_store);
00232          return -1;
00233       }
00234 
00235       lock_store->data = list;
00236       AST_LIST_HEAD_INIT(list);
00237       ast_channel_datastore_add(chan, lock_store);
00238    } else
00239       list = lock_store->data;
00240 
00241    /* Lock already exists? */
00242    AST_LIST_LOCK(&locklist);
00243    AST_LIST_TRAVERSE(&locklist, current, entries) {
00244       if (strcmp(current->name, lockname) == 0) {
00245          break;
00246       }
00247    }
00248 
00249    if (!current) {
00250       if (unloading) {
00251          /* Don't bother */
00252          AST_LIST_UNLOCK(&locklist);
00253          return -1;
00254       }
00255 
00256       /* Create new lock entry */
00257       current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
00258       if (!current) {
00259          AST_LIST_UNLOCK(&locklist);
00260          return -1;
00261       }
00262 
00263       strcpy(current->name, lockname); /* SAFE */
00264       if ((res = ast_mutex_init(&current->mutex))) {
00265          ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
00266          ast_free(current);
00267          AST_LIST_UNLOCK(&locklist);
00268          return -1;
00269       }
00270       if ((res = ast_cond_init(&current->cond, NULL))) {
00271          ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
00272          ast_mutex_destroy(&current->mutex);
00273          ast_free(current);
00274          AST_LIST_UNLOCK(&locklist);
00275          return -1;
00276       }
00277       if (!(current->requesters = ao2_container_alloc(7, null_hash_cb, null_cmp_cb))) {
00278          ast_mutex_destroy(&current->mutex);
00279          ast_cond_destroy(&current->cond);
00280          ast_free(current);
00281          AST_LIST_UNLOCK(&locklist);
00282          return -1;
00283       }
00284       AST_LIST_INSERT_TAIL(&locklist, current, entries);
00285    }
00286    AST_LIST_UNLOCK(&locklist);
00287 
00288    /* Found lock or created one - now find or create the corresponding link in the channel */
00289    AST_LIST_LOCK(list);
00290    AST_LIST_TRAVERSE(list, clframe, list) {
00291       if (clframe->lock_frame == current) {
00292          break;
00293       }
00294    }
00295 
00296    if (!clframe) {
00297       if (unloading) {
00298          /* Don't bother */
00299          AST_LIST_UNLOCK(list);
00300          return -1;
00301       }
00302 
00303       if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
00304          ast_log(LOG_ERROR, "Unable to allocate channel lock frame.  %sLOCK will fail.\n", try ? "TRY" : "");
00305          AST_LIST_UNLOCK(list);
00306          return -1;
00307       }
00308 
00309       clframe->lock_frame = current;
00310       clframe->channel = chan;
00311       AST_LIST_INSERT_TAIL(list, clframe, list);
00312    }
00313    AST_LIST_UNLOCK(list);
00314 
00315    /* If we already own the lock, then we're being called recursively.
00316     * Keep track of how many times that is, because we need to unlock
00317     * the same amount, before we'll release this one.
00318     */
00319    if (current->owner == chan) {
00320       current->count++;
00321       return 0;
00322    }
00323 
00324    /* Link is just an empty flag, used to check whether more than one channel
00325     * is contending for the lock. */
00326    if (!(link = ao2_alloc(sizeof(*link), NULL))) {
00327       return -1;
00328    }
00329 
00330    /* Okay, we have both frames, so now we need to try to lock.
00331     *
00332     * Locking order: always lock locklist first.  We need the
00333     * locklist lock because the broker thread counts whether
00334     * there are requesters with the locklist lock held, and we
00335     * need to hold it, so that when we send our signal, below,
00336     * to wake up the broker thread, it definitely will see that
00337     * a requester exists at that point in time.  Otherwise, we
00338     * could add to the requesters after it has already seen that
00339     * that lock is unoccupied and wait forever for another signal.
00340     */
00341    AST_LIST_LOCK(&locklist);
00342    ast_mutex_lock(&current->mutex);
00343    /* Add to requester list */
00344    ao2_link(current->requesters, link);
00345    pthread_kill(broker_tid, SIGURG);
00346    AST_LIST_UNLOCK(&locklist);
00347 
00348    if ((!current->owner) ||
00349       (!try && !(res = ast_cond_timedwait(&current->cond, &current->mutex, &three_seconds)))) {
00350       res = 0;
00351       current->owner = chan;
00352       current->count++;
00353    } else {
00354       res = -1;
00355    }
00356    /* Remove from requester list */
00357    ao2_unlink(current->requesters, link);
00358    ao2_ref(link, -1);
00359    ast_mutex_unlock(&current->mutex);
00360 
00361    return res;
00362 }

static int load_module ( void   )  [static]
static void* lock_broker ( void *  unused  )  [static]

Definition at line 165 of file func_lock.c.

References ao2_container_count(), ast_cond_signal(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), lock_frame::cond, lock_frame::mutex, lock_frame::owner, and lock_frame::requesters.

Referenced by load_module().

00166 {
00167    struct lock_frame *frame;
00168    struct timespec forever = { 1000000, 0 };
00169    for (;;) {
00170       int found_requester = 0;
00171 
00172       /* Test for cancel outside of the lock */
00173       pthread_testcancel();
00174       AST_LIST_LOCK(&locklist);
00175 
00176       AST_LIST_TRAVERSE(&locklist, frame, entries) {
00177          if (ao2_container_count(frame->requesters)) {
00178             found_requester++;
00179             ast_mutex_lock(&frame->mutex);
00180             if (!frame->owner) {
00181                ast_cond_signal(&frame->cond);
00182             }
00183             ast_mutex_unlock(&frame->mutex);
00184          }
00185       }
00186 
00187       AST_LIST_UNLOCK(&locklist);
00188       pthread_testcancel();
00189 
00190       /* If there are no requesters, then wait for a signal */
00191       if (!found_requester) {
00192          nanosleep(&forever, NULL);
00193       } else {
00194          sched_yield();
00195       }
00196    }
00197    /* Not reached */
00198    return NULL;
00199 }

static void lock_fixup ( void *  data,
struct ast_channel oldchan,
struct ast_channel newchan 
) [static]

Definition at line 143 of file func_lock.c.

References ast_channel_datastore_find(), AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, channel_lock_frame::channel, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, and lock_frame::owner.

00144 {
00145    struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
00146    AST_LIST_HEAD(, channel_lock_frame) *list;
00147    struct channel_lock_frame *clframe = NULL;
00148 
00149    if (!lock_store) {
00150       return;
00151    }
00152    list = lock_store->data;
00153 
00154    AST_LIST_LOCK(list);
00155    AST_LIST_TRAVERSE(list, clframe, list) {
00156       if (clframe->lock_frame->owner == oldchan) {
00157          clframe->lock_frame->owner = newchan;
00158       }
00159       /* We don't move requesters, because the thread stack is different */
00160       clframe->channel = newchan;
00161    }
00162    AST_LIST_UNLOCK(list);
00163 }

static void lock_free ( void *  data  )  [static]

Definition at line 125 of file func_lock.c.

References ast_free, AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, channel_lock_frame::channel, lock_frame::count, channel_lock_frame::lock_frame, and lock_frame::owner.

00126 {
00127    AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
00128    struct channel_lock_frame *clframe;
00129    AST_LIST_LOCK(oldlist);
00130    while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00131       /* Only unlock if we own the lock */
00132       if (clframe->channel == clframe->lock_frame->owner) {
00133          clframe->lock_frame->count = 0;
00134          clframe->lock_frame->owner = NULL;
00135       }
00136       ast_free(clframe);
00137    }
00138    AST_LIST_UNLOCK(oldlist);
00139    AST_LIST_HEAD_DESTROY(oldlist);
00140    ast_free(oldlist);
00141 }

static int lock_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 408 of file func_lock.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), and get_lock().

00409 {
00410    if (chan)
00411       ast_autoservice_start(chan);
00412 
00413    ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00414 
00415    if (chan)
00416       ast_autoservice_stop(chan);
00417 
00418    return 0;
00419 }

static int null_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 206 of file func_lock.c.

References CMP_MATCH.

Referenced by get_lock().

00207 {
00208    return obj == arg ? CMP_MATCH : 0;
00209 }

static int null_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 201 of file func_lock.c.

Referenced by get_lock().

00202 {
00203    return (int)(long) obj;
00204 }

static int trylock_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 421 of file func_lock.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), and get_lock().

00422 {
00423    if (chan)
00424       ast_autoservice_start(chan);
00425 
00426    ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00427 
00428    if (chan)
00429       ast_autoservice_stop(chan);
00430 
00431    return 0;
00432 }

static int unload_module ( void   )  [static]

Definition at line 449 of file func_lock.c.

References ao2_container_count(), ao2_ref, ast_custom_function_unregister(), ast_free, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_mutex_destroy(), lock_frame::mutex, lock_frame::owner, and lock_frame::requesters.

00450 {
00451    struct lock_frame *current;
00452 
00453    /* Module flag */
00454    unloading = 1;
00455 
00456    AST_LIST_LOCK(&locklist);
00457    while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00458       /* If any locks are currently in use, then we cannot unload this module */
00459       if (current->owner || ao2_container_count(current->requesters)) {
00460          /* Put it back */
00461          AST_LIST_INSERT_HEAD(&locklist, current, entries);
00462          AST_LIST_UNLOCK(&locklist);
00463          unloading = 0;
00464          return -1;
00465       }
00466       ast_mutex_destroy(&current->mutex);
00467       ao2_ref(current->requesters, -1);
00468       ast_free(current);
00469    }
00470 
00471    /* No locks left, unregister functions */
00472    ast_custom_function_unregister(&lock_function);
00473    ast_custom_function_unregister(&trylock_function);
00474    ast_custom_function_unregister(&unlock_function);
00475 
00476    pthread_cancel(broker_tid);
00477    pthread_kill(broker_tid, SIGURG);
00478    pthread_join(broker_tid, NULL);
00479 
00480    AST_LIST_UNLOCK(&locklist);
00481 
00482    return 0;
00483 }

static int unlock_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 364 of file func_lock.c.

References ast_channel_datastore_find(), ast_copy_string(), ast_debug, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), lock_frame::count, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, LOG_WARNING, lock_frame::name, and lock_frame::owner.

00365 {
00366    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00367    struct channel_lock_frame *clframe;
00368    AST_LIST_HEAD(, channel_lock_frame) *list;
00369 
00370    if (!lock_store) {
00371       ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
00372       ast_copy_string(buf, "0", len);
00373       return 0;
00374    }
00375 
00376    if (!(list = lock_store->data)) {
00377       ast_debug(1, "This should NEVER happen\n");
00378       ast_copy_string(buf, "0", len);
00379       return 0;
00380    }
00381 
00382    /* Find item in the channel list */
00383    AST_LIST_LOCK(list);
00384    AST_LIST_TRAVERSE(list, clframe, list) {
00385       if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00386          break;
00387       }
00388    }
00389    /* We never destroy anything until channel destruction, which will never
00390     * happen while this routine is executing, so we don't need to hold the
00391     * lock beyond this point. */
00392    AST_LIST_UNLOCK(list);
00393 
00394    if (!clframe) {
00395       /* We didn't have this lock in the first place */
00396       ast_copy_string(buf, "0", len);
00397       return 0;
00398    }
00399 
00400    if (--clframe->lock_frame->count == 0) {
00401       clframe->lock_frame->owner = NULL;
00402    }
00403 
00404    ast_copy_string(buf, "1", len);
00405    return 0;
00406 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialplan mutexes" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, } [static]

Definition at line 494 of file func_lock.c.

Definition at line 494 of file func_lock.c.

pthread_t broker_tid = AST_PTHREADT_NULL [static]

Definition at line 96 of file func_lock.c.

Initial value:
 {
   .name = "LOCK",
   .read = lock_read,
}

Definition at line 434 of file func_lock.c.

struct ast_datastore_info lock_info [static]
Initial value:
 {
   .type = "MUTEX",
   .destroy = lock_free,
   .chan_fixup = lock_fixup,
}

Definition at line 98 of file func_lock.c.

Initial value:
 {
   .name = "TRYLOCK",
   .read = trylock_read,
}

Definition at line 439 of file func_lock.c.

int unloading = 0 [static]

Definition at line 95 of file func_lock.c.

Initial value:
 {
   .name = "UNLOCK",
   .read = unlock_read,
}

Definition at line 444 of file func_lock.c.


Generated by  doxygen 1.6.2