Probable fix of memory leak

This commit is contained in:
Anne de Jong 2022-01-13 13:59:35 +01:00
parent 8fee46a41f
commit 4339ecdbc0
2 changed files with 164 additions and 136 deletions

View File

@ -36,20 +36,40 @@ in a sister repository in a later stage.
If you have any question(s), please feel free to contact us: info@ascee.nl. If you have any question(s), please feel free to contact us: info@ascee.nl.
## Installation # Building from source
### Compilation ## Dependencies
#### Archlinux Optional dependencies, which can be turned ON/OFF using CMake:
- Build tools, including [http://cmake.org](CMake).
- FFTW (For really fast FFT's)
- libUlDAQ, for the Measurement Computing DT9837A USB DAQ box
- GNU Autotools, for compiling libUlDAQ
- RtAudio, for Audio DAQ backends
- Cython, for wrapping the C-code to Python.
- The following Python packages need also be available:
- `Scipy` (which includes Numpy). Install with `sudo apt install
python3-scipy`, or `pacman -S scipy`.
- `appdirs`, which can be grabbed from [https://pypi.org](Pypi)
## Installation of dependencies
## Compilation of LASP
### Archlinux
Compiling the code on Archlinux requires the following packages to be available: Compiling the code on Archlinux requires the following packages to be available:
- openblas-lapack (AUR) - openblas-lapack (AUR)
- Python 3.7 - Python>=3.7
- Numpy (Python-numpy) - Numpy (Python-numpy)
- Cython - Cython
#### Ubuntu / Linux Mint ### Ubuntu / Linux Mint
*Only tested with Linux Mint 18.04*, we require the following packages for *Only tested with Linux Mint 18.04*, we require the following packages for
compilation: compilation:
@ -62,17 +82,3 @@ compilation:
- libopenblas-base - libopenblas-base
- libopenblas-dev - libopenblas-dev
#### Windows specific
Tested using a Anacond / Miniconda Python environment. Please first run the following command:
`conda install fftw`
in case you want the *FFTW* fft backend.
### Dependencies
#### Ubuntu / Linux Mint
*Only tested with Linux Mint 18.04*. The following Dependencies are required
for Ubuntu:

View File

@ -40,91 +40,91 @@ class DT9837A : public Daq {
us nFramesPerBlock; us nFramesPerBlock;
public: public:
DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config) DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config)
: Daq(devinfo, config) { : Daq(devinfo, config) {
// Some sanity checks // Some sanity checks
if (eninchannels.size() != 4) { if (eninchannels.size() != 4) {
throw runtime_error("Invalid length of enabled inChannels vector"); throw runtime_error("Invalid length of enabled inChannels vector");
}
if (enoutchannels.size() != 1) {
throw runtime_error("Invalid length of enabled outChannels vector");
}
stopThread = false;
nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex];
if (nFramesPerBlock < 24 || nFramesPerBlock > 8192) {
throw runtime_error("Unsensible number of samples per block chosen");
}
if (samplerate() < 10000 || samplerate() > 51000) {
throw runtime_error("Invalid sample rate");
}
DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API];
DaqDeviceDescriptor descriptor;
DaqDeviceInterface interfaceType = ANY_IFC;
UlError err;
us numdevs = MAX_DEV_COUNT_PER_API;
err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, (unsigned*) &numdevs);
if (err != ERR_NO_ERROR) {
throw runtime_error("Device inventarization failed");
}
if ((us) api_specific_devindex >= numdevs) {
throw runtime_error("Device number {deviceno} too high {err}. This could "
"happen when the device is currently not connected");
}
descriptor = devdescriptors[api_specific_devindex];
// get a handle to the DAQ device associated with the first descriptor
handle = ulCreateDaqDevice(descriptor);
if (handle == 0) {
throw runtime_error("Unable to create a handle to the specified DAQ "
"device. Is the device currently in use? Please make sure to set "
"the DAQ configuration in duplex mode if simultaneous input and "
"output is required.");
}
err = ulConnectDaqDevice(handle);
if (err != ERR_NO_ERROR) {
showErr(err);
ulReleaseDaqDevice(handle);
handle = 0;
throw runtime_error("Unable to connect to device: {err}");
}
for (us ch = 0; ch < 4; ch++) {
err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0);
showErr(err);
if (err != ERR_NO_ERROR) {
throw runtime_error("Fatal: could normalize channel sensitivity");
} }
CouplingMode cm = inputACCouplingMode[ch] ? CM_AC : CM_DC; if (enoutchannels.size() != 1) {
err = ulAISetConfig(handle, AI_CFG_CHAN_COUPLING_MODE, ch, cm); throw runtime_error("Invalid length of enabled outChannels vector");
}
stopThread = false;
nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex];
if (nFramesPerBlock < 24 || nFramesPerBlock > 8192) {
throw runtime_error("Unsensible number of samples per block chosen");
}
if (samplerate() < 10000 || samplerate() > 51000) {
throw runtime_error("Invalid sample rate");
}
DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API];
DaqDeviceDescriptor descriptor;
DaqDeviceInterface interfaceType = ANY_IFC;
UlError err;
us numdevs = MAX_DEV_COUNT_PER_API;
err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, (unsigned*) &numdevs);
if (err != ERR_NO_ERROR) {
throw runtime_error("Device inventarization failed");
}
if ((us) api_specific_devindex >= numdevs) {
throw runtime_error("Device number {deviceno} too high {err}. This could "
"happen when the device is currently not connected");
}
descriptor = devdescriptors[api_specific_devindex];
// get a handle to the DAQ device associated with the first descriptor
handle = ulCreateDaqDevice(descriptor);
if (handle == 0) {
throw runtime_error("Unable to create a handle to the specified DAQ "
"device. Is the device currently in use? Please make sure to set "
"the DAQ configuration in duplex mode if simultaneous input and "
"output is required.");
}
err = ulConnectDaqDevice(handle);
if (err != ERR_NO_ERROR) { if (err != ERR_NO_ERROR) {
showErr(err); showErr(err);
throw runtime_error("Fatal: could not set AC/DC coupling mode"); ulReleaseDaqDevice(handle);
handle = 0;
throw runtime_error("Unable to connect to device: {err}");
} }
IepeMode iepe = inputIEPEEnabled[ch] ? IEPE_ENABLED : IEPE_DISABLED; for (us ch = 0; ch < 4; ch++) {
err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe);
if (err != ERR_NO_ERROR) { err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0);
showErr(err); showErr(err);
throw runtime_error("Fatal: could not set IEPE mode"); if (err != ERR_NO_ERROR) {
throw runtime_error("Fatal: could normalize channel sensitivity");
}
CouplingMode cm = inputACCouplingMode[ch] ? CM_AC : CM_DC;
err = ulAISetConfig(handle, AI_CFG_CHAN_COUPLING_MODE, ch, cm);
if (err != ERR_NO_ERROR) {
showErr(err);
throw runtime_error("Fatal: could not set AC/DC coupling mode");
}
IepeMode iepe = inputIEPEEnabled[ch] ? IEPE_ENABLED : IEPE_DISABLED;
err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe);
if (err != ERR_NO_ERROR) {
showErr(err);
throw runtime_error("Fatal: could not set IEPE mode");
}
} }
} }
}
DT9837A(const DT9837A &) = delete; DT9837A(const DT9837A &) = delete;
@ -163,12 +163,12 @@ public:
if (hasinput) { if (hasinput) {
assert(!inbuffer); assert(!inbuffer);
inbuffer = inbuffer =
new double[neninchannels() * nFramesPerBlock * 2]; // Watch the 2! new double[neninchannels() * nFramesPerBlock * 2]; // Watch the 2!
} }
if (hasoutput) { if (hasoutput) {
assert(!outbuffer); assert(!outbuffer);
outbuffer = outbuffer =
new double[nenoutchannels() * nFramesPerBlock * 2]; // Watch the 2! new double[nenoutchannels() * nFramesPerBlock * 2]; // Watch the 2!
} }
this->inqueue = inqueue; this->inqueue = inqueue;
this->outqueue = outqueue; this->outqueue = outqueue;
@ -203,6 +203,40 @@ public:
friend void threadfcn(DT9837A *); friend void threadfcn(DT9837A *);
}; };
/**
* @brief Create an empty buffer and fill it with zeros.
*
* @param size The number of elements in the array
*
* @return Pointer to the array
*/
static double* createZeroBuffer(size_t size) {
double* buf = static_cast<double *>(
malloc(sizeof(double) * size));
for (us sample = 0; sample < size; sample++) {
buf[sample] = 0;
}
return buf;
}
/**
* @brief Copy samples from one linear array to the next.
*
* @param[in] from Buffer to copy from
* @param[out] to Buffer to copy to
* @param startFrom The position to start in the from-buffer
* @param startTo The position to start in the to-buffer
* @param N The number of samples to copy.
*/
static inline void copySamples(double* from,double* to,
const us startFrom,const us startTo,const us N) {
for (us sample = 0; sample < N; sample++) {
to[startTo + sample] = from[startFrom + sample];
}
}
void threadfcn(DT9837A *td) { void threadfcn(DT9837A *td) {
std::cerr << "Starting DAQ Thread fcn" << endl; std::cerr << "Starting DAQ Thread fcn" << endl;
@ -271,9 +305,9 @@ void threadfcn(DT9837A *td) {
cerr << "Starting output DAC" << endl; cerr << "Starting output DAC" << endl;
err = ulAOutScan(handle, 0, 0, BIP10VOLTS, err = ulAOutScan(handle, 0, 0, BIP10VOLTS,
/* BIP60VOLTS, */ /* BIP60VOLTS, */
2 * td->nFramesPerBlock, // Watch the 2 here! 2 * td->nFramesPerBlock, // Watch the 2 here!
&samplerate, scanoptions, outscanflags, outbuffer); &samplerate, scanoptions, outscanflags, outbuffer);
if (err != ERR_NO_ERROR) { if (err != ERR_NO_ERROR) {
showErr(err); showErr(err);
@ -315,8 +349,8 @@ void threadfcn(DT9837A *td) {
cerr << "Starting input ADC" << endl; cerr << "Starting input ADC" << endl;
err = ulDaqInScan(handle, indesc, neninchannels, err = ulDaqInScan(handle, indesc, neninchannels,
2 * td->nFramesPerBlock, // Watch the 2 here! 2 * td->nFramesPerBlock, // Watch the 2 here!
&samplerate, scanoptions, inscanflags, inbuffer); &samplerate, scanoptions, inscanflags, inbuffer);
if (err != ERR_NO_ERROR) { if (err != ERR_NO_ERROR) {
showErr(err); showErr(err);
goto exit; goto exit;
@ -348,9 +382,9 @@ void threadfcn(DT9837A *td) {
if (outxstat.currentScanCount > outTotalCount + 2 * nFramesPerBlock) { if (outxstat.currentScanCount > outTotalCount + 2 * nFramesPerBlock) {
cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count=" cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count="
<< outxstat.currentScanCount << outxstat.currentScanCount
<< " while loop count = " << outTotalCount << " while loop count = " << outTotalCount
<< ", probably due to too small buffer size. *****" << endl; << ", probably due to too small buffer size. *****" << endl;
} }
outTotalCount = outxstat.currentScanCount; outTotalCount = outxstat.currentScanCount;
@ -362,22 +396,16 @@ void threadfcn(DT9837A *td) {
if (!botoutenqueued) { if (!botoutenqueued) {
/* cerr << "Copying output buffer to bottom" << endl; */ /* cerr << "Copying output buffer to bottom" << endl; */
double *bufcpy; double *bufcpy;
assert(nenoutchannels > 0);
if (!outqueue->empty()) { if (!outqueue->empty()) {
bufcpy = (double *)outqueue->dequeue(); bufcpy = (double *)outqueue->dequeue();
} else { } else {
cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL "
"QUEUE WITH ZEROS ***********" "QUEUE WITH ZEROS ***********"
<< endl; << endl;
bufcpy = static_cast<double *>( bufcpy = createZeroBuffer(nFramesPerBlock*nenoutchannels);
malloc(sizeof(double) * nFramesPerBlock * nenoutchannels));
for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[sample] = 0;
}
}
assert(nenoutchannels > 0);
for (us sample = 0; sample < nFramesPerBlock; sample++) {
outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample];
} }
copySamples(bufcpy, outbuffer, 0, buffer_mid_idx_out, nFramesPerBlock);
free(bufcpy); free(bufcpy);
botoutenqueued = true; botoutenqueued = true;
} }
@ -386,22 +414,16 @@ void threadfcn(DT9837A *td) {
if (!topoutenqueued) { if (!topoutenqueued) {
/* cerr << "Copying output buffer to top" << endl; */ /* cerr << "Copying output buffer to top" << endl; */
double *bufcpy; double *bufcpy;
assert(nenoutchannels > 0);
if (!outqueue->empty()) { if (!outqueue->empty()) {
bufcpy = (double *)outqueue->dequeue(); bufcpy = (double *)outqueue->dequeue();
} else { } else {
cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL "
"QUEUE WITH ZEROS ***********" "QUEUE WITH ZEROS ***********"
<< endl; << endl;
bufcpy = static_cast<double *>( bufcpy = createZeroBuffer(nFramesPerBlock*nenoutchannels);
malloc(sizeof(double) * nFramesPerBlock * nenoutchannels));
for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[sample] = 0;
}
}
assert(nenoutchannels > 0);
for (us sample = 0; sample < nFramesPerBlock; sample++) {
outbuffer[sample] = bufcpy[sample];
} }
copySamples(bufcpy, outbuffer, 0, 0, nFramesPerBlock);
free(bufcpy); free(bufcpy);
topoutenqueued = true; topoutenqueued = true;
} }
@ -417,10 +439,10 @@ void threadfcn(DT9837A *td) {
assert(inscanstat == SS_RUNNING); assert(inscanstat == SS_RUNNING);
if (inxstat.currentScanCount > inTotalCount + 2 * nFramesPerBlock) { if (inxstat.currentScanCount > inTotalCount + 2 * nFramesPerBlock) {
cerr << "***** ERROR: Missing input sample blocks, count=" cerr << "***** ERROR: Missing input sample blocks, count="
<< inxstat.currentScanCount << inxstat.currentScanCount
<< ", probably due to too small buffer size. Exiting thread. " << ", probably due to too small buffer size. Exiting thread. "
"*****" "*****"
<< endl; << endl;
break; break;
} }
inTotalCount = inxstat.currentScanCount; inTotalCount = inxstat.currentScanCount;
@ -434,11 +456,11 @@ void threadfcn(DT9837A *td) {
us monitoroffset = monitorOutput ? 1 : 0; us monitoroffset = monitorOutput ? 1 : 0;
assert(neninchannels > 0); assert(neninchannels > 0);
for (us channel = 0; channel < (neninchannels - monitoroffset); for (us channel = 0; channel < (neninchannels - monitoroffset);
channel++) { channel++) {
for (us sample = 0; sample < nFramesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] = bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] =
inbuffer[buffer_mid_idx_in + sample * neninchannels + inbuffer[buffer_mid_idx_in + sample * neninchannels +
channel]; channel];
} }
} }
if (monitorOutput) { if (monitorOutput) {
@ -447,7 +469,7 @@ void threadfcn(DT9837A *td) {
us channel = neninchannels - 1; us channel = neninchannels - 1;
for (us sample = 0; sample < nFramesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[sample] = inbuffer[buffer_mid_idx_in + bufcpy[sample] = inbuffer[buffer_mid_idx_in +
sample * neninchannels + channel]; sample * neninchannels + channel];
} }
} }
inqueue->enqueue((void *)bufcpy); inqueue->enqueue((void *)bufcpy);
@ -461,10 +483,10 @@ void threadfcn(DT9837A *td) {
us monitoroffset = monitorOutput ? 1 : 0; us monitoroffset = monitorOutput ? 1 : 0;
assert(neninchannels > 0); assert(neninchannels > 0);
for (us channel = 0; channel < (neninchannels - monitoroffset); for (us channel = 0; channel < (neninchannels - monitoroffset);
channel++) { channel++) {
for (us sample = 0; sample < nFramesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] = bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] =
inbuffer[sample * neninchannels + channel]; inbuffer[sample * neninchannels + channel];
} }
} }
if (monitorOutput) { if (monitorOutput) {
@ -510,7 +532,7 @@ exit:
} }
Daq *createUlDaqDevice(const DeviceInfo &devinfo, Daq *createUlDaqDevice(const DeviceInfo &devinfo,
const DaqConfiguration &config) { const DaqConfiguration &config) {
DT9837A *daq = NULL; DT9837A *daq = NULL;