belle_sip_loop.c 13.1 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

#ifndef WIN32
24 25
#include <unistd.h>
#include <poll.h>
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
typedef struct pollfd belle_sip_pollfd_t;

static int belle_sip_poll(belle_sip_pollfd_t *pfd, int count, int duration){
	int err;
	err=poll(pfd,count,duration);
	if (err==-1 && errno!=EINTR)
		belle_sip_error("poll() error: %s",strerror(errno));
	return err;
}

/*
 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(belle_sip_pollfd_t * pfd){
	unsigned int ret=0;
	short events=pfd->revents;
	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;
}

static void belle_sip_source_to_poll(belle_sip_source_t *s, belle_sip_pollfd_t *pfd, int i){
	pfd[i].fd=s->fd;
	pfd[i].events=belle_sip_event_to_poll(s->events);
	pfd[i].revents=0;
	s->index=i;
}

static unsigned int belle_sip_source_get_revents(belle_sip_source_t *s,belle_sip_pollfd_t *pfd){
71
	return belle_sip_poll_to_event(&pfd[s->index]);
72 73 74 75
}

#else

76 77 78

#include <malloc.h>

Ghislain MARY's avatar
Ghislain MARY committed
79

80 81 82 83
typedef HANDLE belle_sip_pollfd_t;

static void belle_sip_source_to_poll(belle_sip_source_t *s, belle_sip_pollfd_t *pfd,int i){
	s->index=i;
Simon Morlat's avatar
Simon Morlat committed
84
	pfd[i]=s->fd;
85
	
Simon Morlat's avatar
Simon Morlat committed
86 87 88 89 90 91 92 93 94 95 96 97 98
	/*special treatments for windows sockets*/
	if (s->sock!=(belle_sip_socket_t)-1){
		int err;
		long events=0;
		
		if (s->events & BELLE_SIP_EVENT_READ)
			events|=FD_READ;
		if (s->events & BELLE_SIP_EVENT_WRITE)
			events|=FD_WRITE;
		
		err=WSAEventSelect(s->sock,s->fd,events);
		if (err!=0) belle_sip_error("WSAEventSelect() failed: %s",belle_sip_get_socket_error_string());
	}
99 100 101
}

static unsigned int belle_sip_source_get_revents(belle_sip_source_t *s,belle_sip_pollfd_t *pfd){
102 103 104 105
	WSANETWORKEVENTS revents={0};
	int err;
	unsigned int ret=0;
	
Simon Morlat's avatar
Simon Morlat committed
106 107 108 109 110 111 112 113 114 115 116
	/*special treatments for windows sockets*/
	if (s->sock!=(belle_sip_socket_t)-1){
		err=WSAEnumNetworkEvents(s->sock,NULL,&revents);
		if (err!=0){
			belle_sip_error("WSAEnumNetworkEvents() failed: %s socket=%x",belle_sip_get_socket_error_string(),(unsigned int)s->sock);
			return 0;
		}
		if (revents.lNetworkEvents & FD_READ)
			ret|=BELLE_SIP_EVENT_READ;
		if (revents.lNetworkEvents & FD_WRITE)
			ret|=BELLE_SIP_EVENT_WRITE;
117 118 119 120 121 122
	}else{
		if (WaitForSingleObjectEx(s->fd,0,FALSE)==WAIT_OBJECT_0){
			ret=BELLE_SIP_EVENT_READ;
			ResetEvent(s->fd);
		}
	}
123
	return ret;
124 125 126
}

static int belle_sip_poll(belle_sip_pollfd_t *pfd, int count, int duration){
127 128 129
	DWORD ret;
	
	if (count == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
130
		belle_sip_sleep(duration);
131 132 133 134
		return 0;
	}

	ret=WaitForMultipleObjectsEx(count,pfd,FALSE,duration,FALSE);
135 136 137 138 139 140 141 142 143
	if (ret==WAIT_FAILED){
		belle_sip_error("WaitForMultipleObjectsEx() failed.");
		return -1;
	}
	if (ret==WAIT_TIMEOUT){
		return 0;
	}
	return ret-WAIT_OBJECT_0;
}
144

145
#endif
146

147
static void belle_sip_source_destroy(belle_sip_source_t *obj){
148 149 150
	if (obj->node.next || obj->node.prev){
		belle_sip_fatal("Destroying source currently used in main loop !");
	}
Simon Morlat's avatar
Simon Morlat committed
151
	belle_sip_source_uninit(obj);
152 153
}

