lock/unlock di una classe
Spesso capita di dovere bloccare l’utilizzo di una istanza di una classe fino al termine di altre operazioni. Nelle dll di Colibrì si fa uso della macro $_lock_class(..), applicabile a qualunque classe contenete la dichiarazione __lockable.
Per capire come funziona, analizziamo la dichiarazione della classe DOCUMENT di Colibri
class AFX_EXT_CLASS DOCUMENT : public DB_object, public FolderOrganizer, public HISTORY { __lockable public: DOCUMENT(void); virtual ~DOCUMENT(void); // other funcions and methods.. .. .. };
__lockable è una macro che definisce le istanze di DOCUMENT e delle classi derivate come ‘bloccabili‘, inserita nella dichiarazione della classe (in genere all’inizio o alla fine). Banalmente è una semplice dichiarazione di un mutex (locked), accessibile pubblicamente:
#define __lockable public: MUTEX locked;
il mutex viene testato attraverso la macro $_lock_class, che costituisce una barriera all’esecuzione, esattamente come $_lock, utilizzando quest’ultima al suo interno:
#define $_lock_class(cls) $_lock(cls->locked)
Per mostrarne l’uso, analizziamo, come al solito, un pezzo di codice di Colibrì:
Dalla versione 8 Colibrì ha una nuova DLL, (DLL_vision) contenente funzioni di gestione delle telecamere e di manipolazione delle immagini; La DLL fa uso delle funzionalità di OpenCV. In particolare è definita la classe VideoCamera che permette l’acquisizione di video e immagini:
namespace VISION { class AFX_EXT_CLASS VideoCamera { public: VideoCamera(); ~VideoCamera(); bool Open(int id = 0); bool VideoCamera::Open(int id, float w, float h, float fps); bool Capture(); /** \brief cattura un frame e lo trasferisce sulla bitmap \param map dove trasferire (deve essere stata inizializzata alle dimensioni del frame) */ bool Capture(IMAGE *map); ...... // other functions & info ....... __use_private_struct }; }
Siamo interessati alla funzione Capture(IMAGE *map) che cattura un frame dalla telecamera è lo trasferisce nella classe IMAGE delle librerie Colibri; IMAGE deriva da DOCUMENT, quindi contiene la dichiarazione __lockabke.
Analizziamo ora la funzione VideoCamera::Capture, richiamata da un thread di estrazione del video
bool VideoCamera::Capture(IMAGE *map) { __getstr $_lock_class(map) if (Capture()) return Get_frame_to(map); // convert from Mat image class to IMAGE return false; }
La funzione usa la macro _lock_class(map) per bloccare il mutex dichiarato con __lockabke nella IMAGE (che deriva da DOCUMENT..); quindi viene importato in map il frame catturato; l’istanza map viene così protetta dall’uso esterno al thead da parte di altre funzioni, fino alla sua terminazione regolare. Inoltre, l’estrazione del frame non avrà luogo fino a quando la classe risulterà bloccata da funzioni esterne: nel caso specifico analizziamo il thread chiamante VideoCamera::Capture:
static void thread_acquire_camera(CLASS_STR) { $_lock(_$str->acquiring) int k = 0; while (true) { if (_$str->acq_status) { if (_$str->camera->Capture(igrp->img[k])) { igrp->w_preview->Set_doc_image(igrp->img[k], false); PAINT(igrp->w_preview); } } else break; k = (k + 1) % N_IMG_BUF; } }
Dopo avere chiamato la Capture, il thread esegue due operazioni : igrp->w_preview->Set_doc_image(igrp->img[k], false), che assegna l’immagine ad un controllo di visualizzazione (WIDGET_IMAGE), quindi usa la macro PAINT per richiedere la visualizzazione del frame.
mostriamo ora la parte di codice che effettuerà la visualizzazione:
.... .... if (_$str->clb_image) { $_lock_class(_$str->clb_image) _$str->g_image = __$btm(_$str->clb_image); DRW_MEM_IMAGE(_$str, paint); _$str->g_image = 0; return; } .... ....
_$str->clb_image è lo stesso IMAGE* igrp->img[k] contenuto nel thread sopra riportato, che era stato passato alla VideoCamera::Capture. $_lock_class(_$str->clb_image) costituisce una barriera all’uso della istanza, superata solo quando le altre utenze (VideoCamera::Capture..) rilasceranno il mutex relativo (ricordati di __lockabe..).
Per concludere: l’uso della barriera definita da _lock_class garantisce che non sia possibile effettuare operazioni di cattura del frame su un’istanza che è in visualizzazione e, viceversa, non sarà possibile visualizzare un’istanza che sta catturando un frame..