*
* Copyright 2025 Adam Masciola
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <libdex.h>
#define bz_maybe(_ptr, _func) ((_ptr) != NULL ? (_func) ((_ptr)) : NULL)
#define bz_maybe_strdup(_ptr) bz_maybe (_ptr, g_strdup)
#define bz_maybe_ref(_ptr, _ref) ((typeof (_ptr)) bz_maybe (_ptr, _ref))
#define bz_object_maybe_ref(_obj) bz_maybe_ref ((_obj), g_object_ref)
#define bz_dex_maybe_ref(_obj) bz_maybe_ref ((_obj), dex_ref)
#define BZ_RELEASE_DATA(name, unref) \
if ((unref) != NULL) \
{ \
g_clear_pointer (&self->name, (unref)); \
}
#define BZ_RELEASE_UTAG(name, remove) \
if ((remove) != NULL) \
{ \
g_clear_handle_id (&self->name, (remove)); \
}
#define BZ_DEFINE_DATA(name, Name, layout, ...) \
typedef struct _##Name##Data Name##Data; \
struct _##Name##Data \
{ \
gatomicrefcount rc; \
struct layout; \
}; \
G_GNUC_UNUSED \
static inline Name##Data * \
name##_data_new (void) \
{ \
Name##Data *data = NULL; \
data = g_new0 (typeof (*data), 1); \
g_atomic_ref_count_init (&data->rc); \
return data; \
} \
G_GNUC_UNUSED \
static inline Name##Data * \
name##_data_ref (gpointer ptr) \
{ \
Name##Data *self = ptr; \
g_atomic_ref_count_inc (&self->rc); \
return self; \
} \
G_GNUC_UNUSED \
static void \
name##_data_deinit (gpointer ptr) \
{ \
Name##Data *self = ptr; \
__VA_ARGS__ \
} \
G_GNUC_UNUSED \
static void \
name##_data_unref (gpointer ptr) \
{ \
Name##Data *self = ptr; \
if (g_atomic_ref_count_dec (&self->rc)) \
{ \
name##_data_deinit (self); \
g_free (self); \
} \
} \
G_GNUC_UNUSED \
static void \
name##_data_unref_closure (gpointer data, \
GClosure *closure) \
{ \
name##_data_unref (data); \
} \
G_DEFINE_AUTOPTR_CLEANUP_FUNC (Name##Data, name##_data_unref);
typedef DexFuture BzGuard;
static inline void
bz_guard_destroy (BzGuard *guard)
{
if (dex_future_is_pending (guard))
dex_promise_resolve_boolean (DEX_PROMISE (guard), TRUE);
dex_unref (guard);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (BzGuard, bz_guard_destroy);
#define bz_clear_guard(_pp) g_clear_pointer (_pp, bz_guard_destroy)
#define BZ_BEGIN_GUARD_WITH_CONTEXT(_guard, _mutex, _gate) \
G_STMT_START \
{ \
g_autoptr (GMutexLocker) _locker = NULL; \
g_autoptr (DexFuture) _wait = NULL; \
\
_locker = g_mutex_locker_new (_mutex); \
if (*(_guard) == NULL) \
*(_guard) = (DexFuture *) dex_promise_new (); \
if (*(_gate) != NULL) \
{ \
if (dex_future_is_pending (*(_gate))) \
_wait = g_steal_pointer (_gate); \
else \
dex_clear (_gate); \
} \
*(_gate) = dex_ref (*(_guard)); \
g_clear_pointer (&_locker, g_mutex_locker_free); \
\
if (_wait != NULL) \
dex_await (g_steal_pointer (&_wait), NULL); \
} \
G_STMT_END
#define BZ_BEGIN_GUARD(_guard) \
G_STMT_START \
{ \
static GMutex _mutex = { 0 }; \
static BzGuard *_gate = NULL; \
BZ_BEGIN_GUARD_WITH_CONTEXT (_guard, &_mutex, &_gate); \
} \
G_STMT_END
typedef struct
{
gatomicrefcount rc;
GWeakRef ref;
} BzWeakRef;
#define BZ_TYPE_WEAK_REF (bz_weak_ref_get_type ())
GType bz_weak_ref_get_type (void);
BzWeakRef *bz_weak_ref_ref (BzWeakRef *wr);
void bz_weak_ref_unref (BzWeakRef *wr);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (BzWeakRef, bz_weak_ref_unref);
BzWeakRef *bz_weak_ref_new (gpointer object);
G_GNUC_UNUSED
static GWeakRef *
bz_track_weak (gpointer object)
{
GWeakRef *wr = NULL;
if (object == NULL)
return NULL;
wr = g_new0 (typeof (*wr), 1);
g_weak_ref_init (wr, object);
return wr;
}
G_GNUC_UNUSED
static void
bz_weak_release (gpointer ptr)
{
GWeakRef *wr = ptr;
g_weak_ref_clear (wr);
g_free (wr);
}
#define bz_weak_get_or_return(self, wr) \
G_STMT_START \
{ \
(self) = g_weak_ref_get (wr); \
if ((self) == NULL) \
return; \
} \
G_STMT_END
#define bz_weak_get_or_return_reject(self, wr) \
G_STMT_START \
{ \
(self) = g_weak_ref_get (wr); \
if ((self) == NULL) \
return dex_future_new_reject ( \
G_IO_ERROR, \
G_IO_ERROR_CANCELLED, \
"Object was discarded"); \
} \
G_STMT_END
G_GNUC_UNUSED
static void
_bz_debug_print_when_disposed_cb (gpointer ptr);
BZ_DEFINE_DATA (
_bz_debug_dispose_cb,
_BzDebugDisposeCb,
{
GType type;
const char *loc;
guint64 time;
},
_bz_debug_print_when_disposed_cb (self);)
G_GNUC_UNUSED
static void
_bz_debug_print_when_disposed_cb (gpointer ptr)
{
_BzDebugDisposeCbData *data = ptr;
g_print ("%zu OBJECT DISPOSE: type %s; from %s at %zu\n",
g_get_monotonic_time (),
g_type_name (data->type),
data->loc,
data->time);
}
#define BZ_DEBUG_PRINT_WHEN_DISPOSED(_object) \
G_STMT_START \
{ \
g_autoptr (_BzDebugDisposeCbData) _data = NULL; \
\
_data = _bz_debug_dispose_cb_data_new (); \
_data->type = G_OBJECT_TYPE (_object); \
_data->loc = G_STRLOC; \
_data->time = g_get_monotonic_time (); \
\
g_object_set_data_full ( \
G_OBJECT (_object), \
"BZ_DEBUG_PRINT_WHEN_DISPOSED", \
_bz_debug_dispose_cb_data_ref (_data), \
_bz_debug_dispose_cb_data_unref); \
} \
G_STMT_END