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 ast_channel_cmp_cb (void *obj, void *arg, int flags)
static int ast_channel_hash_cb (const void *obj, const int flags)
static int get_lock (struct ast_channel *chan, char *lockname, int trylock)
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 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_LOAD_ORDER , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, .support_level = AST_MODULE_SUPPORT_CORE, }
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 534 of file func_lock.c.

static void __unreg_module ( void   )  [static]

Definition at line 534 of file func_lock.c.

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

Definition at line 226 of file func_lock.c.

References ast_channel_name(), and CMP_MATCH.

Referenced by ast_channels_init(), and get_lock().

00227 {
00228    struct ast_channel *chan = obj, *cmp_args = arg;
00229    return strcasecmp(ast_channel_name(chan), ast_channel_name(cmp_args)) ? 0 : CMP_MATCH;
00230 }

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

Definition at line 220 of file func_lock.c.

References ast_channel_name(), and ast_str_case_hash().

Referenced by ast_channels_init(), and get_lock().

00221 {
00222    const struct ast_channel *chan = obj;
00223    return ast_str_case_hash(ast_channel_name(chan));
00224 }

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

Definition at line 232 of file func_lock.c.

References ao2_container_alloc, ao2_link, ao2_unlink, ast_calloc, ast_channel_cmp_cb(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_hash_cb(), ast_channel_name(), 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, ast_tvnow(), 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, NULL, lock_frame::owner, and lock_frame::requesters.

Referenced by lock_read(), and trylock_read().

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

static int load_module ( void   )  [static]

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

Definition at line 184 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, NULL, lock_frame::owner, and lock_frame::requesters.

Referenced by load_module().

00185 {
00186    struct lock_frame *frame;
00187    struct timespec forever = { 1000000, 0 };
00188    for (;;) {
00189       int found_requester = 0;
00190 
00191       /* Test for cancel outside of the lock */
00192       pthread_testcancel();
00193       AST_LIST_LOCK(&locklist);
00194 
00195       AST_LIST_TRAVERSE(&locklist, frame, entries) {
00196          if (ao2_container_count(frame->requesters)) {
00197             found_requester++;
00198             ast_mutex_lock(&frame->mutex);
00199             if (!frame->owner) {
00200                ast_cond_signal(&frame->cond);
00201             }
00202             ast_mutex_unlock(&frame->mutex);
00203          }
00204       }
00205 
00206       AST_LIST_UNLOCK(&locklist);
00207       pthread_testcancel();
00208 
00209       /* If there are no requesters, then wait for a signal */
00210       if (!found_requester) {
00211          nanosleep(&forever, NULL);
00212       } else {
00213          sched_yield();
00214       }
00215    }
00216    /* Not reached */
00217    return NULL;
00218 }

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

Definition at line 162 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, NULL, and lock_frame::owner.

00163 {
00164    struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
00165    AST_LIST_HEAD(, channel_lock_frame) *list;
00166    struct channel_lock_frame *clframe = NULL;
00167 
00168    if (!lock_store) {
00169       return;
00170    }
00171    list = lock_store->data;
00172 
00173    AST_LIST_LOCK(list);
00174    AST_LIST_TRAVERSE(list, clframe, list) {
00175       if (clframe->lock_frame->owner == oldchan) {
00176          clframe->lock_frame->owner = newchan;
00177       }
00178       /* We don't move requesters, because the thread stack is different */
00179       clframe->channel = newchan;
00180    }
00181    AST_LIST_UNLOCK(list);
00182 }

static void lock_free ( void *  data  )  [static]

Definition at line 144 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, NULL, and lock_frame::owner.

00145 {
00146    AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
00147    struct channel_lock_frame *clframe;
00148    AST_LIST_LOCK(oldlist);
00149    while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00150       /* Only unlock if we own the lock */
00151       if (clframe->channel == clframe->lock_frame->owner) {
00152          clframe->lock_frame->count = 0;
00153          clframe->lock_frame->owner = NULL;
00154       }
00155       ast_free(clframe);
00156    }
00157    AST_LIST_UNLOCK(oldlist);
00158    AST_LIST_HEAD_DESTROY(oldlist);
00159    ast_free(oldlist);
00160 }

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

Definition at line 438 of file func_lock.c.

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

