/* nonblocking-mutex.c generated by valac 0.56.3, the Vala compiler
 * generated from nonblocking-mutex.vala, do not modify */

/*
 * Copyright 2016 Software Freedom Conservancy Inc.
 * Copyright 2018 Michael Gratton <mike@vee.net>
 *
 * This software is licensed under the GNU Lesser General Public License
 * (version 2.1 or later).  See the COPYING file in this distribution.
 */

#include "geary-engine.h"
#include <glib.h>
#include <gio/gio.h>
#include <stdlib.h>
#include <string.h>

enum  {
	GEARY_NONBLOCKING_MUTEX_0_PROPERTY,
	GEARY_NONBLOCKING_MUTEX_NUM_PROPERTIES
};
static GParamSpec* geary_nonblocking_mutex_properties[GEARY_NONBLOCKING_MUTEX_NUM_PROPERTIES];
#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL)))
#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
typedef struct _GearyNonblockingMutexExecuteLockedData GearyNonblockingMutexExecuteLockedData;
typedef struct _GearyNonblockingMutexClaimAsyncData GearyNonblockingMutexClaimAsyncData;

struct _GearyNonblockingMutexPrivate {
	GearyNonblockingSpinlock* spinlock;
	gboolean locked;
	gint next_token;
	gint locked_token;
};

struct _GearyNonblockingMutexExecuteLockedData {
	int _state_;
	GObject* _source_object_;
	GAsyncResult* _res_;
	GTask* _async_result;
	GearyNonblockingMutex* self;
	GearyNonblockingMutexCriticalSection target;
	gpointer target_target;
	GCancellable* cancellable;
	gint token;
	GError* _inner_error1_;
	GError* err;
	GError* _tmp0_;
	const gchar* _tmp1_;
	GError* _inner_error0_;
};

struct _GearyNonblockingMutexClaimAsyncData {
	int _state_;
	GObject* _source_object_;
	GAsyncResult* _res_;
	GTask* _async_result;
	GearyNonblockingMutex* self;
	GCancellable* cancellable;
	gint result;
	gboolean _tmp0_;
	gboolean _tmp1_;
	gint _tmp2_;
	GearyNonblockingSpinlock* _tmp3_;
	GError* _inner_error0_;
};

static gint GearyNonblockingMutex_private_offset;
static gpointer geary_nonblocking_mutex_parent_class = NULL;

static void geary_nonblocking_mutex_execute_locked_data_free (gpointer _data);
static gboolean geary_nonblocking_mutex_execute_locked_co (GearyNonblockingMutexExecuteLockedData* _data_);
static void geary_nonblocking_mutex_execute_locked_ready (GObject* source_object,
                                                   GAsyncResult* _res_,
                                                   gpointer _user_data_);
static void geary_nonblocking_mutex_claim_async_data_free (gpointer _data);
static gboolean geary_nonblocking_mutex_claim_async_co (GearyNonblockingMutexClaimAsyncData* _data_);
static void geary_nonblocking_mutex_claim_async_ready (GObject* source_object,
                                                GAsyncResult* _res_,
                                                gpointer _user_data_);
static void geary_nonblocking_mutex_finalize (GObject * obj);
static GType geary_nonblocking_mutex_get_type_once (void);

static inline gpointer
geary_nonblocking_mutex_get_instance_private (GearyNonblockingMutex* self)
{
	return G_STRUCT_MEMBER_P (self, GearyNonblockingMutex_private_offset);
}

/**
     * Returns true if the {@link Mutex} has been claimed by a task.
     */
gboolean
geary_nonblocking_mutex_is_locked (GearyNonblockingMutex* self)
{
	gboolean result;
	g_return_val_if_fail (GEARY_NONBLOCKING_IS_MUTEX (self), FALSE);
	result = self->priv->locked;
	return result;
}

static void
geary_nonblocking_mutex_execute_locked_data_free (gpointer _data)
{
	GearyNonblockingMutexExecuteLockedData* _data_;
	_data_ = _data;
	_g_object_unref0 (_data_->cancellable);
	_g_object_unref0 (_data_->self);
	g_slice_free (GearyNonblockingMutexExecuteLockedData, _data_);
}

