XMMS2
|
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 }