Skip to content

Latest commit

 

History

History
304 lines (271 loc) · 7.57 KB

File metadata and controls

304 lines (271 loc) · 7.57 KB
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/*
xfermem: unidirectional fast pipe
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Oliver Fromme
old timestamp: Sun Apr 6 02:26:26 MET DST 1997
See xfermem.h for documentation/description.
*/
#include "config.h"
#include "compat.h"
#include "xfermem.h"
#include <string.h>
#include <errno.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <fcntl.h>
#ifndef HAVE_MMAP
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#include "debug.h"
#if defined (HAVE_MMAP) && defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
#define MAP_ANON MAP_ANONYMOUS
#endif
void xfermem_init (txfermem **xf, size_t bufsize, size_t msize, size_t skipbuf)
{
size_t regsize = bufsize + msize + skipbuf + sizeof(txfermem);
#ifdef HAVE_MMAP
# ifdef MAP_ANON
if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0)) == (txfermem *) -1) {
perror ("mmap()");
exit (1);
}
# else
int devzero;
if ((devzero = open("/dev/zero", O_RDWR, 0)) == -1) {
perror ("open(/dev/zero)");
exit (1);
}
if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
MAP_SHARED, devzero, 0)) == (txfermem *) -1) {
perror ("mmap()");
exit (1);
}
close (devzero);
# endif
#else
struct shmid_ds shmemds;
int shmemid;
if ((shmemid = shmget(IPC_PRIVATE, regsize, IPC_CREAT | 0600)) == -1) {
perror ("shmget()");
exit (1);
}
if ((*xf = (txfermem *) shmat(shmemid, 0, 0)) == (txfermem *) -1) {
perror ("shmat()");
shmctl (shmemid, IPC_RMID, &shmemds);
exit (1);
}
if (shmctl(shmemid, IPC_RMID, &shmemds) == -1) {
perror ("shmctl()");
xfermem_done (*xf);
exit (1);
}
#endif
if (socketpair(AF_UNIX, SOCK_STREAM, 0, (*xf)->fd) < 0) {
perror ("socketpair()");
xfermem_done (*xf);
exit (1);
}
(*xf)->freeindex = (*xf)->readindex = 0;
(*xf)->data = ((char *) *xf) + sizeof(txfermem) + msize;
(*xf)->metadata = ((char *) *xf) + sizeof(txfermem);
(*xf)->size = bufsize;
(*xf)->metasize = msize + skipbuf;
}
void xfermem_done (txfermem *xf)
{
if(!xf)
return;
#ifdef HAVE_MMAP
/* Here was a cast to (caddr_t) ... why? Was this needed for SunOS?
Casting to (void*) should silence compilers in case of funny
prototype for munmap(). */
munmap ( (void*)xf, xf->size + xf->metasize + sizeof(txfermem));
#else
if (shmdt((void *) xf) == -1) {
perror ("shmdt()");
exit (1);
}
#endif
}
void xfermem_init_writer (txfermem *xf)
{
if(xf)
close (xf->fd[XF_READER]);
debug1("xfermem writer fd=%i", xf->fd[XF_WRITER]);
}
void xfermem_init_reader (txfermem *xf)
{
if(xf)
close (xf->fd[XF_WRITER]);
debug1("xfermem reader fd=%i", xf->fd[XF_READER]);
}
size_t xfermem_get_freespace (txfermem *xf)
{
size_t freeindex, readindex;
if(!xf)
return 0;
if ((freeindex = xf->freeindex) < 0
|| (readindex = xf->readindex) < 0)
return (0);
if (readindex > freeindex)
return ((readindex - freeindex) - 1);
else
return ((xf->size - (freeindex - readindex)) - 1);
}
size_t xfermem_get_usedspace (txfermem *xf)
{
size_t freeindex, readindex;
if(!xf)
return 0;
if ((freeindex = xf->freeindex) < 0
|| (readindex = xf->readindex) < 0)
return (0);
if (freeindex >= readindex)
return (freeindex - readindex);
else
return (xf->size - (readindex - freeindex));
}
static int xfermem_getcmd_raw (int fd, int block, byte *cmds, int count)
{
fd_set selfds;
int ret;
for (;;) {
struct timeval selto = {0, 0};
FD_ZERO (&selfds);
FD_SET (fd, &selfds);
#ifdef HPUX
switch (select(FD_SETSIZE, (int *) &selfds, NULL, NULL, block ? NULL : &selto))
#else
switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto))
#endif
{
case 0:
if (!block)
return (0);
continue;
case -1:
if (errno == EINTR)
continue;
return (-2);
case 1:
if (FD_ISSET(fd, &selfds))
switch((ret=read(fd, cmds, count)))
{
case 0: /* EOF */
return (-1);
case -1:
if (errno == EINTR)
continue;
return (-3);
default:
return ret;
}
else /* ?!? */
return (-5);
default: /* ?!? */
return (-6);
}
}
}
/* Verbose variant for debugging communication. */
int xfermem_getcmd(int fd, int block)
{
byte cmd;
int res = xfermem_getcmd_raw(fd, block, &cmd, 1);
debug3("xfermem_getcmd(%i, %i) = %i", fd, block, res == 1 ? cmd : res);
return res == 1 ? cmd : res;
}
int xfermem_getcmds(int fd, int block, byte *cmds, int count)
{
int res = xfermem_getcmd_raw(fd, block, cmds, count);
debug5("xfermem_getcmds(%i, %i, %p, %i) = %i"
, fd, block, (void*)cmds, count
, res);
return res;
}
int xfermem_putcmd (int fd, byte cmd)
{
for (;;) {
switch (write(fd, &cmd, 1)) {
case 1:
debug2("xfermem_putcmd(%i, %i) = 1", fd, cmd);
return (1);
case -1:
if (errno != EINTR)
{
debug3("xfermem_putcmd(%i, %i) = -1 (%s)"
, fd, cmd, strerror(errno));
return (-1);
}
}
}
}
/*
There is a basic assumetry between reader and writer:
The reader does work in periodic pieces and can be relied upon to
eventually answer a call. It is important that it does not block
for a significant duration unless it has really nothing to do.
The writer is more undefined in its behaviour, it is controlled by
external agents. You cannot rely on it answering synchronization
requests in a timely manner. But on the other hand, it can be left
hanging for a while. The critical side is that of the reader.
Because of that, it is only sensible to provide a voluntary
xfermem_writer_block() here. The reader does not need such a function.
Only if it has nothing else to do, it will simply block on
xfermem_getcmd(), and the writer promises to xfermem_putcmd() when
something happens.
The writer always sends a wakeup command to the reader since the latter
could be in the process of putting itself to sleep right now, without
a flag indicating so being set yet.
The reader periodically reads from its file descriptor so that it does
not get clogged up with pending messages. It will only (and always) send
a wakeup call in response to a received command.
*/
/* Wait a bit to get a sign of life from the reader.
Returns -1 if even that did not work. */
int xfermem_writer_block(txfermem *xf)
{
int myfd = xf->fd[XF_WRITER];
int result;
xfermem_putcmd(myfd, XF_CMD_PING);
result = xfermem_getcmd(myfd, TRUE);
/* Only a pong to my ping is the expected good answer.
Everything else is a problem to be communicated. */
return (result == XF_CMD_PONG) ? 0 : result;
}
/* Return: 0 on success, -1 on communication error, > 0 for
error on buffer side, some special return code from buffer to be
evaluated. */
int xfermem_write(txfermem *xf, void *buffer, size_t bytes)
{
if(buffer == NULL || bytes < 1) return 0;
/* You weren't so braindead not allocating enough space at all, right? */
while (xfermem_get_freespace(xf) < bytes)
{
int cmd = xfermem_writer_block(xf);
if(cmd) /* Non-successful wait. */
return cmd;
}
/* Now we have enough space. copy the memory, possibly with the wrap. */
if(xf->size - xf->freeindex >= bytes)
{ /* one block of free memory */
memcpy(xf->data+xf->freeindex, buffer, bytes);
}
else
{ /* two blocks */
size_t endblock = xf->size - xf->freeindex;
memcpy(xf->data+xf->freeindex, buffer, endblock);
memcpy(xf->data, (char*)buffer + endblock, bytes-endblock);
}
/* Advance the free space pointer, including the wrap. */
xf->freeindex = (xf->freeindex + bytes) % xf->size;
/* Always notify the buffer process. */
debug("write waking");
return xfermem_putcmd(xf->fd[XF_WRITER], XF_CMD_DATA) < 0
? -1
: 0;
}