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         /* for generic interruption */
  60         int readfd;
  61         int writefd;
  62 #endif
  63 };
  64 
  65 struct ftdm_thread {
  66 #ifdef WIN32
  67         void *handle;
  68 #else
  69         pthread_t handle;
  70 #endif
  71         void *private_data;
  72         ftdm_thread_function_t function;
  73         ftdm_size_t stack_size;
  74 #ifndef WIN32
  75         pthread_attr_t attribute;
  76 #endif
  77 };
  78 
  79 ftdm_size_t thread_default_stacksize = 0;
  80 
  81 FT_DECLARE(void) ftdm_thread_override_default_stacksize(ftdm_size_t size)
  82 {
  83         thread_default_stacksize = size;
  84 }
  85 
  86 static void * FTDM_THREAD_CALLING_CONVENTION thread_launch(void *args)
  87 {
  88         void *exit_val;
  89     ftdm_thread_t *thread = (ftdm_thread_t *)args;
  90         exit_val = thread->function(thread, thread->private_data);
  91 #ifndef WIN32
  92         pthread_attr_destroy(&thread->attribute);
  93 #endif
  94         ftdm_safe_free(thread);
  95 
  96         return exit_val;
  97 }
  98 
  99 FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached(ftdm_thread_function_t func, void *data)
 100 {
 101         return ftdm_thread_create_detached_ex(func, data, thread_default_stacksize);
 102 }
 103 
 104 FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached_ex(ftdm_thread_function_t func, void *data, ftdm_size_t stack_size)
 105 {
 106         ftdm_thread_t *thread = NULL;
 107         ftdm_status_t status = FTDM_FAIL;
 108 
 109         if (!func || !(thread = (ftdm_thread_t *)ftdm_malloc(sizeof(ftdm_thread_t)))) {
 110                 goto done;
 111         }
 112 
 113         thread->private_data = data;
 114         thread->function = func;
 115         thread->stack_size = stack_size;
 116 
 117 #if defined(WIN32)
 118         thread->handle = (void *)_beginthreadex(NULL, (unsigned)thread->stack_size, (unsigned int (__stdcall *)(void *))thread_launch, thread, 0, NULL);
 119         if (!thread->handle) {
 120                 goto fail;
 121         }
 122         CloseHandle(thread->handle);
 123 
 124         status = FTDM_SUCCESS;
 125         goto done;
 126 #else
 127         
 128         if (pthread_attr_init(&thread->attribute) != 0) goto fail;
 129 
 130         if (pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread;
 131 
 132         if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread;
 133 
 134         if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread;
 135 
 136         status = FTDM_SUCCESS;
 137         goto done;
 138  failpthread:
 139         pthread_attr_destroy(&thread->attribute);
 140 #endif
 141 
 142  fail:
 143         if (thread) {
 144                 ftdm_safe_free(thread);
 145         }
 146  done:
 147         return status;
 148 }
 149 
 150 
 151 FT_DECLARE(ftdm_status_t) ftdm_mutex_create(ftdm_mutex_t **mutex)
 152 {
 153         ftdm_status_t status = FTDM_FAIL;
 154 #ifndef WIN32
 155         pthread_mutexattr_t attr;
 156 #endif
 157         ftdm_mutex_t *check = NULL;
 158 
 159         check = (ftdm_mutex_t *)ftdm_malloc(sizeof(**mutex));
 160         if (!check)
 161                 goto done;
 162 #ifdef WIN32
 163         InitializeCriticalSection(&check->mutex);
 164 #else
 165         if (pthread_mutexattr_init(&attr))
 166                 goto done;
 167 
 168         if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))
 169                 goto fail;
 170 
 171         if (pthread_mutex_init(&check->mutex, &attr))
 172                 goto fail;
 173 
 174         goto success;
 175 
 176  fail:
 177         pthread_mutexattr_destroy(&attr);
 178         goto done;
 179 
 180  success:
 181 #endif
 182         *mutex = check;
 183         status = FTDM_SUCCESS;
 184 
 185  done:
 186         return status;
 187 }
 188 
 189 FT_DECLARE(ftdm_status_t) ftdm_mutex_destroy(ftdm_mutex_t **mutex)
 190 {
 191         ftdm_mutex_t *mp = *mutex;
 192         *mutex = NULL;
 193         if (!mp) {
 194                 return FTDM_FAIL;
 195         }
 196 #ifdef WIN32
 197         DeleteCriticalSection(&mp->mutex);
 198 #else
 199         if (pthread_mutex_destroy(&mp->mutex))
 200                 return FTDM_FAIL;
 201 #endif
 202         ftdm_safe_free(mp);
 203         return FTDM_SUCCESS;
 204 }
 205 
 206 FT_DECLARE(ftdm_status_t) _ftdm_mutex_lock(ftdm_mutex_t *mutex)
 207 {
 208 #ifdef WIN32
 209         EnterCriticalSection(&mutex->mutex);
 210 #else
 211         int err;
 212         if ((err = pthread_mutex_lock(&mutex->mutex))) {
 213                 ftdm_log(FTDM_LOG_ERROR, "Failed to lock mutex %d:%s\n", err, strerror(err));
 214                 return FTDM_FAIL;
 215         }
 216 #endif
 217         return FTDM_SUCCESS;
 218 }
 219 
 220 FT_DECLARE(ftdm_status_t) _ftdm_mutex_trylock(ftdm_mutex_t *mutex)
 221 {
 222 #ifdef WIN32
 223         if (!TryEnterCriticalSection(&mutex->mutex))
 224                 return FTDM_FAIL;
 225 #else
 226         if (pthread_mutex_trylock(&mutex->mutex))
 227                 return FTDM_FAIL;
 228 #endif
 229         return FTDM_SUCCESS;
 230 }
 231 
 232 FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex)
 233 {
 234 #ifdef WIN32
 235         LeaveCriticalSection(&mutex->mutex);
 236 #else
 237         if (pthread_mutex_unlock(&mutex->mutex))
 238                 return FTDM_FAIL;
 239 #endif
 240         return FTDM_SUCCESS;
 241 }
 242 
 243 
 244 FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device)
 245 {
 246         ftdm_interrupt_t *interrupt = NULL;
 247 #ifndef WIN32
 248         int fds[2];
 249 #endif
 250 
 251         ftdm_assert_return(ininterrupt != NULL, FTDM_FAIL, "interrupt double pointer is null!\n");
 252 
 253         interrupt = ftdm_calloc(1, sizeof(*interrupt));
 254         if (!interrupt) {
 255                 ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n");
 256                 return FTDM_FAIL;
 257         }
 258 
 259         interrupt->device = device;
 260 #ifdef WIN32
 261         interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL);
 262         if (!interrupt->event) {
 263                 ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n");
 264                 goto failed;
 265         }
 266 #else
 267         if (pipe(fds)) {
 268                 ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno));
 269                 goto failed;
 270         }
 271         interrupt->readfd = fds[0];
 272         interrupt->writefd = fds[1];
 273 #endif
 274 
 275         *ininterrupt = interrupt;
 276         return FTDM_SUCCESS;
 277 
 278 failed:
 279         if (interrupt) {
 280 #ifndef WIN32
 281                 if (interrupt->readfd) {
 282                         close(interrupt->readfd);
 283                         close(interrupt->writefd);
 284                         interrupt->readfd = -1;
 285                         interrupt->writefd = -1;
 286                 }
 287 #endif
 288                 ftdm_safe_free(interrupt);
 289         }
 290         return FTDM_FAIL;
 291 }
 292 
 293 #define ONE_BILLION 1000000000
 294 
 295 FT_DECLARE(ftdm_status_t) ftdm_interrupt_wait(ftdm_interrupt_t *interrupt, int ms)
 296 {
 297         int num = 1;
 298 #ifdef WIN32
 299         DWORD res = 0;
 300         HANDLE ints[2];
 301 #else
 302         int res = 0;
 303         struct pollfd ints[2];
 304         char pipebuf[255];
 305 #endif
 306 
 307         ftdm_assert_return(interrupt != NULL, FTDM_FAIL, "Condition is null!\n");
 308 
 309 
 310         /* start implementation */
 311 #ifdef WIN32
 312         ints[0] = interrupt->event;
 313         if (interrupt->device != FTDM_INVALID_SOCKET) {
 314                 num++;
 315                 ints[1] = interrupt->device;
 316         }
 317         res = WaitForMultipleObjects(num, ints, FALSE, ms >= 0 ? ms : INFINITE);
 318         switch (res) {
 319         case WAIT_TIMEOUT:
 320                 return FTDM_TIMEOUT;
 321         case WAIT_FAILED:
 322         case WAIT_ABANDONED: /* is it right to fail with abandoned? */
 323                 return FTDM_FAIL;
 324         default:
 325                 if (res >= (sizeof(ints)/sizeof(ints[0]))) {
 326                         ftdm_log(FTDM_LOG_ERROR, "Error waiting for freetdm interrupt event (WaitForSingleObject returned %d)\n", res);
 327                         return FTDM_FAIL;
 328                 }
 329                 return FTDM_SUCCESS;
 330         }
 331 #else
 332 pollagain:
 333         ints[0].fd = interrupt->readfd;
 334         ints[0].events = POLLIN;
 335         ints[0].revents = 0;
 336 
 337         if (interrupt->device != FTDM_INVALID_SOCKET) {
 338                 num++;
 339                 ints[1].fd = interrupt->device;
 340                 ints[1].events = POLLIN;
 341                 ints[1].revents = 0;
 342         }
 343 
 344         res = poll(ints, num, ms);
 345 
 346         if (res == -1) {
 347                 if (errno == EINTR) {
 348                         goto pollagain;
 349                 }
 350                 ftdm_log(FTDM_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno));
 351                 return FTDM_FAIL;
 352         }
 353 
 354         if (res == 0) {
 355                 return FTDM_TIMEOUT;
 356         }
 357 
 358         if (ints[0].revents & POLLIN) {
 359                 res = read(ints[0].fd, pipebuf, sizeof(pipebuf));
 360                 if (res == -1) {
 361                         ftdm_log(FTDM_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno));
 362                 }
 363         }
 364 
 365         return FTDM_SUCCESS;
 366 #endif
 367 }
 368 
 369 FT_DECLARE(ftdm_status_t) ftdm_interrupt_signal(ftdm_interrupt_t *interrupt)
 370 {
 371         ftdm_assert_return(interrupt != NULL, FTDM_FAIL, "Interrupt is null!\n");
 372 #ifdef WIN32
 373         if (!SetEvent(interrupt->event)) {
 374                 ftdm_log(FTDM_LOG_ERROR, "Failed to signal interrupt\n");
 375                 return FTDM_FAIL;
 376 
 377         }
 378 #else
 379         int err;
 380         struct pollfd testpoll;
 381         testpoll.revents = 0;
 382         testpoll.events = POLLIN;
 383         testpoll.fd = interrupt->readfd;
 384         err = poll(&testpoll, 1, 0);
 385         if (err == 0 && !(testpoll.revents & POLLIN)) {
 386                 /* we just try to notify if there is nothing on the read fd already, 
 387                  * otherwise users that never call interrupt wait eventually will 
 388                  * eventually have the pipe buffer filled */
 389                 if ((err = write(interrupt->writefd, "w", 1)) != 1) {
 390                         ftdm_log(FTDM_LOG_ERROR, "Failed to signal interrupt: %s\n", errno, strerror(errno));
 391                         return FTDM_FAIL;
 392                 }
 393         }
 394 #endif
 395         return FTDM_SUCCESS;
 396 }
 397 
 398 FT_DECLARE(ftdm_status_t) ftdm_interrupt_destroy(ftdm_interrupt_t **ininterrupt)
 399 {
 400         ftdm_interrupt_t *interrupt = NULL;
 401         ftdm_assert_return(ininterrupt != NULL, FTDM_FAIL, "Interrupt null when destroying!\n");
 402         interrupt = *ininterrupt;
 403 #ifdef WIN32
 404         CloseHandle(interrupt->event);
 405 #else
 406         close(interrupt->readfd);
 407         close(interrupt->writefd);
 408 
 409         interrupt->readfd = -1;
 410         interrupt->writefd = -1;
 411 #endif
 412         ftdm_safe_free(interrupt);
 413         *ininterrupt = NULL;
 414         return FTDM_SUCCESS;
 415 }
 416 
 417 FT_DECLARE(ftdm_status_t) ftdm_interrupt_multiple_wait(ftdm_interrupt_t *interrupts[], ftdm_size_t size, int ms)
 418 {
 419         int numdevices = 0;
 420         unsigned i;
 421 
 422 #if defined(__WINDOWS__)
 423         DWORD res = 0;
 424         HANDLE ints[20];
 425         if (size > (ftdm_array_len(ints)/2)) {
 426                 /* improve if needed: dynamically allocate the list of interrupts *only* when exceeding the default size */
 427                 ftdm_log(FTDM_LOG_CRIT, "Unsupported size of interrupts: %d, implement me!\n", size);
 428                 return FTDM_FAIL;
 429         }
 430 
 431         for (i = 0; i < size; i++) {
 432                 ints[i] = interrupts[i]->event;
 433                 if (interrupts[i]->device != FTDM_INVALID_SOCKET) {
 434 
 435                         ints[size+numdevices] = interrupts[i]->device;
 436                         numdevices++;
 437                 }
 438         }
 439 
 440         res = WaitForMultipleObjects((DWORD)size+numdevices, ints, FALSE, ms >= 0 ? ms : INFINITE);
 441 
 442         switch (res) {
 443         case WAIT_TIMEOUT:
 444                 return FTDM_TIMEOUT;
 445         case WAIT_FAILED:
 446         case WAIT_ABANDONED: /* is it right to fail with abandoned? */
 447                 return FTDM_FAIL;
 448         default:
 449                 if (res >= (size+numdevices)) {
 450                         ftdm_log(FTDM_LOG_ERROR, "Error waiting for freetdm interrupt event (WaitForSingleObject returned %d)\n", res);
 451                         return FTDM_FAIL;
 452                 }
 453                 /* fall-through to FTDM_SUCCESS at the end of the function */
 454         }
 455 #elif defined(__linux__) || defined(__FreeBSD__)
 456         int res = 0;
 457         char pipebuf[255];
 458         struct pollfd ints[size*2];
 459 
 460         memset(&ints, 0, sizeof(ints));
 461 pollagain:
 462         for (i = 0; i < size; i++) {
 463                 ints[i].events = POLLIN;
 464                 ints[i].revents = 0;
 465                 ints[i].fd = interrupts[i]->readfd;
 466                 if (interrupts[i]->device != FTDM_INVALID_SOCKET) {
 467                         ints[size+numdevices].events = POLLIN;
 468                         ints[size+numdevices].revents = 0;
 469                         ints[size+numdevices].fd = interrupts[i]->device;
 470 
 471                         numdevices++;
 472                 }
 473         }
 474 
 475         res = poll(ints, size + numdevices, ms);
 476 
 477         if (res == -1) {
 478                 if (errno == EINTR) {
 479                         goto pollagain;
 480                 }
 481                 ftdm_log(FTDM_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno));
 482                 return FTDM_FAIL;
 483         }
 484 
 485         if (res == 0) {
 486                 return FTDM_TIMEOUT;
 487         }
 488 
 489         /* check for events in the pipes, NOT in the devices */
 490         for (i = 0; i < size; i++) {
 491                 if (ints[i].revents & POLLIN) {
 492                         res = read(ints[i].fd, pipebuf, sizeof(pipebuf));
 493                         if (res == -1) {
 494                                 ftdm_log(FTDM_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno));
 495                         }
 496                 }
 497         }
 498 #else
 499 #endif
 500         return FTDM_SUCCESS;
 501 }
 502 
 503 /* For Emacs:
 504  * Local Variables:
 505  * mode:c
 506  * indent-tabs-mode:t
 507  * tab-width:4
 508  * c-basic-offset:4
 509  * End:
 510  * For VIM:
 511  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 512  */

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