/*! \brief Observer callback for when a contact is created */ static void contact_expiration_observer_created(const void *object) { const struct ast_sip_contact *contact = object; struct contact_expiration *expiration; int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow())); if (ast_tvzero(contact->expiration_time)) { return; } expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!expiration) { return; } expiration->contact = (struct ast_sip_contact*)contact; ao2_ref(expiration->contact, +1); ao2_ref(expiration, +1); if ((expiration->sched = ast_sched_add(sched, expires, contact_expiration_expire, expiration)) < 0) { ao2_ref(expiration, -1); ast_log(LOG_ERROR, "Scheduled expiration for contact '%s' could not be performed, contact may persist past life\n", ast_sorcery_object_get_id(contact)); } else { ao2_link(contact_autoexpire, expiration); } ao2_ref(expiration, -1); }
/* * This function is run in the context of the serializer. * It runs the task with a simple call and reschedules based on the result. */ static int run_task(void *data) { RAII_VAR(struct ast_sip_sched_task *, schtd, ao2_bump(data), ao2_cleanup); int res; int delay; ao2_lock(schtd); schtd->last_start = ast_tvnow(); schtd->is_running = 1; schtd->run_count++; ao2_unlock(schtd); res = schtd->task(schtd->task_data); ao2_lock(schtd); schtd->is_running = 0; schtd->last_end = ast_tvnow(); /* * Don't restart if the task returned 0 or if the interval * was set to 0 while the task was running */ if (!res || !schtd->interval) { schtd->interval = 0; ao2_unlock(schtd); ao2_unlink(tasks, schtd); return -1; } if (schtd->flags & AST_SIP_SCHED_TASK_VARIABLE) { schtd->interval = res; } if (schtd->flags & AST_SIP_SCHED_TASK_DELAY) { delay = schtd->interval; } else { delay = schtd->interval - (ast_tvdiff_ms(schtd->last_end, schtd->last_start) % schtd->interval); } schtd->current_scheduler_id = ast_sched_add(scheduler_context, delay, push_to_serializer, (const void *)schtd); if (schtd->current_scheduler_id < 0) { schtd->interval = 0; ao2_unlock(schtd); ao2_unlink(tasks, schtd); return -1; } ao2_unlock(schtd); return 0; }
struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *serializer, int interval, ast_sip_task sip_task, char *name, void *task_data, enum ast_sip_scheduler_task_flags flags) { #define ID_LEN 13 /* task_deadbeef */ struct ast_sip_sched_task *schtd; int res; if (interval < 0) { return NULL; } schtd = ao2_alloc((sizeof(*schtd) + (!ast_strlen_zero(name) ? strlen(name) : ID_LEN) + 1), schtd_destructor); if (!schtd) { return NULL; } schtd->task_id = ast_atomic_fetchadd_int(&task_count, 1); schtd->serializer = serializer; schtd->task = sip_task; if (!ast_strlen_zero(name)) { strcpy(schtd->name, name); /* Safe */ } else { sprintf(schtd->name, "task_%08x", schtd->task_id); } schtd->task_data = task_data; schtd->flags = flags; schtd->interval = interval; schtd->when_queued = ast_tvnow(); if (flags & AST_SIP_SCHED_TASK_DATA_AO2) { ao2_ref(task_data, +1); } res = ast_sched_add(scheduler_context, interval, push_to_serializer, (const void *)schtd); if (res < 0) { ao2_ref(schtd, -1); return NULL; } else { schtd->current_scheduler_id = res; ao2_link(tasks, schtd); } return schtd; #undef ID_LEN }
/*! \brief Query resolution callback */ static void dns_query_recurring_resolution_callback(const struct ast_dns_query *query) { struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query); struct ast_dns_query *callback_query; /* Create a separate query to invoke the user specific callback on as the * recurring query user data may get used externally (by the unit test) * and thus changing it is problematic */ callback_query = dns_query_alloc(query->name, query->rr_type, query->rr_class, recurring->callback, recurring->user_data); if (callback_query) { /* The result is immutable at this point and can be safely provided */ callback_query->result = query->result; callback_query->callback(callback_query); callback_query->result = NULL; ao2_ref(callback_query, -1); } ao2_lock(recurring); /* So.. if something has not externally cancelled this we can reschedule based on the TTL */ if (!recurring->cancelled) { const struct ast_dns_result *result = ast_dns_query_get_result(query); int ttl = MIN(ast_dns_result_get_lowest_ttl(result), INT_MAX / 1000); if (ttl) { recurring->timer = ast_sched_add(ast_dns_get_sched(), ttl * 1000, dns_query_recurring_scheduled_callback, ao2_bump(recurring)); if (recurring->timer < 0) { /* It is impossible for this to be the last reference as the query has a reference to it */ ao2_ref(recurring, -1); } } } ao2_replace(recurring->active, NULL); ao2_unlock(recurring); }
static char *handle_cli_sched_bench(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ast_sched_context *con; struct timeval start; unsigned int num, i; int *sched_ids = NULL; switch (cmd) { case CLI_INIT: e->command = "sched benchmark"; e->usage = "" "Usage: sched benchmark <num>\n" ""; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != e->args + 1) { return CLI_SHOWUSAGE; } if (sscanf(a->argv[e->args], "%u", &num) != 1) { return CLI_SHOWUSAGE; } if (!(con = ast_sched_context_create())) { ast_cli(a->fd, "Test failed - could not create scheduler context\n"); return CLI_FAILURE; } if (!(sched_ids = ast_malloc(sizeof(*sched_ids) * num))) { ast_cli(a->fd, "Test failed - memory allocation failure\n"); goto return_cleanup; } ast_cli(a->fd, "Testing ast_sched_add() performance - timing how long it takes " "to add %u entries at random time intervals from 0 to 60 seconds\n", num); start = ast_tvnow(); for (i = 0; i < num; i++) { int when = abs(ast_random()) % 60000; if ((sched_ids[i] = ast_sched_add(con, when, sched_cb, NULL)) == -1) { ast_cli(a->fd, "Test failed - sched_add returned -1\n"); goto return_cleanup; } } ast_cli(a->fd, "Test complete - %" PRIi64 " us\n", ast_tvdiff_us(ast_tvnow(), start)); ast_cli(a->fd, "Testing ast_sched_del() performance - timing how long it takes " "to delete %u entries with random time intervals from 0 to 60 seconds\n", num); start = ast_tvnow(); for (i = 0; i < num; i++) { if (ast_sched_del(con, sched_ids[i]) == -1) { ast_cli(a->fd, "Test failed - sched_del returned -1\n"); goto return_cleanup; } } ast_cli(a->fd, "Test complete - %" PRIi64 " us\n", ast_tvdiff_us(ast_tvnow(), start)); return_cleanup: ast_sched_context_destroy(con); if (sched_ids) { ast_free(sched_ids); } return CLI_SUCCESS; }
static int ast_read_callback(void *data) { u_int16_t size; u_int32_t delay = -1; int looper = 1; int retval = 0; int res; struct ast_filestream *s = data; /* Send a frame from the file to the appropriate channel */ while(looper) { if (read(s->fd, &size, 2) != 2) { /* Out of data, or the file is no longer valid. In any case go ahead and stop the stream */ s->owner->streamid = -1; return 0; } /* Looks like we have a frame to read from here */ size = ntohs(size); if (size > G723_MAX_SIZE - sizeof(struct ast_frame)) { ast_log(LOG_WARNING, "Size %d is invalid\n", size); /* The file is apparently no longer any good, as we shouldn't ever get frames even close to this size. */ s->owner->streamid = -1; return 0; } /* Read the data into the buffer */ s->fr->offset = AST_FRIENDLY_OFFSET; s->fr->datalen = size; s->fr->data = s->buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET; if ((res = read(s->fd, s->fr->data , size)) != size) { ast_log(LOG_WARNING, "Short read (%d of %d bytes) (%s)!\n", res, size, strerror(errno)); s->owner->streamid = -1; return 0; } /* Read the delay for the next packet, and schedule again if necessary */ if (read(s->fd, &delay, 4) == 4) delay = ntohl(delay); else delay = -1; #if 0 /* Average out frames <= 50 ms */ if (delay < 50) s->fr->timelen = 30; else s->fr->timelen = delay; #else s->fr->samples = 240; #endif /* Unless there is no delay, we're going to exit out as soon as we have processed the current frame. */ if (delay > VOFR_FUDGE) { looper = 0; /* If there is a delay, lets schedule the next event */ if (delay != s->lasttimeout) { /* We'll install the next timeout now. */ s->owner->streamid = ast_sched_add(s->owner->sched, delay - VOFR_FUDGE, ast_read_callback, s); s->lasttimeout = delay; } else /* Just come back again at the same time */ retval = -1; } /* Lastly, process the frame */ if (ast_write(s->owner, s->fr)) { ast_log(LOG_WARNING, "Failed to write frame\n"); s->owner->streamid = -1; return 0; } } return retval; }