static gpointer
_g_object_ref0 (gpointer self)
{
	return self ? g_object_ref (self) : NULL;
}

void
geary_nonblocking_mutex_execute_locked (GearyNonblockingMutex* self,
                                        GearyNonblockingMutexCriticalSection target,
                                        gpointer target_target,
                                        GCancellable* cancellable,
                                        GAsyncReadyCallback _callback_,
                                        gpointer _user_data_)
{
	GearyNonblockingMutexExecuteLockedData* _data_;
	GearyNonblockingMutex* _tmp0_;
	GCancellable* _tmp1_;
	g_return_if_fail (GEARY_NONBLOCKING_IS_MUTEX (self));
	g_return_if_fail ((cancellable == NULL) || G_TYPE_CHECK_INSTANCE_TYPE (cancellable, g_cancellable_get_type ()));
	_data_ = g_slice_new0 (GearyNonblockingMutexExecuteLockedData);
	_data_->_async_result = g_task_new (G_OBJECT (self), cancellable, _callback_, _user_data_);
	g_task_set_task_data (_data_->_async_result, _data_, geary_nonblocking_mutex_execute_locked_data_free);
	_tmp0_ = _g_object_ref0 (self);
	_data_->self = _tmp0_;
	_data_->target = target;
	_data_->target_target = target_target;
	_tmp1_ = _g_object_ref0 (cancellable);
	_g_object_unref0 (_data_->cancellable);
	_data_->cancellable = _tmp1_;
	geary_nonblocking_mutex_execute_locked_co (_data_);
}

void
geary_nonblocking_mutex_execute_locked_finish (GearyNonblockingMutex* self,
                                               GAsyncResult* _res_,
                                               GError** error)
{
	GearyNonblockingMutexExecuteLockedData* _data_;
	_data_ = g_task_propagate_pointer (G_TASK (_res_), error);
	if (NULL == _data_) {
		return;
	}
}

/**
     * Executes a critical section while protected by this mutex.
     *
     * This high-level method takes care of claiming, executing, then
     * releasing the mutex, without requiring the caller to manage any
     * this.
     *
     * @throws GLib.IOError.CANCELLED thrown if the caller's
     * cancellable is cancelled before execution is completed
     * @throws GLib.Error if an error occurred during execution of
     * //target//.
     */
static void
geary_nonblocking_mutex_execute_locked_ready (GObject* source_object,
                                              GAsyncResult* _res_,
                                              gpointer _user_data_)
{
	GearyNonblockingMutexExecuteLockedData* _data_;
	_data_ = _user_data_;
	_data_->_source_object_ = source_object;
	_data_->_res_ = _res_;
	geary_nonblocking_mutex_execute_locked_co (_data_);
}

