XMMS2

src/xmms/visualization/unixshm.c

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