XMMS2

src/xmms/visualization/unixshm.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2011 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 #define _GNU_SOURCE
00018 #include <sys/shm.h>
00019 #include <sys/sem.h>
00020 #include <sys/stat.h>
00021 #include <errno.h>
00022 
00023 #include "common.h"
00024 
00025 #ifdef _SEM_SEMUN_UNDEFINED
00026     union semun {
00027        int              val;    /* Value for SETVAL */
00028        struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
00029        unsigned short  *array;  /* Array for GETALL, SETALL */
00030        struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux specific) */
00031     };
00032 #endif
00033 
00034 int32_t
00035 init_shm (xmms_visualization_t *vis, int32_t id, int32_t shmid, xmms_error_t *err)
00036 {
00037     struct shmid_ds shm_desc;
00038     int32_t semid;
00039     void *buffer;
00040     int size;
00041     xmms_vis_client_t *c;
00042     xmmsc_vis_unixshm_t *t;
00043     union semun semopts;
00044 
00045     x_fetch_client (id);
00046 
00047     /* MR. DEBUG */
00048     /*  xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "lame, more lame, shm!");
00049         x_release_client ();
00050         return -1; */
00051 
00052 
00053     /* test the shm */
00054     buffer = shmat (shmid, NULL, 0);
00055     if (buffer == (void*)-1) {
00056         xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "couldn't attach to shared memory");
00057         x_release_client ();
00058         return -1;
00059     }
00060     shmctl (shmid, IPC_STAT, &shm_desc);
00061     size = shm_desc.shm_segsz / sizeof (xmmsc_vischunk_t);
00062 
00063     /* setup the semaphore set */
00064     semid = semget (IPC_PRIVATE, 2, S_IRWXU + S_IRWXG + S_IRWXO);
00065     if (semid == -1) {
00066         xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "couldn't create semaphore set");
00067         x_release_client ();
00068         return -1;
00069     }
00070 
00071     /* initially set semaphores - nothing to read, buffersize to write
00072        first semaphore is the server semaphore */
00073     semopts.val = size;
00074     semctl (semid, 0, SETVAL, semopts);
00075     semopts.val = 0;
00076     semctl (semid, 1, SETVAL, semopts);
00077 
00078     /* set up client structure */
00079     c->type = VIS_UNIXSHM;
00080     t = &c->transport.shm;
00081     t->semid = semid;
00082     t->shmid = shmid;
00083     t->buffer = buffer;
00084     t->size = size;
00085     t->pos = 0;
00086 
00087     x_release_client ();
00088 
00089     xmms_log_info ("Visualization client %d initialised using Unix SHM", id);
00090     return semid;
00091 }
00092 
00093 void cleanup_shm (xmmsc_vis_unixshm_t *t)
00094 {
00095     shmdt (t->buffer);
00096     semctl (t->semid, 0, IPC_RMID, 0);
00097 }
00098 
00099 /**
00100  * Decrements the server's semaphor (to write the next chunk)
00101  */
00102 static gboolean
00103 decrement_server (xmmsc_vis_unixshm_t *t)
00104 {
00105     /* alter semaphore 0 by -1, don't block */
00106     struct sembuf op = { 0, -1, IPC_NOWAIT };
00107 
00108     while (semop (t->semid, &op, 1) == -1) {
00109         switch (errno) {
00110         case EINTR:
00111             break;
00112         case EAGAIN:
00113             return FALSE;
00114         default:
00115             perror ("Skipping visualization package");
00116             return FALSE;
00117         }
00118     }
00119     return TRUE;
00120 }
00121 
00122 /**
00123  * Increments the client's semaphor (after a chunk was written)
00124  */
00125 static void
00126 increment_client (xmmsc_vis_unixshm_t *t)
00127 {
00128     /* alter semaphore 1 by 1, no flags */
00129     struct sembuf op = { 1, +1, 0 };
00130 
00131     if (semop (t->semid, &op, 1) == -1) {
00132         /* there should not occur any error */
00133         g_error ("visualization increment_client: %s\n", strerror (errno));
00134     }
00135 }
00136 
00137 gboolean
00138 write_shm (xmmsc_vis_unixshm_t *t, xmms_vis_client_t *c, int32_t id, struct timeval *time, int channels, int size, short *buf)
00139 {
00140     xmmsc_vischunk_t *dest;
00141     short res;
00142 
00143     if (!write_start_shm (id, t, &dest))
00144         return FALSE;
00145 
00146     tv2net (dest->timestamp, time);
00147     dest->format = htons (c->format);
00148     res = fill_buffer (dest->data, &c->prop, channels, size, buf);
00149     dest->size = htons (res);
00150     write_finish_shm (id, t, dest);
00151 
00152     return TRUE;
00153 }
00154 
00155 
00156 gboolean
00157 write_start_shm (int32_t id, xmmsc_vis_unixshm_t *t, xmmsc_vischunk_t **dest)
00158 {
00159     struct shmid_ds shm_desc;
00160 
00161     /* first check if the client is still there */
00162     if (shmctl (t->shmid, IPC_STAT, &shm_desc) == -1) {
00163         g_error ("Checking SHM attachments failed: %s\n", strerror (errno));
00164     }
00165     if (shm_desc.shm_nattch == 1) {
00166         delete_client (id);
00167         return FALSE;
00168     }
00169     if (shm_desc.shm_nattch != 2) {
00170         g_error ("Unbelievable # of SHM attachments: %lu\n",
00171                  (unsigned long) shm_desc.shm_nattch);
00172     }
00173 
00174     if (!decrement_server (t)) {
00175         return FALSE;
00176     }
00177 
00178     *dest = &t->buffer[t->pos];
00179     return TRUE;
00180 }
00181 
00182 void
00183 write_finish_shm (int32_t id, xmmsc_vis_unixshm_t *t, xmmsc_vischunk_t *dest)
00184 {
00185     t->pos = (t->pos + 1) % t->size;
00186     increment_client (t);
00187 }