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

79 80 81 82
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
83
	pfd[i]=s->fd;
84
	
Simon Morlat's avatar
Simon Morlat committed
85 86 87 88 89 90 91 92 93 94 95 96 97
	/*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());
	}
98 99 100
}

static unsigned int belle_sip_source_get_revents(belle_sip_source_t *s,belle_sip_pollfd_t *pfd){
101 102 103 104
	WSANETWORKEVENTS revents={0};
	int err;
	unsigned int ret=0;
	
Simon Morlat's avatar
Simon Morlat committed
105 106 107 108 109 110 111 112 113 114 115
	/*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;
116 117 118 119 120 121
	}else{
		if (WaitForSingleObjectEx(s->fd,0,FALSE)==WAIT_OBJECT_0){
			ret=BELLE_SIP_EVENT_READ;
			ResetEvent(s->fd);
		}
	}
122
	return ret;
123 124 125
}

static int belle_sip_poll(belle_sip_pollfd_t *pfd, int count, int duration){
126 127 128 129 130 131 132 133
	DWORD ret;
	
	if (count == 0) {
		Sleep(duration);
		return 0;
	}

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

144
#endif
145

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

153
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
154
	static unsigned long global_id=1;
155
	s->node.data=s;
Simon Morlat's avatar
Simon Morlat committed
156
	s->id=global_id++;
157 158
	s->fd=fd;
	s->events=events;
159 160 161
	s->timeout=timeout_value_ms;
	s->data=data;
	s->notify=func;
Simon Morlat's avatar
Simon Morlat committed
162
	s->sock=(belle_sip_socket_t)-1;
163 164
}

Simon Morlat's avatar
Simon Morlat committed
165 166 167 168 169 170 171 172 173 174 175
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;
}

176
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){
177
#ifdef WIN32
178 179 180 181
	/*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();
182
	else
183 184 185 186 187
		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);
188
#endif
Simon Morlat's avatar
Simon Morlat committed
189
	s->sock=sock;
Simon Morlat's avatar
Simon Morlat committed
190 191
}

192 193 194 195
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);
}

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

199
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
200
	belle_sip_source_t *s=belle_sip_object_new(belle_sip_source_t);
201 202 203 204 205 206 207
	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);
208 209 210
	return s;
}

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

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

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

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


229
struct belle_sip_main_loop{
230 231
	belle_sip_object_t base;
	belle_sip_list_t *sources;
232 233
	int nsources;
	int run;
234 235
};

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


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

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

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


Simon Morlat's avatar
Simon Morlat committed
277
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){
278
	belle_sip_source_t * s=belle_sip_timeout_source_new(func,data,timeout_value_ms);
Simon Morlat's avatar
Simon Morlat committed
279
	belle_sip_object_set_name((belle_sip_object_t*)s,"timeout");
280
	belle_sip_main_loop_add_source(ml,s);
jehan's avatar
jehan committed
281
	belle_sip_object_unref(s);
Simon Morlat's avatar
Simon Morlat committed
282
	return s->id;
283
}
284

Simon Morlat's avatar
Simon Morlat committed
285
void belle_sip_source_set_timeout(belle_sip_source_t *s, unsigned int value_ms){
Simon Morlat's avatar
Simon Morlat committed
286 287 288
	if (!s->expired){
		s->expire_ms=belle_sip_time_ms()+value_ms;
	}
Simon Morlat's avatar
Simon Morlat committed
289 290 291
	s->timeout=value_ms;
}

Simon Morlat's avatar
Simon Morlat committed
292 293 294 295 296
unsigned int belle_sip_source_get_timeout(const belle_sip_source_t *s){
	return s->timeout;
}


297 298 299 300 301 302 303
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;
}

304
belle_sip_source_t *belle_sip_main_loop_find_source(belle_sip_main_loop_t *ml, unsigned long id){
305 306
	belle_sip_list_t *elem=belle_sip_list_find_custom(ml->sources,match_source_id,(const void*)id);
	if (elem!=NULL){
307
		return (belle_sip_source_t*)elem->data;
308
	}
309 310 311 312 313 314
	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;
315 316
}

317
void belle_sip_main_loop_iterate(belle_sip_main_loop_t *ml){
318
	belle_sip_pollfd_t *pfd=(belle_sip_pollfd_t*)alloca(ml->nsources*sizeof(belle_sip_pollfd_t));
319
	int i=0;
320 321
	belle_sip_source_t *s;
	belle_sip_list_t *elem,*next;
322 323 324 325
	uint64_t min_time_ms=(uint64_t)-1;
	int duration=-1;
	int ret;
	uint64_t cur;
326
	belle_sip_list_t *copy;
327 328
	
	/*prepare the pollfd table */
