Single Producer / Consumer Ring Buffer in Shared Memory - c++

Recently I've been playing about with using shared memory for IPC. One thing I've been trying to implement is a simple ring buffer with 1 process producing and 1 process consuming. Each process has its own sequence number to track its position. These sequence numbers are updated using atomic ops to ensure the correct values are visible to the other process. The producer will block once the ring buffer is full. The code is lock free in that no semaphores or mutexes are used.
Performance wise I'm getting roughly 20 million messages per second on my rather modest VM - Pretty happy with that :)
What I'm curious about how 'correct' my code is. Can anyone spot any inherent issues / race conditions? Here's my code. Thanks in advance for any comments.
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#define SHM_ID "/mmap-test"
#define BUFFER_SIZE 4096
#define SLEEP_NANOS 1000 // 1 micro
struct Message
{
long _id;
char _data[128];
};
struct RingBuffer
{
size_t _rseq;
char _pad1[64];
size_t _wseq;
char _pad2[64];
Message _buffer[BUFFER_SIZE];
};
void
producerLoop()
{
int size = sizeof( RingBuffer );
int fd = shm_open( SHM_ID, O_RDWR | O_CREAT, 0600 );
ftruncate( fd, size+1 );
// create shared memory area
RingBuffer* rb = (RingBuffer*)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
close( fd );
// initialize our sequence numbers in the ring buffer
rb->_wseq = rb->_rseq = 0;
int i = 0;
timespec tss;
tss.tv_sec = 0;
tss.tv_nsec = SLEEP_NANOS;
while( 1 )
{
// as long as the consumer isn't running behind keep producing
while( (rb->_wseq+1)%BUFFER_SIZE != rb->_rseq%BUFFER_SIZE )
{
// write the next entry and atomically update the write sequence number
Message* msg = &rb->_buffer[rb->_wseq%BUFFER_SIZE];
msg->_id = i++;
__sync_fetch_and_add( &rb->_wseq, 1 );
}
// give consumer some time to catch up
nanosleep( &tss, 0 );
}
}
void
consumerLoop()
{
int size = sizeof( RingBuffer );
int fd = shm_open( SHM_ID, O_RDWR, 0600 );
if( fd == -1 ) {
perror( "argh!!!" ); return;
}
// lookup producers shared memory area
RingBuffer* rb = (RingBuffer*)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
// initialize our sequence numbers in the ring buffer
size_t seq = 0;
size_t pid = -1;
timespec tss;
tss.tv_sec = 0;
tss.tv_nsec = SLEEP_NANOS;
while( 1 )
{
// while there is data to consume
while( seq%BUFFER_SIZE != rb->_wseq%BUFFER_SIZE )
{
// get the next message and validate the id
// id should only ever increase by 1
// quit immediately if not
Message msg = rb->_buffer[seq%BUFFER_SIZE];
if( msg._id != pid+1 ) {
printf( "error: %d %d\n", msg._id, pid ); return;
}
pid = msg._id;
++seq;
}
// atomically update the read sequence in the ring buffer
// making it visible to the producer
__sync_lock_test_and_set( &rb->_rseq, seq );
// wait for more data
nanosleep( &tss, 0 );
}
}
int
main( int argc, char** argv )
{
if( argc != 2 ) {
printf( "please supply args (producer/consumer)\n" ); return -1;
} else if( strcmp( argv[1], "consumer" ) == 0 ) {
consumerLoop();
} else if( strcmp( argv[1], "producer" ) == 0 ) {
producerLoop();
} else {
printf( "invalid arg: %s\n", argv[1] ); return -1;
}
}

Seems correct to me at a first glance. I realize that you are happy with the performance but a fun experiment might be to use something more light weight than a __sync_fetch_and_add. AFAIK it is a full memory barrier, which is expensive. Since there is a single producer and a single consumer, a release and a corresponding acquire operation should give you better performance. Facebook's Folly library has a single producer single consumer queue that uses the new C++11 atomics here: https://github.com/facebook/folly/blob/master/folly/ProducerConsumerQueue.h

Related

get packet number in libpcap callback