static gboolean
geary_nonblocking_mutex_execute_locked_co (GearyNonblockingMutexExecuteLockedData* _data_)
{
	switch (_data_->_state_) {
		case 0:
		goto _state_0;
		case 1:
		goto _state_1;
		default:
		g_assert_not_reached ();
	}
	_state_0:
	_data_->_state_ = 1;
	geary_nonblocking_mutex_claim_async (_data_->self, _data_->cancellable, geary_nonblocking_mutex_execute_locked_ready, _data_);
	return FALSE;
	_state_1:
	_data_->token = geary_nonblocking_mutex_claim_finish (_data_->self, _data_->_res_, &_data_->_inner_error0_);
	if (G_UNLIKELY (_data_->_inner_error0_ != NULL)) {
		g_task_return_error (_data_->_async_result, _data_->_inner_error0_);
		g_object_unref (_data_->_async_result);
		return FALSE;
	}
	{
		_data_->target (_data_->target_target, &_data_->_inner_error0_);
		if (G_UNLIKELY (_data_->_inner_error0_ != NULL)) {
			goto __finally0;
		}
	}
	__finally0:
	{
		{
			geary_nonblocking_mutex_release (_data_->self, &_data_->token, &_data_->_inner_error1_);
			if (G_UNLIKELY (_data_->_inner_error1_ != NULL)) {
				goto __catch1_g_error;
			}
		}
		goto __finally1;
		__catch1_g_error:
		{
			_data_->err = _data_->_inner_error1_;
			_data_->_inner_error1_ = NULL;
			_data_->_tmp0_ = _data_->err;
			_data_->_tmp1_ = _data_->_tmp0_->message;
			g_debug ("nonblocking-mutex.vala:64: Mutex error releasing token: %s", _data_->_tmp1_);
			_g_error_free0 (_data_->err);
		}
		__finally1:
		if (G_UNLIKELY (_data_->_inner_error1_ != NULL)) {
			g_task_return_error (_data_->_async_result, _data_->_inner_error1_);
			g_object_unref (_data_->_async_result);
			return FALSE;
		}
	}
	if (G_UNLIKELY (_data_->_inner_error0_ != NULL)) {
		g_task_return_error (_data_->_async_result, _data_->_inner_error0_);
		g_object_unref (_data_->_async_result);
		return FALSE;
	}
	g_task_return_pointer (_data_->_async_result, _data_, NULL);
	if (_data_->_state_ != 0) {
		while (!g_task_get_completed (_data_->_async_result)) {
			g_main_context_iteration (g_task_get_context (_data_->_async_result), TRUE);
		}
	}
	g_object_unref (_data_->_async_result);
	return FALSE;
}

static void
geary_nonblocking_mutex_claim_async_data_free (gpointer _data)
{
	GearyNonblockingMutexClaimAsyncData* _data_;
	_data_ = _data;
	_g_object_unref0 (_data_->cancellable);
	_g_object_unref0 (_data_->self);
	g_slice_free (GearyNonblockingMutexClaimAsyncData, _data_);
}

void
geary_nonblocking_mutex_claim_async (GearyNonblockingMutex* self,
                                     GCancellable* cancellable,
                                     GAsyncReadyCallback _callback_,
                                     gpointer _user_data_)
{
	GearyNonblockingMutexClaimAsyncData* _data_;
	GearyNonblockingMutex* _tmp0_;
	GCancellable* _tmp1_;
	g_return_if_fail (GEARY_NONBLOCKING_IS_MUTEX (self));
	g_return_if_fail ((cancellable == NULL) || G_TYPE_CHECK_INSTANCE_TYPE (cancellable, g_cancellable_get_type ()));
	_data_ = g_slice_new0 (GearyNonblockingMutexClaimAsyncData);
	_data_->_async_result = g_task_new (G_OBJECT (self), cancellable, _callback_, _user_data_);
	g_task_set_task_data (_data_->_async_result, _data_, geary_nonblocking_mutex_claim_async_data_free);
	_tmp0_ = _g_object_ref0 (self);
	_data_->self = _tmp0_;
	_tmp1_ = _g_object_ref0 (cancellable);
	_g_object_unref0 (_data_->cancellable);
	_data_->cancellable = _tmp1_;
	geary_nonblocking_mutex_claim_async_co (_data_);
}

gint
geary_nonblocking_mutex_claim_finish (GearyNonblockingMutex* self,
                                      GAsyncResult* _res_,
                                      GError** error)
{
	gint result;
	GearyNonblockingMutexClaimAsyncData* _data_;
	_data_ = g_task_propagate_pointer (G_TASK (_res_), error);
	if (NULL == _data_) {
		gint _tmp0_ = 0;
		return _tmp0_;
	}
	result = _data_->result;
	return result;
}

/**
     * Locks the mutex for execution inside a critical section.
     *
     * If already claimed, this call will block asynchronously waiting
     * for the mutex to be released.
     *
     * @return A token which must be passed to {@link release} when
     * the critical section has completed executing.
     */
static void
geary_nonblocking_mutex_claim_async_ready (GObject* source_object,
                                           GAsyncResult* _res_,
                                           gpointer _user_data_)
{
	GearyNonblockingMutexClaimAsyncData* _data_;
	_data_ = _user_data_;
	_data_->_source_object_ = source_object;
	_data_->_res_ = _res_;
	geary_nonblocking_mutex_claim_async_co (_data_);
}