154
static void belle_sip_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms){
Simon Morlat's avatar
Simon Morlat committed
155
	static unsigned long global_id=1;
156
	s->node.data=s;
Simon Morlat's avatar
Simon Morlat committed
157
	s->id=global_id++;
158 159
	s->fd=fd;
	s->events=events;
160 161 162
	s->timeout=timeout_value_ms;
	s->data=data;
	s->notify=func;
Simon Morlat's avatar
Simon Morlat committed
163
	s->sock=(belle_sip_socket_t)-1;
164 165
}

Simon Morlat's avatar
Simon Morlat committed
166 167 168 169 170 171 172 173 174 175 176
void belle_sip_source_uninit(belle_sip_source_t *obj){
#ifdef WIN32
	if (obj->sock!=(belle_sip_socket_t)-1){
		WSACloseEvent(obj->fd);
		obj->fd=(WSAEVENT)-1;
	}
#endif
	obj->fd=(belle_sip_fd_t)-1;
	obj->sock=(belle_sip_socket_t)-1;
}

177
void belle_sip_socket_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_socket_t sock, unsigned int events, unsigned int timeout_value_ms){
178
#ifdef WIN32
179 180 181 182
	/*on windows, the fd to poll is not the socket */
	belle_sip_fd_t fd=(belle_sip_fd_t)-1;
	if (sock!=(belle_sip_socket_t)-1)
		fd=WSACreateEvent();
183
	else
184 185 186 187 188
		fd=(WSAEVENT)-1;
	belle_sip_source_init(s,func,data,fd,events,timeout_value_ms);
	
#else
	belle_sip_source_init(s,func,data,sock,events,timeout_value_ms);
189
#endif
Simon Morlat's avatar
Simon Morlat committed
190
	s->sock=sock;
Simon Morlat's avatar
Simon Morlat committed
191 192
}

193 194 195 196
void belle_sip_fd_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms){
	belle_sip_source_init(s,func,data,fd,events,timeout_value_ms);
}

197
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_source_t);
jehan's avatar
jehan committed
198
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
199

200
belle_sip_source_t * belle_sip_socket_source_new(belle_sip_source_func_t func, void *data, belle_sip_socket_t sock, unsigned int events, unsigned int timeout_value_ms){
Simon Morlat's avatar
Simon Morlat committed
201
	belle_sip_source_t *s=belle_sip_object_new(belle_sip_source_t);
202 203 204 205 206 207 208
	belle_sip_socket_source_init(s,func,data,sock,events,timeout_value_ms);
	return s;
}

belle_sip_source_t * belle_sip_fd_source_new(belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms){
	belle_sip_source_t *s=belle_sip_object_new(belle_sip_source_t);
	belle_sip_fd_source_init(s,func,data,fd,events,timeout_value_ms);
209 210 211
	return s;
}

212
belle_sip_source_t * belle_sip_timeout_source_new(belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms){
213
	return belle_sip_socket_source_new(func,data,(belle_sip_socket_t)-1,0,timeout_value_ms);
214
}
215

216 217 218 219
unsigned long belle_sip_source_get_id(belle_sip_source_t *s){
	return s->id;
}

Simon Morlat's avatar
Simon Morlat committed
220 221 222 223 224
int belle_sip_source_set_events(belle_sip_source_t* source, int event_mask) {
	source->events = event_mask;
	return 0;
}

225
belle_sip_socket_t belle_sip_source_get_socket(const belle_sip_source_t* source) {
226
	return source->sock;
Simon Morlat's avatar
Simon Morlat committed
227 228 229
}


230
struct belle_sip_main_loop{
231 232
	belle_sip_object_t base;
	belle_sip_list_t *sources;
233
	belle_sip_object_pool_t *pool;
234 235
	int nsources;
	int run;
236 237
};

Simon Morlat's avatar
Simon Morlat committed
238
void belle_sip_main_loop_remove_source(belle_sip_main_loop_t *ml, belle_sip_source_t *source){
jehan's avatar
jehan committed
239
	if (!source->node.next && !source->node.prev && &source->node!=ml->sources) return; /*nothing to do*/
240
	source->cancelled=TRUE;
241 242 243 244 245 246 247 248 249
	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);
}


250
static void belle_sip_main_loop_destroy(belle_sip_main_loop_t *ml){
Simon Morlat's avatar
Simon Morlat committed
251 252 253
	while (ml->sources){
		belle_sip_main_loop_remove_source(ml,(belle_sip_source_t*)ml->sources->data);
	}
Simon Morlat's avatar
Simon Morlat committed
254
	belle_sip_object_unref(ml->pool);
255 256
}

257
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_main_loop_t);
Simon Morlat's avatar
Simon Morlat committed
258
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
259

