belle_sip_loop.c 8.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
	belle-sip - SIP (RFC3261) library.
    Copyright (C) 2010  Belledonne Communications SARL

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "belle-sip/belle-sip.h"
#include "belle_sip_internal.h"

22 23 24
#include <unistd.h>
#include <poll.h>

25

26
static void belle_sip_source_destroy(belle_sip_source_t *obj){
27 28 29
	if (obj->node.next || obj->node.prev){
		belle_sip_fatal("Destroying source currently used in main loop !");
	}
30 31
}

32 33


Simon Morlat's avatar
Simon Morlat committed
34 35
void belle_sip_fd_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, int fd, unsigned int events, unsigned int timeout_value_ms){
	static unsigned long global_id=1;
36
	s->node.data=s;
Simon Morlat's avatar
Simon Morlat committed
37
	s->id=global_id++;
38 39
	s->fd=fd;
	s->events=events;
40 41 42
	s->timeout=timeout_value_ms;
	s->data=data;
	s->notify=func;
Simon Morlat's avatar
Simon Morlat committed
43 44
}

45
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_source_t);
jehan's avatar
jehan committed
46
BELLE_SIP_INSTANCIATE_VPTR(belle_sip_source_t,belle_sip_object_t,belle_sip_source_destroy,NULL,NULL,FALSE);
Simon Morlat's avatar
Simon Morlat committed
47

48
belle_sip_source_t * belle_sip_fd_source_new(belle_sip_source_func_t func, void *data, int fd, unsigned int events, unsigned int timeout_value_ms){
Simon Morlat's avatar
Simon Morlat committed
49
	belle_sip_source_t *s=belle_sip_object_new(belle_sip_source_t);
Simon Morlat's avatar
Simon Morlat committed
50
	belle_sip_fd_source_init(s,func,data,fd,events,timeout_value_ms);
51 52 53
	return s;
}

54 55 56
belle_sip_source_t * belle_sip_timeout_source_new(belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms){
	return belle_sip_fd_source_new(func,data,-1,0,timeout_value_ms);
}
57

58 59 60 61
unsigned long belle_sip_source_get_id(belle_sip_source_t *s){
	return s->id;
}

Simon Morlat's avatar
Simon Morlat committed
62 63 64 65 66 67 68 69 70 71
int belle_sip_source_set_events(belle_sip_source_t* source, int event_mask) {
	source->events = event_mask;
	return 0;
}

belle_sip_fd_t belle_sip_source_get_fd(const belle_sip_source_t* source) {
	return source->fd;
}


72
struct belle_sip_main_loop{
73 74
	belle_sip_object_t base;
	belle_sip_list_t *sources;
75 76 77 78
	belle_sip_source_t *control;
	int nsources;
	int run;
	int control_fds[2];
79 80
};

Simon Morlat's avatar
Simon Morlat committed
81
void belle_sip_main_loop_remove_source(belle_sip_main_loop_t *ml, belle_sip_source_t *source){
jehan's avatar
jehan committed
82
	if (!source->node.next && !source->node.prev && &source->node!=ml->sources) return; /*nothing to do*/
83
	source->cancelled=TRUE;
84 85 86 87 88 89 90 91 92
	ml->sources=belle_sip_list_remove_link(ml->sources,&source->node);
	ml->nsources--;
	
	if (source->on_remove)
		source->on_remove(source);
	belle_sip_object_unref(source);
}


93
static void belle_sip_main_loop_destroy(belle_sip_main_loop_t *ml){
94
	belle_sip_main_loop_remove_source(ml,ml->control);
95 96
	close(ml->control_fds[0]);
	close(ml->control_fds[1]);
jehan's avatar
jehan committed
97
	belle_sip_object_unref(ml->control);
98 99
}

100
static int main_loop_done(void *data, unsigned int events){
Simon Morlat's avatar
Simon Morlat committed
101 102 103 104 105
	belle_sip_main_loop_t * m=(belle_sip_main_loop_t*)data;
	char tmp;
	if (read(m->control_fds[0],&tmp,sizeof(tmp))!=1){
		belle_sip_error("Problem on control fd of main loop.");
	}
106 107
	return TRUE;
}
108