static gboolean
geary_nonblocking_mutex_claim_async_co (GearyNonblockingMutexClaimAsyncData* _data_)
{
	switch (_data_->_state_) {
		case 0:
		goto _state_0;
		case 1:
		goto _state_1;
		default:
		g_assert_not_reached ();
	}
	_state_0:
	{
		_data_->_tmp0_ = TRUE;
		while (TRUE) {
			if (!_data_->_tmp0_) {
			}
			_data_->_tmp0_ = FALSE;
			if (!_data_->self->priv->locked) {
				_data_->self->priv->locked = TRUE;
				{
					_data_->_tmp1_ = TRUE;
					while (TRUE) {
						if (!_data_->_tmp1_) {
							if (!(_data_->self->priv->locked_token == GEARY_NONBLOCKING_MUTEX_INVALID_TOKEN)) {
								break;
							}
						}
						_data_->_tmp1_ = FALSE;
						_data_->_tmp2_ = _data_->self->priv->next_token;
						_data_->self->priv->next_token = _data_->_tmp2_ + 1;
						_data_->self->priv->locked_token = _data_->_tmp2_;
					}
				}
				_data_->result = _data_->self->priv->locked_token;
				g_task_return_pointer (_data_->_async_result, _data_, NULL);
				if (_data_->_state_ != 0) {
					while (!g_task_get_completed (_data_->_async_result)) {
						g_main_context_iteration (g_task_get_context (_data_->_async_result), TRUE);
					}
				}
				g_object_unref (_data_->_async_result);
				return FALSE;
			}
			_data_->_tmp3_ = _data_->self->priv->spinlock;
			_data_->_state_ = 1;
			geary_nonblocking_lock_wait_async (G_TYPE_CHECK_INSTANCE_CAST (_data_->_tmp3_, GEARY_NONBLOCKING_TYPE_LOCK, GearyNonblockingLock), _data_->cancellable, geary_nonblocking_mutex_claim_async_ready, _data_);
			return FALSE;
			_state_1:
			geary_nonblocking_lock_wait_finish (G_TYPE_CHECK_INSTANCE_CAST (_data_->_tmp3_, GEARY_NONBLOCKING_TYPE_LOCK, GearyNonblockingLock), _data_->_res_, &_data_->_inner_error0_);
			if (G_UNLIKELY (_data_->_inner_error0_ != NULL)) {
				g_task_return_error (_data_->_async_result, _data_->_inner_error0_);
				g_object_unref (_data_->_async_result);
				return FALSE;
			}
		}
	}
}

/**
     * Releases the lock at the end of executing a critical section.
     *
     * The token returned by {@link claim_async} must be supplied as a
     * parameter.  It will be modified by this call so it can't be
     * reused.
     *
     * Throws IOError.INVALID_ARGUMENT if the token was not the one
     * returned by claim_async.
     */
void
geary_nonblocking_mutex_release (GearyNonblockingMutex* self,
                                 gint* token,
                                 GError** error)
{
	gboolean _tmp0_ = FALSE;
	GearyNonblockingSpinlock* _tmp2_;
	GError* _inner_error0_ = NULL;
	g_return_if_fail (GEARY_NONBLOCKING_IS_MUTEX (self));
	if ((*token) != self->priv->locked_token) {
		_tmp0_ = TRUE;
	} else {
		_tmp0_ = (*token) == GEARY_NONBLOCKING_MUTEX_INVALID_TOKEN;
	}
	if (_tmp0_) {
		GError* _tmp1_;
		_tmp1_ = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Token %d is not the lock token", *token);
		_inner_error0_ = _tmp1_;
		g_propagate_error (error, _inner_error0_);
		return;
	}
	self->priv->locked = FALSE;
	*token = GEARY_NONBLOCKING_MUTEX_INVALID_TOKEN;
	self->priv->locked_token = GEARY_NONBLOCKING_MUTEX_INVALID_TOKEN;
	_tmp2_ = self->priv->spinlock;
	geary_nonblocking_lock_notify (G_TYPE_CHECK_INSTANCE_CAST (_tmp2_, GEARY_NONBLOCKING_TYPE_LOCK, GearyNonblockingLock), &_inner_error0_);
	if (G_UNLIKELY (_inner_error0_ != NULL)) {
		g_propagate_error (error, _inner_error0_);
		return;
	}
}

