/*! \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);
}
Example #2
0
/*
 * 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;
}
Example #3
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
}
Example #4
0
/*! \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);
}
Example #5
0
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;
}
Example #6
0
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;
}