HeapAlloc Windows

36 views
Skip to first unread message

Mauro

unread,
Dec 7, 2010, 11:29:18 PM12/7/10
to C Talks
Buenas:
Para usar el heapAlloc por las limitaciones q dan en el
TP Insomnio, leimos la documentacion y vimos un poco la funcion aún
asi no sabemos como implementar esto. La idea q se nos ocurrio es
dentro de cada uno de los threads de windows, al principio, generar un
heap con la funcion HeapCreate y al final un HeapFree. Dentro de cada
thread cada vez q necesitemos memoria usar un HeapAlloc como si fuera
un malloc.

Estamos en lo correcto en pensar usarlo asi?

Si nos darias una mano te agradeceriamos mucho!
un saludo y gracias

Matías Dumrauf

unread,
Dec 8, 2010, 1:04:35 AM12/8/10
to c-t...@googlegroups.com
Mauro, cómo va todo?

Efectivamente, están en lo correcto. La idea del uso de Heaps es justamente esa: Cada proceso/thread posea su propio Heap, no serializable, del cual se reserve memoria dinámicamente.

Dado que el Heap debe poseer un tamaño mínimo de 1 MB, ser No Serializable, y crecer tanto como memoria halla en el sistema, se deben pasar los argumentos correctos.

Los prototipos de las funciones son los siguientes:

HANDLE WINAPI HeapCreate(
  __in  DWORD flOptions,
  __in  SIZE_T dwInitialSize,
  __in  SIZE_T dwMaximumSize
);

"If dwMaximumSize is 0, the heap can grow in size. The heap's size is limited only by the available memory."

BOOL WINAPI HeapDestroy(
__in HANDLE hHeap

);
LPVOID WINAPI HeapAlloc(
  __in  HANDLE hHeap,
  __in  DWORD dwFlags,
  __in  SIZE_T dwBytes
);

BOOL WINAPI HeapFree(

  __in  HANDLE hHeap,

  __in  DWORD dwFlags,

  __in  LPVOID lpMem

);

Las llamadas a funciones se deben hacer de la siguiente manera:

     /* Crear un Heap */
HANDLE  heap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024, 0);

/* Alocar memoria dinamicamente */
char   *var  = HeapAlloc(heap, HEAP_NO_SERIALIZE, strlen("Hello World") + 1);

strcpy(var, "Hello World");
puts(var);

/* Liberar la memoria */
HeapFree(heapHEAP_NO_SERIALIZE, var);

/* Destruir el Heap */
HeapDestroy(heap);


Saludos, y éxitos. Cualquier cosa, no dudes en consultar.

Matías Dumrauf



--
Recibiste este mensaje porque estás suscrito al Grupo "C Talks".
Si quieres publicar en este grupo, envía un mensaje de correo
electrónico a c-t...@googlegroups.com
Para anular la suscripción a este grupo, envía un mensaje a
c-talks+u...@googlegroups.com
Para obtener más opciones, visita este grupo en
http://groups.google.com.ar/group/c-talks?hl=es.

Marina Revelli

unread,
Dec 8, 2010, 10:01:57 PM12/8/10
to c-t...@googlegroups.com
Hola Matías,
 
y que pasa en el caso donde 2 threads deben utilizar el mismo heap?
 
Por ejemplo, nosotros tenemos 1 que agrega los nodos a la cola y otro que los libera.
 
gracias!

Matías Dumrauf

unread,
Dec 8, 2010, 11:26:31 PM12/8/10
to c-t...@googlegroups.com
En ese caso, se pueden plantear 3 escenarios:

1) El más simple sería que se libere en el heap del thread actual. Ésto conceptualmente estaría mal, ya que causaría un hueco (Fragmentación Externa) dentro del Heap que al estar reservado sería inaccesible.

2) Hacer que la Cola tenga su propio Heap. Si el Proceso está programado en C++, se puede implementar una Clase Cola e instanciarla antes de lanzar los threads, es decir, en el Proceso. Que tenga un atributo privado que sea un HANDLE representando al Heap. Entonces, cada método público Agregar/Sacar utiliza siempre ese Heap para reservar/liberar memoria dinámicamente.

