root/src/ftdm_threadmutex.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. FT_DECLARE
  2. thread_launch
  3. FT_DECLARE
  4. FT_DECLARE
  5. FT_DECLARE
  6. FT_DECLARE
  7. FT_DECLARE
  8. FT_DECLARE
  9. FT_DECLARE
  10. FT_DECLARE
  11. FT_DECLARE
  12. FT_DECLARE
  13. FT_DECLARE
  14. FT_DECLARE

   1 /* 
   2  * Cross Platform Thread/Mutex abstraction
   3  * Copyright(C) 2007 Michael Jerris
   4  *
   5  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
   6  * copies of the Software, and permit persons to whom the Software is
   7  * furnished to do so.
   8  *
   9  * This work is provided under this license on an "as is" basis, without warranty of any kind,
  10  * either expressed or implied, including, without limitation, warranties that the covered code
  11  * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire
  12  * risk as to the quality and performance of the covered code is with you. Should any covered
  13  * code prove defective in any respect, you (not the initial developer or any other contributor)
  14  * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty
  15  * constitutes an essential part of this license. No use of any covered code is authorized hereunder
  16  * except under this disclaimer. 
  17  *
  18  * Contributors: 
  19  *
  20  * Moises Silva <moy@sangoma.com>
  21  *
  22  */
  23 
  24 #ifdef WIN32
  25 /* required for TryEnterCriticalSection definition.  Must be defined before windows.h include */
  26 #define _WIN32_WINNT 0x0400
  27 #endif
  28 
  29 #include "private/ftdm_core.h"
  30 #include "ftdm_threadmutex.h"
  31 
  32 #ifdef WIN32
  33 #include <process.h>
  34 
  35 #define FTDM_THREAD_CALLING_CONVENTION __stdcall
  36 
  37 struct ftdm_mutex {
  38         CRITICAL_SECTION mutex;
  39 };
  40 
  41 #else
  42 #include <pthread.h>
  43 #include <poll.h>
  44 
  45 #define FTDM_THREAD_CALLING_CONVENTION
  46 
  47 struct ftdm_mutex {
  48         pthread_mutex_t mutex;
  49 };
  50 
  51 #endif
  52 
  53 struct ftdm_interrupt {
  54         ftdm_socket_t device;
  55 #ifdef WIN32
  56         /* for generic interruption */
  57         HANDLE event;
  58 #else
  59         /* In theory we could be using thread conditions for generic interruption,
  60          * however, Linux does not have a primitive like Windows WaitForMultipleObjects
  61          * to wait for both thread condition and file descriptors, therefore we decided
  62          * to use a dummy pipe for generic interruption/condition logic
  63          * */
  64         int readfd;
  65         int writefd;
  66 #endif
  67 };
  68 
  69 struct ftdm_thread {
  70 #ifdef WIN32
  71         void *handle;
  72 #else
  73         pthread_t handle;
  74 #endif
  75         void *private_data;
  76         ftdm_thread_function_t function;
  77         ftdm_size_t stack_size;
  78 #ifndef WIN32
  79         pthread_attr_t attribute;
  80 #endif
  81 };
  82 
  83 ftdm_size_t thread_default_stacksize = 0;
  84 
  85 FT_DECLARE(void) ftdm_thread_override_default_stacksize(ftdm_size_t size)
  86 {
  87         thread_default_stacksize = size;
  88 }
  89 
  90 static void * FTDM_THREAD_CALLING_CONVENTION thread_launch(void *args)
  91 {
  92         void *exit_val;
  93     ftdm_thread_t *thread = (ftdm_thread_t *)args;
  94         exit_val = thread->function(thread, thread->private_data);
  95 #ifndef WIN32
  96         pthread_attr_destroy(&thread->attribute);
  97 #endif
  98         ftdm_safe_free(thread);
  99 
 100         return exit_val;
 101 }
 102 
 103 FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached(ftdm_thread_function_t func, void *data)
 104 {
 105         return ftdm_thread_create_detached_ex(func, data, thread_default_stacksize);
 106 }
 107 
 108 FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached_ex(ftdm_thread_function_t func, void *data, ftdm_size_t stack_size)
 109 {
 110         ftdm_thread_t *thread = NULL;
 111         ftdm_status_t status = FTDM_FAIL;
 112 
 113         if (!func || !(thread = (ftdm_thread_t *)ftdm_malloc(sizeof(ftdm_thread_t)))) {
 114                 goto done;
 115         }
 116 
 117         thread->private_data = data;
 118         thread->function = func;
 119         thread->stack_size = stack_size;
 120 
 121 #if defined(WIN32)
 122         thread->handle = (void *)_beginthreadex(NULL, (unsigned)thread->stack_size, (unsigned int (__stdcall *)(void *))thread_launch, thread, 0, NULL);
 123         if (!thread->handle) {
 124                 goto fail;
 125         }
 126         CloseHandle(thread->handle);
 127 
 128         status = FTDM_SUCCESS;
 129         goto done;
 130 #else
 131         
 132         if (pthread_attr_init(&thread->attribute) != 0) goto fail;
 133 
 134         if (pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread;
 135 
 136         if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread;
 137 
 138         if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread;
 139 
 140         status = FTDM_SUCCESS;
 141         goto done;
 142  failpthread:
 143         pthread_attr_destroy(&thread->attribute);
 144 #endif
 145 
 146  fail:
 147         if (thread) {
 148                 ftdm_safe_free(thread);
 149         }
 150  done:
 151         return status;
 152 }
 153 
 154 
 155 FT_DECLARE(ftdm_status_t) ftdm_mutex_create(ftdm_mutex_t **mutex)
 156 {
 157         ftdm_status_t status = FTDM_FAIL;
 158 #ifndef WIN32
 159         pthread_mutexattr_t attr;
 160 #endif
 161         ftdm_mutex_t *check = NULL;
 162 
 163         check = (ftdm_mutex_t *)ftdm_malloc(sizeof(**mutex));
 164         if (!check)
 165                 goto done;
 166 #ifdef WIN32
 167         InitializeCriticalSection(&check->mutex);
 168 #else
 169         if (pthread_mutexattr_init(&attr))
 170                 goto done;
 171 
 172         if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))
 173                 goto fail;
 174 
 175         if (pthread_mutex_init(&check->mutex, &attr))
 176                 goto fail;
 177 
 178         goto success;
 179 
 180  fail:
 181         pthread_mutexattr_destroy(&attr);
 182         goto done;
 183 
 184  success:
 185 #endif
 186         *mutex = check;
 187         status = FTDM_SUCCESS;
 188 
 189  done:
 190         return status;
 191 }
 192 
 193 FT_DECLARE(ftdm_status_t) ftdm_mutex_destroy(ftdm_mutex_t **mutex)
 194 {
 195         ftdm_mutex_t *mp = *mutex;
 196         *mutex = NULL;
 197         if (!mp) {
 198                 return FTDM_FAIL;
 199         }
 200 #ifdef WIN32
 201         DeleteCriticalSection(&mp->mutex);
 202 #else
 203         if (pthread_mutex_destroy(&mp->mutex))
 204                 return FTDM_FAIL;
 205 #endif
 206         ftdm_safe_free(mp);
 207         return FTDM_SUCCESS;
 208 }
 209 
 210 FT_DECLARE(ftdm_status_t) _ftdm_mutex_lock(ftdm_mutex_t *mutex)
 211 {
 212 #ifdef WIN32
 213         EnterCriticalSection(&mutex->mutex);
 214 #else
 215         int err;
 216         if ((err = pthread_mutex_lock(&mutex->mutex))) {
 217                 ftdm_log(FTDM_LOG_ERROR, "Failed to lock mutex %d:%s\n", err, strerror(err));
 218                 return FTDM_FAIL;
 219         }
 220 #endif
 221         return FTDM_SUCCESS;
 222 }
 223 
 224 FT_DECLARE(ftdm_status_t) _ftdm_mutex_trylock(ftdm_mutex_t *mutex)
 225 {
 226 #ifdef WIN32
 227         if (!TryEnterCriticalSection(&mutex->mutex))
 228                 return FTDM_FAIL;
 229 #else
 230         if (pthread_mutex_trylock(&mutex->mutex))
 231                 return FTDM_FAIL;
 232 #endif
 233         return FTDM_SUCCESS;
 234 }
 235 
 236 FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex)
 237 {
 238 #ifdef WIN32
 239         LeaveCriticalSection(&mutex->mutex);
 240 #else
 241         if (pthread_mutex_unlock(&mutex->mutex))
 242                 return FTDM_FAIL;
 243 #endif
 244         return FTDM_SUCCESS;
 245 }
 246 
 247 
 248 FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device)
 249 {
 250         ftdm_status_t status = FTDM_SUCCESS;
 251         ftdm_interrupt_t *interrupt = NULL;
 252 #ifndef WIN32
 253         int fds[2];
 254 #endif
 255 
 256         ftdm_assert_return(ininterrupt != NULL, FTDM_FAIL, "interrupt double pointer is null!\n");
 257 
 258         interrupt = ftdm_calloc(1, sizeof(*interrupt));
 259         if (!interrupt) {
 260                 ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n");
 261                 return FTDM_ENOMEM;
 262         }
 263 
 264         interrupt->device = device;
 265 #ifdef WIN32
 266         interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL);
 267         if (!interrupt->event) {
 268                 ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n");
 269                 status = FTDM_ENOMEM;
 270                 goto failed;
 271         }
 272 #else
 273         if (pipe(fds)) {
 274                 ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno));
 275                 status = FTDM_FAIL;
 276                 goto failed;
 277         }
 278         interrupt->readfd = fds[0];
 279         interrupt->writefd = fds[1];
 280 #endif
 281 
 282         *ininterrupt = interrupt;
 283         return FTDM_SUCCESS;
 284 
 285 failed:
 286         if (interrupt) {
 287 #ifndef WIN32
 288                 if (interrupt->readfd) {
 289                         close(interrupt->readfd);
 290                         close(interrupt->writefd);
 291                         interrupt->readfd = -1;
 292                         interrupt->writefd = -1;
 293                 }
 294 #endif
 295                 ftdm_safe_free(interrupt);
 296         }
 297         return status;
 298 }
 299 
 300 #define ONE_BILLION 1000000000
 301 
 302 FT_DECLARE(ftdm_status_t) ftdm_interrupt_wait(ftdm_interrupt_t *interrupt, int ms)
 303 {
 304         int num = 1;
 305 #ifdef WIN32
 306         DWORD res = 0;
 307         HANDLE ints[2];
 308 #else
 309         int res = 0;
 310         struct pollfd ints[2];
 311         char pipebuf[255];
 312 #endif
 313 
 314         ftdm_assert_return(interrupt != NULL, FTDM_FAIL, "Condition is null!\n");
 315 
 316 
 317         /* start implementation */
 318 #ifdef WIN32
 319         ints[0] = interrupt->event;
 320         if (interrupt->device != FTDM_INVALID_SOCKET) {
 321                 num++;
 322                 ints[1] = interrupt->device;
 323         }
 324         res = WaitForMultipleObjects(num, ints, FALSE, ms >= 0 ? ms : INFINITE);
 325         switch (res) {
 326         case WAIT_TIMEOUT:
 327                 return FTDM_TIMEOUT;
 328         case WAIT_FAILED:
 329         case WAIT_ABANDONED: /* is it right to fail with abandoned? */
 330                 return FTDM_FAIL;
 331         default:
 332                 if (res >= (sizeof(ints)/sizeof(ints[0]))) {
 333                         ftdm_log(FTDM_LOG_ERROR, "Error waiting for freetdm interrupt event (WaitForSingleObject returned %d)\n", res);
 334                         return FTDM_FAIL;
 335                 }
 336                 return FTDM_SUCCESS;
 337         }
 338 #else
 339 pollagain:
 340         ints[0].fd = interrupt->readfd;
 341         ints[0].events = POLLIN;
 342         ints[0].revents = 0;
 343 
 344         if (interrupt->device != FTDM_INVALID_SOCKET) {
 345                 num++;
 346                 ints[1].fd = interrupt->device;
 347                 ints[1].events = POLLIN;
 348                 ints[1].revents = 0;
 349         }
 350 
 351         res = poll(ints, num, ms);
 352 
 353         if (res == -1) {
 354                 if (errno == EINTR) {
 355                         goto pollagain;
 356                 }
 357                 ftdm_log(FTDM_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno));
 358                 return FTDM_FAIL;
 359         }
 360 
 361         if (res == 0) {
 362                 return FTDM_TIMEOUT;
 363         }
 364 
 365         if (ints[0].revents & POLLIN) {
 366                 res = read(ints[0].fd, pipebuf, sizeof(pipebuf));
 367                 if (res == -1) {
 368                         ftdm_log(FTDM_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno));
 369                 }
 370         }
 371 
 372         return FTDM_SUCCESS;
 373 #endif
 374 }
 375 
 376 FT_DECLARE(ftdm_status_t) ftdm_interrupt_signal(ftdm_interrupt_t *interrupt)
 377 {
 378         ftdm_assert_return(interrupt != NULL, FTDM_FAIL, "Interrupt is null!\n");
 379 #ifdef WIN32
 380         if (!SetEvent(interrupt->event)) {
 381                 ftdm_log(FTDM_LOG_ERROR, "Failed to signal interrupt\n");
 382                 return FTDM_FAIL;
 383 
 384         }
 385 #else
 386         int err;
 387         struct pollfd testpoll;
 388         testpoll.revents = 0;
 389         testpoll.events = POLLIN;
 390         testpoll.fd = interrupt->readfd;
 391         err = poll(&testpoll, 1, 0);
 392         if (err == 0 && !(testpoll.revents & POLLIN)) {
 393                 /* we just try to notify if there is nothing on the read fd already, 
 394                  * otherwise users that never call interrupt wait eventually will 
 395                  * eventually have the pipe buffer filled */
 396                 if ((err = write(interrupt->writefd, "w", 1)) != 1) {
 397                         ftdm_log(FTDM_LOG_ERROR, "Failed to signal interrupt: %s\n", errno, strerror(errno));
 398                         return FTDM_FAIL;
 399                 }
 400         }
 401 #endif
 402         return FTDM_SUCCESS;
 403 }
 404 
 405 FT_DECLARE(ftdm_status_t) ftdm_interrupt_destroy(ftdm_interrupt_t **ininterrupt)
 406 {
 407         ftdm_interrupt_t *interrupt = NULL;
 408         ftdm_assert_return(ininterrupt != NULL, FTDM_FAIL, "Interrupt null when destroying!\n");
 409         interrupt = *ininterrupt;
 410 #ifdef WIN32
 411         CloseHandle(interrupt->event);
 412 #else
 413         close(interrupt->readfd);
 414         close(interrupt->writefd);
 415 
 416         interrupt->readfd = -1;
 417         interrupt->writefd = -1;
 418 #endif
 419         ftdm_safe_free(interrupt);
 420         *ininterrupt = NULL;
 421         return FTDM_SUCCESS;
 422 }
 423 
 424 FT_DECLARE(ftdm_status_t) ftdm_interrupt_multiple_wait(ftdm_interrupt_t *interrupts[], ftdm_size_t size, int ms)
 425 {
 426         int numdevices = 0;
 427         unsigned i = 0;
 428 
 429 #if defined(__WINDOWS__)
 430         DWORD res = 0;
 431         HANDLE ints[20];
 432         if (size > (ftdm_array_len(ints)/2)) {
 433                 /* improve if needed: dynamically allocate the list of interrupts *only* when exceeding the default size */
 434                 ftdm_log(FTDM_LOG_CRIT, "Unsupported size of interrupts: %d, implement me!\n", size);
 435                 return FTDM_FAIL;
 436         }
 437 
 438         for (i = 0; i < size; i++) {
 439                 ints[i] = interrupts[i]->event;
 440                 if (interrupts[i]->device != FTDM_INVALID_SOCKET) {
 441 
 442                         ints[size+numdevices] = interrupts[i]->device;
 443                         numdevices++;
 444                 }
 445         }
 446 
 447         res = WaitForMultipleObjects((DWORD)size+numdevices, ints, FALSE, ms >= 0 ? ms : INFINITE);
 448 
 449         switch (res) {
 450         case WAIT_TIMEOUT:
 451                 return FTDM_TIMEOUT;
 452         case WAIT_FAILED:
 453         case WAIT_ABANDONED: /* is it right to fail with abandoned? */
 454                 return FTDM_FAIL;
 455         default:
 456                 if (res >= (size+numdevices)) {
 457                         ftdm_log(FTDM_LOG_ERROR, "Error waiting for freetdm interrupt event (WaitForSingleObject returned %d)\n", res);
 458                         return FTDM_FAIL;
 459                 }
 460                 /* fall-through to FTDM_SUCCESS at the end of the function */
 461         }
 462 #elif defined(__linux__) || defined(__FreeBSD__)
 463         int res = 0;
 464         char pipebuf[255];
 465         struct pollfd ints[size*2];
 466 
 467         memset(&ints, 0, sizeof(ints));
 468 pollagain:
 469         for (i = 0; i < size; i++) {
 470                 ints[i].events = POLLIN;
 471                 ints[i].revents = 0;
 472                 ints[i].fd = interrupts[i]->readfd;
 473                 if (interrupts[i]->device != FTDM_INVALID_SOCKET) {
 474                         ints[size+numdevices].events = POLLIN;
 475                         ints[size+numdevices].revents = 0;
 476                         ints[size+numdevices].fd = interrupts[i]->device;
 477 
 478                         numdevices++;
 479                 }
 480         }
 481 
 482         res = poll(ints, size + numdevices, ms);
 483 
 484         if (res == -1) {
 485                 if (errno == EINTR) {
 486                         goto pollagain;
 487                 }
 488                 ftdm_log(FTDM_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno));
 489                 return FTDM_FAIL;
 490         }
 491 
 492         if (res == 0) {
 493                 return FTDM_TIMEOUT;
 494         }
 495 
 496         /* check for events in the pipes, NOT in the devices */
 497         for (i = 0; i < size; i++) {
 498                 if (ints[i].revents & POLLIN) {
 499                         res = read(ints[i].fd, pipebuf, sizeof(pipebuf));
 500                         if (res == -1) {
 501                                 ftdm_log(FTDM_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno));
 502                         }
 503                 }
 504         }
 505 #else
 506         /* for MacOS compilation, unused vars */
 507         numdevices = i;
 508 #endif
 509         return FTDM_SUCCESS;
 510 }
 511 
 512 /* For Emacs:
 513  * Local Variables:
 514  * mode:c
 515  * indent-tabs-mode:t
 516  * tab-width:4
 517  * c-basic-offset:4
 518  * End:
 519  * For VIM:
 520  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 521  */

/* [<][>][^][v][top][bottom][index][help] */