/*
 * linc.c: This file is part of the linc library.
 *
 * Authors:
 *    Elliot Lee     (sopwith@redhat.com)
 *    Michael Meeks  (michael@ximian.com)
 *    Mark McLouglin (mark@skynet.ie) & others
 *
 * Copyright 2001, Red Hat, Inc., Ximian, Inc.,
 *                 Sun Microsystems, Inc.
 */
#include <signal.h>
#include "linc-debug.h"
#include "linc-private.h"

static gboolean linc_threaded = FALSE;
static gboolean linc_mutex_new_called = FALSE;
GMainLoop      *linc_loop = NULL;
GMainContext   *linc_context = NULL;
static GMutex  *linc_lifecycle_mutex = NULL;

#ifdef LINC_SSL_SUPPORT
SSL_METHOD *linc_ssl_method;
SSL_CTX    *linc_ssl_ctx;
#endif

/**
 * linc_set_threaded:
 * @threaded: whether to do locking
 * 
 *   This routine turns threading on or off for the whole
 * ORB, it should be called (TRUE) if threading is desired
 * before any of the ORB initialization occurs.
 **/
void
linc_set_threaded (gboolean threaded)
{
	if (linc_mutex_new_called)
		g_error ("You need to set this before using the ORB");
	linc_threaded = threaded;
}

/**
 * linc_init:
 * @init_threads: if we want threading enabled.
 * 
 * Initialize linc.
 **/
void
linc_init (gboolean init_threads)
{
	if ((init_threads || linc_threaded) &&
	    !g_thread_supported ())
		g_thread_init (NULL);

	if (!linc_threaded && init_threads)
		linc_threaded = TRUE;

	g_type_init ();

	/*
	 * Linc's raison d'etre is for ORBit2 and Bonobo
	 *
	 * In Bonobo, components and containers must not crash if the
	 * remote end crashes.  If a remote server crashes and then we
	 * try to make a CORBA call on it, we may get a SIGPIPE.  So,
	 * for lack of a better solution, we ignore SIGPIPE here.  This
	 * is open for reconsideration in the future.
	 *
	 * When SIGPIPE is ignored, write() calls which would
	 * ordinarily trigger a signal will instead return -1 and set
	 * errno to EPIPE.  So linc will be able to catch these
	 * errors instead of letting them kill the component.
	 *
	 * Possibilities are the MSG_PEEK trick, where you test if the
	 * connection is dead right before doing the writev().  That
	 * approach has two problems:
	 *
	 *   1. There is the possibility of a race condition, where
	 *      the remote end calls right after the test, and right
	 *      before the writev().
	 * 
	 *   2. An extra system call per write might be regarded by
	 *      some as a performance hit.
	 *
	 * Another possibility is to surround the call to writev() in
	 * linc_connection_writev (linc-connection.c) with something like
	 * this:
	 *
	 *		linc_ignore_sigpipe = 1;
	 *
	 *		result = writev ( ... );
	 *
	 *		linc_ignore_sigpipe = 0;
	 *
	 * The SIGPIPE signal handler will check the global
	 * linc_ignore_sigpipe variable and ignore the signal if it
	 * is 1.  If it is 0, it can proxy to the user's original
	 * signal handler.  This is a real possibility.
	 */
	signal (SIGPIPE, SIG_IGN);
	
	linc_context = g_main_context_new ();
	linc_loop    = g_main_loop_new (linc_context, TRUE);
	
#ifdef LINC_SSL_SUPPORT
	SSLeay_add_ssl_algorithms ();
	linc_ssl_method = SSLv23_method ();
	linc_ssl_ctx = SSL_CTX_new (linc_ssl_method);
#endif

	linc_lifecycle_mutex = linc_mutex_new ();
}

/**
 * linc_main_iteration:
 * @block_for_reply: whether we should wait for a reply
 * 
 * This routine iterates the linc mainloop, which has
 * only the linc sources registered against it.
 **/
void
linc_main_iteration (gboolean block_for_reply)
{
	g_main_context_iteration (
		linc_context, block_for_reply);
}

/**
 * linc_main_pending:
 * 
 * determines if the linc mainloop has any pending work to process.
 * 
 * Return value: TRUE if the linc mainloop has any pending work to process.
 **/
gboolean
linc_main_pending (void)
{
	return g_main_context_pending (linc_context);
}

/**
 * linc_main_loop_run:
 * 
 * Runs the linc mainloop; blocking until the loop is exited.
 **/
void
linc_main_loop_run (void)
{
	g_main_loop_run (linc_loop);
}

/**
 * linc_mutex_new:
 * 
 * Creates a mutes, iff threads are supported, initialized and
 * linc_set_threaded has been called.
 * 
 * Return value: a new GMutex, or NULL if one is not required.
 **/
GMutex *
linc_mutex_new (void)
{
	linc_mutex_new_called = TRUE;

#ifdef G_THREADS_ENABLED
	if (linc_threaded && g_thread_supported ())
		return g_mutex_new ();
#endif

	return NULL;
}

GMutex *
linc_object_get_mutex (void)
{
	return linc_lifecycle_mutex;
}

gpointer
linc_object_ref (GObject *object)
{
	gpointer ret;

	LINC_MUTEX_LOCK   (linc_lifecycle_mutex);

	ret = g_object_ref (object);

	LINC_MUTEX_UNLOCK (linc_lifecycle_mutex);

	return ret;
}

void
linc_object_unref (GObject *object)
{
	gboolean last_ref;

	LINC_MUTEX_LOCK   (linc_lifecycle_mutex);

	if (!(last_ref = (object->ref_count == 1)))
		g_object_unref (object);

	LINC_MUTEX_UNLOCK (linc_lifecycle_mutex);

	if (last_ref) /* take it outside the guard */
		g_object_unref (object);
}

GMainLoop *
linc_main_get_loop (void)
{
	return linc_loop;
}

GMainContext *
linc_main_get_context (void)
{
	return linc_context;
}


syntax highlighted by Code2HTML, v. 0.9.1