<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" xmlns:xi="http://www.w3.org/2003/XInclude" type="guide" style="task" id="custom-gsource.c" xml:lang="cs">
<info>
<link type="guide" xref="c#examples"/>
<credit type="author copyright">
<name>Philip Withnall</name>
<email its:translate="no">philip.withnall@collabora.co.uk</email>
<years>2015</years>
</credit>
<include xmlns="http://www.w3.org/2001/XInclude" href="legal.xml"/>
<desc>Výuka psaní vlastní implementace <code>GSource</code>.</desc>
</info>
<title>Vlastní GSource</title>
<synopsis>
<title>Souhrn</title>
<p>Tento článek je výukou ve vytváření vlastních <code>GSource</code>. Ohledně referenční dokumentace se podívejte do <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource">referenční příručky k API knihovny GLib</link>.</p>
</synopsis>
<section id="what-is-gsource">
<title>Co je to <code>GSource</code>?</title>
<p><link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource"><code>GSource</code></link> je očekávaná událost s přiřazenou funkcí zpětného volání, která bude vyvolána, když je událost přijata. Událostí může být například vypršení času nebo přijetí dat na soketu.</p>
<p>GLib obsahuje různé typy <code>GSource</code>, ale umožňuje také aplikacím definovat si své vlastní, díky čemuž můžete začlenit vlastní události do hlavní smyčky.</p>
<p>Struktura <code>GSource</code> a jeho virtuální funkce jsou podrobně zdokumentovány v <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSourceFuncs">referenční příručce k API GLib</link>.</p>
</section>
<section id="queue-source">
<title>Zdroj „fronta zpráv“</title>
<p>Jak funkční příklad je použit zdroj fronty zpráv, který posílá svá zpětná volání, kdykoliv je do interní fronty ve zdroji zařazena zpráva (teoreticky z jiného vlákna).</p>
<p>Tento typ zdroje je vhodný pro efektivní přenosy velkého množství zpráv mezi hlavními kontexty. Alternativou je přenášet každou zprávu jako samostatný <code>GSource</code> pomocí <code>g_source_attach()</code>. Pro opravdu velké množství zpráv to znamená velký počet přidělování a uvolňování zdrojů <code>GSource</code>.</p>
<section id="gsource-structure">
<title>Struktura</title>
<p>Nejprve potřebujeme deklarovat strukturu pro zdroj. Ta musí obsahovat <code>GSource</code> jako svého rodiče, následovaného soukromými poli pro zdroj: frontou <code>queue</code> a funkcí <code>destroy_message</code>, která se volá, aby po dokončení práce se zprávou, zprávu uvolnila z paměti.</p>
<code mime="text/x-csrc">
typedef struct {
GSource parent;
GAsyncQueue *queue; /* vlastněno */
GDestroyNotify destroy_message;
} MessageQueueSource;</code>
</section>
<section id="prepare-function">
<title>Funkce prepare()</title>
<p>Následně připravíme funkci, které musí být pro zdroj definována. Určuje, jestli je zdroj připraven posílat. Protože tento zdroj používá frontu v paměti, můžeme to určit pomocí kontroly délky fronty. Jestliže jsou ve frontě nějaké prvky, zdroj je může posílat, aby je obsloužil.</p>
<code mime="text/x-csrc">
return (g_async_queue_length (message_queue_source->queue) > 0);</code>
</section>
<section id="check-function">
<title>Funkce check()</title>
<p>Protože tento zdroj nemá žádné popisovače souborů, funkce prepare a check dělají v podstatě tu samou práci, takže funkce check není zapotřebí. Nastavením pole na <code>NULL</code> v <code>GSourceFuncs</code> funkce check pro tento typ zdroje obejdeme.</p>
</section>
<section id="dispatch-function">
<title>Funkce dispatch()</title>
<p>U tohoto zdroje je funkce <code>dispatch</code> místo, kde se nachází to nejsložitější. Musí vyřadit zprávu z fronty, pak tuto zprávu předat do funkce zpětného volání z <code>GSource</code>. Ve frontě nemusí být žádná zpráva: i přesto, že funkce <code>prepare</code> vrátila <code>true</code>, jiný zdroj postavený nad touto frontou mohl být mezitím obeslán a zprávu nakonec z fronty odebrat. Navíc, jestliže nebylo nastaveno žádné zpětné volání pro <code>GSource</code> (což je dovolené), musí být zpráva zlikvidována a tiše zahozena.</p>
<p>Když je nastavena zpráva a zpětné volání, může být zpětné volání vyvoláno zprávou a jeho návratová hodnota je propagována jako návratová hodnota funkce <code>dispatch</code>. Je to <code>FALSE</code> pro zlikvidování <code>GSource</code> a <code>TRUE</code> pro jeho zachování, stejně jako u <code>GSourceFunc</code> — tyto sémantiky jsou stejné u všech implementací funkce <code>dispatch</code>.</p>
<code mime="text/x-csrc">
/* Vytáhne zprávu z fronty */
message = g_async_queue_try_pop (message_queue_source->queue);
/* Když žádná zpráva není, opustíme to */
if (message == NULL)
{
/* Udrží zdroj pro zpracování následující zprávy */
return TRUE;
}
/* @func může být %NULL, pokud nění určeno žádné zpětné volání
* Jestli tomu tak je, zahodí zprávu */
if (func == NULL)
{
if (message_queue_source->destroy_message != NULL)
{
message_queue_source->destroy_message (message);
}
/* Udrží zdroj pro spotřebování následující zprávy */
return TRUE;
}
return func (message, user_data);</code>
</section>
<section id="callback">
<title>Funkce zpětného volání</title>
<p>Zpětné volání z <code>GSource</code> nemusí mít typ <code>GSourceFunc</code>. Může to být jakýkoliv typ funkce, který je volán ve funkci <code>dispatch</code>, pokud je daný typ dostatečně zdokumentován.</p>
<p>Normálně se k nastavení funkce zpětného volání pro instanci zdroje používá <code>g_source_set_callback()</code>. Spolu s <code>GDestroyNotify</code> může být udržován silný odkaz kvůli zachování objektu při životě, dokud je existuje zdroj:</p>
<code mime="text/x-csrc">
g_source_set_callback (source, callback_func,
g_object_ref (object_to_strong_ref),
(GDestroyNotify) g_object_unref);</code>
<p>Nicméně, <code>GSource</code> má nepřímou vrstvu pro získání tohoto zpětného volání, vystavenou jako <code>g_source_set_callback_indirect()</code>. Ta umožňuje u <code>GObject</code> nastavit <code>GClosure</code> jako zpětné volání pro zdroj, která umožňuje zdroje, které jsou automaticky likvidovány, když je objekt finalizován – <em>slabý</em> odkaz, v kontrastu k <em>silnému</em> odkazu výše:</p>
<code mime="text/x-csrc">
g_source_set_closure (source,
g_cclosure_new_object (callback_func,
object_to_weak_ref));</code>
<p>Obecně je možné i uzavřené „fiktivní“ zpětné volání, které může být použito, když je potřeba, aby zdroj existoval, ale není potřeba provést žádnou činnost při jeho zpětném volání:</p>
<code mime="text/x-csrc">
g_source_set_dummy_callback (source);</code>
</section>
<section id="constructor">
<title>Konstruktor</title>
<p>Konečně může být napsána definice <code>GSourceFuncs</code> pro <code>GSource</code>, spolu s funkcí konstruktoru. V praxi je typické vystavit nové typy zdrojů jednoduše jako <code>GSource</code>, ne jako podtyp struktury, takže konstruktor vrací <code>GSource*</code>.</p>
<p>Konstruktor v tomto příkladu rovněž předvádí použití podřízeného zdroje pro podporu pohodlného zrušení. Když je <code>GCancellable</code> zrušen, bude obesláno zpětné volání aplikace a může ověřit zrušení. (Kód aplikace bude muset vytvořit ukazatel na <code>GCancellable</code> dostupný jeho zpětnému volání, v podobě parametru s uživatelskými daty zpětného volání v <code>g_source_set_callback()</code>.)</p>
<code mime="text/x-csrc">
GSource *
message_queue_source_new (GAsyncQueue *queue,
GDestroyNotify destroy_message,
GCancellable *cancellable)
{
GSource *source; /* přezdívka pro @message_queue_source */
MessageQueueSource *message_queue_source; /* přezdívka pro @source */
g_return_val_if_fail (queue != NULL, NULL);
g_return_val_if_fail (cancellable == NULL ||
G_IS_CANCELLABLE (cancellable), NULL);
source = g_source_new (&message_queue_source_funcs,
sizeof (MessageQueueSource));
message_queue_source = (MessageQueueSource *) source;
/* Volající může později přepsat tento název něčím vhodnějším. */
g_source_set_name (source, "MessageQueueSource");
message_queue_source->queue = g_async_queue_ref (queue);
message_queue_source->destroy_message = destroy_message;
/* Přidá zdroj, který lze zrušit. */
if (cancellable != NULL)
{
GSource *cancellable_source;
cancellable_source = g_cancellable_source_new (cancellable);
g_source_set_dummy_callback (cancellable_source);
g_source_add_child_source (source, cancellable_source);
g_source_unref (cancellable_source);
}
return source;
}</code>
</section>
</section>
<section id="full-listing">
<title>Celý příklad</title>
<listing>
<title>Úplný kód příkladu</title>
<code mime="text/x-csrc">/**
* MessageQueueSource:
*
* Jde o #GSource obalující #GAsyncQueue a je poslán kdykoliv je do fronty
* vložena zpráva. Zprávy mohou být vkládány z libovolného vlákna.
*
* Zpětná volání poslaná od #MessageQueueSource mají typ
* #MessageQueueSourceFunc.
*
* #MessageQueueSource podporuje přídávání synovských zdrojů #GCancellable,
* které budou poslány navíc, když je poskytující #GCancellable rušen.
*/
typedef struct {
GSource parent;
GAsyncQueue *queue; /* vlastník */
GDestroyNotify destroy_message;
} MessageQueueSource;
/**
* MessageQueueSourceFunc:
* @message: (úplný přenos) (může být null): zprávy vytažená z fronty
* @user_data: uživatelská data poskytnutá do g_source_set_callback()
*
* Typ funkce zpětného volání pro #MessageQueueSource
*/
typedef gboolean (*MessageQueueSourceFunc) (gpointer message,
gpointer user_data);
static gboolean
message_queue_source_prepare (GSource *source,
gint *timeout_)
{
MessageQueueSource *message_queue_source = (MessageQueueSource *) source;
return (g_async_queue_length (message_queue_source->queue) > 0);
}
static gboolean
message_queue_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MessageQueueSource *message_queue_source = (MessageQueueSource *) source;
gpointer message;
MessageQueueSourceFunc func = (MessageQueueSourceFunc) callback;
/* Vytáhne zprávu z fronty */
message = g_async_queue_try_pop (message_queue_source->queue);
/* Když zde není žádná zpráva, skončit */
if (message == NULL)
{
/* Zachová zdroj pro obsluhu následující zprávy */
return TRUE;
}
/* @func může být %NULL, když nebylo určeno žádné zpětné volání
* Pokud tomu tak je, zahodí zprávu */
if (func == NULL)
{
if (message_queue_source->destroy_message != NULL)
{
message_queue_source->destroy_message (message);
}
/* Zachová zdroj pro zpracování následující zprávy */
return TRUE;
}
return func (message, user_data);
}
static void
message_queue_source_finalize (GSource *source)
{
MessageQueueSource *message_queue_source = (MessageQueueSource *) source;
g_async_queue_unref (message_queue_source->queue);
}
static gboolean
message_queue_source_closure_callback (gpointer message,
gpointer user_data)
{
GClosure *closure = user_data;
GValue param_value = G_VALUE_INIT;
GValue result_value = G_VALUE_INIT;
gboolean retval;
/* Vyvolaná funkce je zodpovědná za uvolnění @message */
g_value_init (&result_value, G_TYPE_BOOLEAN);
g_value_init (&param_value, G_TYPE_POINTER);
g_value_set_pointer (&param_value, message);
g_closure_invoke (closure, &result_value, 1, &param_value, NULL);
retval = g_value_get_boolean (&result_value);
g_value_unset (&param_value);
g_value_unset (&result_value);
return retval;
}
static GSourceFuncs message_queue_source_funcs =
{
message_queue_source_prepare,
NULL, /* check */
message_queue_source_dispatch,
message_queue_source_finalize,
(GSourceFunc) message_queue_source_closure_callback,
NULL,
};
/**
* message_queue_source_new:
* @queue: fronta ke zkontrolování
* @destroy_message: (může být null): funkce pro uvolnění zprávy nebo %NULL
* @cancellable: (může být null): #GCancellable nebo %NULL
*
* Vytvoří nový #MessageQueueSource typu #GSource, který posílá při každé
* zprávě přidané do fronty
*
* Pokud je funkce zpětného volání typu #MessageQueueSourceFunc napojená na
* vrácený #GSource pomocí g_source_set_callback(), bude vyvolána pro každou
* zpráv, přičemž zpráva bude předána jako první argument. Zodpovídá za
* uvolnění zprávy. Pokud žádné zpětné volání není nastaveno, je zpráva
* automaticky uvolněna, stejně jako byla přidána.
*
* Vrací: (úplný přenos): nový #MessageQueueSource
*/
GSource *
message_queue_source_new (GAsyncQueue *queue,
GDestroyNotify destroy_message,
GCancellable *cancellable)
{
GSource *source; /* přezdívka pro @message_queue_source */
MessageQueueSource *message_queue_source; /* přezdívka @source */
g_return_val_if_fail (queue != NULL, NULL);
g_return_val_if_fail (cancellable == NULL ||
G_IS_CANCELLABLE (cancellable), NULL);
source = g_source_new (&message_queue_source_funcs,
sizeof (MessageQueueSource));
message_queue_source = (MessageQueueSource *) source;
/* Volající může přepsat tento název něčím vhodnější pro pozdější použití */
g_source_set_name (source, "MessageQueueSource");
message_queue_source->queue = g_async_queue_ref (queue);
message_queue_source->destroy_message = destroy_message;
/* Přidá zrušitelný zdroj */
if (cancellable != NULL)
{
GSource *cancellable_source;
cancellable_source = g_cancellable_source_new (cancellable);
g_source_set_dummy_callback (cancellable_source);
g_source_add_child_source (source, cancellable_source);
g_source_unref (cancellable_source);
}
return source;
}
</code>
</listing>
</section>
<section id="further-examples">
<title>Další příklady</title>
<p>Zdroje mohou být mnohem složitější, než ve výše uvedeném příkladu. V knihovně <link href="http://nice.freedesktop.org/">libnice</link> je vlastní <code>GSource</code> zapotřebí k dotazování sady soketů, která se dynamicky mění. Implementace je dána jako <code>ComponentSource</code> v <link href="http://cgit.freedesktop.org/libnice/libnice/tree/agent/component.c#n941">component.c</link> a předvádí podstatně složitější použití funkce <code>prepare</code>.</p>
<p>Jiným příkladem je vlastní zdroj k rozhraní GnuTLS s GLib v jeho implementaci <code>GTlsConnection</code>. <link href="https://git.gnome.org/browse/glib-networking/tree/tls/gnutls/gtlsconnection-gnutls.c#n871"><code>GTlsConnectionGnutlsSource</code></link> synchronizuje hlavní vlákno s pracovním vláknem TLS, které provádí blokování operací TLS.</p>
</section>
</page>