Buffer Circular y algo de mutex en linux

Posted by Ahharu | Posted in | Posted on 02:28

Buenassssssssss, resultase que un buen día yo quería ser feliz y trabajar con pipes para sincronizar 2 threads, la vaina es que era en linux así que me dije "mi mismo, usemos pipes como en windows" y pues así fue, así que lalalala fui a ver pero no me daban las mismas facilidades que en windows, por ende solo quedaba una opción, codearme mi propia pipe así que comencemos.

Primeramente ahi que especifica que en windows las pipes nos permiten definir en su creación un tamaño fijo para el buffer, esto es en gran medida una buena ventaja, aclarar que las pipes en todos los casos no son buenas y su performance puede llegar a ser terrorífico si lo miramos de cerca :P.

Mi objetivo es simplemente intercambiar información entre threads de forma simple, la forma mas simple que se me vino a la mente era un buffer circular, que básicamente es un espacio de memoria de tamaño fijo.

El problema que se presenta es como sincronizar threads de forma correcta mas cuando el buffer a de ser multiplataforma; por una parte vamos a usar Secciones criticas y sus respectivas funciones.

En este caso nos valemos de macros para windows y linux:

#ifdef WIN32

/** Define Critical Section type */
typedef CRITICAL_SECTION SECTION_HANDLE;

/** Critical Sections macro */
#define INIT_SECTION( x ) InitializeCriticalSection( (x) )
/** Critical Sections macro */
#define DELETE_SECTION( x ) DeleteCriticalSection( (x) )
/** Critical Sections macro */
#define ENTER_SECTION( x ) EnterCriticalSection( (x) )
/** Critical Sections macro */
#define TRY_ENTER_SECTION( x ) TryEnterCriticalSection( (x) )
/** Critical Sections macro */
#define LEAVE_SECTION( x ) LeaveCriticalSection( (x) )
#else
/** Define Critical Section type in linux */
typedef pthread_mutex_t SECTION_HANDLE;

/** Critical Sections macro */
#define INIT_SECTION( x ) pthread_mutex_t n = PTHREAD_MUTEX_INITIALIZER; *(x) = n
/** Critical Sections macro */
#define DELETE_SECTION( x ) (x)
/** Critical Sections macro */
#define ENTER_SECTION( x ) pthread_mutex_lock( (x) )
/** Critical Sections macro */
#define TRY_ENTER_SECTION( x ) pthread_mutex_trylock( (x) ) ? 0 : 1
/** Critical Sections macro */
#define LEAVE_SECTION( x ) pthread_mutex_unlock( (x) )
#endif


Recordar que en linux las secciones criticas no necesitan ser eliminadas por ende no hay una función para ello, algo que puede representar un dolor de cabeza para muchos es la creación:

#define INIT_SECTION( x ) pthread_mutex_t n = PTHREAD_MUTEX_INITIALIZER; *(x) = n


lo definimos de esta forma debido a que por extraño que parezca y si mi memoria no me falla PTHREAD_MUTEX_INITIALIZER esta definido como un array lo cual es vayandole putada y debido a restricciones del lenguaje ( C++ ) no es posible hacerlo sin una variable auxiliar :-\.

Ya con estos macros solo nos queda entrar al buffer y realizar las operaciones, claro en caso de que ya halla alguien adentro seria un tanto encabronante el tener un deadlock; así que nos hacemos un waiter =D, realmente en linux no muchas personas saben como y resultan implementando verdaderas cochinadas, así que dejo mi implementación como mera y vana documentación xD.

Write

int CBuffer::Write( const byte *pbtSrc, long lSize, u_int uiTimeout)
{
u_long ulTmp = 0;

if (pbtSrc == NULL || lSize <= 0 || lSize > CBUFF_MAX_BUFF_SIZE)
return CBUFF_INVALID_PARAMS;

if (WaitUnlock( uiTimeout ) == false)
return CBUFF_UNABLE_TO_LOCK;

if (GetAvaliableWriteSize() < lSize) {
Unlock();
return CBUFF_NOT_ENOUGH_SPACE;
}
....blablabla



Read:

int CBuffer::Read( byte *pbtDst, long lSize, u_int uiTimeout)
{
u_long ulTmp = 0;

if (pbtDst == NULL || lSize <= 0)
return CBUFF_INVALID_PARAMS;

if (WaitUnlock( uiTimeout ) == false)
return CBUFF_UNABLE_TO_LOCK;

if (GetAvaliableReadSize() < lSize) {
Unlock();
return CBUFF_NOT_ENOUGH_SPACE;
}
....blablabla


Y la parte que todos esperaban ( joder quien lee estas cosas? ) el waiter:

bool CBuffer::WaitUnlock( u_int uiTimeout )
{

if (uiTimeout == 0 || IsLocked() == false)
return true;

if (uiTimeout == CBUFF_INFINITE) {
ENTER_SECTION( &m_shBuff );
return true;
}
#ifdef WIN32
bool bRet = false;
timeval tvInit;

gettimeofday( &tvInit, NULL);

while(GetAndDiffTime( &tvInit ) < uiTimeout && bRet == false) {
if ((bRet = TRY_ENTER_SECTION( &m_shBuff )) != false)
break;
Sleep( CBUFF_WAIT_VRFY_TIME );
}

return bRet;
#else
time_t tTemp = 0;
timespec tsTime;
int iRet = 0;

memset( &tsTime, 0x00, sizeof( timespec ));

tsTime.tv_sec = tTemp = ((uiTimeout > 1000) ? uiTimeout/1000 : 0);
tsTime.tv_nsec = (uiTimeout - (tTemp * 1000)) * 1000000L;

iRet = pthread_mutex_timedlock( &m_shBuff, &tsTime);

return (!iRet);
#endif
}



Que hice una cochinada en Windows....si...después la arreglo ( No en serio xD ), y como que ya eso fue todo el post :P.

Salu2.

Att: Iker

Comments (0)

Publicar un comentario en la entrada