260
belle_sip_main_loop_t *belle_sip_main_loop_new(void){
Simon Morlat's avatar
Simon Morlat committed
261
	belle_sip_main_loop_t*m=belle_sip_object_new(belle_sip_main_loop_t);
262
	m->pool=belle_sip_object_pool_push();
263 264 265 266 267 268 269 270
	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;
	}
271
	belle_sip_object_ref(source);
Simon Morlat's avatar
Simon Morlat committed
272
	if (source->timeout>=0){
273 274
		source->expire_ms=belle_sip_time_ms()+source->timeout;
	}
275
	ml->sources=belle_sip_list_append_link(ml->sources,&source->node);
276
	ml->nsources++;
277 278
}

jehan's avatar
jehan committed
279 280 281 282 283
belle_sip_source_t* belle_sip_main_loop_create_timeout(belle_sip_main_loop_t *ml
														, belle_sip_source_func_t func
														, void *data
														, unsigned int timeout_value_ms
														,const char* timer_name) {
284
	belle_sip_source_t * s=belle_sip_timeout_source_new(func,data,timeout_value_ms);
jehan's avatar
jehan committed
285
	belle_sip_object_set_name((belle_sip_object_t*)s,timer_name);
286
	belle_sip_main_loop_add_source(ml,s);
jehan's avatar
jehan committed
287 288
	return s;
}
Simon Morlat's avatar
Simon Morlat committed
289

jehan's avatar
jehan committed
290 291
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){
	belle_sip_source_t * s=belle_sip_main_loop_create_timeout(ml,func,data,timeout_value_ms,"Timer");
jehan's avatar
jehan committed
292
	belle_sip_object_unref(s);
Simon Morlat's avatar
Simon Morlat committed
293
	return s->id;
294
}
295

Simon Morlat's avatar
Simon Morlat committed
296 297 298 299 300 301 302
void belle_sip_main_loop_do_later(belle_sip_main_loop_t *ml, belle_sip_callback_t func, void *data){
	belle_sip_source_t * s=belle_sip_main_loop_create_timeout(ml,(belle_sip_source_func_t)func,data,0,"defered task");
	s->oneshot=TRUE;
	belle_sip_object_unref(s);
}


Simon Morlat's avatar
Simon Morlat committed
303
void belle_sip_source_set_timeout(belle_sip_source_t *s, unsigned int value_ms){
Simon Morlat's avatar
Simon Morlat committed
304 305 306
	if (!s->expired){
		s->expire_ms=belle_sip_time_ms()+value_ms;
	}
Simon Morlat's avatar
Simon Morlat committed
307 308 309
	s->timeout=value_ms;
}

Simon Morlat's avatar
Simon Morlat committed
310 311 312 313 314
unsigned int belle_sip_source_get_timeout(const belle_sip_source_t *s){
	return s->timeout;
}


315 316 317 318 319 320 321
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;
}

322
belle_sip_source_t *belle_sip_main_loop_find_source(belle_sip_main_loop_t *ml, unsigned long id){
323 324
	belle_sip_list_t *elem=belle_sip_list_find_custom(ml->sources,match_source_id,(const void*)id);
	if (elem!=NULL){
325
		return (belle_sip_source_t*)elem->data;
326
	}
327 328 329 330 331 332
	return NULL;
}

void belle_sip_main_loop_cancel_source(belle_sip_main_loop_t *ml, unsigned long id){
	belle_sip_source_t *s=belle_sip_main_loop_find_source(ml,id);
	if (s) s->cancelled=TRUE;
333 334
}