109 110

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_main_loop_t);
Simon Morlat's avatar
Simon Morlat committed
111
BELLE_SIP_INSTANCIATE_VPTR(belle_sip_main_loop_t,belle_sip_object_t,belle_sip_main_loop_destroy,NULL,NULL,FALSE);
Simon Morlat's avatar
Simon Morlat committed
112

113
belle_sip_main_loop_t *belle_sip_main_loop_new(void){
Simon Morlat's avatar
Simon Morlat committed
114
	belle_sip_main_loop_t*m=belle_sip_object_new(belle_sip_main_loop_t);
115 116 117
	if (pipe(m->control_fds)==-1){
		belle_sip_fatal("Could not create control pipe.");
	}
Simon Morlat's avatar
Simon Morlat committed
118 119
	m->control=belle_sip_fd_source_new(main_loop_done,m,m->control_fds[0],BELLE_SIP_EVENT_READ,-1);
	belle_sip_object_set_name((belle_sip_object_t*)m->control,"main loop control fd");
120
	belle_sip_main_loop_add_source(m,m->control);
121 122 123 124 125 126 127 128
	return m;
}

void belle_sip_main_loop_add_source(belle_sip_main_loop_t *ml, belle_sip_source_t *source){
	if (source->node.next || source->node.prev){
		belle_sip_fatal("Source is already linked somewhere else.");
		return;
	}
129
	belle_sip_object_ref(source);
Simon Morlat's avatar
Simon Morlat committed
130
	if (source->timeout>=0){
131 132
		source->expire_ms=belle_sip_time_ms()+source->timeout;
	}
133
	ml->sources=belle_sip_list_append_link(ml->sources,&source->node);
134
	ml->nsources++;
135 136 137
}


Simon Morlat's avatar
Simon Morlat committed
138
unsigned long belle_sip_main_loop_add_timeout(belle_sip_main_loop_t *ml, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms){
139
	belle_sip_source_t * s=belle_sip_timeout_source_new(func,data,timeout_value_ms);
Simon Morlat's avatar
Simon Morlat committed
140
	belle_sip_object_set_name((belle_sip_object_t*)s,"timeout");
141
	belle_sip_main_loop_add_source(ml,s);
jehan's avatar
jehan committed
142
	belle_sip_object_unref(s);
Simon Morlat's avatar
Simon Morlat committed
143
	return s->id;
144
}
145

Simon Morlat's avatar
Simon Morlat committed
146
void belle_sip_source_set_timeout(belle_sip_source_t *s, unsigned int value_ms){
Simon Morlat's avatar
Simon Morlat committed
147 148 149
	if (!s->expired){
		s->expire_ms=belle_sip_time_ms()+value_ms;
	}
Simon Morlat's avatar
Simon Morlat committed
150 151 152
	s->timeout=value_ms;
}

Simon Morlat's avatar
Simon Morlat committed
153 154 155 156 157
unsigned int belle_sip_source_get_timeout(const belle_sip_source_t *s){
	return s->timeout;
}


158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
static int match_source_id(const void *s, const void *pid){
	if ( ((belle_sip_source_t*)s)->id==(unsigned long)pid){
		return 0;
	}
	return -1;
}

void belle_sip_main_loop_cancel_source(belle_sip_main_loop_t *ml, unsigned long id){
	belle_sip_list_t *elem=belle_sip_list_find_custom(ml->sources,match_source_id,(const void*)id);
	if (elem!=NULL){
		belle_sip_source_t *s=(belle_sip_source_t*)elem->data;
		s->cancelled=TRUE;
	}
}

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
/*
 Poll() based implementation of event loop.
 */

static int belle_sip_event_to_poll(unsigned int events){
	int ret=0;
	if (events & BELLE_SIP_EVENT_READ)
		ret|=POLLIN;
	if (events & BELLE_SIP_EVENT_WRITE)
		ret|=POLLOUT;
	if (events & BELLE_SIP_EVENT_ERROR)
		ret|=POLLERR;
	return ret;
}

static unsigned int belle_sip_poll_to_event(short events){
	unsigned int ret=0;
	if (events & POLLIN)
		ret|=BELLE_SIP_EVENT_READ;
	if (events & POLLOUT)
		ret|=BELLE_SIP_EVENT_WRITE;
	if (events & POLLERR)
		ret|=BELLE_SIP_EVENT_ERROR;
	return ret;
}

