LCOV - code coverage report
Current view: top level - pulsecore - lock-autospawn.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 139 152 91.4 %
Date: 2012-07-17 Functions: 12 12 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 60 112 53.6 %

           Branch data     Line data    Source code
       1                 :            : /***
       2                 :            :   This file is part of PulseAudio.
       3                 :            : 
       4                 :            :   Copyright 2008 Lennart Poettering
       5                 :            : 
       6                 :            :   PulseAudio is free software; you can redistribute it and/or modify
       7                 :            :   it under the terms of the GNU Lesser General Public License as published
       8                 :            :   by the Free Software Foundation; either version 2.1 of the License,
       9                 :            :   or (at your option) any later version.
      10                 :            : 
      11                 :            :   PulseAudio is distributed in the hope that it will be useful, but
      12                 :            :   WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      14                 :            :   General Public License for more details.
      15                 :            : 
      16                 :            :   You should have received a copy of the GNU Lesser General Public License
      17                 :            :   along with PulseAudio; if not, write to the Free Software
      18                 :            :   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
      19                 :            :   USA.
      20                 :            : ***/
      21                 :            : 
      22                 :            : #ifdef HAVE_CONFIG_H
      23                 :            : #include <config.h>
      24                 :            : #endif
      25                 :            : 
      26                 :            : #include <errno.h>
      27                 :            : #include <string.h>
      28                 :            : #include <signal.h>
      29                 :            : 
      30                 :            : #ifdef HAVE_PTHREAD
      31                 :            : #include <pthread.h>
      32                 :            : #endif
      33                 :            : 
      34                 :            : #include <pulse/gccmacro.h>
      35                 :            : #include <pulse/xmalloc.h>
      36                 :            : 
      37                 :            : #include <pulsecore/i18n.h>
      38                 :            : #include <pulsecore/poll.h>
      39                 :            : #include <pulsecore/mutex.h>
      40                 :            : #include <pulsecore/thread.h>
      41                 :            : #include <pulsecore/core-util.h>
      42                 :            : 
      43                 :            : #include "lock-autospawn.h"
      44                 :            : 
      45                 :            : /* So, why do we have this complex code here with threads and pipes
      46                 :            :  * and stuff? For two reasons: POSIX file locks are per-process, not
      47                 :            :  * per-file descriptor. That means that two contexts within the same
      48                 :            :  * process that try to create the autospawn lock might end up assuming
      49                 :            :  * they both managed to lock the file. And then, POSIX locking
      50                 :            :  * operations are synchronous. If two contexts run from the same event
      51                 :            :  * loop it must be made sure that they do not block each other, but
      52                 :            :  * that the locking operation can happen asynchronously. */
      53                 :            : 
      54                 :            : #define AUTOSPAWN_LOCK "autospawn.lock"
      55                 :            : 
      56                 :            : static pa_mutex *mutex;
      57                 :            : 
      58                 :            : static unsigned n_ref = 0;
      59                 :            : static int lock_fd = -1;
      60                 :            : static pa_mutex *lock_fd_mutex = NULL;
      61                 :            : static pa_thread *thread = NULL;
      62                 :            : static int pipe_fd[2] = { -1, -1 };
      63                 :            : 
      64                 :            : static enum {
      65                 :            :     STATE_IDLE,
      66                 :            :     STATE_OWNING,
      67                 :            :     STATE_TAKEN,
      68                 :            :     STATE_FAILED
      69                 :            : } state = STATE_IDLE;
      70                 :            : 
      71                 :            : static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
      72                 :            : 
      73                 :          4 : static int ref(void) {
      74                 :            : 
      75         [ +  + ]:          4 :     if (n_ref > 0) {
      76                 :            : 
      77         [ -  + ]:          3 :         pa_assert(pipe_fd[0] >= 0);
      78         [ -  + ]:          3 :         pa_assert(pipe_fd[1] >= 0);
      79         [ -  + ]:          3 :         pa_assert(lock_fd_mutex);
      80                 :            : 
      81                 :          3 :         n_ref++;
      82                 :            : 
      83                 :          3 :         return 0;
      84                 :            :     }
      85                 :            : 
      86         [ -  + ]:          1 :     pa_assert(!lock_fd_mutex);
      87         [ -  + ]:          1 :     pa_assert(state == STATE_IDLE);
      88         [ -  + ]:          1 :     pa_assert(lock_fd < 0);
      89         [ -  + ]:          1 :     pa_assert(!thread);
      90         [ -  + ]:          1 :     pa_assert(pipe_fd[0] < 0);
      91         [ -  + ]:          1 :     pa_assert(pipe_fd[1] < 0);
      92                 :            : 
      93         [ +  - ]:          1 :     if (pa_pipe_cloexec(pipe_fd) < 0)
      94                 :            :         return -1;
      95                 :            : 
      96                 :          1 :     pa_make_fd_nonblock(pipe_fd[1]);
      97                 :          1 :     pa_make_fd_nonblock(pipe_fd[0]);
      98                 :            : 
      99                 :          1 :     lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
     100                 :            : 
     101                 :          1 :     n_ref = 1;
     102                 :          4 :     return 0;
     103                 :            : }
     104                 :            : 
     105                 :          4 : static void unref(pa_bool_t after_fork) {
     106                 :            : 
     107         [ -  + ]:          4 :     pa_assert(n_ref > 0);
     108         [ -  + ]:          4 :     pa_assert(pipe_fd[0] >= 0);
     109         [ -  + ]:          4 :     pa_assert(pipe_fd[1] >= 0);
     110         [ -  + ]:          4 :     pa_assert(lock_fd_mutex);
     111                 :            : 
     112                 :          4 :     n_ref--;
     113                 :            : 
     114         [ +  + ]:          4 :     if (n_ref > 0)
     115                 :          4 :         return;
     116                 :            : 
     117         [ +  - ]:          1 :     if (thread) {
     118                 :          1 :         pa_thread_free(thread);
     119                 :          1 :         thread = NULL;
     120                 :            :     }
     121                 :            : 
     122                 :          1 :     pa_mutex_lock(lock_fd_mutex);
     123                 :            : 
     124         [ -  + ]:          1 :     pa_assert(state != STATE_TAKEN);
     125                 :            : 
     126         [ +  - ]:          1 :     if (state == STATE_OWNING) {
     127                 :            : 
     128         [ -  + ]:          1 :         pa_assert(lock_fd >= 0);
     129                 :            : 
     130         [ -  + ]:          1 :         if (after_fork)
     131                 :          0 :             pa_close(lock_fd);
     132                 :            :         else {
     133                 :            :             char *lf;
     134                 :            : 
     135         [ -  + ]:          1 :             if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
     136                 :          0 :                 pa_log_warn(_("Cannot access autospawn lock."));
     137                 :            : 
     138                 :          1 :             pa_unlock_lockfile(lf, lock_fd);
     139                 :          1 :             pa_xfree(lf);
     140                 :            :         }
     141                 :            :     }
     142                 :            : 
     143                 :          1 :     lock_fd = -1;
     144                 :          1 :     state = STATE_IDLE;
     145                 :            : 
     146                 :          1 :     pa_mutex_unlock(lock_fd_mutex);
     147                 :            : 
     148                 :          1 :     pa_mutex_free(lock_fd_mutex);
     149                 :          1 :     lock_fd_mutex = NULL;
     150                 :            : 
     151                 :          1 :     pa_close(pipe_fd[0]);
     152                 :          1 :     pa_close(pipe_fd[1]);
     153                 :          1 :     pipe_fd[0] = pipe_fd[1] = -1;
     154                 :            : }
     155                 :            : 
     156                 :          5 : static void ping(void) {
     157                 :            :     ssize_t s;
     158                 :            : 
     159         [ -  + ]:          5 :     pa_assert(pipe_fd[1] >= 0);
     160                 :            : 
     161                 :            :     for (;;) {
     162                 :          5 :         char x = 'x';
     163                 :            : 
     164         [ -  + ]:          5 :         if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
     165                 :            :             break;
     166                 :            : 
     167         [ #  # ]:          0 :         pa_assert(s < 0);
     168                 :            : 
     169         [ #  # ]:          0 :         if (errno == EAGAIN)
     170                 :            :             break;
     171                 :            : 
     172         [ #  # ]:          0 :         pa_assert(errno == EINTR);
     173                 :            :     }
     174                 :          5 : }
     175                 :            : 
     176                 :          3 : static void wait_for_ping(void) {
     177                 :            :     ssize_t s;
     178                 :            :     char x;
     179                 :            :     struct pollfd pfd;
     180                 :            :     int k;
     181                 :            : 
     182         [ -  + ]:          3 :     pa_assert(pipe_fd[0] >= 0);
     183                 :            : 
     184                 :            :     memset(&pfd, 0, sizeof(pfd));
     185                 :          3 :     pfd.fd = pipe_fd[0];
     186                 :          3 :     pfd.events = POLLIN;
     187                 :            : 
     188         [ -  + ]:          3 :     if ((k = pa_poll(&pfd, 1, -1)) != 1) {
     189         [ #  # ]:          0 :         pa_assert(k < 0);
     190         [ #  # ]:          0 :         pa_assert(errno == EINTR);
     191         [ +  + ]:          3 :     } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
     192         [ -  + ]:          1 :         pa_assert(s < 0);
     193         [ -  + ]:          1 :         pa_assert(errno == EAGAIN);
     194                 :            :     }
     195                 :          3 : }
     196                 :            : 
     197                 :         13 : static void empty_pipe(void) {
     198                 :            :     char x[16];
     199                 :            :     ssize_t s;
     200                 :            : 
     201         [ -  + ]:         13 :     pa_assert(pipe_fd[0] >= 0);
     202                 :            : 
     203         [ +  + ]:         13 :     if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
     204         [ -  + ]:         11 :         pa_assert(s < 0);
     205         [ -  + ]:         11 :         pa_assert(errno == EAGAIN);
     206                 :            :     }
     207                 :         13 : }
     208                 :            : 
     209                 :          1 : static void thread_func(void *u) {
     210                 :            :     int fd;
     211                 :            :     char *lf;
     212                 :            : 
     213                 :            : #ifdef HAVE_PTHREAD
     214                 :            :     sigset_t fullset;
     215                 :            : 
     216                 :            :     /* No signals in this thread please */
     217                 :          1 :     sigfillset(&fullset);
     218                 :          1 :     pthread_sigmask(SIG_BLOCK, &fullset, NULL);
     219                 :            : #endif
     220                 :            : 
     221         [ -  + ]:          1 :     if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
     222                 :          0 :         pa_log_warn(_("Cannot access autospawn lock."));
     223                 :          0 :         goto fail;
     224                 :            :     }
     225                 :            : 
     226         [ +  - ]:          1 :     if ((fd = pa_lock_lockfile(lf)) < 0)
     227                 :            :         goto fail;
     228                 :            : 
     229                 :          1 :     pa_mutex_lock(lock_fd_mutex);
     230         [ -  + ]:          1 :     pa_assert(state == STATE_IDLE);
     231                 :          1 :     lock_fd = fd;
     232                 :          1 :     state = STATE_OWNING;
     233                 :          1 :     pa_mutex_unlock(lock_fd_mutex);
     234                 :            : 
     235                 :          1 :     goto finish;
     236                 :            : 
     237                 :            : fail:
     238                 :          0 :     pa_mutex_lock(lock_fd_mutex);
     239         [ #  # ]:          0 :     pa_assert(state == STATE_IDLE);
     240                 :          0 :     state = STATE_FAILED;
     241                 :          0 :     pa_mutex_unlock(lock_fd_mutex);
     242                 :            : 
     243                 :            : finish:
     244                 :          1 :     pa_xfree(lf);
     245                 :            : 
     246                 :          1 :     ping();
     247                 :          1 : }
     248                 :            : 
     249                 :            : static int start_thread(void) {
     250                 :            : 
     251         [ +  + ]:          4 :     if (!thread)
     252         [ +  - ]:          1 :         if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
     253                 :            :             return -1;
     254                 :            : 
     255                 :            :     return 0;
     256                 :            : }
     257                 :            : 
     258                 :         22 : static void create_mutex(void) {
     259         [ +  + ]:         22 :     PA_ONCE_BEGIN {
     260                 :          1 :         mutex = pa_mutex_new(FALSE, FALSE);
     261                 :          1 :     } PA_ONCE_END;
     262                 :         22 : }
     263                 :            : 
     264                 :         27 : static void destroy_mutex(void) {
     265         [ +  + ]:         27 :     if (mutex)
     266                 :          1 :         pa_mutex_free(mutex);
     267                 :         27 : }
     268                 :            : 
     269                 :          4 : int pa_autospawn_lock_init(void) {
     270                 :          4 :     int ret = -1;
     271                 :            : 
     272                 :          4 :     create_mutex();
     273                 :          4 :     pa_mutex_lock(mutex);
     274                 :            : 
     275         [ +  - ]:          4 :     if (ref() < 0)
     276                 :            :         ret = -1;
     277                 :            :     else
     278                 :          4 :         ret = pipe_fd[0];
     279                 :            : 
     280                 :          4 :     pa_mutex_unlock(mutex);
     281                 :            : 
     282                 :          4 :     return ret;
     283                 :            : }
     284                 :            : 
     285                 :         10 : int pa_autospawn_lock_acquire(pa_bool_t block) {
     286                 :         10 :     int ret = -1;
     287                 :            : 
     288                 :         10 :     create_mutex();
     289                 :         10 :     pa_mutex_lock(mutex);
     290         [ -  + ]:         10 :     pa_assert(n_ref >= 1);
     291                 :            : 
     292                 :         10 :     pa_mutex_lock(lock_fd_mutex);
     293                 :            : 
     294                 :            :     for (;;) {
     295                 :            : 
     296                 :         13 :         empty_pipe();
     297                 :            : 
     298         [ +  + ]:         13 :         if (state == STATE_OWNING) {
     299                 :          4 :             state = STATE_TAKEN;
     300                 :          4 :             ret = 1;
     301                 :          4 :             break;
     302                 :            :         }
     303                 :            : 
     304         [ +  - ]:          9 :         if (state == STATE_FAILED) {
     305                 :            :             ret = -1;
     306                 :            :             break;
     307                 :            :         }
     308                 :            : 
     309         [ +  + ]:          9 :         if (state == STATE_IDLE)
     310         [ +  - ]:          4 :             if (start_thread() < 0)
     311                 :            :                 break;
     312                 :            : 
     313         [ +  + ]:          9 :         if (!block) {
     314                 :            :             ret = 0;
     315                 :            :             break;
     316                 :            :         }
     317                 :            : 
     318                 :          3 :         pa_mutex_unlock(lock_fd_mutex);
     319                 :          3 :         pa_mutex_unlock(mutex);
     320                 :            : 
     321                 :          3 :         wait_for_ping();
     322                 :            : 
     323                 :          3 :         pa_mutex_lock(mutex);
     324                 :          3 :         pa_mutex_lock(lock_fd_mutex);
     325                 :          3 :     }
     326                 :            : 
     327                 :         10 :     pa_mutex_unlock(lock_fd_mutex);
     328                 :            : 
     329                 :         10 :     pa_mutex_unlock(mutex);
     330                 :            : 
     331                 :         10 :     return ret;
     332                 :            : }
     333                 :            : 
     334                 :          4 : void pa_autospawn_lock_release(void) {
     335                 :            : 
     336                 :          4 :     create_mutex();
     337                 :          4 :     pa_mutex_lock(mutex);
     338         [ -  + ]:          4 :     pa_assert(n_ref >= 1);
     339                 :            : 
     340         [ -  + ]:          4 :     pa_assert(state == STATE_TAKEN);
     341                 :          4 :     state = STATE_OWNING;
     342                 :            : 
     343                 :          4 :     ping();
     344                 :            : 
     345                 :          4 :     pa_mutex_unlock(mutex);
     346                 :          4 : }
     347                 :            : 
     348                 :          4 : void pa_autospawn_lock_done(pa_bool_t after_fork) {
     349                 :            : 
     350                 :          4 :     create_mutex();
     351                 :          4 :     pa_mutex_lock(mutex);
     352         [ -  + ]:          4 :     pa_assert(n_ref >= 1);
     353                 :            : 
     354                 :          4 :     unref(after_fork);
     355                 :            : 
     356                 :          4 :     pa_mutex_unlock(mutex);
     357                 :          4 : }

Generated by: LCOV version 1.9