- extra check in castsink prevent null deref
- fix typos - change bytecounter (size limit) to long instead of int - fix some string/buffer strangeness in icecast2.cpp - increase ringbuffer size from 2 to 5 seconds in JackDspSource.cpp - prevent jack killing us on buffer overflow, we just report and continue - new producer/consumer scheme in MultiThreadedConnector.cpp it now runs parallel really - some compiler warnings fixed
This commit is contained in:
parent
8ac1639800
commit
43d89e2796
|
@ -1,4 +1,17 @@
|
||||||
next version
|
next version
|
||||||
|
o Fix 'Ring Ruffer' reports.
|
||||||
|
- Increased buffer for jack to 5 seconds
|
||||||
|
- prevent darkice termination by jack, report no fatal problem when we
|
||||||
|
have a ringbuffer overflow, can happen during startup
|
||||||
|
If we can not handle input audio fast enough we just ignore the buffer
|
||||||
|
and skip it, and just report it.
|
||||||
|
- new multithreaded connector code, now handles encoders in parallel
|
||||||
|
and does not spin waiting, cpu load will be very much lower now
|
||||||
|
Codes uses 2 condition variables to report data availability and
|
||||||
|
consumer thread availability
|
||||||
|
- Hopes are that glitching reports will be a thing of the past
|
||||||
|
- minor compiler warnings fixed
|
||||||
|
(Fix by Edwin van den Oetelaar)
|
||||||
o Issue #56: Wrong icecast2 password isn't properly reported, fixed.
|
o Issue #56: Wrong icecast2 password isn't properly reported, fixed.
|
||||||
thanks to Filipe Roque <flip.roque@gmail.com>
|
thanks to Filipe Roque <flip.roque@gmail.com>
|
||||||
o Issue #57: BufferedSink makes streams invalid, fixed.
|
o Issue #57: BufferedSink makes streams invalid, fixed.
|
||||||
|
|
|
@ -290,7 +290,11 @@ class CastSink : public Sink, public virtual Reporter
|
||||||
inline virtual bool
|
inline virtual bool
|
||||||
isOpen ( void ) const throw ()
|
isOpen ( void ) const throw ()
|
||||||
{
|
{
|
||||||
|
Sink *s = getSink();
|
||||||
|
if (s)
|
||||||
return getSink()->isOpen();
|
return getSink()->isOpen();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -252,7 +252,7 @@ Connector :: open ( void ) throw ( Exception )
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Transfer some data from the source to the sink
|
* Transfer some data from the source to the sink
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
unsigned int
|
unsigned long
|
||||||
Connector :: transfer ( unsigned long bytes,
|
Connector :: transfer ( unsigned long bytes,
|
||||||
unsigned int bufSize,
|
unsigned int bufSize,
|
||||||
unsigned int sec,
|
unsigned int sec,
|
||||||
|
@ -271,7 +271,7 @@ Connector :: transfer ( unsigned long bytes,
|
||||||
|
|
||||||
unsigned char * buf = new unsigned char[bufSize];
|
unsigned char * buf = new unsigned char[bufSize];
|
||||||
|
|
||||||
reportEvent( 6, "Connector :: tranfer, bytes", bytes);
|
reportEvent( 6, "Connector :: transfer, bytes", bytes);
|
||||||
|
|
||||||
for ( b = 0; !bytes || b < bytes; ) {
|
for ( b = 0; !bytes || b < bytes; ) {
|
||||||
unsigned int d = 0;
|
unsigned int d = 0;
|
||||||
|
|
|
@ -228,7 +228,7 @@ class Connector : public virtual Referable, public virtual Reporter
|
||||||
* @return the number of bytes read from the Source.
|
* @return the number of bytes read from the Source.
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
virtual unsigned int
|
virtual unsigned long
|
||||||
transfer ( unsigned long bytes,
|
transfer ( unsigned long bytes,
|
||||||
unsigned int bufSize,
|
unsigned int bufSize,
|
||||||
unsigned int sec,
|
unsigned int sec,
|
||||||
|
|
|
@ -70,12 +70,6 @@
|
||||||
static const char fileid[] = "$Id$";
|
static const char fileid[] = "$Id$";
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Size of string conversion buffer
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
#define STRBUF_SIZE 32
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Expected positive response from server begins like this.
|
* Expected positive response from server begins like this.
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
@ -125,7 +119,8 @@ IceCast2 :: sendLogin ( void ) throw ( Exception )
|
||||||
Sink * sink = getSink();
|
Sink * sink = getSink();
|
||||||
Source * source = getSocket();
|
Source * source = getSocket();
|
||||||
const char * str;
|
const char * str;
|
||||||
char resp[STRBUF_SIZE];
|
const int buflen = 1024; // some small buffer size
|
||||||
|
char resp[buflen]; // a little buffer
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
unsigned int lenExpected;
|
unsigned int lenExpected;
|
||||||
|
|
||||||
|
@ -178,7 +173,7 @@ IceCast2 :: sendLogin ( void ) throw ( Exception )
|
||||||
sink->write( str, strlen(str));
|
sink->write( str, strlen(str));
|
||||||
{
|
{
|
||||||
// send source:<password> encoded as base64
|
// send source:<password> encoded as base64
|
||||||
char * source = "source:";
|
const char * source = "source:";
|
||||||
const char * pwd = getPassword();
|
const char * pwd = getPassword();
|
||||||
char * tmp = new char[Util::strLen(source) +
|
char * tmp = new char[Util::strLen(source) +
|
||||||
Util::strLen(pwd) + 1];
|
Util::strLen(pwd) + 1];
|
||||||
|
@ -197,10 +192,7 @@ IceCast2 :: sendLogin ( void ) throw ( Exception )
|
||||||
// send the ice- headers
|
// send the ice- headers
|
||||||
str = "\nice-bitrate: ";
|
str = "\nice-bitrate: ";
|
||||||
sink->write( str, strlen( str));
|
sink->write( str, strlen( str));
|
||||||
if ( log10(getBitRate()) >= (STRBUF_SIZE-2) ) {
|
|
||||||
throw Exception( __FILE__, __LINE__,
|
|
||||||
"bitrate does not fit string buffer", getBitRate());
|
|
||||||
}
|
|
||||||
sprintf( resp, "%d", getBitRate());
|
sprintf( resp, "%d", getBitRate());
|
||||||
sink->write( resp, strlen( resp));
|
sink->write( resp, strlen( resp));
|
||||||
|
|
||||||
|
@ -243,10 +235,10 @@ IceCast2 :: sendLogin ( void ) throw ( Exception )
|
||||||
|
|
||||||
// read the response, expected response begins with responseOK
|
// read the response, expected response begins with responseOK
|
||||||
lenExpected = Util::strLen( responseOK);
|
lenExpected = Util::strLen( responseOK);
|
||||||
if ( (len = source->read( resp, STRBUF_SIZE-1)) < lenExpected ) {
|
if ( (len = source->read( resp, buflen )) < lenExpected ) {
|
||||||
return false;
|
return false; // short read, no need to continue
|
||||||
}
|
}
|
||||||
resp[lenExpected] = 0;
|
resp[lenExpected] = '\x00'; // end string, truncate to expected length
|
||||||
|
|
||||||
reportEvent(5,resp);
|
reportEvent(5,resp);
|
||||||
|
|
||||||
|
@ -261,13 +253,16 @@ IceCast2 :: sendLogin ( void ) throw ( Exception )
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !Util::strEq( resp, responseOK) ) {
|
if ( !Util::strEq( resp, responseOK) ) {
|
||||||
return false;
|
// some unexpected response from server
|
||||||
|
throw Exception( __FILE__, __LINE__,
|
||||||
|
"Icecast2 - Unexpected response from server");
|
||||||
}
|
}
|
||||||
|
|
||||||
// suck anything that the other side has to say
|
// suck anything that the other side has to say
|
||||||
while ( source->canRead( 0, 0) &&
|
while ( source->canRead( 0, 0) &&
|
||||||
(len = source->read( resp, STRBUF_SIZE-1)) );
|
(len = source->read( resp, buflen )));
|
||||||
|
|
||||||
|
// all is well, we are connected
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -259,10 +259,12 @@ JackDspSource :: open ( void ) throw ( Exception )
|
||||||
|
|
||||||
|
|
||||||
// Create a ring buffer for each channel
|
// Create a ring buffer for each channel
|
||||||
rb_size = 2
|
/* will take about 1 MB buffer for each channel */
|
||||||
* jack_get_sample_rate(client)
|
rb_size = 5 /* number of seconds */
|
||||||
* sizeof (jack_default_audio_sample_t);
|
* jack_get_sample_rate(client) /* eg 48000 */
|
||||||
for (c=0; c<getChannel(); c++) {
|
* sizeof (jack_default_audio_sample_t); /* eg 4 bytes */
|
||||||
|
|
||||||
|
for (c=0; c < getChannel(); c++) {
|
||||||
rb[c] = jack_ringbuffer_create(rb_size);
|
rb[c] = jack_ringbuffer_create(rb_size);
|
||||||
if (!rb[c]) {
|
if (!rb[c]) {
|
||||||
throw Exception( __FILE__, __LINE__,
|
throw Exception( __FILE__, __LINE__,
|
||||||
|
@ -344,8 +346,8 @@ JackDspSource :: read ( void * buf,
|
||||||
unsigned int len ) throw ( Exception )
|
unsigned int len ) throw ( Exception )
|
||||||
{
|
{
|
||||||
jack_nframes_t samples = len / 2 / getChannel();
|
jack_nframes_t samples = len / 2 / getChannel();
|
||||||
jack_nframes_t samples_read[2] = {0,0};
|
jack_nframes_t samples_read[2] = { 0, 0 };
|
||||||
short * output = (short*)buf;
|
short * output = (short*) buf;
|
||||||
unsigned int c, n;
|
unsigned int c, n;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
|
@ -363,14 +365,14 @@ JackDspSource :: read ( void * buf,
|
||||||
// We must be sure to fetch as many data on both channels
|
// We must be sure to fetch as many data on both channels
|
||||||
int minBytesAvailable = samples * sizeof( jack_default_audio_sample_t );
|
int minBytesAvailable = samples * sizeof( jack_default_audio_sample_t );
|
||||||
|
|
||||||
for (c=0; c<getChannel(); c++) {
|
for (c=0; c < getChannel(); c++) {
|
||||||
int readable = jack_ringbuffer_read_space(rb[c]);
|
int readable = jack_ringbuffer_read_space(rb[c]);
|
||||||
if (readable < minBytesAvailable) {
|
if (readable < minBytesAvailable) {
|
||||||
minBytesAvailable = readable;
|
minBytesAvailable = readable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (c=0; c<getChannel(); c++) {
|
for (c=0; c < getChannel(); c++) {
|
||||||
// Copy frames from ring buffer to temporary buffer
|
// Copy frames from ring buffer to temporary buffer
|
||||||
// and then convert samples to output buffer
|
// and then convert samples to output buffer
|
||||||
int bytes_read = jack_ringbuffer_read(rb[c],
|
int bytes_read = jack_ringbuffer_read(rb[c],
|
||||||
|
@ -459,11 +461,22 @@ JackDspSource :: process_callback( jack_nframes_t nframes, void *arg )
|
||||||
|
|
||||||
/* copy data to ringbuffer; one per channel */
|
/* copy data to ringbuffer; one per channel */
|
||||||
for (c=0; c < self->getChannel(); c++) {
|
for (c=0; c < self->getChannel(); c++) {
|
||||||
char *buf = (char*)jack_port_get_buffer(self->ports[c], nframes);
|
/* check space */
|
||||||
size_t len = jack_ringbuffer_write(self->rb[c], buf, to_write);
|
size_t len;
|
||||||
if (len < to_write) {
|
if (jack_ringbuffer_write_space(self->rb[c]) < to_write) {
|
||||||
Reporter::reportEvent( 1, "failed to write to ring ruffer");
|
/* buffer is overflowing, skip the incoming data */
|
||||||
return 1;
|
jack_ringbuffer_write_advance(self->rb[c], to_write);
|
||||||
|
/* prevent blocking the ring buffer by updating internal pointers
|
||||||
|
* jack will now not terminate on xruns
|
||||||
|
*/
|
||||||
|
Reporter::reportEvent( 1, "ring buffer full, skipping data");
|
||||||
|
/* We do not return error to jack callback handler and keep going */
|
||||||
|
} else {
|
||||||
|
/* buffer has space, put data into ringbuffer */
|
||||||
|
len = jack_ringbuffer_write(self->rb[c], (char *) jack_port_get_buffer(
|
||||||
|
self->ports[c], nframes), to_write);
|
||||||
|
if (len != to_write)
|
||||||
|
Reporter::reportEvent( 1, "failed to write to ring buffer (can not happen)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,9 +68,10 @@ void
|
||||||
MultiThreadedConnector :: init ( bool reconnect ) throw ( Exception )
|
MultiThreadedConnector :: init ( bool reconnect ) throw ( Exception )
|
||||||
{
|
{
|
||||||
this->reconnect = reconnect;
|
this->reconnect = reconnect;
|
||||||
|
pthread_mutex_init( &mutex_start, 0);
|
||||||
pthread_mutex_init( &mutexProduce, 0);
|
pthread_cond_init( &cond_start, 0);
|
||||||
pthread_cond_init( &condProduce, 0);
|
pthread_mutex_init( &mutex_done, 0);
|
||||||
|
pthread_cond_init( &cond_done, 0);
|
||||||
threads = 0;
|
threads = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +87,10 @@ MultiThreadedConnector :: strip ( void ) throw ( Exception )
|
||||||
threads = 0;
|
threads = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cond_destroy( &condProduce);
|
pthread_cond_destroy( &cond_start);
|
||||||
pthread_mutex_destroy( &mutexProduce);
|
pthread_mutex_destroy( &mutex_start);
|
||||||
|
pthread_cond_destroy( &cond_done);
|
||||||
|
pthread_mutex_destroy( &mutex_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,8 +103,10 @@ MultiThreadedConnector :: MultiThreadedConnector (
|
||||||
: Connector( connector)
|
: Connector( connector)
|
||||||
{
|
{
|
||||||
reconnect = connector.reconnect;
|
reconnect = connector.reconnect;
|
||||||
mutexProduce = connector.mutexProduce;
|
mutex_start = connector.mutex_start;
|
||||||
condProduce = connector.condProduce;
|
cond_start = connector.cond_start;
|
||||||
|
mutex_done = connector.mutex_done;
|
||||||
|
cond_done = connector.cond_done;
|
||||||
|
|
||||||
if ( threads ) {
|
if ( threads ) {
|
||||||
delete[] threads;
|
delete[] threads;
|
||||||
|
@ -112,7 +117,6 @@ MultiThreadedConnector :: MultiThreadedConnector (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Assignment operator
|
* Assignment operator
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
@ -124,8 +128,10 @@ MultiThreadedConnector :: operator= ( const MultiThreadedConnector & connector )
|
||||||
Connector::operator=( connector);
|
Connector::operator=( connector);
|
||||||
|
|
||||||
reconnect = connector.reconnect;
|
reconnect = connector.reconnect;
|
||||||
mutexProduce = connector.mutexProduce;
|
mutex_start = connector.mutex_start;
|
||||||
condProduce = connector.condProduce;
|
cond_start = connector.cond_start;
|
||||||
|
mutex_done = connector.mutex_done;
|
||||||
|
cond_done = connector.cond_done;
|
||||||
|
|
||||||
if ( threads ) {
|
if ( threads ) {
|
||||||
delete[] threads;
|
delete[] threads;
|
||||||
|
@ -139,7 +145,6 @@ MultiThreadedConnector :: operator= ( const MultiThreadedConnector & connector )
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Open the source and all the sinks if needed
|
* Open the source and all the sinks if needed
|
||||||
* Create the sink threads
|
* Create the sink threads
|
||||||
|
@ -173,7 +178,7 @@ MultiThreadedConnector :: open ( void ) throw ( Exception )
|
||||||
threadData->connector = this;
|
threadData->connector = this;
|
||||||
threadData->ixSink = i;
|
threadData->ixSink = i;
|
||||||
threadData->accepting = true;
|
threadData->accepting = true;
|
||||||
threadData->isDone = true;
|
threadData->isDone = 1; // 1==STOP, activate thread in transfer()
|
||||||
if ( pthread_create( &(threadData->thread),
|
if ( pthread_create( &(threadData->thread),
|
||||||
&threadAttr,
|
&threadAttr,
|
||||||
ThreadData::threadFunction,
|
ThreadData::threadFunction,
|
||||||
|
@ -187,10 +192,10 @@ MultiThreadedConnector :: open ( void ) throw ( Exception )
|
||||||
unsigned int j;
|
unsigned int j;
|
||||||
|
|
||||||
// signal to stop for all running threads
|
// signal to stop for all running threads
|
||||||
pthread_mutex_lock( &mutexProduce);
|
pthread_mutex_lock( &mutex_start);
|
||||||
running = false;
|
running = false;
|
||||||
pthread_cond_broadcast( &condProduce);
|
pthread_cond_broadcast( &cond_start);
|
||||||
pthread_mutex_unlock( &mutexProduce);
|
pthread_mutex_unlock( &mutex_start);
|
||||||
|
|
||||||
for ( j = 0; j < i; ++j ) {
|
for ( j = 0; j < i; ++j ) {
|
||||||
pthread_join( threads[j].thread, 0);
|
pthread_join( threads[j].thread, 0);
|
||||||
|
@ -209,14 +214,14 @@ MultiThreadedConnector :: open ( void ) throw ( Exception )
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Transfer some data from the source to the sink
|
* Transfer some data from the source to the sink
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
unsigned int
|
unsigned long
|
||||||
MultiThreadedConnector :: transfer ( unsigned long bytes,
|
MultiThreadedConnector :: transfer ( unsigned long bytes,
|
||||||
unsigned int bufSize,
|
unsigned int bufSize,
|
||||||
unsigned int sec,
|
unsigned int sec,
|
||||||
unsigned int usec )
|
unsigned int usec )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
{
|
{
|
||||||
unsigned int b;
|
unsigned long byteCounter; // when we reach byteCounter thread will end
|
||||||
|
|
||||||
if ( numSinks == 0 ) {
|
if ( numSinks == 0 ) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -229,47 +234,72 @@ MultiThreadedConnector :: transfer ( unsigned long bytes,
|
||||||
dataBuffer = new unsigned char[bufSize];
|
dataBuffer = new unsigned char[bufSize];
|
||||||
dataSize = 0;
|
dataSize = 0;
|
||||||
|
|
||||||
reportEvent( 6, "MultiThreadedConnector :: tranfer, bytes", bytes);
|
/* if bytes==0 transfer until end of program,
|
||||||
|
* if bytes>0 transfer upto number of bytes
|
||||||
|
*/
|
||||||
|
reportEvent( 6, "MultiThreadedConnector::transfer count:", bytes);
|
||||||
|
byteCounter = 0; // init, no data bytes sent yet
|
||||||
|
|
||||||
for ( b = 0; !bytes || b < bytes; ) {
|
|
||||||
if ( source->canRead( sec, usec) ) {
|
while (running && (bytes == 0 || byteCounter < bytes)) {
|
||||||
|
|
||||||
|
if (source->canRead(sec, usec)) {
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
dataSize = source->read(dataBuffer, bufSize);
|
||||||
pthread_mutex_lock( &mutexProduce);
|
byteCounter += dataSize;
|
||||||
dataSize = source->read( dataBuffer, bufSize);
|
|
||||||
b += dataSize;
|
|
||||||
|
|
||||||
// check for EOF
|
// check for EOF
|
||||||
if ( dataSize == 0 ) {
|
if (dataSize == 0) {
|
||||||
reportEvent( 3, "MultiThreadedConnector :: transfer, EOF");
|
reportEvent(3, "MultiThreadedConnector :: transfer, EOF");
|
||||||
pthread_mutex_unlock( &mutexProduce);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( i = 0; i < numSinks; ++i ) {
|
pthread_mutex_lock(&mutex_start);
|
||||||
threads[i].isDone = false;
|
for (i = 0; i < numSinks; ++i) {
|
||||||
|
if (threads[i].accepting)
|
||||||
|
threads[i].isDone = 0; // consumers => RUN
|
||||||
}
|
}
|
||||||
|
pthread_cond_broadcast(&cond_start); // kick the waiting consumers to look again
|
||||||
// tell sink threads that there is some data available
|
pthread_mutex_unlock(&mutex_start); // UNLOCK, release the consumers' cond variable
|
||||||
pthread_cond_broadcast( &condProduce);
|
|
||||||
|
|
||||||
// wait for all sink threads to get done with this data
|
// wait for all sink threads to get done with this data
|
||||||
while ( true ) {
|
// we do not spin here, we just wait for an event from the consumers
|
||||||
for ( i = 0; i < numSinks && threads[i].isDone; ++i );
|
pthread_mutex_lock(&mutex_done);
|
||||||
if ( i == numSinks ) {
|
while ( 1 ) {
|
||||||
|
int rc = 0;
|
||||||
|
// wait for condition : releases mutex so other thread can change condition
|
||||||
|
rc = pthread_cond_wait(&cond_done, &mutex_done);
|
||||||
|
// mutex is locked again
|
||||||
|
if (rc != 0) {
|
||||||
|
reportEvent(1, "MultiThreadedConnector pthread_cond_wait() fail");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pthread_cond_wait( &condProduce, &mutexProduce);
|
|
||||||
|
int acceptor_count=0;
|
||||||
|
int stopped_count=0;
|
||||||
|
for (i = 0; i < numSinks; ++i) {
|
||||||
|
if (threads[i].accepting) {
|
||||||
|
acceptor_count++; // number of accepting threads
|
||||||
|
if (threads[i].isDone == 1)
|
||||||
|
stopped_count++; // number of accepting threads which have STOP
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock( &mutexProduce);
|
}
|
||||||
|
// break when all accepting threads are done
|
||||||
|
if (acceptor_count == stopped_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// at least one thread has not set the STOP flag yet
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&mutex_done);
|
||||||
|
// at this point all consumers are done with the block
|
||||||
} else {
|
} else {
|
||||||
reportEvent( 3, "MultiThreadedConnector :: transfer, can't read");
|
reportEvent(3,"MultiThreadedConnector :: transfer, can't read");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] dataBuffer;
|
delete[] dataBuffer;
|
||||||
return b;
|
return byteCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -278,70 +308,114 @@ MultiThreadedConnector :: transfer ( unsigned long bytes,
|
||||||
* Read the presented data
|
* Read the presented data
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
MultiThreadedConnector :: sinkThread( int ixSink )
|
MultiThreadedConnector::sinkThread(int ixSink)
|
||||||
{
|
{
|
||||||
ThreadData * threadData = &threads[ixSink];
|
ThreadData * threadData = &threads[ixSink];
|
||||||
Sink * sink = sinks[ixSink].get();
|
Sink * sink = sinks[ixSink].get( );
|
||||||
|
|
||||||
while ( running ) {
|
while ( running )
|
||||||
|
{
|
||||||
// wait for some data to become available
|
// wait for some data to become available
|
||||||
pthread_mutex_lock( &mutexProduce);
|
// producer sets isDone==0 when consumer can continue
|
||||||
while ( running && threadData->isDone ) {
|
// producer sets isDone==2 or running==0 to request termination
|
||||||
pthread_cond_wait( &condProduce, &mutexProduce);
|
pthread_mutex_lock( &mutex_start ); // LOCK
|
||||||
}
|
int rc=0;
|
||||||
if ( !running ) {
|
while ( (rc==0) && running && (threadData->isDone==1) )
|
||||||
pthread_mutex_unlock( &mutexProduce);
|
{
|
||||||
break;
|
// wait for condition, releases lock
|
||||||
|
rc = pthread_cond_wait( &cond_start, &mutex_start );
|
||||||
|
// we hold the lock again
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock( &mutex_start ); // UNLOCK
|
||||||
|
|
||||||
if ( threadData->cut) {
|
// something wrong or signal to quit detected
|
||||||
sink->cut();
|
// break out of this loop, will end the thread
|
||||||
|
if ( running==false || threadData->isDone==2 || rc != 0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ( threadData->cut )
|
||||||
|
{
|
||||||
|
sink->cut( );
|
||||||
threadData->cut = false;
|
threadData->cut = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( threadData->accepting ) {
|
if ( threadData->accepting )
|
||||||
if ( sink->canWrite( 0, 0) ) {
|
{
|
||||||
try {
|
if ( sink->canWrite( 0, 0 ) )
|
||||||
sink->write( dataBuffer, dataSize);
|
{
|
||||||
} catch ( Exception & e ) {
|
try
|
||||||
|
{
|
||||||
|
sink->write( dataBuffer, dataSize );
|
||||||
|
} catch ( Exception & e )
|
||||||
|
{
|
||||||
// something wrong. don't accept more data, try to
|
// something wrong. don't accept more data, try to
|
||||||
// reopen the sink next time around
|
// reopen the sink next time around
|
||||||
threadData->accepting = false;
|
threadData->accepting = false;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
reportEvent( 4,
|
reportEvent( 4,
|
||||||
"MultiThreadedConnector :: sinkThread can't write ",
|
"MultiThreadedConnector :: sinkThread can't write ",
|
||||||
ixSink);
|
ixSink );
|
||||||
// don't care if we can't write
|
// don't care if we can't write
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
threadData->isDone = true;
|
|
||||||
pthread_cond_broadcast( &condProduce);
|
|
||||||
pthread_mutex_unlock( &mutexProduce);
|
|
||||||
|
|
||||||
if ( !threadData->accepting ) {
|
if ( !threadData->accepting ) {
|
||||||
if ( reconnect ) {
|
if ( reconnect ) {
|
||||||
reportEvent( 4,
|
reportEvent( 4,
|
||||||
"MultiThreadedConnector :: sinkThread reconnecting ",
|
"MultiThreadedConnector :: sinkThread reconnecting ",
|
||||||
ixSink);
|
ixSink );
|
||||||
// if we're not accepting, try to reopen the sink
|
// if we're not accepting, try to reopen the sink
|
||||||
try {
|
try {
|
||||||
sink->close();
|
sink->close( );
|
||||||
Util::sleep(1L, 0L);
|
Util::sleep( 1L, 0L );
|
||||||
sink->open();
|
sink->open( );
|
||||||
sched_yield();
|
sched_yield( );
|
||||||
threadData->accepting = sink->isOpen();
|
threadData->accepting = sink->isOpen( );
|
||||||
} catch ( Exception & e ) {
|
} catch ( Exception & e ) {
|
||||||
// don't care, just try and try again
|
// don't care, just try and try again
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// if !reconnect, just stop the connector
|
// if !reconnect, just stop the connector
|
||||||
running = false;
|
// running = false; /* kill the whole application */
|
||||||
|
// tell that we used the databuffer, do not wait for us anymore
|
||||||
|
pthread_mutex_lock( &mutex_done );
|
||||||
|
threadData->isDone = 1; // 1==STOP
|
||||||
|
pthread_mutex_unlock( &mutex_done );
|
||||||
|
reportEvent( 4,
|
||||||
|
"MultiThreadedConnector :: sinkThread no reconnect? ",
|
||||||
|
ixSink );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
threadData->accepting = false;
|
||||||
|
sink->close( );
|
||||||
|
} catch ( Exception & e )
|
||||||
|
{
|
||||||
|
// don't care, just try and try again
|
||||||
|
reportEvent( 9,
|
||||||
|
"MultiThreadedConnector :: sinkThread do not care2 ",
|
||||||
|
ixSink );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
pthread_mutex_lock( &mutex_done );
|
||||||
|
threadData->isDone = 1; // producer will check this flag
|
||||||
|
pthread_cond_signal( &cond_done ); // signal producer
|
||||||
|
pthread_mutex_unlock( &mutex_done );
|
||||||
|
|
||||||
|
} /* is running */
|
||||||
|
|
||||||
|
/* just make sure nobody will be waiting for us when we terminate */
|
||||||
|
pthread_mutex_lock( &mutex_done );
|
||||||
|
threadData->isDone = 1; // STOP
|
||||||
|
pthread_cond_signal( &cond_done ); // signal producer
|
||||||
|
pthread_mutex_unlock( &mutex_done );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Signal to each sink to cut what they've done so far, and start anew.
|
* Signal to each sink to cut what they've done so far, and start anew.
|
||||||
|
@ -369,16 +443,15 @@ MultiThreadedConnector :: close ( void ) throw ( Exception )
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
// signal to stop for all threads
|
// signal to stop for all threads
|
||||||
pthread_mutex_lock( &mutexProduce);
|
pthread_mutex_lock( &mutex_start );
|
||||||
running = false;
|
running = false;
|
||||||
pthread_cond_broadcast( &condProduce);
|
pthread_cond_broadcast( &cond_start );
|
||||||
pthread_mutex_unlock( &mutexProduce);
|
pthread_mutex_unlock( &mutex_start );
|
||||||
|
|
||||||
// wait for all the threads to finish
|
// wait for all the threads to finish
|
||||||
for ( i = 0; i < numSinks; ++i ) {
|
for ( i = 0; i < numSinks; ++i ) {
|
||||||
pthread_join( threads[i].thread, 0);
|
pthread_join( threads[i].thread, 0);
|
||||||
}
|
}
|
||||||
pthread_attr_destroy( &threadAttr);
|
|
||||||
|
|
||||||
Connector::close();
|
Connector::close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
* Marks if the thread has processed the last batch
|
* Marks if the thread has processed the last batch
|
||||||
* of data.
|
* of data.
|
||||||
*/
|
*/
|
||||||
bool isDone;
|
int isDone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag to show that the sink should be made to cut in the
|
* A flag to show that the sink should be made to cut in the
|
||||||
|
@ -123,7 +123,7 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
this->ixSink = 0;
|
this->ixSink = 0;
|
||||||
this->thread = 0;
|
this->thread = 0;
|
||||||
this->accepting = false;
|
this->accepting = false;
|
||||||
this->isDone = false;
|
this->isDone = 1; // 0==RUN 1=STOP 2=TERMINATE
|
||||||
this->cut = false;
|
this->cut = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,15 +138,19 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
threadFunction( void * param );
|
threadFunction( void * param );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/* mutex and cond variable for signaling new data
|
||||||
* The mutex of this object.
|
* the consumers wait for this
|
||||||
*/
|
*/
|
||||||
pthread_mutex_t mutexProduce;
|
pthread_mutex_t mutex_start;
|
||||||
|
pthread_cond_t cond_start; // producer sets this
|
||||||
|
|
||||||
/**
|
/* mutex and cond variable for signaling that a single thread has
|
||||||
* The conditional variable for presenting new data.
|
* finished working on a block of data
|
||||||
|
* the producer waits for this, then checks if everyone is done
|
||||||
|
* and maybe waits again until everyone has finished on the data
|
||||||
*/
|
*/
|
||||||
pthread_cond_t condProduce;
|
pthread_mutex_t mutex_done;
|
||||||
|
pthread_cond_t cond_done; // consumer sets this
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The thread attributes.
|
* The thread attributes.
|
||||||
|
@ -159,7 +163,7 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
ThreadData * threads;
|
ThreadData * threads;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signal if we're running or not, so the threads no if to stop.
|
* Signal if we're running or not running
|
||||||
*/
|
*/
|
||||||
bool running;
|
bool running;
|
||||||
|
|
||||||
|
@ -314,7 +318,7 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
* @return the number of bytes read from the Source.
|
* @return the number of bytes read from the Source.
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
virtual unsigned int
|
virtual unsigned long
|
||||||
transfer ( unsigned long bytes,
|
transfer ( unsigned long bytes,
|
||||||
unsigned int bufSize,
|
unsigned int bufSize,
|
||||||
unsigned int sec,
|
unsigned int sec,
|
||||||
|
|
|
@ -226,7 +226,7 @@ aflibConverter::resample( /* number of output samples returned */
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
aflibConverter::err_ret(char *s)
|
aflibConverter::err_ret(const char *s)
|
||||||
{
|
{
|
||||||
aflib_debug("resample: %s \n\n",s); /* Display error message */
|
aflib_debug("resample: %s \n\n",s); /* Display error message */
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -110,7 +110,7 @@ private:
|
||||||
operator=(const aflibConverter& op);
|
operator=(const aflibConverter& op);
|
||||||
|
|
||||||
int
|
int
|
||||||
err_ret(char *s);
|
err_ret(const char *s);
|
||||||
|
|
||||||
void
|
void
|
||||||
deleteMemory();
|
deleteMemory();
|
||||||
|
|
|
@ -97,7 +97,8 @@ void output_message(::aflibDebug::Level level, const char *msg) {
|
||||||
default:
|
default:
|
||||||
break; // avoid compile warning
|
break; // avoid compile warning
|
||||||
}
|
}
|
||||||
system(buff);
|
int r = system(buff);
|
||||||
|
if (r<0) fprintf(stderr, "aflibDebug, system() failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue