Re: [gnet] Iterating the event loop within a callback



On Sunday 02 May 2004 03:07, Michael Torrie wrote:

Hi Michael,

> I'm trying to do something kind of strange here.  From previous list
> communication, I know it's possible, but my test app does not work.
> Here's the logic flow:
>
> on the main data call-back, if the routine sees a certain piece of data,
> then it resets the data call-back to another call-back, asks for another
> line of data, and then iterates the event loop (allowing the other
> call-back to process data, and then return to the point in this
> callback).  This of course, doesn't do anything.  I don't think it's
> possible to do what I want the way I have done it (having browsed the
> gnet code a bit).
>
> My question is, how do I accomplish what  I want to do?  Again I want to
> be able to, in the middle of a callback, ask for more data which will be
> processed by a callback, and then return to the spot where I left off in
> the original callback.

What you are doing (while events pending do main iteration after the 
gnet_conn_readline) won't work in most cases, because it assumes that the 
data of the next line is already available, which won't be true in most cases 
(and then events pending will be FALSE while waiting for more data to arrive, 
and your loop exits).

In any case, what you are doing is not really the best way to do things, in my 
humble opinion. Even if you made it work [1], it would probably lead to code 
that is fairly hard to understand. Personally, I would do something similar 
to what I did in the attached code.

Cheers
 -Tim

[1] You can probably get it to work by creating a new glib main loop for the 
default context (NULL), and then running it after changing the callback and 
calling gnet_conn_readline(). Then in your extra_data callback, you simply 
call g_main_loop_quit() and you're back in the other callback where you 
called g_main_loop_run(). But as I said, this is rather ugly IMHO.
/* test program -- adapted from gnet example echo server
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <gnet.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <signal.h>
#include <fcntl.h>
#include <getopt.h>

typedef enum
{
  STATE_NORMAL,
  STATE_WAITING_FOR_EXTRA_DATA,
} ConnState;

typedef struct _ConnData ConnData;

struct _ConnData
{
  gchar     **lines;
  guint       num_lines;
  ConnState   state;
};

static GServer* ob_server = NULL;
static void ob_sig_int (int signum);
static void
ob_sig_int (int signum)
{
  gnet_server_delete (ob_server);
  exit (EXIT_FAILURE);
}

/*in*
 * Private server functions
 **/
static void request_get_message (GConn *conn, 
                                 GConnEvent *event, 
                                 gpointer user_data);

static void ob_server_func (GServer* server, GConn* conn, gpointer user_data);

int
main(int argc, char** argv)
{
	int port;
	GServer* server;
	GMainLoop* main_loop;
	int c;
	int digit_optind = 0;
	int option_index;
	int this_option_optind;

	gint flag;

	gnet_init ();

	/* Create the main loop */
	main_loop = g_main_new (FALSE);

	/* Create the server */
	server = gnet_server_new (NULL, 8899, ob_server_func, NULL);
	if (!server) {
		fprintf (stderr, "Error: Could not start server\n");
		exit (EXIT_FAILURE);
	}

	ob_server = server;
	signal (SIGINT, ob_sig_int);


	/* Start the main loop */
	g_main_run(main_loop);

	exit (EXIT_SUCCESS);
	return 0;
}


static void
ob_server_func (GServer* server, GConn* conn, gpointer user_data)
{
	if (conn) 
	{
		ConnData *cd;
		
		g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Server connection");

		cd = g_new (ConnData, 1);
		cd->lines = NULL;
		cd->num_lines = 0;
		cd->state = STATE_NORMAL;

		gnet_conn_set_callback (conn, request_get_message, cd);
		gnet_conn_set_watch_error (conn, TRUE);

		/* make a connection to ldap, so we can proxy */
		gnet_conn_readline (conn);
	} else {
		/* Error */
		gnet_server_delete (server);
		exit (EXIT_FAILURE);
	}
}


static void
request_got_line (GConn *conn, const gchar *line, ConnData *cd)
{
	cd->num_lines++;

	cd->lines = g_realloc (cd->lines, cd->num_lines * sizeof(gchar*));
	
	/* gnet_conn_readline() always zero-terminates the lines. */
	cd->lines[cd->num_lines-1] = g_strdup (line);
	
	switch (cd->state)
	{
		case STATE_NORMAL:
		{
			g_print ("Got normal line: %s\n", line);
			
			if (strncmp(line, "ad", 2) == 0)
				cd->state = STATE_WAITING_FOR_EXTRA_DATA;
			
			gnet_conn_readline (conn); /* ask for another line */
		}
		break;

		case STATE_WAITING_FOR_EXTRA_DATA:
		{
			g_assert (cd->num_lines > 1);
			g_print ("Got extra data line: %s\n", line);
			g_print ("  Previous line was: %s\n", cd->lines[cd->num_lines-2]);
			cd->state = STATE_NORMAL;
			gnet_conn_readline (conn); /* ask for another line */
		}
		break;
	}
}


static void 
request_get_message (GConn      *conn,
                     GConnEvent *event, 
                     gpointer    user_data)
{
	ConnData *cd; 
	gchar     buffer[1024];  /* chars, not pointer to chars! */

	cd = (ConnData *) user_data;
	
	switch (event->type) 
	{
		case GNET_CONN_READ:
			request_got_line (conn, event->buffer, cd);
			break;

		case GNET_CONN_WRITE:
			/* Do nothing */
			break;
		case GNET_CONN_CLOSE:
		case GNET_CONN_TIMEOUT:
		case GNET_CONN_ERROR:
			gnet_conn_delete (conn);
			/* gotta do clean up in here, I think */
			break;
		default:
			g_assert_not_reached ();
	}
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]