I'm using libpcap to process the WS output.
My question is: can I have access in the packet number in the pcap_loop callback? Or I will have to use a static variable?
EDIT:
As requested:
long Foo:Main()
{
handle = pcap_open_dead( DLT_EN10MB, MAX_PACKET_SIZE );
if( !handle )
{
}
dumper = pcap_dump_open( handle, fileOut.ToString() );
if( !dumper )
{
}
handle = pcap_open_offline( fileNameStr.ToString(), errbuf );
if( !handle )
{
}
if( pcap_compile( handle, &fp, FltString.ToString(), 0, net ) == PCAP_ERROR )
{
}
// Set filter for JREAP only
if( pcap_setfilter( handle, &fp ) == PCAP_ERROR )
{
}
unchar *uncharThis = reinterpret_cast<unchar*>( this );
// The pcap_loop is implemented like:
// for( int i = 0; i < num_of_packets; i++ )
// ProcessPackets();
// where i is the current packet number to process
int ret_val = pcap_loop( handle, 0, ProcessPackets, uncharThis );
if( ret_val == PCAP_ERROR )
{
}
}
bool Foo::ProcessPackets(unchar *userData, const struct pcap_pkthdr *pkthdr, const unchar *packet)
{
// This function will be called for every packet in the pcap file
// that satisfy the filter condition.
// Inside this function do I have access to the packet number.
// Do I have an access to the variable `i` from the comment above
// Or I will have to introduce a static variable here?
}
libpcap does not keep track of the ordinal numbers of packets, so you'll have to maintain a packet count in your code.

Is inotify a one shot only solution