Si está implementa en C, incluir en la estructura de la Cola un campo más: el Heap. Crear la Cola en el Scope del Proceso (es decir, antes de lanzar los Threads), y las funciones para Crear, Destruir, Agregar y Sacar no van a recibir como argumento el Heap del Proceso/Thread, sino que lo van a obtener directamente de la estructura Cola.

Quedaría así:

struct t_nodo{
    void          *data;
    struct t_nodo *next;
);
typedef struct t_nodo Nodo;

typedef struct _COLA{
    HANDLE  heap;
    Nodo   *head;
    Nodo   *tail;
} Cola;


3) Usar el servicio de Message Queue del SO para trabajar con Memoria Compartida (MSMQ en Windows y Mqueue en Sistemas tipo Unix, ya que respetan el standard POSIX).


Saludos,

Matías Dumrauf

Marina Revelli

unread,
Dec 12, 2010, 4:44:26 PM12/12/10
to c-t...@googlegroups.com
Hola Matías, me queda 1 duda respecto a lo que pusiste en el punto 1. En la parte de especificaciones, en el tp se aclara que:
 
"Dado que el tiempo de vida de cada thread será corto, este no se ocupará de la fragmentación que se genere  en  el  heap.  Cuando  el  thread  ya  no  necesite  el  heap  este  lo  deberá  eliminar  para  liberar  los recursos. "
 
Pero si justamente tengo 2 hilos que comparten heap, uno que agrega nodos y otro que saca, no entiendo cómo referenciar al mismo heap si no se crean como variable global.

Matías Dumrauf

unread,
Dec 13, 2010, 2:22:50 PM12/13/10
to c-t...@googlegroups.com
Marina, yo no te dije que tengas dos hilos que compartan un Heap. Justamente eso está contraindicado en las especificaciones.

A lo que me refería era que en la recomendación del "punto 1)", liberes la memoria (un HeapFree) en un Thread, del puntero reservado en el otro Thread (de otro Heap). Y eso es lo que no se espera que se tenga que terminar haciendo. Es decir, si se va a hacer un Heap para ese Thread/Proceso es para que sólo él reserve/libere memoria. Por ende, estaría mal liberar memoria en otro Heap. Pero """funcionaría"""" (nótense las múltiples comillas).

Me parece que va a ser (muchísimo) mejor que tomes la segunda opción: un Heap para tu Tad/Clase Cola. Si necesitás ayuda con la implementación, decime que subo un par de ejemplos.


Saludos, y muchos éxitos.

Matías Dumrauf

Marina Revelli

unread,
Dec 13, 2010, 2:59:28 PM12/13/10
to c-t...@googlegroups.com
Yo siempre había entendido que eran 2 hilos con = heap, o sea que siempre entendí mal. Ahora de curiosa entonces, no se supone que si yo defino el heap localmente para cada hilo, si pongo heapFree no me va reconocer el heap del otro? A eso me refería como hacer para que entre hilos reconozcan sus heaps respectivos.
 
Voy a tratar de hacer lo de la cola, podrías mandarme esos ejemplos? porque no creo que sea tan amistoso como parece.
 
Muchas gracias y perdón por tantas preguntas!

Matías Dumrauf

unread,
Dec 13, 2010, 8:10:03 PM12/13/10
to c-t...@googlegroups.com
En realidad, es más fácil de lo que parece.

Para empezar: Sí. Se supone que si yo quiero liberarle memoria a un Heap incorrecto debería fallar, pero a veces en Windows el VS 2005 lo pasa por alto. Es más, si compilás el proyecto en Release Mode y ejecutás tu proyecto por línea de comandos en vez de darle Play desde el IDE, el error nunca se presenta.

Podría pasar que justo el 2do Thread reservó la misma cantidad de bytes, o mayor, y por eso no falle. Pero bueno. Como te dije antes: conceptualmente está mal liberarle a un Heap memoria de otro que no le corresponde. Ya que te queda inevitablemente un Memory Leak en el Heap del 1er Thread.