void belle_sip_main_loop_iterate(belle_sip_main_loop_t *ml){
	struct pollfd *pfd=(struct pollfd*)alloca(ml->nsources*sizeof(struct pollfd));
	int i=0;
202 203
	belle_sip_source_t *s;
	belle_sip_list_t *elem,*next;
204 205 206 207
	uint64_t min_time_ms=(uint64_t)-1;
	int duration=-1;
	int ret;
	uint64_t cur;
208
	belle_sip_list_t *copy;
209 210
	
	/*prepare the pollfd table */
211 212 213 214 215 216 217 218 219 220 221
	for(elem=ml->sources;elem!=NULL;elem=next){
		next=elem->next;
		s=(belle_sip_source_t*)elem->data;
		if (!s->cancelled){
			if (s->fd!=-1){
				pfd[i].fd=s->fd;
				pfd[i].events=belle_sip_event_to_poll (s->events);
				pfd[i].revents=0;
				s->index=i;
				++i;
			}
222
			if (s->timeout>=0){
223 224 225
				if (min_time_ms>s->expire_ms){
					min_time_ms=s->expire_ms;
				}
226
			}
227
		}else belle_sip_main_loop_remove_source (ml,s);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
	}
	
	if (min_time_ms!=(uint64_t)-1 ){
		/* compute the amount of time to wait for shortest timeout*/
		cur=belle_sip_time_ms();
		int64_t diff=min_time_ms-cur;
		if (diff>0)
			duration=(int)diff;
		else 
			duration=0;
	}
	/* do the poll */
	ret=poll(pfd,i,duration);
	if (ret==-1 && errno!=EINTR){
		belle_sip_error("poll() error: %s",strerror(errno));
		return;
	}
	cur=belle_sip_time_ms();
246
	copy=belle_sip_list_copy_with_data(ml->sources,(void *(*)(void*))belle_sip_object_ref);
247
	/* examine poll results*/
248
	for(elem=copy;elem!=NULL;elem=elem->next){
249
		unsigned revents=0;
250 251 252 253 254 255 256
		s=(belle_sip_source_t*)elem->data;

		if (!s->cancelled){
			if (s->fd!=-1){
				if (pfd[s->index].revents!=0){
					revents=belle_sip_poll_to_event(pfd[s->index].revents);		
				}
257
			}
258
			if (revents!=0 || (s->timeout>=0 && cur>=s->expire_ms)){
Simon Morlat's avatar
Simon Morlat committed
259
				char *objdesc=belle_sip_object_to_string((belle_sip_object_t*)s);
Simon Morlat's avatar
Simon Morlat committed
260
				s->expired=TRUE;
261
				if (s->timeout>1 /*FIXME, should be 0*/) belle_sip_message("source %s notified revents=%u, timeout=%i",objdesc,revents,s->timeout);
Simon Morlat's avatar
Simon Morlat committed
262
				belle_sip_free(objdesc);
263 264 265 266 267 268 269
				ret=s->notify(s->data,revents);
				if (ret==0){
					/*this source needs to be removed*/
					belle_sip_main_loop_remove_source(ml,s);
				}else if (revents==0){
					/*timeout needs to be started again */
					s->expire_ms+=s->timeout;
Simon Morlat's avatar
Simon Morlat committed
270
					s->expired=FALSE;
271
				}
272
			}
273
		}else belle_sip_main_loop_remove_source(ml,s);
274
	}
275
	belle_sip_list_free_with_data(copy,belle_sip_object_unref);
276 277 278 279 280 281 282 283 284 285 286
}

void belle_sip_main_loop_run(belle_sip_main_loop_t *ml){
	ml->run=1;
	while(ml->run){
		belle_sip_main_loop_iterate(ml);
	}
}

void belle_sip_main_loop_quit(belle_sip_main_loop_t *ml){
	ml->run=0;
jehan's avatar
jehan committed
287 288 289
	//if (write(ml->control_fds[1],"a",1)==-1){
	//	belle_sip_error("Fail to write to main loop control fd.");
	//}
290 291
}

Simon Morlat's avatar
Simon Morlat committed
292 293 294 295
void belle_sip_main_loop_sleep(belle_sip_main_loop_t *ml, int milliseconds){
	belle_sip_main_loop_add_timeout(ml,(belle_sip_source_func_t)belle_sip_main_loop_quit,ml,milliseconds);
	belle_sip_main_loop_run(ml);
}