GearyNonblockingMutex*
geary_nonblocking_mutex_construct (GType object_type)
{
	GearyNonblockingMutex * self = NULL;
	self = (GearyNonblockingMutex*) geary_base_object_construct (object_type);
	return self;
}

GearyNonblockingMutex*
geary_nonblocking_mutex_new (void)
{
	return geary_nonblocking_mutex_construct (GEARY_NONBLOCKING_TYPE_MUTEX);
}

static void
geary_nonblocking_mutex_class_init (GearyNonblockingMutexClass * klass,
                                    gpointer klass_data)
{
	geary_nonblocking_mutex_parent_class = g_type_class_peek_parent (klass);
	g_type_class_adjust_private_offset (klass, &GearyNonblockingMutex_private_offset);
	G_OBJECT_CLASS (klass)->finalize = geary_nonblocking_mutex_finalize;
}

static void
geary_nonblocking_mutex_instance_init (GearyNonblockingMutex * self,
                                       gpointer klass)
{
	GearyNonblockingSpinlock* _tmp0_;
	self->priv = geary_nonblocking_mutex_get_instance_private (self);
	_tmp0_ = geary_nonblocking_spinlock_new (NULL);
	self->priv->spinlock = _tmp0_;
	self->priv->locked = FALSE;
	self->priv->next_token = GEARY_NONBLOCKING_MUTEX_INVALID_TOKEN + 1;
	self->priv->locked_token = GEARY_NONBLOCKING_MUTEX_INVALID_TOKEN;
}

static void
geary_nonblocking_mutex_finalize (GObject * obj)
{
	GearyNonblockingMutex * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (obj, GEARY_NONBLOCKING_TYPE_MUTEX, GearyNonblockingMutex);
	_g_object_unref0 (self->priv->spinlock);
	G_OBJECT_CLASS (geary_nonblocking_mutex_parent_class)->finalize (obj);
}

/**
 * A primitive for creating critical sections inside of asynchronous tasks.
 *
 * Two methods can be used for executing code protected by this
 * mutex. The easiest is to create a {@link CriticalSection} delegate
 * and pass it to {@link execute_locked}. This will manage acquiring
 * the lock as needed. The lower-level method is to call {@link
 * claim_async}, execute the critical section, then ensure {@link
 * release} is always called afterwards.
 *
 * This class is ''not'' thread safe and should only be used by
 * asynchronous tasks.
 */
 G_GNUC_NO_INLINE static GType
geary_nonblocking_mutex_get_type_once (void)
{
	static const GTypeInfo g_define_type_info = { sizeof (GearyNonblockingMutexClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) geary_nonblocking_mutex_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (GearyNonblockingMutex), 0, (GInstanceInitFunc) geary_nonblocking_mutex_instance_init, NULL };
	GType geary_nonblocking_mutex_type_id;
	geary_nonblocking_mutex_type_id = g_type_register_static (GEARY_TYPE_BASE_OBJECT, "GearyNonblockingMutex", &g_define_type_info, 0);
	GearyNonblockingMutex_private_offset = g_type_add_instance_private (geary_nonblocking_mutex_type_id, sizeof (GearyNonblockingMutexPrivate));
	return geary_nonblocking_mutex_type_id;
}

GType
geary_nonblocking_mutex_get_type (void)
{
	static gsize geary_nonblocking_mutex_type_id__once = 0;
	if (g_once_init_enter (&geary_nonblocking_mutex_type_id__once)) {
		GType geary_nonblocking_mutex_type_id;
		geary_nonblocking_mutex_type_id = geary_nonblocking_mutex_get_type_once ();
		g_once_init_leave (&geary_nonblocking_mutex_type_id__once, geary_nonblocking_mutex_type_id);
	}
	return geary_nonblocking_mutex_type_id__once;
}