I have a program whiuch utilizes inotify.
What it does is it start watching the directory for the file being created. When it happens the program reads the content then deletes the file.
Then the user initiates an action which will create the same file again. However, inotify does not see that the file has been created a second time and the file is not processed.
Code is as follows:
fileCreated = false;
m_wd1 = inotify_add_watch( m_fd, "/tmp", IN_CREATE );
if( m_wd1 == -1 )
{
}
else
{
while( true )
{
poll_num = poll( &fds, nfds, -1 );
if( poll_num == -1 )
{
if( errno == EINTR )
continue;
syslog( LOG_ERR, "Fail to run poll" );
result = 1;
}
else if( poll_num > 0 && ( fds.revents & POLLIN ) )
{
syslog( LOG_DEBUG, "Polling is successful" );
for( ;; )
{
len = read( m_fd, buf, sizeof( buf ) );
if( len == -1 && errno != EAGAIN )
{
syslog( LOG_ERR, "Failure to read the inotify event" );
result = 1;
break;
}
for( ptr = buf; ptr < buf + len; ptr += sizeof( struct inotify_event ) + event->len )
{
event = (const struct inotify_event *) ptr;
if( event->mask & IN_CREATE )
{
std::string name( event->name );
if( name == "scan_results" )
{
fileCreated = true;
break;
}
}
}
if( fileCreated || result )
break;
}
}
if( fileCreated )
{
std::ifstream log( "scan_results" );
if( log.rdstate() & std::ifstream::failbit ) != 0 )
{
}
else
{
}
log.close();
if( remove( "scan_results" ) != 0 )
{
syslog( LOG_ERR, "Failed to remove the file" );
}
else
{
syslog( LOG_DEBUG, "File deleted successfully" );
}
}
fileCreated = false;
The while() loop runs only once. When the action happens second time I see a message "Polling is successful".
Should I add IN_MODIFY as a mask for inotify?
If it matters - this code is running inside std::thread.
Turns out that polling plus reading inotify is not thread-safe.
So to overcome this I had to add inotify_init() to every thread. So now every thread have its own inotify file descriptor. And now it looks like program works.
Thank you everybody for reading and trying to help.
Well, I have an answer, it's not a short one and it's directly from the man 7 inotify page in linked above in the comments. Your two pending questions from the narrowing of your questions in the comments are:
if I call remove() on the file - does this mean that the watch in the directory for file creation will be removed?; and
if it is true can you give me some pseudocode for the workaround?
The answer to (1) is Yes, but the watch may be recycled causing the next file/dir to which it is assigned to potentially read the pending requests for the file/dir that was closed/deleted:
When a watch descriptor is removed by calling inotify_rm_watch(2) (or
because a watch file is deleted or the filesystem that contains it is
unmounted), ...
(caveat to be aware of)
... any pending unread events for that watch descriptor
remain available to read. As watch descriptors are subsequently
allocated with inotify_add_watch(2), the kernel cycles through the
range of possible watch descriptors (0 to INT_MAX) incrementally.
When allocating a free watch descriptor, no check is made to see
whether that watch descriptor number has any pending unread events in
the inotify queue. Thus, it can happen that a watch descriptor is
reallocated even when pending unread events exist for a previous
incarnation of that watch descriptor number, with the result that the
application might then read those events and interpret them as
belonging to the file associated with the newly recycled watch
descriptor. In practice, the likelihood of hitting this bug may be
extremely low, since it requires that an application cycle through
INT_MAX watch descriptors, release a watch descriptor while leaving
unread events for that watch descriptor in the queue, and then
recycle that watch descriptor. For this reason, and because there
have been no reports of the bug occurring in real-world applications,
as of Linux 3.15, no kernel changes have yet been made to eliminate
this possible bug.
(I doubt you will ever have that many watches open)
In answer to the second question, the pseudo code would simply be to check the mask for:
IN_DELETE_SELF
Watched file/directory was itself deleted. (This event
also occurs if an object is moved to another filesystem,
since mv(1) in effect copies the file to the other
filesystem and then deletes it from the original filesys‐
tem.) In addition, an IN_IGNORED event will subsequently
be generated for the watch descriptor.
So you can check with either the mask IN_DELETE_SELF or IN_IGNORED if the file/directory being watched is deleted.
I can't see exactly what is wrong with your code since you haven't provided an mcve, but watching a directory should give you all of the subscribed events for the directory until you stop reading them.
Here's an example of using inotify to watch a directory...
#include <iostream>
#include <unistd.h>
#include <sys/inotify.h>
int main(int argc, char *argv[]) {
const int ifd = inotify_init();
const int iwd = inotify_add_watch(ifd, "/tmp/inotify", IN_CREATE);
char buf[4096];
while (read(ifd, buf, sizeof(buf)) > 0) {
const struct inotify_event* ie = (const struct inotify_event*) buf;
std::cout << ie->name << std::endl;
}
return 0;
}
(jason#pi) [4868] ~/tmp touch /tmp/inotify/foo
(jason#pi) [4880] ~/tmp touch /tmp/inotify/bar
(jason#pi) [4881] ~/tmp rm /tmp/inotify/foo
(jason#pi) [4882] ~/tmp touch /tmp/inotify/foo
(jason#pi) [4879] ~/tmp ./so
foo
bar
foo

Complex getchar() operation in omxplayer

Recently I have been trying to redirect the output of a named pipe into a running instance of omxplayer (see here for a minimum working example that doesn't control omxplayer even though it should). I suspected that it had something to do with reading and writing to pipes --perhaps a new line got appended-- so I got some help and wrote a C program which writes to a pipe and reads from it (see here) but turns out that it is not a read/write error either. So I went and traced omxplayer's code thinking that no matter how complicated it was, eventually there has to be a place which has standard C++ code which reads user input, and thank god I found it. Here is the method which, to the best of my understanding, is responsible for getting user input and preparing it for the Dbus to do all its heavenly goodness:
void Keyboard::Process()
{
while(!m_bStop && conn && dbus_connection_read_write_dispatch(conn, 0))
{
int ch[8];
int chnum = 0;
while ((ch[chnum] = getchar()) != EOF) chnum++;
if (chnum > 1) ch[0] = ch[chnum - 1] | (ch[chnum - 2] << 8);
if (m_keymap[ch[0]] != 0)
send_action(m_keymap[ch[0]]);
else
Sleep(20);
}
}
As far as I can glean, the while(!m_bStop... is a conditional just to make sure things are still working, m_keymap is a cypher which matches integers such as 'p' or 'q' to enum values such as ACTION_PAUSE and ACTION_QUIT, and I presume send_action() just gets the ball rolling.
Questions:
Here is what I do not understand:
How is EOF detected when I am not even pressing Enter --> while ((ch[chnum] = getchar()) != EOF) (in case you are confused by this, when a movie is playing I press p to pause the film, not p and then Enter or Ctrl+D). I have attached a small script below labeled getchar.c which illustrates how it is looping forever.
Why are we looping for potentially more than 8 iterations in the while loop while ((ch[chnum] = getchar()) != EOF) chnum++ when the array ch is only of length 8?
This might be implementation specific, but why an array of size 8 when all inputs are guaranteed to be 1 character long (I can both see it here in the map, and by the fact that keys are processed instantly). Is this in any way related to the arrow keys and the escape key?
Assuming it is possible to have more than 1 character, somehow, what is this line supposed to do if (chnum > 1) ch[0] = ch[chnum - 1] | (ch[chnum - 2] << 8)?
Knowing, finally, how omxplayer reads user generated input, can anyone tell me why my simple script, labelled omxplayer_test.c, does not succeed in controlling the player?
getchar.c:
#include <stdio.h>
int main( int argc, char *argv[] ) {
int ch [ 8 ];
int chnum = 0;
while ( ( ch [ chnum ] = getchar() ) != EOF ) chnum++;
printf ( "You will never make it here!\n" );
return 0;
}
omxplayer_test.c:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#define PIPE_PATH "testpipe"
#define VIDEO_PATH "Matrix.mkv"
#define P_MESSAGE "p"
#define Q_MESSAGE "q"
#define I_MESSAGE "."
#define VIDEO_PLAYER "omxplayer"
#define SLEEP_PERIOD 2L
int main()
{
int fd;
pid_t pid;
pid_t wpid;
int status;
char shellCmd [ 1000 ];
struct timespec time1, time2; //used for sleeping
bool parent;
char c;
parent = false;
//Make pipe BEFORE forking
mkfifo ( PIPE_PATH, 0666 );
if ( ( pid = fork () ) < 0 )
{
perror ( "Fork Failed\n" );
return -1;
}
else if ( pid == 0 )
{ //first child launches the movie
sprintf ( shellCmd, "%s %s < %s >/dev/null 2>&1", VIDEO_PLAYER, VIDEO_PATH, PIPE_PATH );
if ( system ( shellCmd ) == -1 )
{
printf ( "Error: %s\n", shellCmd );
fflush(stdout);
}
printf ("First child finished\n");
fflush (stdout);
}
else
{
if ( ( pid = fork () ) < 0 )
{
perror ( "Fork Failed\n" );
return -1;
}
else if ( pid == 0 )
{ //second child waits x seconds then pauses/unpauses/quits movie
time1.tv_sec = SLEEP_PERIOD; //sleep for x seconds
time1.tv_nsec = 0L; //Dont worry about milli seconds
nanosleep ( &time1, &time2 );
//Suprisingly, this signal which gets ball rolling works
fd = open ( PIPE_PATH, O_WRONLY );
write ( fd, I_MESSAGE, sizeof ( I_MESSAGE ) );
nanosleep ( &time1, &time2 );
printf ( "Sleep over, pausing movie\n");
fflush(stdout);
write ( fd, I_MESSAGE, sizeof ( P_MESSAGE ) );
nanosleep ( &time1, &time2 );
printf ( "Sleep over, unpausing movie\n");
fflush(stdout);
write ( fd, P_MESSAGE, sizeof ( P_MESSAGE ) );
nanosleep ( &time1, &time2 );
printf ( "Sleep over, quiting movie\n");
fflush(stdout);
write ( fd, Q_MESSAGE, sizeof ( Q_MESSAGE ) );
close ( fd );
printf ("Second child finished\n");
fflush (stdout);
}
else
{
parent = true;
}
}
while ( ( wpid = wait ( &status ) ) > 0 )
{
printf ( "Exit status of %d was %d (%s)\n", ( int ) wpid, status, ( status == 0 ) ? "accept" : "reject" );
fflush(stdout);
}
if ( parent == true )
{
printf ("deleting pipe\n");
fflush(stdout);
unlink ( PIPE_PATH );
}
return 0;
}
#puk I stumbled upon your 'old´ question but in case you didn't have it answered by yourself.
Look at something similar on https://github.com/popcornmix/omxplayer/issues/131. As omxplayer releases 0.3.x comes from this 'popcornmix' repository, I will answer these questions there because it is a better place for your omxplayer questions ;)
But I will answer the question why your omxplayer_test.c isn't working here, as it is your code that let omxplayer fail :) Strictly not true, as it is a current issue in omxplayer :(
The sending of I_MESSAGE surprises me the most, as I don't know of any keyboard input handling on a '.' character. On the other hand, 'i' commands omxplayer to go to the previous chapter. So if you didn't key-mapped anything on the '.' input key or meant the real 'i' actions, just leave that out; it isn't (and shouldn't be) needed to start omxplayer.
To pause omxplayer, your typo'd it with the I_MESSAGE in stead of P_MESSAGE.
But sending commands to omxplayer with the write() and the sizeof() of the message causes the same effect as mentioned in forementioned issue. The sizeof() of an x_MESSAGE gives 2 as result back, and not 1 (one)! The sizeof() counts in the '\0'-character of a (c-coded) string; e.g. "p" is stored as 'p''\0', so at least 2 characters. So use strlen() (which needs #include <string.h>) in stead, as it will sent only the x_MESSAGE character.

How to use AudioQueue to play a sound for Mac OSX in C++

I am trying to play a sound on OSX, from a buffer (eg: Equivalent of Windows "PlaySound" function).
I have put together some C++ code to play audio with AudioQueue (as it is my understanding that this is the easiest way to play audio on OSX).
However, no sound is ever generated, and the audio callback function is never called.
Does anybody know what I'm doing wrong, or does anyone have a simple C/C++ example of how to play a sound on OSX?
#include
#include
#define BUFFER_COUNT 3
static struct AQPlayerState {
AudioStreamBasicDescription desc;
AudioQueueRef queue;
AudioQueueBufferRef buffers[BUFFER_COUNT];
unsigned buffer_size;
} state;
static void audio_callback (void *aux, AudioQueueRef aq, AudioQueueBufferRef bufout)
{
printf("I never get called!\n");
#define nsamples 4096
short data[nsamples];
for (int i=0;imAudioDataByteSize = nsamples * sizeof(short) * 1;
assert(bufout->mAudioDataByteSize mAudioData, data, bufout->mAudioDataByteSize);
AudioQueueEnqueueBuffer(state.queue, bufout, 0, NULL);
}
void audio_init()
{
int i;
bzero(&state, sizeof(state));
state.desc.mFormatID = kAudioFormatLinearPCM;
state.desc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
state.desc.mSampleRate = 44100;
state.desc.mChannelsPerFrame = 1;
state.desc.mFramesPerPacket = 1;
state.desc.mBytesPerFrame = sizeof (short) * state.desc.mChannelsPerFrame;
state.desc.mBytesPerPacket = state.desc.mBytesPerFrame;
state.desc.mBitsPerChannel = (state.desc.mBytesPerFrame*8)/state.desc.mChannelsPerFrame;
state.desc.mReserved = 0;
state.buffer_size = state.desc.mBytesPerFrame * state.desc.mSampleRate;
if (noErr != AudioQueueNewOutput(&state.desc, audio_callback, 0, NULL, NULL, 0, &state.queue)) {
printf("audioqueue error\n");
return;
}
// Start some empty playback so we'll get the callbacks that fill in the actual audio.
for (i = 0; i mAudioDataByteSize = state.buffer_size;
AudioQueueEnqueueBuffer(state.queue, state.buffers[i], 0, NULL);
}
if (noErr != AudioQueueStart(state.queue, NULL)) printf("AudioQueueStart failed\n");
printf("started audio\n");
}
int main() {
audio_init();
while (1) {
printf("I can't hear anything!\n");
}
}
REFS:
developer.apple.com Audio Queue Services Programming Guide: Playing Audio
developer.apple.com Audio Queue Services Reference
where to start with audio synthesis on iPhone Answer by Andy J Buchanan
Note that I had to explicitly set mAudioDataByteSize to the size I allocated.
In the docs they mention that it is initially set to zero, and that's what I found.
The docs don't say why but I suspect it's to allow for variable size buffers or something?
/* Ben's Audio Example for OSX 10.5+ (yeah Audio Queue)
Ben White, Nov, 2011
Makefile:
example: example.c
gcc -o $# $< -Wimplicit -framework AudioToolbox \
-framework CoreFoundation -lm
*/
#include "AudioToolbox/AudioToolbox.h"
typedef struct {
double phase, phase_inc;
int count;
} PhaseBlah;
void callback (void *ptr, AudioQueueRef queue, AudioQueueBufferRef buf_ref)
{
OSStatus status;
PhaseBlah *p = ptr;
AudioQueueBuffer *buf = buf_ref;
int nsamp = buf->mAudioDataByteSize / 2;
short *samp = buf->mAudioData;
int ii;
printf ("Callback! nsamp: %d\n", nsamp);
for (ii = 0; ii < nsamp; ii++) {
samp[ii] = (int) (30000.0 * sin(p->phase));
p->phase += p->phase_inc;
//printf("phase: %.3f\n", p->phase);
}
p->count++;
status = AudioQueueEnqueueBuffer (queue, buf_ref, 0, NULL);
printf ("Enqueue status: %d\n", status);
}
int main (int argc, char *argv[])
{
AudioQueueRef queue;
PhaseBlah phase = { 0, 2 * 3.14159265359 * 450 / 44100 };
OSStatus status;
AudioStreamBasicDescription fmt = { 0 };
AudioQueueBufferRef buf_ref, buf_ref2;
fmt.mSampleRate = 44100;
fmt.mFormatID = kAudioFormatLinearPCM;
fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
fmt.mFramesPerPacket = 1;
fmt.mChannelsPerFrame = 1; // 2 for stereo
fmt.mBytesPerPacket = fmt.mBytesPerFrame = 2; // x2 for stereo
fmt.mBitsPerChannel = 16;
status = AudioQueueNewOutput(&fmt, callback, &phase, CFRunLoopGetCurrent(),
kCFRunLoopCommonModes, 0, &queue);
if (status == kAudioFormatUnsupportedDataFormatError) puts ("oops!");
else printf("NewOutput status: %d\n", status);
status = AudioQueueAllocateBuffer (queue, 20000, &buf_ref);
printf ("Allocate status: %d\n", status);
AudioQueueBuffer *buf = buf_ref;
printf ("buf: %p, data: %p, len: %d\n", buf, buf->mAudioData, buf->mAudioDataByteSize);
buf->mAudioDataByteSize = 20000;
callback (&phase, queue, buf_ref);
status = AudioQueueAllocateBuffer (queue, 20000, &buf_ref2);
printf ("Allocate2 status: %d\n", status);
buf = buf_ref2;
buf->mAudioDataByteSize = 20000;
callback (&phase, queue, buf_ref2);
status = AudioQueueSetParameter (queue, kAudioQueueParam_Volume, 1.0);
printf ("Volume status: %d\n", status);
status = AudioQueueStart (queue, NULL);
printf ("Start status: %d\n", status);
while (phase.count < 15)
CFRunLoopRunInMode (
kCFRunLoopDefaultMode,
0.25, // seconds
false // don't return after source handled
);
return 0;
}
Based somewhat on the answer by bw1024, I created this complete ogg vorbis player with libvorbisfile.
It expands on the previous answer, demonstrates how to use a function to fill in the audio buffers (as in, doesn't generate the sound by itself) and adds end-of-playback detection with an event listener callback.
The code itself is heavily commented, which hopefully explains everything that needs to be explained.
I tried to keep it as close to "production quality" of both Audio Queues and libvorbisfile, so it contains "real" error conditions and checks for exceptional circumstances; such as variable sample rate in the vorbis file, which it can't handle.
I hope none of the noise distracts from its value as a sample.
// vorplay.c - by Johann `Myrkraverk' Oskarsson <johann#myrkraverk.com>
// In the interest of example code, it's explicitly licensed under the
// WTFPL, see the bottom of the file or http://www.wtfpl.net/ for details.
#include <pthread.h> // For pthread_exit().
#include <vorbis/vorbisfile.h>
#include <AudioToolbox/AudioToolbox.h>
#include <stdio.h>
// This is a complete example of an Ogg Vorbis player based on the vorbisfile
// library and the audio queue API in OS X.
// It can be either taken as an example of how to use libvorbisfile, or
// audio queue programming.
// There are many "magic number" constants in the code, and understanding
// them requires looking up the relevant documentation. Some, such as
// the number of buffers in the audio queue and the size of each buffer
// are the result of experimentation. A "real application" may benefit
// from allowing the user to tweak these, in order to resolve audio stutters.
// Error handling is done very simply in order to focus on the example code
// while still resembling "production code." Here, we use the
// if ( status = foo() ) { ... }
// syntax for error checking. The assignment in if()s is not an error.
// If your compiler is complaining, you can use its equivalent of the
// GCC switch -Wno-parentheses to silence it.
// Assuming you'll want to use libvorbisfile from mac ports, you can
// compile it like this.
// gcc -c -I/opt/local/include \
// vorplay.c \
// -Wno-parentheses
// And link with
// gcc -o vorplay vorplay.o \
// -L/opt/local/lib -lvorbisfile \
// -framework AudioToolbox
// The start/stop listener...
void listener( void *vorbis, AudioQueueRef queue, AudioQueuePropertyID id )
{
// Here, we're only listening for start/stop, so don't need to check
// the id; it's always kAudioQueueProperty_IsRunning in our case.
UInt32 running = 0;
UInt32 size = sizeof running;
/* OggVorbis_File *vf = (OggVorbis_File *) vorbis; */
OSStatus status = -1;
if ( status = AudioQueueGetProperty( queue, id, &running, &size ) ) {
printf( "AudioQueueGetProperty status = %d; running = %d\n",
status, running );
exit( 1 );
}
if ( !running ) {
// In a "real example" we'd clean up the vf pointer with ov_clear() and
// the audio queue with AudioQueueDispose(); however, the latter is
// better not called from within the listener function, so we just
// exit normally.
exit( 0 );
// In a "real" application, we might signal the termination with
// a pthread condition variable, or something similar, instead;
// where the waiting thread would call AudioQueueDispose(). It is
// "safe" to call ov_clear() here, but there's no point.
}
}
// The audio queue callback...
void callback( void *vorbis, AudioQueueRef queue, AudioQueueBufferRef buffer )
{
OggVorbis_File *vf = (OggVorbis_File *) vorbis;
int section = 0;
OSStatus status = -1;
// The parameters here are congruent with our format specification for
// the audio queue. We read directly into the audio queue buffer.
long r = ov_read( vf, buffer->mAudioData, buffer->mAudioDataBytesCapacity,
0, 2, 1, &section );
// As an extra precaution, check if the current buffer is the same sample
// rate and channel number as the audio queue.
{
vorbis_info *vinfo = ov_info( vf, section );
if ( vinfo == NULL ) {
printf( "ov_info status = NULL\n" );
exit( 1 );
}
AudioStreamBasicDescription description;
UInt32 size = sizeof description;
if ( status = AudioQueueGetProperty( queue,
kAudioQueueProperty_StreamDescription,
&description,
&size ) ) {
printf( "AudioQueueGetProperty status = %d\n", status );
exit( 1 );
}
// If we were using some other kind of audio playback API, such as OSS4
// we could simply change the sample rate and channel number on the fly.
// However, with an audio queue, we'd have to use a different
// one, afaict; so we don't handle it at all in this example.
if ( vinfo->rate != description.mSampleRate ) {
printf( "We don't handle changes in sample rate.\n" );
exit( 1 );
}
if ( vinfo->channels != description.mChannelsPerFrame ) {
printf( "We don't handle changes in channel numbers.\n" );
exit( 1 );
}
}
// The real "callback"...
if ( r == 0 ) { // No more data, stop playing.
// Flush data, to make sure we play to the end.
if ( status = AudioQueueFlush( queue ) ) {
printf( "AudioQueueFlush status = %d\n", status );
exit( 1 );
}
// Stop asynchronously.
if ( status = AudioQueueStop( queue, false ) ) {
printf( "AudioQueueStop status = %d\n", status );
exit( 1 );
}
} else if ( r < 0 ) { // Some error?
printf( "ov_read status = %ld\n", r );
exit( 1 );
} else { // The normal course of action.
// ov_read() may not return exactly the number of bytes we requested.
// so we update the buffer size per call.
buffer->mAudioDataByteSize = r;
if ( status = AudioQueueEnqueueBuffer( queue, buffer, 0, 0 ) ) {
printf( "AudioQueueEnqueueBuffer status = %d, r = %ld\n", status, r );
exit( 1 );
}
}
}
int main( int argc, char *argv[] )
{
// The very simple command line argument check.
if ( argc != 2 ) {
printf( "Usage: vorplay <file>\n" );
exit( 1 );
}
FILE *file = fopen( argv[ 1 ], "r" );
if ( file == NULL ) {
printf( "Unable to open input file.\n" );
exit( 1 );
}
OggVorbis_File vf;
// Using OV_CALLBACKS_DEFAULT means ov_clear() will close the file.
// However, this particular example doesn't use that function.
// A typical place for it might be the listener(), when we stop
// playing.
if ( ov_open_callbacks( file, &vf, 0, 0, OV_CALLBACKS_DEFAULT ) ) {
printf( "ov_open_callbacks() failed. Not an Ogg Vorbis file?\n" );
exit( 1 );
}
// For the sample rate and channel number in the audio.
vorbis_info *vinfo = ov_info( &vf, -1 );
if ( vinfo == NULL ) {
printf( "ov_info status = NULL\n" );
exit( 1 );
}
// The audio queue format specification. This structure must be set
// to values congruent to the ones we use with ov_read().
AudioStreamBasicDescription format = { 0 };
// First, the constants. The format is quite hard coded, both here
// and in the calls to ov_read().
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags =
kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
format.mFramesPerPacket = 1;
format.mBitsPerChannel = 16;
// Load the sample rate and channel number from the vorbis file.
format.mSampleRate = vinfo->rate;
format.mChannelsPerFrame = vinfo->channels;
// The number of bytes depends on the channel number.
format.mBytesPerPacket =
format.mBytesPerFrame = 2 * vinfo->channels; // times two, for 16bit
OSStatus status = -1;
AudioQueueRef queue;
// Create the audio queue with the desired format. Notice that we
// use the OggVorbis_File pointer as the data far the callback.
if ( status = AudioQueueNewOutput( &format, callback,
&vf, NULL, NULL, 0, &queue ) ) {
printf( "AudioQueueNewOutput status = %d\n", status );
exit( 1 );
}
// For me distortions happen with 3 buffers; hence the magic number 5.
AudioQueueBufferRef buffers[ 5 ];
for ( int i = 0; i < sizeof buffers / sizeof (AudioQueueBufferRef); i++ ) {
// For each buffer...
// The size of the buffer is a magic number. 4096 is good enough, too.
if ( status = AudioQueueAllocateBuffer( queue, 8192, &buffers[ i ] ) ) {
printf( "AudioQueueAllocateBuffer status = %d\n", status );
exit( 1 );
}
// Enqueue buffers, before play. According to the process outlined
// in the Audio Queue Services Programming Guide, we must do this
// before calling AudioQueueStart() and it's simplest to do it like
// this.
callback( &vf, queue, buffers[ i ] );
}
// We set the volume to maximum; even though the docs say it's the
// default.
if ( status = AudioQueueSetParameter( queue,
kAudioQueueParam_Volume, 1.0 ) ) {
printf( "AudioQueueSetParameter status = %d\n", status );
exit( 1 );
}
// Here, we might want to call AudioQueuePrime if we were playing one
// of the supported compressed formats. However, since we only have
// raw PCM buffers to play, I don't see the point. Maybe playing will
// start faster with it, after AudioQueueStart() but I still don't see
// the point for this example; if there's a delay, it'll happen anyway.
// We add a listener for the start/stop event, so we know when to call
// exit( 0 ) and terminate the application. We also give it the vf
// pointer, even though it's not used in our listener().
if ( status = AudioQueueAddPropertyListener( queue,
kAudioQueueProperty_IsRunning,
listener,
&vf ) ) {
printf( "AudioQueueAddPropertyListener status = %d\n", status );
exit( 1 );
}
// And then start to play the file.
if ( status = AudioQueueStart( queue, 0 ) ) {
printf( "AudioQueueStart status = %d\n", status );
exit( 1 );
}
// Work's For Me[tm]. This trick to make sure the process doesn't
// terminate before the song has played "works for me" on
// OS X 10.10.3. If you're going to use this same trick in production
// code, you might as well turn off the joinability of the main thread,
// with pthread_detach() and also make sure no callback or listener is
// using data from the stack. Unlike this example.
pthread_exit( 0 );
return 0; // never reached, left intact in case some compiler complains.
}
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// Version 2, December 2004
//
// Copyright (C) 2015 Johann `Myrkraverk' Oskarsson
// <johann#myrkraverk.com>
//
// Everyone is permitted to copy and distribute verbatim or modified
// copies of this license document, and changing it is allowed as long
// as the name is changed.
//
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
//
// 0. You just DO WHAT THE FUCK YOU WANT TO.

Read() from file descriptor hangs

Hey, hopefully this should be my last PTY-related question and I can move onto more exciting issues. (c;
Here's a set of small functions I have written for creating and reading/writing to a pty: http://pastebin.com/m4fcee34d The only problem is that they don't work! After I run the initializer and writeToPty( "ls -l" ) , 'output' from readFromPty is still empty.
Ubuntu, QT C++
EDITED: Ok, I can confirm all this stuff works except for the read loop. In the debuggers' locals/watchers tab it shows that the QString 'output' actually does get the right data put in it, but after it ( the read() ) runs out of characters from the output it runs and then hangs. What is going on and how can I fix it?
Thanks! (c:
#include <iostream>
#include <unistd.h>
#include <utmp.h>
#include <pty.h>
#include <QString>
#include <QThread>
// You also need libutil in your .pro file for this to compile.
class CMkPty
{
public:
CMkPty( int *writeChannel, int *readChannel );
~CMkPty();
int runInPty( char *command );
int writeToPty( char *input );
int readFromPty( QString output );
int m_nPid;
private:
int m_nMaster, m_nSlave, m_nPosition, m_nBytes;
char *m_chName;
void safe_print( char *s );
char m_output;
};
CMkPty::CMkPty( int *masterFD, int *slaveFD )
{
openpty( &m_nMaster, &m_nSlave, (char*)0, __null, __null );
m_nPid = fork();
*masterFD = m_nMaster;
*slaveFD = m_nSlave;
if( m_nPid == 0 )
{
login_tty( m_nSlave );
execl( "/bin/bash", "-l", (char*)0 );
return;
}
else if( m_nPid > 0 )
{
return;
}
else if( m_nPid < 0 )
{
std::cout << "Failed to fork." ;
return;
}
}
CMkPty::~CMkPty()
{
close( m_nMaster );
close( m_nSlave );
}
int CMkPty::writeToPty( char *szInput )
{
int nWriteTest;
write( m_nMaster, szInput, sizeof( szInput ) );
nWriteTest = write( m_nMaster, "\n", 1 );
if( nWriteTest < 0 )
{
std::cout << "Write to PTY failed" ;
return -1;
}
return 0;
}
int CMkPty::readFromPty( QString output )
{
char buffer[ 160 ];
m_nBytes = sizeof( buffer );
while ( ( m_nPosition = read( m_nMaster, buffer, m_nBytes ) ) > 0 )
{
buffer[ m_nPosition ] = 0;
output += buffer;
}
return 0;
}
EDIT: Here's a link to the question with the code that finally worked for me.
I'm note entirely familiar with posix, but after reading this page http://pwet.fr/man/linux/fonctions_bibliotheques/posix/read I had some insight. What's more, I don't see you adjusting your M_nBytes value if you haven't read as much as you were expecting on the first pass of the loop.
edit: from that link, perhaps this will be of some help:
If some process has the pipe open for writing and O_NONBLOCK is clear, read() shall block the calling thread until some data is written or the pipe is closed by all processes that had the pipe open for writing.
When attempting to read a file (other than a pipe or FIFO) that supports non-blocking reads and has no data currently available:
*
If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available.
so essentially, if you're not in an error state, and you tell it to keep reading, it will block until it finds something to read.