How passing argument using handler Context (*ctx)


#1

Hello everybody,

i read wip document, but it seems not very helpful to understand how passing an argument to an handler using “*ctx”.

An example make it possible to understand much better and more quickly. So, can anyone introduce to me how using it with a simple example ?

Thanks you in advance !


#2

You can use it to attach any kind of data you wish to an event handler: put these data in a table or a struct, pass its address at channel creation time; in the event handler, cast the void* back into the correct table/struct type. When releasing the channel, don’t forget to clean any reserved resources.

Let’s say you want to create a function send_msg( char *peer_addr, u16 peer_port, char *msg), which connects through TCP to peer_addr:peer_port, sends msg and closes the communication:

// warning, untested code

static void evh_msg( wip_event_t *ev, void *ctx);

void send_msg( char *peer_addr, u16 peer_port, char *msg) {
  char *msg_copy = adl_memGet( strlen( msg) + 1);
  if( ! msg_copy) { wip_debug( "Memory error :-( \n"); return; }
  strcpy( msg_copy, msg);
  wip_tcpClientCreate( peer_addr, peer_port, evh_msg, (void*) msg_copy);
}

static void evh_msg( wip_event_t *ev, void *ctx) {
  switch( ev->kind) {
    case WIP_CEV_OPEN: {
      char *msg = (char*) ctx; // cast back to real type
      wip_write( ev->channel, msg, strlen( msg));
      // cleanup:      
      adl_memRelease( ctx);
      wip_close( ev->channel);
      break;
    }
    case WIP_CEV_ERROR: {
      wip_debug( "[SAMPLE] error, can't connect\n");
      break;
    }
  }
}

#3

Maybe you should copy the original message to the new buffer before you call wip_tcpClientCreate() …

Regards, Ralf


#4

Thank you very much for your helpful reply fft and Ralf !

This week i’m in OpenAT training course. So, i ask how works context. Here an explanation :

In fact, context is stored in a part of WIP library. So, when you use *ctx, the “msg_copy” array will be copied in a space array dedicated to the wip_channel_t returned by the “tcpClientCreate” function.

So, you don’t need to save or copy the message in any buffer. when the “event_handler” will be called, data will be retrieved automatically.

But, as i describe above, ctx is stored in a part of library. So, your code will crashed fft because you release a part of the WIP dedicated array in your sample. if you do that :

So, it will crashed !

you should release the memory allocated to the “msg_copy” after tcpClientCreate function. the right code will be :

// warning, untested code 

static void evh_msg( wip_event_t *ev, void *ctx); 

void send_msg( char *peer_addr, u16 peer_port, char *msg) { 
  char *msg_copy = adl_memGet( strlen( msg) + 1); 
  wip_tcpClientCreate( peer_addr, peer_port, evh_msg, (void*) msg_copy); 
  adl_memRelease( msg_copy);
} 

static void evh_msg( wip_event_t *ev, void *ctx) { 
  switch( ev->kind) { 
    case WIP_CEV_OPEN: { 
      char *msg = (char*) ctx; // cast back to real type 
      wip_write( ev->channel, msg, strlen( msg)); 
      // cleanup:      
      wip_close( ev->channel); 
      break; 
    } 
    case WIP_CEV_ERROR: { 
      wip_debug( "[SAMPLE] error, can't connect\n"); 
      break;

to modify context specified on tcpClientCreate() function use wip_setCtx() function before event_handler will be called !

I hope this would help anybody which will be worried about *ctx !

Thank you fft and Ralf :wink: for your help

(Please let me known if you have any remarks)


#5

@Ralf: forgot the strcpy indeed, thanks! (code in previous post fixed).

@afaurep: the idea here is that I choose to copy the message, so that it will be properly sent even if it’s modified/destroyed between the call to send_msg() and its actual emission by the event handler. My point was just to show that arbitrarily big data can be passed as ctx, provided that you release them properly. The example is of limited interest, but that’s the shortest reasonable use case I could think of.

As for how ctx is used internally: it’s just a pointer. The lib only keeps that pointer, it doesn’t know what it points to; it doesn’t even require it to be a valid pointer, actually. If you want to store more than sizeof(void*) bytes, you have to handle your memory chunk all by yourself, and that includes getting and releasing memory blocks. In the case above, the call to wip_memRelease() will work correctly.