diff --git a/Andor/andor.cpp b/Andor/andor.cpp index 2ac82051..53e58a69 100644 --- a/Andor/andor.cpp +++ b/Andor/andor.cpp @@ -11,6 +11,8 @@ namespace Andor { + std::recursive_mutex Interface::sdk_mtx; + /***** Andor::SDK::_IsInternalMechanicalShutter *****************************/ /** * @brief wrapper for Andor SDK GetCapabilities @@ -1738,6 +1740,9 @@ namespace Andor { char info[128]; long error; + // the scan selects and initializes the cameras one at a time + std::lock_guard sdk_lock( sdk_mtx ); + // just FYI, really // error = ( andor ? andor->_GetVersionInfo( AT_SDKVersion, info, 128 ) : ERROR ); @@ -1823,8 +1828,11 @@ namespace Andor { this->_handle = handle_in; - // Set the handle and initialize the SDK for this camera + // Set the handle and initialize the SDK for this camera. sdk_mtx keeps it + // the current camera through the calls below, except around set_image(), + // which takes image_data_mutex. // + std::unique_lock sdk_lock( sdk_mtx ); if (error==NO_ERROR) error = ( andor ? andor->_SetCurrentCamera( this->_handle ) : ERROR ); if (error==NO_ERROR) error = ( andor ? andor->_Initialize() : ERROR ); if (error==NO_ERROR) error = ( andor ? andor->_GetCameraSerialNumber( this->camera_info.serial_number ) : ERROR ); @@ -1850,9 +1858,12 @@ namespace Andor { if (error==NO_ERROR) error = ( andor ? andor->_SetTriggerMode( 0 ) : ERROR ); if (error==NO_ERROR) error = ( andor ? andor->_SetPreAmpGain( 0 ) : ERROR ); if (error==NO_ERROR) error = ( andor ? andor->_SetCoolerMode( 1 ) : ERROR ); + sdk_lock.unlock(); if (error==NO_ERROR) error = set_image( this->camera_info.hbin, this->camera_info.vbin, 1, this->camera_info.hend, 1, this->camera_info.vend ); + sdk_lock.lock(); + if (error==NO_ERROR) error = select_camera( this->_handle ); // may have changed while unlocked if (error==NO_ERROR) error = ( andor ? andor->_GetNumberADChannels( this->camera_info.adchans ) : ERROR ); if (error==NO_ERROR) error = get_speeds(); if (error==NO_ERROR) error = set_output_amplifier( AMPTYPE_CONV ); @@ -1896,13 +1907,20 @@ namespace Andor { int temp=999; long error = this->get_temperature( temp ); + // sdk_mtx through the cooler commands and ShutDown(), which acts on the + // current camera, so make that this one (while it is still open). + // Released before image_data_mutex below. + // + { + std::lock_guard sdk_lock( sdk_mtx ); + if ( this->is_andor_open ) select_camera(this->_handle); + // Turn off the cooler // if ( error==NO_ERROR && temp >= this->camera_info.temp_min && temp <= -20 ) { if ( andor ) { - select_camera(this->_handle); andor->_SetTemperature( 20 ); andor->_CoolerOFF(); } @@ -1923,6 +1941,7 @@ namespace Andor { // this->is_andor_open=false; ShutDown(); + } std::lock_guard lock(image_data_mutex); @@ -1955,6 +1974,9 @@ namespace Andor { logwrite( "Andor::Interface::selct_camera", "ERROR camera handle "+std::to_string(handle)+" not open" ); return ERROR; } + // Callers hold sdk_mtx across this and their SDK calls; taking it here as + // well keeps any selection from landing inside another thread's. + std::lock_guard sdk_lock( sdk_mtx ); return ( andor ? andor->_SetCurrentCamera( handle ) : ERROR ); } /***** Andor::Interface::select_camera **************************************/ @@ -1977,6 +1999,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_StartAcquisition() : ERROR ); @@ -2005,6 +2028,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // Are we acquiring? @@ -2038,7 +2062,8 @@ namespace Andor { * */ uint16_t* Interface::get_image_data() { - select_camera(this->_handle); + // no camera selection: this reads our own buffer, not the SDK, and + // selecting here could switch cameras under another thread's SDK calls return this->image_data; } /***** Andor::Interface::get_image_data *************************************/ @@ -2082,6 +2107,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // stop acquiring @@ -2165,6 +2191,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // send the SetShutter command @@ -2198,6 +2225,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); return ( andor ? andor->_GetDetector( x, y ) : ERROR ); @@ -2225,6 +2253,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); error |= ( andor ? andor->_GetStatus( status ) : ERROR ); // current status of Andor SDK @@ -2260,6 +2289,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // Get Number of AD Chans and HS Speeds if needed @@ -2298,6 +2328,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // Get Number of AD Chans and HS Speeds if needed @@ -2437,6 +2468,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // find the index of the speed in the vector @@ -2501,6 +2533,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); float speed = this->camera_info.vsspeeds[index]; @@ -2541,10 +2574,14 @@ namespace Andor { } // for multiple camera systems it is necessary to force the handle! + // sdk_mtx only around the SDK call: image_data_mutex is taken below. // + long error; + { + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); - - long error = ( andor ? andor->_SetImage( hbin, vbin, hstart, hend, vstart, vend ) : ERROR ); + error = ( andor ? andor->_SetImage( hbin, vbin, hstart, hend, vstart, vend ) : ERROR ); + } if ( error==NO_ERROR ) { message << hbin << " " << vbin << " " @@ -2613,6 +2650,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetFanMode( mode ) : ERROR ); @@ -2708,6 +2746,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); if (error==NO_ERROR) error = ( andor ? andor->_SetImageFlip( hflip, vflip ) : ERROR ); @@ -2772,6 +2811,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); if (error==NO_ERROR) error = ( andor ? andor->_SetImageRotate( rotdir ) : ERROR ); @@ -2800,6 +2840,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); return ( andor ? andor->_GetEMGainRange( low, high ) : ERROR ); @@ -2826,6 +2867,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_GetEMCCDGain( gain ) : ERROR ); @@ -2865,6 +2907,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetOutputAmplifier( type ) : ERROR ); @@ -2914,6 +2957,7 @@ namespace Andor { else { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); error = ( andor ? andor->_SetEMCCDGain( gain ) : ERROR ); @@ -2951,6 +2995,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); if ( temp >= this->camera_info.temp_max ) { @@ -3014,6 +3059,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); std::string status; @@ -3055,6 +3101,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetKineticCycleTime( time ) : ERROR ); @@ -3092,6 +3139,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetNumberAccumulations( number ) : ERROR ); @@ -3124,6 +3172,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetAccumulationCycleTime( time ) : ERROR ); @@ -3156,6 +3205,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetNumberKinetics( number ) : ERROR ); @@ -3192,6 +3242,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // Verify that FrameTransfer is supported @@ -3248,6 +3299,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetReadMode( mode ) : ERROR ); @@ -3300,6 +3352,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_SetAcquisitionMode( mode ) : ERROR ); @@ -3344,10 +3397,9 @@ namespace Andor { return ERROR; } - // for multiple camera systems it is necessary to force the handle! + // Waits on this camera by handle, so no camera selection, and sdk_mtx is + // not held: other threads may use the SDK while this one waits. // - select_camera(this->_handle); - long error = ( andor ? andor->_WaitForAcquisitionByHandleTimeOut( this->_handle, timeout ) : ERROR ); return error; @@ -3395,6 +3447,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); long error = ( andor ? andor->_GetMostRecentImage16( this->image_data, this->bufsz ) : ERROR ); @@ -3457,6 +3510,7 @@ namespace Andor { // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); if (error==NO_ERROR) error = ( andor ? andor->_GetMostRecentImage16( this->image_data, this->bufsz ) : ERROR ); @@ -3512,10 +3566,12 @@ namespace Andor { // error = this->start_acquisition(); - // Wait for acquisition (timeout set to 5s over exptime) + // Wait for acquisition (timeout set to 5s over exptime) on THIS camera, by + // handle. WaitForAcquisitionTimeOut waits on whichever camera is current, + // and another thread (e.g. the temperature poll) may have changed that. // int timeout = static_cast( (this->camera_info.exptime + 5.)*1000. ); - if (error==NO_ERROR) error = ( andor ? andor->_WaitForAcquisitionTimeOut( timeout ) : ERROR ); + if (error==NO_ERROR) error = ( andor ? andor->_WaitForAcquisitionByHandleTimeOut( this->_handle, timeout ) : ERROR ); } // ^^ acquire_state cleared upon leaving this scope ^^ @@ -3530,9 +3586,17 @@ namespace Andor { } memset( this->image_data, '\0', this->bufsz*sizeof(uint16_t) ); - // get the data + // Get the data and the temperature from THIS camera. Select it and hold + // sdk_mtx through the reads: without that, a camera selected meanwhile by + // another thread got its last frame read in here (L showing R's image). // + { + std::lock_guard sdk_lock( sdk_mtx ); + if (error==NO_ERROR) error = select_camera( this->_handle ); if (error==NO_ERROR) error = ( andor ? andor->_GetAcquiredData16( this->image_data, this->bufsz ) : ERROR ); + if (error==NO_ERROR) error = ( andor ? andor->_GetTemperature( this->camera_info.ccdtemp, + this->camera_info.temp_status ) : ERROR ); + } float alpha = ( this->weight > 0 ? 1.0f/this->weight : 1.0f ); @@ -3548,11 +3612,6 @@ namespace Andor { } } - // get the temperature reading - // - if (error==NO_ERROR) error = ( andor ? andor->_GetTemperature( this->camera_info.ccdtemp, - this->camera_info.temp_status ) : ERROR ); - // Store the exposure start time // timespec timenow = Time::getTimeNow(); // get the time NOW @@ -3796,6 +3855,7 @@ return NO_ERROR; // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // Can't change if acquisition in progress @@ -3888,6 +3948,7 @@ return NO_ERROR; // for multiple camera systems it is necessary to force the handle! // + std::lock_guard sdk_lock( sdk_mtx ); select_camera(this->_handle); // Can't read if acquisition in progress diff --git a/Andor/andor.h b/Andor/andor.h index 40616662..01b0b5a2 100644 --- a/Andor/andor.h +++ b/Andor/andor.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "common.h" #include "network.h" #include "utilities.h" @@ -438,6 +439,13 @@ namespace Andor { Andor::AndorBase* andor; ///< pointer to the Andor object to use // std::unique_ptr image_data; std::mutex image_data_mutex; + + /// The Andor SDK has ONE current camera per process. Hold this from + /// select_camera() through the SDK calls that follow it, so no other + /// thread can change the current camera in between. It is shared by + /// every camera in the process and is always the innermost lock: never + /// take image_data_mutex while holding it. + static std::recursive_mutex sdk_mtx; unsigned short* image_data; float* avg_data; int num_avg_frames;