00439 {
00440    if (!chan) {
00441       return -1;
00442    }
00443    ast_autoservice_start(chan);
00444    ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00445    ast_autoservice_stop(chan);
00446 
00447    return 0;
00448 }

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

Definition at line 450 of file func_lock.c.

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

00451 {
00452    if (!chan) {
00453       return -1;
00454    }
00455    ast_autoservice_start(chan);
00456    ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00457    ast_autoservice_stop(chan);
00458 
00459    return 0;
00460 }

static int unload_module ( void   )  [static]

Definition at line 480 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, AST_PTHREADT_NULL, lock_frame::mutex, NULL, lock_frame::owner, and lock_frame::requesters.

00481 {
00482    struct lock_frame *current;
00483 
00484    /* Module flag */
00485    unloading = 1;
00486 
00487    AST_LIST_LOCK(&locklist);
00488    while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00489       /* If any locks are currently in use, then we cannot unload this module */
00490       if (current->owner || ao2_container_count(current->requesters)) {
00491          /* Put it back */
00492          AST_LIST_INSERT_HEAD(&locklist, current, entries);
00493          AST_LIST_UNLOCK(&locklist);
00494          unloading = 0;
00495          return -1;
00496       }
00497       ast_mutex_destroy(&current->mutex);
00498       ao2_ref(current->requesters, -1);
00499       ast_free(current);
00500    }
00501 
00502    /* No locks left, unregister functions */
00503    ast_custom_function_unregister(&lock_function);
00504    ast_custom_function_unregister(&trylock_function);
00505    ast_custom_function_unregister(&unlock_function);
00506 
00507    if (broker_tid != AST_PTHREADT_NULL) {
00508       pthread_cancel(broker_tid);
00509       pthread_kill(broker_tid, SIGURG);
00510       pthread_join(broker_tid, NULL);
00511    }
00512 
00513    AST_LIST_UNLOCK(&locklist);
00514 
00515    return 0;
00516 }

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

Definition at line 389 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, NULL, and lock_frame::owner.

00390 {
00391    struct ast_datastore *lock_store;
00392    struct channel_lock_frame *clframe;
00393    AST_LIST_HEAD(, channel_lock_frame) *list;
00394 
00395    if (!chan) {
00396       return -1;
00397    }
00398 
00399    lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00400    if (!lock_store) {
00401       ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
00402       ast_copy_string(buf, "0", len);
00403       return 0;
00404    }
00405 
00406    if (!(list = lock_store->data)) {
00407       ast_debug(1, "This should NEVER happen\n");
00408       ast_copy_string(buf, "0", len);
00409       return 0;
00410    }
00411 
00412    /* Find item in the channel list */
00413    AST_LIST_LOCK(list);
00414    AST_LIST_TRAVERSE(list, clframe, list) {
00415       if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00416          break;
00417       }
00418    }
00419    /* We never destroy anything until channel destruction, which will never
00420     * happen while this routine is executing, so we don't need to hold the
00421     * lock beyond this point. */
00422    AST_LIST_UNLOCK(list);
00423 
00424    if (!clframe) {
00425       /* We didn't have this lock in the first place */
00426       ast_copy_string(buf, "0", len);
00427       return 0;
00428    }
00429 
00430    if (--clframe->lock_frame->count == 0) {
00431       clframe->lock_frame->owner = NULL;
00432    }
00433 
00434    ast_copy_string(buf, "1", len);
00435    return 0;
00436 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, .support_level = AST_MODULE_SUPPORT_CORE, } [static]

Definition at line 534 of file func_lock.c.

Definition at line 534 of file func_lock.c.

pthread_t broker_tid = AST_PTHREADT_NULL [static]

Definition at line 115 of file func_lock.c.

Initial value:

 {
   .name = "LOCK",
   .read = lock_read,
   .read_max = 2,
}

Definition at line 462 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 117 of file func_lock.c.

Initial value:

 {
   .name = "TRYLOCK",
   .read = trylock_read,
   .read_max = 2,
}

Definition at line 468 of file func_lock.c.

int unloading = 0 [static]

Definition at line 114 of file func_lock.c.

Referenced by sip_outbound_publish_client_destroy(), and unload_module().

Initial value:

 {
   .name = "UNLOCK",
   .read = unlock_read,
   .read_max = 2,
}

Definition at line 474 of file func_lock.c.


Generated on Thu Apr 16 06:31:19 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6