335
void belle_sip_main_loop_iterate(belle_sip_main_loop_t *ml){
336 337
	size_t pfd_size = ml->nsources * sizeof(belle_sip_pollfd_t);
	belle_sip_pollfd_t *pfd=(belle_sip_pollfd_t*)alloca(pfd_size);
338
	int i=0;
339 340
	belle_sip_source_t *s;
	belle_sip_list_t *elem,*next;
341 342 343 344
	uint64_t min_time_ms=(uint64_t)-1;
	int duration=-1;
	int ret;
	uint64_t cur;
345
	belle_sip_list_t *copy;
346
	int can_clean=belle_sip_object_pool_cleanable(ml->pool); /*iterate might not be called by the thread that created the main loop*/ 
Simon Morlat's avatar
Simon Morlat committed
347
	belle_sip_object_pool_t *tmp_pool=NULL;
348 349 350
	
	if (!can_clean){
		/*Push a temporary pool for the time of the iterate loop*/
Simon Morlat's avatar
Simon Morlat committed
351
		tmp_pool=belle_sip_object_pool_push();
352
	}
353 354
	
	/*prepare the pollfd table */
355
	memset(pfd, 0, pfd_size);
356 357 358 359
	for(elem=ml->sources;elem!=NULL;elem=next){
		next=elem->next;
		s=(belle_sip_source_t*)elem->data;
		if (!s->cancelled){
360
			if (s->fd!=(belle_sip_fd_t)-1){
361
				belle_sip_source_to_poll(s,pfd,i);
362 363
				++i;
			}
364
			if (s->timeout>=0){
365 366 367
				if (min_time_ms>s->expire_ms){
					min_time_ms=s->expire_ms;
				}
368
			}
369
		}else belle_sip_main_loop_remove_source (ml,s);
370 371 372
	}
	
	if (min_time_ms!=(uint64_t)-1 ){
373
		int64_t diff;
374 375
		/* compute the amount of time to wait for shortest timeout*/
		cur=belle_sip_time_ms();
376
		diff=min_time_ms-cur;
377 378 379 380 381 382
		if (diff>0)
			duration=(int)diff;
		else 
			duration=0;
	}
	/* do the poll */
383 384
	ret=belle_sip_poll(pfd,i,duration);
	if (ret==-1){
385 386 387
		return;
	}
	cur=belle_sip_time_ms();
388
	copy=belle_sip_list_copy_with_data(ml->sources,(void *(*)(void*))belle_sip_object_ref);
389
	/* examine poll results*/
390
	for(elem=copy;elem!=NULL;elem=elem->next){
391
		unsigned revents=0;
392 393 394
		s=(belle_sip_source_t*)elem->data;

		if (!s->cancelled){
395
			if (s->fd!=(belle_sip_fd_t)-1){
jehan's avatar
jehan committed
396 397 398 399 400 401
				if (s->notify_required) { /*for testing purpose to force channel to read*/
					revents=BELLE_SIP_EVENT_READ;
					s->notify_required=0; /*reset*/
				} else {
					revents=belle_sip_source_get_revents(s,pfd);
				}
402
			}
403
			if (revents!=0 || (s->timeout>=0 && cur>=s->expire_ms)){
Simon Morlat's avatar
Simon Morlat committed
404
				char *objdesc=belle_sip_object_to_string((belle_sip_object_t*)s);
Simon Morlat's avatar
Simon Morlat committed
405
				s->expired=TRUE;
jehan's avatar
jehan committed
406 407
				if (revents==0)
					revents=BELLE_SIP_EVENT_TIMEOUT;
jehan's avatar
jehan committed
408
				if (s->timeout>0)/*to avoid too many traces*/ belle_sip_debug("source %s notified revents=%u, timeout=%i",objdesc,revents,s->timeout);
Simon Morlat's avatar
Simon Morlat committed
409
				belle_sip_free(objdesc);
410
				ret=s->notify(s->data,revents);
Simon Morlat's avatar
Simon Morlat committed
411
				if (ret==BELLE_SIP_STOP || s->oneshot){
412 413
					/*this source needs to be removed*/
					belle_sip_main_loop_remove_source(ml,s);
jehan's avatar
jehan committed
414
				}else if (revents==BELLE_SIP_EVENT_TIMEOUT){
415 416
					/*timeout needs to be started again */
					s->expire_ms+=s->timeout;
Simon Morlat's avatar
Simon Morlat committed
417
					s->expired=FALSE;
418
				}
419
			}
420
		}else belle_sip_main_loop_remove_source(ml,s);
421
	}
422
	belle_sip_list_free_with_data(copy,belle_sip_object_unref);
Simon Morlat's avatar
Simon Morlat committed
423 424
	if (can_clean) belle_sip_object_pool_clean(ml->pool);
	else if (tmp_pool) belle_sip_object_unref(tmp_pool);
425 426 427 428 429 430 431 432 433
}

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

jehan's avatar
jehan committed
434
int belle_sip_main_loop_quit(belle_sip_main_loop_t *ml){
435
	ml->run=0;
Simon Morlat's avatar
Simon Morlat committed
436
	return BELLE_SIP_STOP;
437 438
}

Simon Morlat's avatar
Simon Morlat committed
439
void belle_sip_main_loop_sleep(belle_sip_main_loop_t *ml, int milliseconds){
jehan's avatar
jehan committed
440
	unsigned long timer_id = belle_sip_main_loop_add_timeout(ml,(belle_sip_source_func_t)belle_sip_main_loop_quit,ml,milliseconds);
Simon Morlat's avatar
Simon Morlat committed
441
	belle_sip_main_loop_run(ml);
jehan's avatar
jehan committed
442
	belle_sip_main_loop_cancel_source(ml,timer_id);
Simon Morlat's avatar
Simon Morlat committed
443
}
444