329 330 331 332
	for(elem=ml->sources;elem!=NULL;elem=next){
		next=elem->next;
		s=(belle_sip_source_t*)elem->data;
		if (!s->cancelled){
333
			if (s->fd!=(belle_sip_fd_t)-1){
334
				belle_sip_source_to_poll(s,pfd,i);
335 336
				++i;
			}
337
			if (s->timeout>=0){
338 339 340
				if (min_time_ms>s->expire_ms){
					min_time_ms=s->expire_ms;
				}
341
			}
342
		}else belle_sip_main_loop_remove_source (ml,s);
343 344 345
	}
	
	if (min_time_ms!=(uint64_t)-1 ){
346
		int64_t diff;
347 348
		/* compute the amount of time to wait for shortest timeout*/
		cur=belle_sip_time_ms();
349
		diff=min_time_ms-cur;
350 351 352 353 354 355
		if (diff>0)
			duration=(int)diff;
		else 
			duration=0;
	}
	/* do the poll */
356 357
	ret=belle_sip_poll(pfd,i,duration);
	if (ret==-1){
358 359 360
		return;
	}
	cur=belle_sip_time_ms();
361
	copy=belle_sip_list_copy_with_data(ml->sources,(void *(*)(void*))belle_sip_object_ref);
362
	/* examine poll results*/
363
	for(elem=copy;elem!=NULL;elem=elem->next){
364
		unsigned revents=0;
365 366 367
		s=(belle_sip_source_t*)elem->data;

		if (!s->cancelled){
368
			if (s->fd!=(belle_sip_fd_t)-1){
369
				revents=belle_sip_source_get_revents(s,pfd);		
370
			}
371
			if (revents!=0 || (s->timeout>=0 && cur>=s->expire_ms)){
Simon Morlat's avatar
Simon Morlat committed
372
				char *objdesc=belle_sip_object_to_string((belle_sip_object_t*)s);
Simon Morlat's avatar
Simon Morlat committed
373
				s->expired=TRUE;
374
				if (s->timeout>0)/*to avoid too many traces*/ belle_sip_message("source %s notified revents=%u, timeout=%i",objdesc,revents,s->timeout);
Simon Morlat's avatar
Simon Morlat committed
375
				belle_sip_free(objdesc);
376 377 378 379 380 381 382
				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
383
					s->expired=FALSE;
384
				}
385
			}
386
		}else belle_sip_main_loop_remove_source(ml,s);
387
	}
388
	belle_sip_list_free_with_data(copy,belle_sip_object_unref);
389
	belle_sip_object_delete_unowned();
390 391 392 393 394 395 396 397 398
}

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
399
int belle_sip_main_loop_quit(belle_sip_main_loop_t *ml){
400
	ml->run=0;
Simon Morlat's avatar
Simon Morlat committed
401
	return BELLE_SIP_STOP;
402 403
}

Simon Morlat's avatar
Simon Morlat committed
404
void belle_sip_main_loop_sleep(belle_sip_main_loop_t *ml, int milliseconds){
jehan's avatar
jehan committed
405
	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
406
	belle_sip_main_loop_run(ml);
jehan's avatar
jehan committed
407
	belle_sip_main_loop_cancel_source(ml,timer_id);
Simon Morlat's avatar
Simon Morlat committed
408
}
409