|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
xfermem: unidirectional fast pipe
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
|
|
Packit |
c32a2d |
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
|
Packit |
c32a2d |
initially written by Oliver Fromme
|
|
Packit |
c32a2d |
old timestamp: Sun Apr 6 02:26:26 MET DST 1997
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
See xfermem.h for documentation/description.
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "config.h"
|
|
Packit |
c32a2d |
#include "compat.h"
|
|
Packit |
c32a2d |
#include "xfermem.h"
|
|
Packit |
c32a2d |
#include <string.h>
|
|
Packit |
c32a2d |
#include <errno.h>
|
|
Packit |
c32a2d |
#include <sys/uio.h>
|
|
Packit |
c32a2d |
#include <sys/mman.h>
|
|
Packit |
c32a2d |
#include <sys/socket.h>
|
|
Packit |
c32a2d |
#include <fcntl.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifndef HAVE_MMAP
|
|
Packit |
c32a2d |
#include <sys/ipc.h>
|
|
Packit |
c32a2d |
#include <sys/shm.h>
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#if defined (HAVE_MMAP) && defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
|
|
Packit |
c32a2d |
#define MAP_ANON MAP_ANONYMOUS
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void xfermem_init (txfermem **xf, size_t bufsize, size_t msize, size_t skipbuf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t regsize = bufsize + msize + skipbuf + sizeof(txfermem);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef HAVE_MMAP
|
|
Packit |
c32a2d |
# ifdef MAP_ANON
|
|
Packit |
c32a2d |
if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
|
|
Packit |
c32a2d |
MAP_ANON | MAP_SHARED, -1, 0)) == (txfermem *) -1) {
|
|
Packit |
c32a2d |
perror ("mmap()");
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
# else
|
|
Packit |
c32a2d |
int devzero;
|
|
Packit |
c32a2d |
if ((devzero = open("/dev/zero", O_RDWR, 0)) == -1) {
|
|
Packit |
c32a2d |
perror ("open(/dev/zero)");
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
|
|
Packit |
c32a2d |
MAP_SHARED, devzero, 0)) == (txfermem *) -1) {
|
|
Packit |
c32a2d |
perror ("mmap()");
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
close (devzero);
|
|
Packit |
c32a2d |
# endif
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
struct shmid_ds shmemds;
|
|
Packit |
c32a2d |
int shmemid;
|
|
Packit |
c32a2d |
if ((shmemid = shmget(IPC_PRIVATE, regsize, IPC_CREAT | 0600)) == -1) {
|
|
Packit |
c32a2d |
perror ("shmget()");
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if ((*xf = (txfermem *) shmat(shmemid, 0, 0)) == (txfermem *) -1) {
|
|
Packit |
c32a2d |
perror ("shmat()");
|
|
Packit |
c32a2d |
shmctl (shmemid, IPC_RMID, &shmemds);
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if (shmctl(shmemid, IPC_RMID, &shmemds) == -1) {
|
|
Packit |
c32a2d |
perror ("shmctl()");
|
|
Packit |
c32a2d |
xfermem_done (*xf);
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, (*xf)->fd) < 0) {
|
|
Packit |
c32a2d |
perror ("socketpair()");
|
|
Packit |
c32a2d |
xfermem_done (*xf);
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
(*xf)->freeindex = (*xf)->readindex = 0;
|
|
Packit |
c32a2d |
(*xf)->data = ((char *) *xf) + sizeof(txfermem) + msize;
|
|
Packit |
c32a2d |
(*xf)->metadata = ((char *) *xf) + sizeof(txfermem);
|
|
Packit |
c32a2d |
(*xf)->size = bufsize;
|
|
Packit |
c32a2d |
(*xf)->metasize = msize + skipbuf;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void xfermem_done (txfermem *xf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!xf)
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
#ifdef HAVE_MMAP
|
|
Packit |
c32a2d |
/* Here was a cast to (caddr_t) ... why? Was this needed for SunOS?
|
|
Packit |
c32a2d |
Casting to (void*) should silence compilers in case of funny
|
|
Packit |
c32a2d |
prototype for munmap(). */
|
|
Packit |
c32a2d |
munmap ( (void*)xf, xf->size + xf->metasize + sizeof(txfermem));
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
if (shmdt((void *) xf) == -1) {
|
|
Packit |
c32a2d |
perror ("shmdt()");
|
|
Packit |
c32a2d |
exit (1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void xfermem_init_writer (txfermem *xf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(xf)
|
|
Packit |
c32a2d |
close (xf->fd[XF_READER]);
|
|
Packit |
c32a2d |
debug1("xfermem writer fd=%i", xf->fd[XF_WRITER]);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void xfermem_init_reader (txfermem *xf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(xf)
|
|
Packit |
c32a2d |
close (xf->fd[XF_WRITER]);
|
|
Packit |
c32a2d |
debug1("xfermem reader fd=%i", xf->fd[XF_READER]);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
size_t xfermem_get_freespace (txfermem *xf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t freeindex, readindex;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!xf)
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if ((freeindex = xf->freeindex) < 0
|
|
Packit |
c32a2d |
|| (readindex = xf->readindex) < 0)
|
|
Packit |
c32a2d |
return (0);
|
|
Packit |
c32a2d |
if (readindex > freeindex)
|
|
Packit |
c32a2d |
return ((readindex - freeindex) - 1);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
return ((xf->size - (freeindex - readindex)) - 1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
size_t xfermem_get_usedspace (txfermem *xf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t freeindex, readindex;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!xf)
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if ((freeindex = xf->freeindex) < 0
|
|
Packit |
c32a2d |
|| (readindex = xf->readindex) < 0)
|
|
Packit |
c32a2d |
return (0);
|
|
Packit |
c32a2d |
if (freeindex >= readindex)
|
|
Packit |
c32a2d |
return (freeindex - readindex);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
return (xf->size - (readindex - freeindex));
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int xfermem_getcmd_raw (int fd, int block, byte *cmds, int count)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fd_set selfds;
|
|
Packit |
c32a2d |
int ret;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
for (;;) {
|
|
Packit |
c32a2d |
struct timeval selto = {0, 0};
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
FD_ZERO (&selfds);
|
|
Packit |
c32a2d |
FD_SET (fd, &selfds);
|
|
Packit |
c32a2d |
#ifdef HPUX
|
|
Packit |
c32a2d |
switch (select(FD_SETSIZE, (int *) &selfds, NULL, NULL, block ? NULL : &selto))
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto))
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case 0:
|
|
Packit |
c32a2d |
if (!block)
|
|
Packit |
c32a2d |
return (0);
|
|
Packit |
c32a2d |
continue;
|
|
Packit |
c32a2d |
case -1:
|
|
Packit |
c32a2d |
if (errno == EINTR)
|
|
Packit |
c32a2d |
continue;
|
|
Packit |
c32a2d |
return (-2);
|
|
Packit |
c32a2d |
case 1:
|
|
Packit |
c32a2d |
if (FD_ISSET(fd, &selfds))
|
|
Packit |
c32a2d |
switch((ret=read(fd, cmds, count)))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case 0: /* EOF */
|
|
Packit |
c32a2d |
return (-1);
|
|
Packit |
c32a2d |
case -1:
|
|
Packit |
c32a2d |
if (errno == EINTR)
|
|
Packit |
c32a2d |
continue;
|
|
Packit |
c32a2d |
return (-3);
|
|
Packit |
c32a2d |
default:
|
|
Packit |
c32a2d |
return ret;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else /* ?!? */
|
|
Packit |
c32a2d |
return (-5);
|
|
Packit |
c32a2d |
default: /* ?!? */
|
|
Packit |
c32a2d |
return (-6);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Verbose variant for debugging communication. */
|
|
Packit |
c32a2d |
int xfermem_getcmd(int fd, int block)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
byte cmd;
|
|
Packit |
c32a2d |
int res = xfermem_getcmd_raw(fd, block, &cmd, 1);
|
|
Packit |
c32a2d |
debug3("xfermem_getcmd(%i, %i) = %i", fd, block, res == 1 ? cmd : res);
|
|
Packit |
c32a2d |
return res == 1 ? cmd : res;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
int xfermem_getcmds(int fd, int block, byte *cmds, int count)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int res = xfermem_getcmd_raw(fd, block, cmds, count);
|
|
Packit |
c32a2d |
debug5("xfermem_getcmds(%i, %i, %p, %i) = %i"
|
|
Packit |
c32a2d |
, fd, block, (void*)cmds, count
|
|
Packit |
c32a2d |
, res);
|
|
Packit |
c32a2d |
return res;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
int xfermem_putcmd (int fd, byte cmd)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
for (;;) {
|
|
Packit |
c32a2d |
switch (write(fd, &cmd, 1)) {
|
|
Packit |
c32a2d |
case 1:
|
|
Packit |
c32a2d |
debug2("xfermem_putcmd(%i, %i) = 1", fd, cmd);
|
|
Packit |
c32a2d |
return (1);
|
|
Packit |
c32a2d |
case -1:
|
|
Packit |
c32a2d |
if (errno != EINTR)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug3("xfermem_putcmd(%i, %i) = -1 (%s)"
|
|
Packit |
c32a2d |
, fd, cmd, strerror(errno));
|
|
Packit |
c32a2d |
return (-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
There is a basic assumetry between reader and writer:
|
|
Packit |
c32a2d |
The reader does work in periodic pieces and can be relied upon to
|
|
Packit |
c32a2d |
eventually answer a call. It is important that it does not block
|
|
Packit |
c32a2d |
for a significant duration unless it has really nothing to do.
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
The writer is more undefined in its behaviour, it is controlled by
|
|
Packit |
c32a2d |
external agents. You cannot rely on it answering synchronization
|
|
Packit |
c32a2d |
requests in a timely manner. But on the other hand, it can be left
|
|
Packit |
c32a2d |
hanging for a while. The critical side is that of the reader.
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
Because of that, it is only sensible to provide a voluntary
|
|
Packit |
c32a2d |
xfermem_writer_block() here. The reader does not need such a function.
|
|
Packit |
c32a2d |
Only if it has nothing else to do, it will simply block on
|
|
Packit |
c32a2d |
xfermem_getcmd(), and the writer promises to xfermem_putcmd() when
|
|
Packit |
c32a2d |
something happens.
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
The writer always sends a wakeup command to the reader since the latter
|
|
Packit |
c32a2d |
could be in the process of putting itself to sleep right now, without
|
|
Packit |
c32a2d |
a flag indicating so being set yet.
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
The reader periodically reads from its file descriptor so that it does
|
|
Packit |
c32a2d |
not get clogged up with pending messages. It will only (and always) send
|
|
Packit |
c32a2d |
a wakeup call in response to a received command.
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Wait a bit to get a sign of life from the reader.
|
|
Packit |
c32a2d |
Returns -1 if even that did not work. */
|
|
Packit |
c32a2d |
int xfermem_writer_block(txfermem *xf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int myfd = xf->fd[XF_WRITER];
|
|
Packit |
c32a2d |
int result;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
xfermem_putcmd(myfd, XF_CMD_PING);
|
|
Packit |
c32a2d |
result = xfermem_getcmd(myfd, TRUE);
|
|
Packit |
c32a2d |
/* Only a pong to my ping is the expected good answer.
|
|
Packit |
c32a2d |
Everything else is a problem to be communicated. */
|
|
Packit |
c32a2d |
return (result == XF_CMD_PONG) ? 0 : result;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Return: 0 on success, -1 on communication error, > 0 for
|
|
Packit |
c32a2d |
error on buffer side, some special return code from buffer to be
|
|
Packit |
c32a2d |
evaluated. */
|
|
Packit |
c32a2d |
int xfermem_write(txfermem *xf, void *buffer, size_t bytes)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(buffer == NULL || bytes < 1) return 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* You weren't so braindead not allocating enough space at all, right? */
|
|
Packit |
c32a2d |
while (xfermem_get_freespace(xf) < bytes)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int cmd = xfermem_writer_block(xf);
|
|
Packit |
c32a2d |
if(cmd) /* Non-successful wait. */
|
|
Packit |
c32a2d |
return cmd;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Now we have enough space. copy the memory, possibly with the wrap. */
|
|
Packit |
c32a2d |
if(xf->size - xf->freeindex >= bytes)
|
|
Packit |
c32a2d |
{ /* one block of free memory */
|
|
Packit |
c32a2d |
memcpy(xf->data+xf->freeindex, buffer, bytes);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{ /* two blocks */
|
|
Packit |
c32a2d |
size_t endblock = xf->size - xf->freeindex;
|
|
Packit |
c32a2d |
memcpy(xf->data+xf->freeindex, buffer, endblock);
|
|
Packit |
c32a2d |
memcpy(xf->data, (char*)buffer + endblock, bytes-endblock);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Advance the free space pointer, including the wrap. */
|
|
Packit |
c32a2d |
xf->freeindex = (xf->freeindex + bytes) % xf->size;
|
|
Packit |
c32a2d |
/* Always notify the buffer process. */
|
|
Packit |
c32a2d |
debug("write waking");
|
|
Packit |
c32a2d |
return xfermem_putcmd(xf->fd[XF_WRITER], XF_CMD_DATA) < 0
|
|
Packit |
c32a2d |
? -1
|
|
Packit |
c32a2d |
: 0;
|
|
Packit |
c32a2d |
}
|