aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--channels/chan_sip.c145
-rw-r--r--include/asterisk/sched.h11
2 files changed, 116 insertions, 40 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 6eeea7e56..8ebe9d520 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -2475,15 +2475,6 @@ static void sip_destroy_peer(struct sip_peer *peer)
peer->chanvars = NULL;
}
- /* If the schedule delete fails, that means the schedule is currently
- * running, which means we should wait for that thread to complete.
- * Otherwise, there's a crashable race condition.
- *
- * NOTE: once peer is refcounted, this probably is no longer necessary.
- */
- AST_SCHED_DEL(sched, peer->expire);
- AST_SCHED_DEL(sched, peer->pokeexpire);
-
register_peer_exten(peer, FALSE);
ast_free_ha(peer->ha);
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT))
@@ -2636,8 +2627,15 @@ static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_i
/* Cache peer */
ast_copy_flags(&peer->flags[1],&global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
- AST_SCHED_DEL(sched, peer->expire);
- peer->expire = ast_sched_add(sched, (global_rtautoclear) * 1000, expire_register, (void *)peer);
+ if (!AST_SCHED_DEL(sched, peer->expire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+ peer->expire = ast_sched_add(sched, (global_rtautoclear) * 1000, expire_register, ASTOBJ_REF(peer));
+ if (peer->expire == -1) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
}
ASTOBJ_CONTAINER_LINK(&peerl,peer);
} else {
@@ -7917,20 +7915,26 @@ static int expire_register(const void *data)
*/
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT) ||
ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
- peer = ASTOBJ_CONTAINER_UNLINK(&peerl, peer); /* Remove from peer list */
- ASTOBJ_UNREF(peer, sip_destroy_peer); /* Remove from memory */
+ peer = ASTOBJ_CONTAINER_UNLINK(&peerl, peer);
+ ASTOBJ_UNREF(peer, sip_destroy_peer);
}
+ ASTOBJ_UNREF(peer, sip_destroy_peer);
+
return 0;
}
/*! \brief Poke peer (send qualify to check if peer is alive and well) */
static int sip_poke_peer_s(const void *data)
{
- struct sip_peer *peer = (struct sip_peer *)data;
+ struct sip_peer *peer = (struct sip_peer *) data;
peer->pokeexpire = -1;
+
sip_poke_peer(peer);
+
+ ASTOBJ_UNREF(peer, sip_destroy_peer);
+
return 0;
}
@@ -7983,12 +7987,26 @@ static void reg_source_db(struct sip_peer *peer)
peer->addr.sin_port = htons(port);
if (sipsock < 0) {
/* SIP isn't up yet, so schedule a poke only, pretty soon */
- AST_SCHED_DEL(sched, peer->pokeexpire);
- peer->pokeexpire = ast_sched_add(sched, ast_random() % 5000 + 1, sip_poke_peer_s, peer);
+ if (!AST_SCHED_DEL(sched, peer->pokeexpire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+ peer->pokeexpire = ast_sched_add(sched, ast_random() % 5000 + 1, sip_poke_peer_s, ASTOBJ_REF(peer));
+ if (peer->pokeexpire == -1) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
} else
sip_poke_peer(peer);
- AST_SCHED_DEL(sched, peer->expire);
- peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, peer);
+ if (!AST_SCHED_DEL(sched, peer->expire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+ peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, ASTOBJ_REF(peer));
+ if (peer->expire == -1) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
register_peer_exten(peer, TRUE);
}
@@ -8123,7 +8141,10 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
} else if (!strcasecmp(curi, "*") || !expiry) { /* Unregister this peer */
/* This means remove all registrations and return OK */
memset(&peer->addr, 0, sizeof(peer->addr));
- AST_SCHED_DEL(sched, peer->expire);
+ if (!AST_SCHED_DEL(sched, peer->expire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
destroy_association(peer);
@@ -8188,13 +8209,23 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
if (curi && ast_strlen_zero(peer->username))
ast_copy_string(peer->username, curi, sizeof(peer->username));
- AST_SCHED_DEL(sched, peer->expire);
+ if (!AST_SCHED_DEL(sched, peer->expire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
if (expiry > max_expiry)
expiry = max_expiry;
if (expiry < min_expiry)
expiry = min_expiry;
- peer->expire = ast_test_flag(&peer->flags[0], SIP_REALTIME) ? -1 :
- ast_sched_add(sched, (expiry + 10) * 1000, expire_register, peer);
+ if (ast_test_flag(&peer->flags[0], SIP_REALTIME)) {
+ peer->expire = -1;
+ } else {
+ peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, ASTOBJ_REF(peer));
+ if (peer->expire == -1) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+ }
pvt->expiry = expiry;
snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expiry, peer->username, peer->fullcontact);
if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT))
@@ -12624,13 +12655,20 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
peer->name, s, pingtime);
}
- AST_SCHED_DEL(sched, peer->pokeexpire);
+ if (!AST_SCHED_DEL(sched, peer->pokeexpire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
/* Try again eventually */
peer->pokeexpire = ast_sched_add(sched,
is_reachable ? DEFAULT_FREQ_OK : DEFAULT_FREQ_NOTOK,
- sip_poke_peer_s, peer);
+ sip_poke_peer_s, ASTOBJ_REF(peer));
+ if (peer->pokeexpire == -1) {
+ ASTOBJ_UNREF(peer, sip_destroy_peer);
+ }
}
/*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */
@@ -15798,9 +15836,20 @@ static int sip_poke_noanswer(const void *data)
peer->call = NULL;
peer->lastms = -1;
ast_device_state_changed("SIP/%s", peer->name);
- /* Try again quickly */
- AST_SCHED_DEL(sched, peer->pokeexpire);
+
+ /* This function gets called one place outside of the scheduler ... */
+ if (!AST_SCHED_DEL(sched, peer->pokeexpire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+
+ /* There is no need to ASTOBJ_REF() here. Just let the scheduled callback
+ * inherit the reference that the current callback already has. */
peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
+ if (peer->pokeexpire == -1) {
+ ASTOBJ_UNREF(peer, sip_destroy_peer);
+ }
+
return 0;
}
@@ -15815,7 +15864,10 @@ static int sip_poke_peer(struct sip_peer *peer)
if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
/* IF we have no IP, or this isn't to be monitored, return
imeediately after clearing things out */
- AST_SCHED_DEL(sched, peer->pokeexpire);
+ if (!AST_SCHED_DEL(sched, peer->pokeexpire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
peer->lastms = 0;
peer->call = NULL;
return 0;
@@ -15848,7 +15900,11 @@ static int sip_poke_peer(struct sip_peer *peer)
build_via(p);
build_callid_pvt(p);
- AST_SCHED_DEL(sched, peer->pokeexpire);
+ if (!AST_SCHED_DEL(sched, peer->pokeexpire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+
p->relatedpeer = peer;
ast_set_flag(&p->flags[0], SIP_OUTGOING);
#ifdef VOCAL_DATA_HACK
@@ -15858,11 +15914,18 @@ static int sip_poke_peer(struct sip_peer *peer)
xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2);
#endif
gettimeofday(&peer->ps, NULL);
- if (xmitres == XMIT_ERROR)
- sip_poke_noanswer(peer); /* Immediately unreachable, network problems */
- else {
- AST_SCHED_DEL(sched, peer->pokeexpire);
- peer->pokeexpire = ast_sched_add(sched, peer->maxms * 2, sip_poke_noanswer, peer);
+ if (xmitres == XMIT_ERROR) {
+ sip_poke_noanswer(ASTOBJ_REF(peer)); /* Immediately unreachable, network problems */
+ } else {
+ if (!AST_SCHED_DEL(sched, peer->pokeexpire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
+ peer->pokeexpire = ast_sched_add(sched, peer->maxms * 2, sip_poke_noanswer, ASTOBJ_REF(peer));
+ if (peer->pokeexpire == -1) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
}
return 0;
@@ -16664,7 +16727,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
}
} else {
/* Non-dynamic. Make sure we become that way if we're not */
- AST_SCHED_DEL(sched, peer->expire);
+ if (!AST_SCHED_DEL(sched, peer->expire)) {
+ struct sip_peer *peer_ptr = peer;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
ast_clear_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC);
if (!obproxyfound || !strcasecmp(v->name, "outboundproxy")) {
if (ast_get_ip_or_srv(&peer->addr, v->value, srvlookup ? "_sip._udp" : NULL)) {
@@ -17841,9 +17907,16 @@ static void sip_poke_all_peers(void)
ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
ASTOBJ_WRLOCK(iterator);
- AST_SCHED_DEL(sched, iterator->pokeexpire);
+ if (!AST_SCHED_DEL(sched, iterator->pokeexpire)) {
+ struct sip_peer *peer_ptr = iterator;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
ms += 100;
- iterator->pokeexpire = ast_sched_add(sched, ms, sip_poke_peer_s, iterator);
+ iterator->pokeexpire = ast_sched_add(sched, ms, sip_poke_peer_s, ASTOBJ_REF(iterator));
+ if (iterator->pokeexpire == -1) {
+ struct sip_peer *peer_ptr = iterator;
+ ASTOBJ_UNREF(peer_ptr, sip_destroy_peer);
+ }
ASTOBJ_UNLOCK(iterator);
} while (0)
);
diff --git a/include/asterisk/sched.h b/include/asterisk/sched.h
index d80beb5bc..829a1b0f6 100644
--- a/include/asterisk/sched.h
+++ b/include/asterisk/sched.h
@@ -54,14 +54,17 @@ extern "C" {
* macro should NOT be used.
*/
#define AST_SCHED_DEL(sched, id) \
- do { \
+ ({ \
int _count = 0; \
- while (id > -1 && ast_sched_del(sched, id) && ++_count < 10) \
+ int _sched_res = -1; \
+ while (id > -1 && (_sched_res = ast_sched_del(sched, id)) && ++_count < 10) \
usleep(1); \
- if (_count == 10 && option_debug > 2) \
+ if (_count == 10 && option_debug > 2) { \
ast_log(LOG_DEBUG, "Unable to cancel schedule ID %d.\n", id); \
+ } \
id = -1; \
- } while (0);
+ (_sched_res); \
+ })
struct sched_context;