Con respecto a la solución que te propuse. Estás programando en C o en C++? Para saber si te doy un ejemplo con Tads o con Clases.

Marina Revelli

unread,
Dec 14, 2010, 3:31:02 PM12/14/10
to c-t...@googlegroups.com
Si mal no recuerdo, tengo que usar C.
Muchas gracias!

Matías Dumrauf

unread,
Dec 21, 2010, 9:28:03 PM12/21/10
to c-t...@googlegroups.com
Marina, perdona la tardanza. Estuve algo complicado y después me colgué.

La idea es bastante simple. Supongamos que en tu Header tengas lo siguiente:

typedef struct linkElement{
void               *data;
struct linkElement *next;
} LinkElement;

typedef struct {
HANDLE       heap;
LinkElement *head;
LinkElement *tail;
int          elem_count;
HANDLE       sem;
} Queue;

Queue *Queue_create(HANDLE callingProcessHeap);
void   Queue_push(Queue *q, void *data);
void  *Queue_pop(Queue *q);
void   Queue_destroy(HANDLE callingProcessHeapQueue *q);

Fijate que lo único que cambiaría de la interfaz sería agregarle el Heap a la estructura y los prototipos de Queue_create() y Queue_destroy(). Ésto es para reservar/liberar la memoria para la estructura tipo Queue. La implementación, modificando un poco el tad Queue que expuse durante la C Talk III, sería así:

Queue *Queue_create(HANDLE callingProcessHeap){
Queue* q = (Queue*) HeapAlloc(callingProcessHeap, HEAP_NO_SERIALIZE, sizeof(t_queue));
q->head = NULL;
q->tail = NULL;
q->elem_count = 0;

if ((q->mutex = (HANDLE) CreateMutex(NULL, FALSE, NULL)) == NULL){
HeapFree(callingProcessHeap, HEAP_NO_SERIALIZE, q);
return NULL;
}
if ((q->heap = (HANDLE) HeapCreate(HEAP_NO_SERIALIZE, 1024*1024, 0)) == NULL) {
CloseHandle(q->mutex);
HeapFree(callingProcessHeap, HEAP_NO_SERIALIZE, q);
return NULL;
}
return q;
}

void   Queue_push(Queue *q, void *data){
WaitForSingleObject(q->mutex, INFINITE);

if( data != NULL ){
LinkElement *auxElement = (LinkElement*) HeapAlloc(q->heap, HEAP_NO_SERIALIZE, sizeof(LinkElement));
auxElement->data = data;
auxElement->next = NULL;
if( q->tail != NULL ){
q->tail->next = auxElement;
q->tail = q->tail->next;
}else{
q->head = auxElement;
q->tail = auxElement;
}
q->elem_count++;
}

ReleaseMutex(q->mutex);
}

void  *Queue_pop(Queue *q){
void *data = NULL;
WaitForSingleObject(q->mutex, INFINITE);

if( q->head != NULL ){
LinkElement *element = q->head;
q->head = q->head->next;
if( q->head == NULL ){
q->tail = NULL;
}
data = element->data;
HeapFree(q->heap, HEAP_NO_SERIALIZE, element);
q->elem_count--;
}

ReleaseMutex(q->mutex);
return data;
}

void   Queue_destroy(HANDLE callingProcessHeap, Queue *q){
CloseHandle(q->mutex);
HeapDestroy(q->heap);
HeapFree(callingProcessHeap, HEAP_NO_SERIALIZE, q);
}

Como ves, inevitablemente tendrías que hacer una primer reserva al Heap pasado como argumento en la función Queue_create(). Este Heap es únicamente para reservar memoria para la estructura. Dado que varios Threads comparten la misma Queue, sería el Heap del Proceso, previamente creado. Lo mismo ocurre con Queue_destroy(): se destruye la Queue en el Proceso, no en los Threads.

Y dentro de Queue_push() y Queue_pop(), se reserva/libera del Heap de la Queue.


Saludos, y cualquier cosa: el Grupo no se toma vacaciones =P.

Matías Dumrauf
Reply all
Reply to author
Forward
0 new messages