981 lines
100 KiB
HTML
981 lines
100 KiB
HTML
|
<!doctype html>
|
|||
|
<html class="no-js">
|
|||
|
<head><meta charset="utf-8"/>
|
|||
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|||
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
|||
|
<link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" /><link rel="next" title="Using GLib Lists" href="lists.html" /><link rel="prev" title="Pre- and Post-Conditions" href="pre-and-post-conditions.html" />
|
|||
|
|
|||
|
<meta name="generator" content="sphinx-4.3.0, furo 2022.06.21"/>
|
|||
|
<title>Main Contexts - GNOME Developer Documentation</title>
|
|||
|
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
|||
|
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=40978830699223671f4072448e654b5958f38b89" />
|
|||
|
<link rel="stylesheet" type="text/css" href="../_static/tabs.css" />
|
|||
|
<link rel="stylesheet" type="text/css" href="../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|||
|
<link rel="stylesheet" type="text/css" href="../_static/gnome.css" />
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<style>
|
|||
|
body {
|
|||
|
--color-code-background: #f8f8f8;
|
|||
|
--color-code-foreground: black;
|
|||
|
--color-brand-primary: #4a86cf;
|
|||
|
--color-brand-content: #4a86cf;
|
|||
|
|
|||
|
}
|
|||
|
@media not print {
|
|||
|
body[data-theme="dark"] {
|
|||
|
--color-code-background: #202020;
|
|||
|
--color-code-foreground: #d0d0d0;
|
|||
|
|
|||
|
}
|
|||
|
@media (prefers-color-scheme: dark) {
|
|||
|
body:not([data-theme="light"]) {
|
|||
|
--color-code-background: #202020;
|
|||
|
--color-code-foreground: #d0d0d0;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</style></head>
|
|||
|
<body>
|
|||
|
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|||
|
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|||
|
<title>Contents</title>
|
|||
|
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|||
|
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|||
|
<title>Menu</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|||
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|||
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|||
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|||
|
<title>Expand</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|||
|
<polyline points="9 18 15 12 9 6"></polyline>
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|||
|
<title>Light mode</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|||
|
<circle cx="12" cy="12" r="5"></circle>
|
|||
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|||
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|||
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|||
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|||
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|||
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|||
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|||
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|||
|
<title>Dark mode</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|||
|
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|||
|
<title>Auto light/dark mode</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|||
|
<circle cx="12" cy="12" r="9" />
|
|||
|
<path d="M13 12h5" />
|
|||
|
<path d="M13 15h4" />
|
|||
|
<path d="M13 18h1" />
|
|||
|
<path d="M13 9h4" />
|
|||
|
<path d="M13 6h1" />
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
</svg>
|
|||
|
|
|||
|
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|||
|
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|||
|
<label class="overlay sidebar-overlay" for="__navigation"></label>
|
|||
|
<label class="overlay toc-overlay" for="__toc"></label>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<div class="page">
|
|||
|
<header class="mobile-header">
|
|||
|
<div class="header-left">
|
|||
|
<label class="nav-overlay-icon" for="__navigation">
|
|||
|
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|||
|
</label>
|
|||
|
</div>
|
|||
|
<div class="header-center">
|
|||
|
<a href="../index.html"><div class="brand">GNOME Developer Documentation</div></a>
|
|||
|
</div>
|
|||
|
<div class="header-right">
|
|||
|
<label class="toc-overlay-icon toc-header-icon" for="__toc">
|
|||
|
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|||
|
</label>
|
|||
|
</div>
|
|||
|
</header>
|
|||
|
<aside class="sidebar-drawer">
|
|||
|
<div class="sidebar-container">
|
|||
|
|
|||
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
|
|||
|
|
|||
|
|
|||
|
<span class="sidebar-brand-text">GNOME Developer Documentation</span>
|
|||
|
|
|||
|
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
|
|||
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
|||
|
<input type="hidden" name="check_keywords" value="yes">
|
|||
|
<input type="hidden" name="area" value="default">
|
|||
|
</form>
|
|||
|
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|||
|
<p class="caption" role="heading"><span class="caption-text">Contents</span></p>
|
|||
|
<ul class="current">
|
|||
|
<li class="toctree-l1 has-children"><a class="reference internal" href="../introduction.html">Platform Introduction</a><input class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l2 has-children"><a class="reference internal" href="../introduction/components.html">Platform Components</a><input class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../introduction/overview/libraries.html">Libraries</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../introduction/overview/services.html">Services</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="../introduction/languages.html">Programming Languages</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="../introduction/builder.html">GNOME Builder</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="../introduction/flatpak.html">Flatpak</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l1 has-children"><a class="reference internal" href="../guidelines.html">Guidelines</a><input class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l2 has-children"><a class="reference internal" href="../guidelines/programming.html">Programming Guidelines</a><input class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/programming/coding-style.html">C Coding Style</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/programming/memory-management.html">Managing Memory</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/programming/writing-good-code.html">The Importance of Writing Good Code</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/programming/optimizing.html">Optimizing GNOME Applications</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/programming/namespacing.html">Namespacing</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/programming/introspection.html">Introspection</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l2 has-children"><a class="reference internal" href="../guidelines/accessibility.html">Accessibility</a><input class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/accessibility/coding-guidelines.html">Coding Guidelines for Supporting Accessibility</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/accessibility/custom-widgets.html">Making Custom Components Accessible</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l2 has-children"><a class="reference internal" href="../guidelines/localization.html">Localization</a><input class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/localization/practices.html">Best Practices for Localization</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l2 has-children"><a class="reference internal" href="../guidelines/maintainer.html">Maintainer Guidelines</a><input class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/maintainer/api-stability.html">API Stability</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/maintainer/parallel-installability.html">Parallel Installability</a></li>
|
|||
|
<li class="toctree-l3"><a class="reference internal" href="../guidelines/maintainer/integrating.html">Integrating with GNOME</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="../guidelines/devel-docs.html">Developer Documentation Style Guidelines</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l1 current has-children"><a class="reference internal" href="../tutorials.html">Tutorials</a><input checked="" class="toctree-checkbox" id="toctree-checkbox-8" name="toctree-checkbox-8" role="switch" type="checkbox"/><label for="toctree-checkbox-8"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="current">
|
|||
|
<li class="toctree-l2 has-children"><a class="reference internal" href="beginners.html">Beginners Tutorials</a><input class="toctree-checkbox" id="toctree-checkbox-9" name="toctree-checkbox-9" role="switch" type="checkbox"/><label for="toctree-checkbox-9"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l3 has-children"><a class="reference internal" href="beginners/getting_started.html">Getting Started</a><input class="toctree-checkbox" id="toctree-checkbox-10" name="toctree-checkbox-10" role="switch" type="checkbox"/><label for="toctree-checkbox-10"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/getting_started/content_view.html">Adding A Content View</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/getting_started/opening_files.html">Loading Content From A File</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/getting_started/cursor_position.html">Showing The Cursor Position</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/getting_started/saving_files.html">Saving The Content To A File</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/getting_started/saving_state.html">Saving The Application State</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/getting_started/adding_toasts.html">Notifying The User With Toasts</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/getting_started/dark_mode.html">Forcing The Dark Color Scheme</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l3 has-children"><a class="reference internal" href="beginners/components.html">UI components</a><input class="toctree-checkbox" id="toctree-checkbox-11" name="toctree-checkbox-11" role="switch" type="checkbox"/><label for="toctree-checkbox-11"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/window.html">Windows</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/image.html">Images</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/label.html">Labels</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/button.html">Buttons</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/box.html">Boxes</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/stack.html">Stacks</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/leaflet.html">Leaflets</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/toggle.html">Toggles</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/check_box.html">Check Boxes</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/radio_button.html">Radio Buttons</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/spin_button.html">Spin Buttons</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/link_button.html">Link Buttons</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/switch.html">Switches</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/menu_button.html">Menu Buttons</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/entry.html">Entries</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/password_entry.html">Password Entries</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/message_dialog.html">Messages</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/file_dialog.html">File Dialogs</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/spinner.html">Spinners</a></li>
|
|||
|
<li class="toctree-l4"><a class="reference internal" href="beginners/components/level_bar.html">Level Bars</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="application-id.html">Application ID</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="application.html">Using GtkApplication</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="save-state.html">Saving and Loading Window State</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="notifications.html">Using Notifications</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="themed-icons.html">Themed Icons</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="deprecations.html">Dealing With Deprecations</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="actions.html">Actions</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="menus.html">Menus</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="search-provider.html">Writing a Search Provider</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="pre-and-post-conditions.html">Pre- and Post-Conditions</a></li>
|
|||
|
<li class="toctree-l2 current current-page"><a class="current reference internal" href="#">Main Contexts</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="lists.html">Using GLib Lists</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="threading.html">Threading</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="asynchronous-programming.html">Asynchronous Programming</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="drag-and-drop.html">Drag and Drop</a></li>
|
|||
|
<li class="toctree-l2"><a class="reference internal" href="widget-templates.html">Widget Templates</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
</aside>
|
|||
|
<main class="main">
|
|||
|
<div class="content">
|
|||
|
<article role="main">
|
|||
|
<label class="toc-overlay-icon toc-content-icon" for="__toc">
|
|||
|
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|||
|
</label>
|
|||
|
<section id="main-contexts">
|
|||
|
<h1>Main Contexts<a class="headerlink" href="#main-contexts" title="Permalink to this headline">#</a></h1>
|
|||
|
<section id="summary">
|
|||
|
<h2>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">#</a></h2>
|
|||
|
<ul class="simple">
|
|||
|
<li><p>Use <code class="docutils literal notranslate"><span class="pre">g_main_context_invoke_full()</span></code> to invoke functions in other threads,
|
|||
|
assuming every thread has a thread default main context which runs throughout
|
|||
|
the lifetime of that thread</p></li>
|
|||
|
<li><p>Use <code class="docutils literal notranslate"><span class="pre">GTask</span></code> to run a function in the background without caring about the
|
|||
|
specific thread used</p></li>
|
|||
|
<li><p>Liberally use assertions to check which context executes each function, and
|
|||
|
add these assertions when first writing the code</p></li>
|
|||
|
<li><p>Explicitly document contexts a function is expected to be called in, a
|
|||
|
callback will be invoked in, or a signal will be emitted in</p></li>
|
|||
|
<li><p>Beware of <code class="docutils literal notranslate"><span class="pre">g_idle_add()</span></code> and similar functions which implicitly use the
|
|||
|
global-default main context</p></li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="what-is-gmaincontext">
|
|||
|
<h2>What is GMainContext?<a class="headerlink" href="#what-is-gmaincontext" title="Permalink to this headline">#</a></h2>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is a generalized implementation of an event loop, useful for
|
|||
|
implementing polled file I/O or event-based widget systems (such as GTK). It is
|
|||
|
at the core of almost every GLib application. To understand <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code>
|
|||
|
requires understanding <code class="docutils literal notranslate"><span class="pre">poll()</span></code> and polled I/O.</p>
|
|||
|
<p>A <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> has a set of sources which are ‘attached’ to it, each of
|
|||
|
which can be thought of as an expected event with an associated callback
|
|||
|
function which will be invoked when that event is received; or equivalently as a
|
|||
|
set of file descriptors (FDs) to check. An event could be a timeout or data
|
|||
|
being received on a socket, for example. One iteration of the event loop will:</p>
|
|||
|
<ul class="simple">
|
|||
|
<li><p>Prepare sources, determining if any of them are ready to dispatch immediately.</p></li>
|
|||
|
<li><p>Poll the sources, blocking the current thread until an event is received for
|
|||
|
one of the sources.</p></li>
|
|||
|
<li><p>Check which of the sources received an event (several could have).</p></li>
|
|||
|
<li><p>Dispatch callbacks from those sources.</p></li>
|
|||
|
</ul>
|
|||
|
<p>This is explained very well in the <a class="reference external" href="https://docs.gtk.org/glib/main-loop.html">GLib documentation</a>.</p>
|
|||
|
<p>At its core, <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is just a <code class="docutils literal notranslate"><span class="pre">poll()</span></code> loop, with the preparation,
|
|||
|
check and dispatch stages of the loop corresponding to the normal preamble and
|
|||
|
postamble in a typical <code class="docutils literal notranslate"><span class="pre">poll()</span></code> loop implementation. Typically, some
|
|||
|
complexity is needed in non-trivial <code class="docutils literal notranslate"><span class="pre">poll()</span></code>-using applications to track the
|
|||
|
lists of FDs which are being polled. Additionally, <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> adds a lot
|
|||
|
of useful functionality which vanilla <code class="docutils literal notranslate"><span class="pre">poll()</span></code> doesn’t support. Most
|
|||
|
importantly, it adds thread safety.</p>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is completely thread safe, meaning that a <code class="docutils literal notranslate"><span class="pre">GSource</span></code> can be
|
|||
|
created in one thread and attached to a <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> running in another
|
|||
|
thread. A typical use for this might be to allow worker threads to control which
|
|||
|
sockets are being listened to by a <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> in a central I/O thread.
|
|||
|
Each <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is ‘acquired’ by a thread for each iteration it’s put
|
|||
|
through. Other threads cannot iterate a <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> without acquiring it,
|
|||
|
which guarantees that a <code class="docutils literal notranslate"><span class="pre">GSource</span></code> and its FDs will only be polled by one
|
|||
|
thread at once (since each <code class="docutils literal notranslate"><span class="pre">GSource</span></code> is attached to at most one
|
|||
|
<code class="docutils literal notranslate"><span class="pre">GMainContext</span></code>). A <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> can be swapped between threads across
|
|||
|
iterations, but this is expensive.</p>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is used instead of <code class="docutils literal notranslate"><span class="pre">poll()</span></code> mostly for convenience, as it
|
|||
|
transparently handles dynamically managing the array of FDs to pass to
|
|||
|
<code class="docutils literal notranslate"><span class="pre">poll()</span></code>, especially when operating over multiple threads. This is done by
|
|||
|
encapsulating file descriptors inside a <code class="docutils literal notranslate"><span class="pre">GSource</span></code>, which decide whether those
|
|||
|
FDs should be passed to the <code class="docutils literal notranslate"><span class="pre">poll()</span></code> call on each ‘prepare’ stage of the main
|
|||
|
context iteration.</p>
|
|||
|
</section>
|
|||
|
<section id="what-is-gmainloop">
|
|||
|
<h2>What is GMainLoop?<a class="headerlink" href="#what-is-gmainloop" title="Permalink to this headline">#</a></h2>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">GMainLoop</span></code> is essentially the following few lines of code, once reference
|
|||
|
counting and locking have been removed:</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">loop</span><span class="o">-></span><span class="n">is_running</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TRUE</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="o">-></span><span class="n">is_running</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">quit_condition</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">loop</span><span class="o">-></span><span class="n">is_running</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FALSE</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_main_context_iteration</span><span class="w"> </span><span class="p">(</span><span class="n">context</span><span class="p">,</span><span class="w"> </span><span class="n">TRUE</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Setting <code class="docutils literal notranslate"><span class="pre">quit_condition</span></code> to <code class="docutils literal notranslate"><span class="pre">TRUE</span></code> will cause the loop to terminate once the
|
|||
|
current main context iteration ends.</p>
|
|||
|
<p>Hence, <code class="docutils literal notranslate"><span class="pre">GMainLoop</span></code> is a convenient, thread-safe way of running a
|
|||
|
<code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> to process events until a desired exit condition is met, at
|
|||
|
which point <code class="docutils literal notranslate"><span class="pre">g_main_loop_quit()</span></code> should be called. Typically, in a UI program,
|
|||
|
this will be the user clicking ‘exit’. In a socket handling program, this might
|
|||
|
be the final socket closing.</p>
|
|||
|
<p>It is important not to confuse main contexts with main loops. Main contexts do
|
|||
|
the bulk of the work: preparing source lists, waiting for events, and
|
|||
|
dispatching callbacks. A main loop simply iterates a context.</p>
|
|||
|
</section>
|
|||
|
<section id="default-contexts">
|
|||
|
<h2>Default Contexts<a class="headerlink" href="#default-contexts" title="Permalink to this headline">#</a></h2>
|
|||
|
<p>One of the important features of <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is its support for ‘default’
|
|||
|
contexts. There are two levels of default context: the thread-default, and the
|
|||
|
global-default. The global-default (accessed using <code class="docutils literal notranslate"><span class="pre">g_main_context_default()</span></code>) is
|
|||
|
run by GTK when <code class="docutils literal notranslate"><span class="pre">g_application_run()</span></code> is called. It’s also used for timeouts
|
|||
|
(<code class="docutils literal notranslate"><span class="pre">g_timeout_add()</span></code>) and idle callbacks (<code class="docutils literal notranslate"><span class="pre">g_idle_add()</span></code>) — these won’t be dispatched
|
|||
|
unless the default context is running!</p>
|
|||
|
<p>Thread-default contexts are generally used for I/O operations which need to run
|
|||
|
and dispatch callbacks in a thread. By calling
|
|||
|
<code class="docutils literal notranslate"><span class="pre">g_main_context_push_thread_default()</span></code> before starting an I/O operation, the
|
|||
|
thread-default context is set and the I/O operation can add its sources to that
|
|||
|
context. The context can then be run in a new main loop in an I/O thread,
|
|||
|
causing the callbacks to be dispatched on that thread’s stack rather than on the
|
|||
|
stack of the thread running the global-default main context. This allows I/O
|
|||
|
operations to be run entirely in a separate thread without explicitly passing a
|
|||
|
specific GMainContext pointer around everywhere.</p>
|
|||
|
<p>Conversely, by starting a long-running operation with a specific thread-default
|
|||
|
context set, the calling code can guarantee that the operation’s callbacks will
|
|||
|
be emitted in that context, even if the operation itself runs in a worker
|
|||
|
thread. This is the principle behind <code class="docutils literal notranslate"><span class="pre">GTask</span></code>: when a new <code class="docutils literal notranslate"><span class="pre">GTask</span></code> is created, it
|
|||
|
stores a reference to the current thread-default context, and dispatches its
|
|||
|
completion callback in that context, even if the task itself is run using
|
|||
|
<code class="docutils literal notranslate"><span class="pre">g_task_run_in_thread()</span></code>.</p>
|
|||
|
<p>For example, the code below will run a <code class="docutils literal notranslate"><span class="pre">GTask</span></code> which performs two writes in
|
|||
|
parallel from a thread. The callbacks for the writes will be dispatched in the
|
|||
|
worker thread, whereas the callback from the task as a whole will be dispatched
|
|||
|
in the interesting_context.</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GMainLoop</span><span class="w"> </span><span class="o">*</span><span class="n">main_loop</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">n_remaining</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"> </span><span class="n">WriteData</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* This is always called in the same thread as thread_cb() because</span>
|
|||
|
<span class="cm"> * it’s always dispatched in the @worker_context. */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">write_cb</span><span class="w"> </span><span class="p">(</span><span class="n">GObject</span><span class="w"> </span><span class="o">*</span><span class="n">source_object</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GAsyncResult</span><span class="w"> </span><span class="o">*</span><span class="n">result</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">WriteData</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">user_data</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GOutputStream</span><span class="w"> </span><span class="o">*</span><span class="n">stream</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">G_OUTPUT_STREAM</span><span class="w"> </span><span class="p">(</span><span class="n">source_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GError</span><span class="w"> </span><span class="o">*</span><span class="n">error</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gssize</span><span class="w"> </span><span class="n">len</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Finish the write. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_output_stream_write_finish</span><span class="w"> </span><span class="p">(</span><span class="n">stream</span><span class="p">,</span><span class="w"> </span><span class="n">result</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">error</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">error</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_error</span><span class="w"> </span><span class="p">(</span><span class="s">"Error: %s"</span><span class="p">,</span><span class="w"> </span><span class="n">error</span><span class="o">-></span><span class="n">message</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_error_free</span><span class="w"> </span><span class="p">(</span><span class="n">error</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Check whether all parallel operations have finished. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">write_data</span><span class="o">-></span><span class="n">n_remaining</span><span class="o">--</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">write_data</span><span class="o">-></span><span class="n">n_remaining</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_loop_quit</span><span class="w"> </span><span class="p">(</span><span class="n">write_data</span><span class="o">-></span><span class="n">main_loop</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* This is called in a new thread. */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">thread_cb</span><span class="w"> </span><span class="p">(</span><span class="n">GTask</span><span class="w"> </span><span class="o">*</span><span class="n">task</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">source_object</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">task_data</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GCancellable</span><span class="w"> </span><span class="o">*</span><span class="n">cancellable</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="cm">/* These streams come from somewhere else in the program: */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GOutputStream</span><span class="w"> </span><span class="o">*</span><span class="n">output_stream1</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="n">output_stream</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">worker_context</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GBytes</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">guint8</span><span class="w"> </span><span class="o">*</span><span class="n">buf</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gsize</span><span class="w"> </span><span class="n">len</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Set up a worker context for the writes’ callbacks. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">worker_context</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_main_context_new</span><span class="w"> </span><span class="p">();</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_context_push_thread_default</span><span class="w"> </span><span class="p">(</span><span class="n">worker_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Set up the writes. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">write_data</span><span class="p">.</span><span class="n">n_remaining</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">write_data</span><span class="p">.</span><span class="n">main_loop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_main_loop_new</span><span class="w"> </span><span class="p">(</span><span class="n">worker_context</span><span class="p">,</span><span class="w"> </span><span class="n">FALSE</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_task_get_task_data</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_bytes_get_data</span><span class="w"> </span><span class="p">(</span><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">len</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_output_stream_write_async</span><span class="w"> </span><span class="p">(</span><span class="n">output_stream1</span><span class="p">,</span><span class="w"> </span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="n">len</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">G_PRIORITY_DEFAULT</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">write_cb</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="o">&</span><span class="n">write_data</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_output_stream_write_async</span><span class="w"> </span><span class="p">(</span><span class="n">output_stream2</span><span class="p">,</span><span class="w"> </span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="n">len</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">G_PRIORITY_DEFAULT</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">write_cb</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="o">&</span><span class="n">write_data</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Run the main loop until both writes have finished. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_loop_run</span><span class="w"> </span><span class="p">(</span><span class="n">write_data</span><span class="p">.</span><span class="n">main_loop</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_task_return_boolean</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">,</span><span class="w"> </span><span class="n">TRUE</span><span class="p">);</span><span class="w"> </span><span class="cm">/* ignore errors */</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_main_loop_unref</span><span class="w"> </span><span class="p">(</span><span class="n">write_data</span><span class="p">.</span><span class="n">main_loop</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_main_context_pop_thread_default</span><span class="w"> </span><span class="p">(</span><span class="n">worker_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_context_unref</span><span class="w"> </span><span class="p">(</span><span class="n">worker_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* This can be called from any thread. Its @callback will always be</span>
|
|||
|
<span class="cm"> * dispatched in the thread which currently owns</span>
|
|||
|
<span class="cm"> * @interesting_context. */</span><span class="w"></span>
|
|||
|
<span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">parallel_writes_async</span><span class="w"> </span><span class="p">(</span><span class="n">GBytes</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">interesting_context</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GCancellable</span><span class="w"> </span><span class="o">*</span><span class="n">cancellable</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GAsyncReadyCallback</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GTask</span><span class="w"> </span><span class="o">*</span><span class="n">task</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_main_context_push_thread_default</span><span class="w"> </span><span class="p">(</span><span class="n">interesting_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">task</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_task_new</span><span class="w"> </span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">cancellable</span><span class="p">,</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span><span class="w"> </span><span class="n">user_data</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_task_set_task_data</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">(</span><span class="n">GDestroyNotify</span><span class="p">)</span><span class="w"> </span><span class="n">g_bytes_unref</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_task_run_in_thread</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">,</span><span class="w"> </span><span class="n">thread_cb</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_object_unref</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_main_context_pop_thread_default</span><span class="w"> </span><span class="p">(</span><span class="n">interesting_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
<section id="implicit-use-of-the-global-default-main-context">
|
|||
|
<h2>Implicit Use of the Global-Default Main Context<a class="headerlink" href="#implicit-use-of-the-global-default-main-context" title="Permalink to this headline">#</a></h2>
|
|||
|
<p>Several functions implicitly add sources to the global-default main context.
|
|||
|
They should not be used in threaded code. Instead, use <code class="docutils literal notranslate"><span class="pre">g_source_attach()</span></code>
|
|||
|
with the <code class="docutils literal notranslate"><span class="pre">GSource</span></code> created by the replacement function from the table below.</p>
|
|||
|
<p>Implicit use of the global-default main context means the callback functions are
|
|||
|
invoked in the main thread, typically resulting in work being brought back from
|
|||
|
a worker thread into the main thread.</p>
|
|||
|
<div class="table-wrapper colwidths-given docutils container">
|
|||
|
<table class="colwidths-given docutils align-default">
|
|||
|
<colgroup>
|
|||
|
<col style="width: 50%" />
|
|||
|
<col style="width: 50%" />
|
|||
|
</colgroup>
|
|||
|
<thead>
|
|||
|
<tr class="row-odd"><th class="head"><p>Do not use</p></th>
|
|||
|
<th class="head"><p>Use instead</p></th>
|
|||
|
</tr>
|
|||
|
</thead>
|
|||
|
<tbody>
|
|||
|
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">g_timeout_add()</span></code></p></td>
|
|||
|
<td><p><code class="docutils literal notranslate"><span class="pre">g_timeout_source_new()</span></code></p></td>
|
|||
|
</tr>
|
|||
|
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">g_idle_add()</span></code></p></td>
|
|||
|
<td><p><code class="docutils literal notranslate"><span class="pre">g_idle_source_new()</span></code></p></td>
|
|||
|
</tr>
|
|||
|
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">g_child_watch_add()</span></code></p></td>
|
|||
|
<td><p><code class="docutils literal notranslate"><span class="pre">g_child_watch_source_new()</span></code></p></td>
|
|||
|
</tr>
|
|||
|
</tbody>
|
|||
|
</table>
|
|||
|
</div>
|
|||
|
<p>So to delay some computation in a worker thread, use the following code:</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">guint</span><span class="w"></span>
|
|||
|
<span class="nf">schedule_computation</span><span class="w"> </span><span class="p">(</span><span class="n">guint</span><span class="w"> </span><span class="n">delay_seconds</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="cm">/* Get the calling context. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">context</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_main_context_get_thread_default</span><span class="w"> </span><span class="p">();</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">GSource</span><span class="w"> </span><span class="o">*</span><span class="n">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_timeout_source_new_seconds</span><span class="w"> </span><span class="p">(</span><span class="n">delay_seconds</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_source_set_callback</span><span class="w"> </span><span class="p">(</span><span class="n">source</span><span class="p">,</span><span class="w"> </span><span class="n">do_computation</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_source_attach</span><span class="w"> </span><span class="p">(</span><span class="n">source</span><span class="p">,</span><span class="w"> </span><span class="n">context</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_source_unref</span><span class="w"> </span><span class="p">(</span><span class="n">source</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* The ID can be used with the same @context to</span>
|
|||
|
<span class="cm"> * cancel the scheduled computation if needed. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">id</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">do_computation</span><span class="w"> </span><span class="p">(</span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="c1">// ...</span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
<section id="using-gmaincontext-in-a-library">
|
|||
|
<h2>Using GMainContext in a Library<a class="headerlink" href="#using-gmaincontext-in-a-library" title="Permalink to this headline">#</a></h2>
|
|||
|
<p>At a high level, library code must not make changes to main contexts which could
|
|||
|
affect the execution of an application using the library, for example by
|
|||
|
changing when the application’s sources are dispatched. There are various best
|
|||
|
practices which can be followed to aid this.</p>
|
|||
|
<p>Never iterate a context created outside the library, including the
|
|||
|
global-default or thread-default contexts. Otherwise, sources created in the
|
|||
|
application may be dispatched when the application is not expecting it, causing
|
|||
|
re-entrancy problems for the application code.</p>
|
|||
|
<p>Always remove sources from a main context before dropping the library’s last
|
|||
|
reference to the context, especially if it may have been exposed to the
|
|||
|
application (for example, as a thread-default). Otherwise the application may
|
|||
|
keep a reference to the main context and continue iterating it after the library
|
|||
|
has returned, potentially causing unexpected source dispatches in the library.
|
|||
|
This is equivalent to not assuming that dropping the library’s last reference to
|
|||
|
a main context will finalize that context.</p>
|
|||
|
<p>If the library is designed to be used from multiple threads, or in a
|
|||
|
context-aware fashion, always document which context each callback will be
|
|||
|
dispatched in. For example, “callbacks will always be dispatched in the context
|
|||
|
which is the thread-default at the time of the object’s construction”.
|
|||
|
Developers using the library’s API need to know this information.</p>
|
|||
|
<p>Use <code class="docutils literal notranslate"><span class="pre">g_main_context_invoke()</span></code> to ensure callbacks are dispatched in the right
|
|||
|
context. It’s much easier than manually using <code class="docutils literal notranslate"><span class="pre">g_idle_source_new()</span></code> to
|
|||
|
transfer work between contexts.</p>
|
|||
|
<p>Libraries should never use <code class="docutils literal notranslate"><span class="pre">g_main_context_default()</span></code> (or, equivalently, pass
|
|||
|
NULL to a <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code>-typed parameter). Always store and explicitly use a
|
|||
|
specific <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code>, even if it often points to some default context. This
|
|||
|
makes the code easier to split out into threads in future, if needed, without
|
|||
|
causing hard-to-debug problems caused by callbacks being invoked in the wrong
|
|||
|
context.</p>
|
|||
|
<p>Write things asynchronously internally (using <code class="docutils literal notranslate"><span class="pre">GTask</span></code> where appropriate), and
|
|||
|
keep synchronous wrappers at the very top level of an API, where they can be
|
|||
|
implemented by calling <code class="docutils literal notranslate"><span class="pre">g_main_context_iteration()</span></code> on a specific
|
|||
|
<code class="docutils literal notranslate"><span class="pre">GMainContext</span></code>. Again, this makes future refactoring easier. This is
|
|||
|
demonstrated in the previous example: the thread uses
|
|||
|
<code class="docutils literal notranslate"><span class="pre">g_output_stream_write_async()</span></code> rather than <code class="docutils literal notranslate"><span class="pre">g_output_stream_write()</span></code>. A
|
|||
|
worker thread may be used instead, and this can simplify the callback chain for
|
|||
|
long series of asynchronous calls; but at the cost of increased complexity in
|
|||
|
verifying the code is race-free.</p>
|
|||
|
<p>Always match pushes and pops of the thread-default main context:
|
|||
|
<code class="docutils literal notranslate"><span class="pre">g_main_context_push_thread_default()</span></code> and
|
|||
|
<code class="docutils literal notranslate"><span class="pre">g_main_context_pop_thread_default()</span></code>.</p>
|
|||
|
</section>
|
|||
|
<section id="ensuring-functions-are-called-in-the-right-context">
|
|||
|
<h2>Ensuring Functions are Called in the Right Context<a class="headerlink" href="#ensuring-functions-are-called-in-the-right-context" title="Permalink to this headline">#</a></h2>
|
|||
|
<p>The ‘right context’ is the thread-default main context of the thread the
|
|||
|
function should be executing in. This assumes the typical case that every thread
|
|||
|
has a single main context running in a main loop. A main context effectively
|
|||
|
provides a work or message queue for the thread — something which the thread can
|
|||
|
periodically check to determine if there is work pending from another thread.
|
|||
|
Putting a message on this queue – invoking a function in another main context –
|
|||
|
will result in it eventually being dispatched in that thread.</p>
|
|||
|
<p>For example, if an application does a long and CPU-intensive computation it
|
|||
|
should schedule this in a background thread so that UI updates in the main
|
|||
|
thread are not blocked. The results of the computation, however, might need to
|
|||
|
be displayed in the UI, so some UI update function must be called in the main
|
|||
|
thread once the computation’s complete.</p>
|
|||
|
<p>Furthermore, if the computation function can be limited to a single thread, it
|
|||
|
becomes easy to eliminate the need for locking a lot of the data it accesses.
|
|||
|
This assumes that other threads are implemented similarly and hence most data is
|
|||
|
only accessed by a single thread, with threads communicating by message passing.
|
|||
|
This allows each thread to update its data at its leisure, which significantly
|
|||
|
simplifies locking.</p>
|
|||
|
<p>For some functions, there might be no reason to care which context they’re
|
|||
|
executed in, perhaps because they’re asynchronous and hence do not block the
|
|||
|
context. However, it is still advisable to be explicit about which context is
|
|||
|
used, since those functions may emit signals or invoke callbacks, and for
|
|||
|
reasons of thread safety it’s necessary to know which threads those signal
|
|||
|
handlers or callbacks are going to be invoked in.</p>
|
|||
|
<p>For example, the progress callback in <code class="docutils literal notranslate"><span class="pre">g_file_copy_async()</span></code> is documented as
|
|||
|
being called in the thread-default main context at the time of the initial call.</p>
|
|||
|
</section>
|
|||
|
<section id="principles-of-invocation">
|
|||
|
<h2>Principles of Invocation<a class="headerlink" href="#principles-of-invocation" title="Permalink to this headline">#</a></h2>
|
|||
|
<p>The core principle of invoking a function in a specific context is simple, and
|
|||
|
is walked through below to explain the concepts. In practice the convenience
|
|||
|
method, <code class="docutils literal notranslate"><span class="pre">g_main_context_invoke_full()</span></code> should be used instead.</p>
|
|||
|
<p>A <code class="docutils literal notranslate"><span class="pre">GSource</span></code> has to be added to the target <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code>, which will invoke
|
|||
|
the function when it’s dispatched. This <code class="docutils literal notranslate"><span class="pre">GSource</span></code> should almost always be an
|
|||
|
idle source created with <code class="docutils literal notranslate"><span class="pre">g_idle_source_new()</span></code>, but this doesn’t have to be
|
|||
|
the case. It could be a timeout source so that the function is executed after a
|
|||
|
delay, for example.</p>
|
|||
|
<p>The <code class="docutils literal notranslate"><span class="pre">GSource</span></code> will be dispatched as soon as it’s ready, calling the function
|
|||
|
on the thread’s stack. In the case of an idle source, this will be as soon as
|
|||
|
all sources at a higher priority have been dispatched — this can be tweaked
|
|||
|
using the idle source’s priority parameter with <code class="docutils literal notranslate"><span class="pre">g_source_set_priority()</span></code>. The
|
|||
|
source will typically then be destroyed so the function is only executed once
|
|||
|
(though again, this doesn’t have to be the case).</p>
|
|||
|
<p>Data can be passed between threads as the <code class="docutils literal notranslate"><span class="pre">user_data</span></code> passed to the
|
|||
|
<code class="docutils literal notranslate"><span class="pre">GSource</span></code>’s callback. This is set on the source using
|
|||
|
<code class="docutils literal notranslate"><span class="pre">g_source_set_callback()</span></code>, along with the callback function to invoke. Only a
|
|||
|
single pointer is provided, so if multiple data fields need passing, they must
|
|||
|
be wrapped in an allocated structure.</p>
|
|||
|
<p>The example below demonstrates the underlying principles, but there are
|
|||
|
convenience methods explained below which simplify things.</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/* Main function for the background thread, thread1. */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="n">gpointer</span><span class="w"></span>
|
|||
|
<span class="nf">thread1_main</span><span class="w"> </span><span class="p">(</span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">thread1_main_context</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">user_data</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GMainLoop</span><span class="w"> </span><span class="o">*</span><span class="n">main_loop</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Set up the thread’s context and run it forever. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_context_push_thread_default</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">main_loop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_main_loop_new</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">,</span><span class="w"> </span><span class="n">FALSE</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_loop_run</span><span class="w"> </span><span class="p">(</span><span class="n">main_loop</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_loop_unref</span><span class="w"> </span><span class="p">(</span><span class="n">main_loop</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_main_context_pop_thread_default</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_context_unref</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* A data closure structure to carry multiple variables between</span>
|
|||
|
<span class="cm"> * threads. */</span><span class="w"></span>
|
|||
|
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gchar</span><span class="w"> </span><span class="o">*</span><span class="n">some_string</span><span class="p">;</span><span class="w"> </span><span class="cm">/* owned */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">some_int</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GObject</span><span class="w"> </span><span class="o">*</span><span class="n">some_object</span><span class="p">;</span><span class="w"> </span><span class="cm">/* owned */</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"> </span><span class="n">MyFuncData</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">my_func_data_free</span><span class="w"> </span><span class="p">(</span><span class="n">MyFuncData</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">data</span><span class="o">-></span><span class="n">some_string</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_clear_object</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">data</span><span class="o">-></span><span class="n">some_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">data</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">my_func</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="n">gchar</span><span class="w"> </span><span class="o">*</span><span class="n">some_string</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">some_int</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GObject</span><span class="w"> </span><span class="o">*</span><span class="n">some_object</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="cm">/* Do something long and CPU intensive! */</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* Convert an idle callback into a call to my_func(). */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="n">gboolean</span><span class="w"></span>
|
|||
|
<span class="nf">my_func_idle</span><span class="w"> </span><span class="p">(</span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">MyFuncData</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">user_data</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">my_func</span><span class="w"> </span><span class="p">(</span><span class="n">data</span><span class="o">-></span><span class="n">some_string</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_int</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">G_SOURCE_REMOVE</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* Function to be called in the main thread to schedule a call to</span>
|
|||
|
<span class="cm"> * my_func() in thread1, passing the given parameters along. */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">invoke_my_func</span><span class="w"> </span><span class="p">(</span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">thread1_main_context</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">gchar</span><span class="w"> </span><span class="o">*</span><span class="n">some_string</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">some_int</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GObject</span><span class="w"> </span><span class="o">*</span><span class="n">some_object</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GSource</span><span class="w"> </span><span class="o">*</span><span class="n">idle_source</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">MyFuncData</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Create a data closure to pass all the desired variables</span>
|
|||
|
<span class="cm"> * between threads. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_new0</span><span class="w"> </span><span class="p">(</span><span class="n">MyFuncData</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_strdup</span><span class="w"> </span><span class="p">(</span><span class="n">some_string</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">some_int</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_object_ref</span><span class="w"> </span><span class="p">(</span><span class="n">some_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Create a new idle source, set my_func() as the callback with</span>
|
|||
|
<span class="cm"> * some data to be passed between threads, bump up the priority</span>
|
|||
|
<span class="cm"> * and schedule it by attaching it to thread1’s context. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">idle_source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_idle_source_new</span><span class="w"> </span><span class="p">();</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_source_set_callback</span><span class="w"> </span><span class="p">(</span><span class="n">idle_source</span><span class="p">,</span><span class="w"> </span><span class="n">my_func_idle</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">(</span><span class="n">GDestroyNotify</span><span class="p">)</span><span class="w"> </span><span class="n">my_func_data_free</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_source_set_priority</span><span class="w"> </span><span class="p">(</span><span class="n">idle_source</span><span class="p">,</span><span class="w"> </span><span class="n">G_PRIORITY_DEFAULT</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_source_attach</span><span class="w"> </span><span class="p">(</span><span class="n">idle_source</span><span class="p">,</span><span class="w"> </span><span class="n">thread1_main_context</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_source_unref</span><span class="w"> </span><span class="p">(</span><span class="n">idle_source</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* Main function for the main thread. */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">main</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GThread</span><span class="w"> </span><span class="o">*</span><span class="n">thread1</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">thread1_main_context</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Spawn a background thread and pass it a reference to its</span>
|
|||
|
<span class="cm"> * GMainContext. Retain a reference for use in this thread</span>
|
|||
|
<span class="cm"> * too. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">thread1_main_context</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_main_context_new</span><span class="w"> </span><span class="p">();</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_thread_new</span><span class="w"> </span><span class="p">(</span><span class="s">"thread1"</span><span class="p">,</span><span class="w"> </span><span class="n">thread1_main</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_context_ref</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">));</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Maybe set up your UI here, for example. */</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Invoke my_func() in the other thread. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">invoke_my_func</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="s">"some data which needs passing between threads"</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="mi">123456</span><span class="p">,</span><span class="w"> </span><span class="n">some_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Continue doing other work. */</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This invocation is uni-directional: it calls <code class="docutils literal notranslate"><span class="pre">my_func()</span></code> in <code class="docutils literal notranslate"><span class="pre">thread1</span></code>, but
|
|||
|
there’s no way to return a value to the main thread. To do that, the same
|
|||
|
principle needs to be used again, invoking a callback function in the main
|
|||
|
thread. It’s a straightforward extension which isn’t covered here.</p>
|
|||
|
<p>To maintain thread safety, data which is potentially accessed by multiple
|
|||
|
threads must make those accesses mutually exclusive using a mutex. Data
|
|||
|
potentially accessed by multiple threads: <code class="docutils literal notranslate"><span class="pre">thread1_main_context</span></code>, passed in
|
|||
|
the fork call to <code class="docutils literal notranslate"><span class="pre">thread1_main</span></code>; and <code class="docutils literal notranslate"><span class="pre">some_object</span></code>, a reference to which is
|
|||
|
passed in the data closure. Critically, GLib guarantees that <code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is
|
|||
|
thread safe, so sharing <code class="docutils literal notranslate"><span class="pre">thread1_main_context</span></code> between threads is safe. The
|
|||
|
example assumes that other code accessing <code class="docutils literal notranslate"><span class="pre">some_object</span></code> is thread safe.</p>
|
|||
|
<p>Note that <code class="docutils literal notranslate"><span class="pre">some_string</span></code> and <code class="docutils literal notranslate"><span class="pre">some_int</span></code> cannot be accessed from both threads,
|
|||
|
because copies of them are passed to <code class="docutils literal notranslate"><span class="pre">thread1</span></code>, rather than the originals.
|
|||
|
This is a standard technique for making cross-thread calls thread safe without
|
|||
|
requiring locking. It also avoids the problem of synchronizing freeing
|
|||
|
<code class="docutils literal notranslate"><span class="pre">some_string</span></code>.</p>
|
|||
|
<p>Similarly, a reference to <code class="docutils literal notranslate"><span class="pre">some_object</span></code> is transferred to <code class="docutils literal notranslate"><span class="pre">thread1</span></code>, which
|
|||
|
works around the issue of synchronizing destruction of the object.</p>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">g_idle_source_new()</span></code> is used rather than the simpler <code class="docutils literal notranslate"><span class="pre">g_idle_add()</span></code> so the
|
|||
|
<code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> to attach to can be specified.</p>
|
|||
|
</section>
|
|||
|
<section id="convenience-method-g-main-context-invoke-full">
|
|||
|
<h2>Convenience Method: g_main_context_invoke_full()<a class="headerlink" href="#convenience-method-g-main-context-invoke-full" title="Permalink to this headline">#</a></h2>
|
|||
|
<p>This is simplified greatly by the convenience method,
|
|||
|
<code class="docutils literal notranslate"><span class="pre">g_main_context_invoke_full()</span></code>. It invokes a callback so that the specified
|
|||
|
<code class="docutils literal notranslate"><span class="pre">GMainContext</span></code> is owned during the invocation. Owning a main context is almost
|
|||
|
always equivalent to running it, and hence the function is invoked in the thread
|
|||
|
for which the specified context is the thread-default.</p>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">g_main_context_invoke()</span></code> can be used instead if the user data does not need
|
|||
|
to be freed by a <code class="docutils literal notranslate"><span class="pre">GDestroyNotify</span></code> callback after the invocation returns.</p>
|
|||
|
<p>Modifying the earlier example, the <code class="docutils literal notranslate"><span class="pre">invoke_my_func()</span></code> function can be replaced
|
|||
|
by the following:</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">invoke_my_func</span><span class="w"> </span><span class="p">(</span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">thread1_main_context</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">gchar</span><span class="w"> </span><span class="o">*</span><span class="n">some_string</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">some_int</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GObject</span><span class="w"> </span><span class="o">*</span><span class="n">some_object</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">MyFuncData</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Create a data closure to pass all the desired variables</span>
|
|||
|
<span class="cm"> * between threads. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_new0</span><span class="w"> </span><span class="p">(</span><span class="n">MyFuncData</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_strdup</span><span class="w"> </span><span class="p">(</span><span class="n">some_string</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">some_int</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_object_ref</span><span class="w"> </span><span class="p">(</span><span class="n">some_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Invoke the function. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_context_invoke_full</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">G_PRIORITY_DEFAULT</span><span class="p">,</span><span class="w"> </span><span class="n">my_func_idle</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">(</span><span class="n">GDestroyNotify</span><span class="p">)</span><span class="w"> </span><span class="n">my_func_data_free</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Consider what happens if <code class="docutils literal notranslate"><span class="pre">invoke_my_func()</span></code> were called from <code class="docutils literal notranslate"><span class="pre">thread1</span></code>,
|
|||
|
rather than from the main thread. With the original implementation, the idle
|
|||
|
source would be added to <code class="docutils literal notranslate"><span class="pre">thread1</span></code>’s context and dispatched on the context’s
|
|||
|
next iteration (assuming no pending dispatches with higher priorities). With the
|
|||
|
improved implementation, <code class="docutils literal notranslate"><span class="pre">g_main_context_invoke_full()</span></code> will notice that the
|
|||
|
specified context is already owned by the thread (or ownership can be acquired
|
|||
|
by it), and will call <code class="docutils literal notranslate"><span class="pre">my_func_idle()</span></code> directly, rather than attaching a
|
|||
|
source to the context and delaying the invocation to the next context iteration.</p>
|
|||
|
<p>This subtle behavior difference doesn’t matter in most cases, but is worth
|
|||
|
bearing in mind since it can affect blocking behavior (<code class="docutils literal notranslate"><span class="pre">invoke_my_func()</span></code>
|
|||
|
would go from taking negligible time, to taking the same amount of time as
|
|||
|
<code class="docutils literal notranslate"><span class="pre">my_func()</span></code> before returning).</p>
|
|||
|
</section>
|
|||
|
<section id="checking-threading">
|
|||
|
<h2>Checking Threading<a class="headerlink" href="#checking-threading" title="Permalink to this headline">#</a></h2>
|
|||
|
<p>It is useful to document which thread each function should be called in, in the form of an assertion:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g_assert</span> <span class="p">(</span><span class="n">g_main_context_is_owner</span> <span class="p">(</span><span class="n">expected_main_context</span><span class="p">));</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>If that’s put at the top of each function, any assertion failure will highlight
|
|||
|
a case where a function has been called from the wrong thread. It is much easier
|
|||
|
to write these assertions when initially developing code, rather than debugging
|
|||
|
race conditions which can easily result from a function being called in the
|
|||
|
wrong thread.</p>
|
|||
|
<p>This technique can also be applied to signal emissions and callbacks, improving
|
|||
|
type safety as well as asserting the right context is used. Note that signal
|
|||
|
emission via <code class="docutils literal notranslate"><span class="pre">g_signal_emit()</span></code> is synchronous, and doesn’t involve a main
|
|||
|
context at all.</p>
|
|||
|
<p>For example, instead of using the following when emitting a signal:</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">guint</span><span class="w"> </span><span class="n">param1</span><span class="p">;</span><span class="w"> </span><span class="cm">/* arbitrary example parameters */</span><span class="w"></span>
|
|||
|
<span class="n">gchar</span><span class="w"> </span><span class="o">*</span><span class="n">param2</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="n">guint</span><span class="w"> </span><span class="n">retval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="n">g_signal_emit_by_name</span><span class="w"> </span><span class="p">(</span><span class="n">my_object</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="s">"some-signal"</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">param1</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">param2</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="o">&</span><span class="n">retval</span><span class="p">);</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The following can be used:</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">guint</span><span class="w"></span>
|
|||
|
<span class="nf">emit_some_signal</span><span class="w"> </span><span class="p">(</span><span class="n">GObject</span><span class="w"> </span><span class="o">*</span><span class="n">my_object</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">param1</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">gchar</span><span class="w"> </span><span class="o">*</span><span class="n">param2</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">retval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_assert</span><span class="w"> </span><span class="p">(</span><span class="n">g_main_context_is_owner</span><span class="w"> </span><span class="p">(</span><span class="n">expected_main_context</span><span class="p">));</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="n">g_signal_emit_by_name</span><span class="w"> </span><span class="p">(</span><span class="n">my_object</span><span class="p">,</span><span class="w"> </span><span class="s">"some-signal"</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">param1</span><span class="p">,</span><span class="w"> </span><span class="n">param2</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">retval</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">retval</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
<section id="gtask">
|
|||
|
<h2>GTask<a class="headerlink" href="#gtask" title="Permalink to this headline">#</a></h2>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">GTask</span></code> provides a slightly different approach to invoking functions in other
|
|||
|
threads, which is more suited to the case where a function should be executed in
|
|||
|
some background thread, but not a specific one.</p>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">GTask</span></code> takes a data closure and a function to execute, and provides ways to
|
|||
|
return the result from this function. It handles everything necessary to run
|
|||
|
that function in an arbitrary thread belonging to some thread pool internal to
|
|||
|
GLib.</p>
|
|||
|
<p>By combining <code class="docutils literal notranslate"><span class="pre">g_main_context_invoke_full()</span></code> and <code class="docutils literal notranslate"><span class="pre">GTask</span></code>, it is possible to
|
|||
|
run a task in a specific context and effortlessly return its result to the
|
|||
|
current context:</p>
|
|||
|
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/* This will be invoked in thread1. */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="n">gboolean</span><span class="w"></span>
|
|||
|
<span class="nf">my_func_idle</span><span class="w"> </span><span class="p">(</span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GTask</span><span class="w"> </span><span class="o">*</span><span class="n">task</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">G_TASK</span><span class="w"> </span><span class="p">(</span><span class="n">user_data</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">MyFuncData</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gboolean</span><span class="w"> </span><span class="n">retval</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Call my_func() and propagate its returned boolean to</span>
|
|||
|
<span class="cm"> * the main thread. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_task_get_task_data</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">retval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">my_func</span><span class="w"> </span><span class="p">(</span><span class="n">data</span><span class="o">-></span><span class="n">some_string</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_int</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_task_return_boolean</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">,</span><span class="w"> </span><span class="n">retval</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">G_SOURCE_REMOVE</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="cm">/* Whichever thread this is invoked in, the @callback will be</span>
|
|||
|
<span class="cm"> * invoked in, once my_func() has finished and returned a result. */</span><span class="w"></span>
|
|||
|
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"></span>
|
|||
|
<span class="nf">invoke_my_func_with_result</span><span class="w"> </span><span class="p">(</span><span class="n">GMainContext</span><span class="w"> </span><span class="o">*</span><span class="n">thread1_main_context</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">gchar</span><span class="w"> </span><span class="o">*</span><span class="n">some_string</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">some_int</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GObject</span><span class="w"> </span><span class="o">*</span><span class="n">some_object</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">GAsyncReadyCallback</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">)</span><span class="w"></span>
|
|||
|
<span class="p">{</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">MyFuncData</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">;</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Create a data closure to pass all the desired variables</span>
|
|||
|
<span class="cm"> * between threads. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_new0</span><span class="w"> </span><span class="p">(</span><span class="n">MyFuncData</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_strdup</span><span class="w"> </span><span class="p">(</span><span class="n">some_string</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">some_int</span><span class="p">;</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">data</span><span class="o">-></span><span class="n">some_object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_object_ref</span><span class="w"> </span><span class="p">(</span><span class="n">some_object</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Create a GTask to handle returning the result to the current</span>
|
|||
|
<span class="cm"> * thread-default main context. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">task</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_task_new</span><span class="w"> </span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span><span class="w"> </span><span class="n">user_data</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_task_set_task_data</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">(</span><span class="n">GDestroyNotify</span><span class="p">)</span><span class="w"> </span><span class="n">my_func_data_free</span><span class="p">);</span><span class="w"></span>
|
|||
|
|
|||
|
<span class="w"> </span><span class="cm">/* Invoke the function. */</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">g_main_context_invoke_full</span><span class="w"> </span><span class="p">(</span><span class="n">thread1_main_context</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">G_PRIORITY_DEFAULT</span><span class="p">,</span><span class="w"> </span><span class="n">my_func_idle</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="n">task</span><span class="p">,</span><span class="w"></span>
|
|||
|
<span class="w"> </span><span class="p">(</span><span class="n">GDestroyNotify</span><span class="p">)</span><span class="w"> </span><span class="n">g_object_unref</span><span class="p">);</span><span class="w"></span>
|
|||
|
<span class="p">}</span><span class="w"></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
|
|||
|
</article>
|
|||
|
<footer>
|
|||
|
|
|||
|
<div class="related-pages">
|
|||
|
<a class="next-page" href="lists.html">
|
|||
|
<div class="page-info">
|
|||
|
<div class="context">
|
|||
|
<span>Next</span>
|
|||
|
</div>
|
|||
|
<div class="title">Using GLib Lists</div>
|
|||
|
</div>
|
|||
|
<svg><use href="#svg-arrow-right"></use></svg>
|
|||
|
</a>
|
|||
|
<a class="prev-page" href="pre-and-post-conditions.html">
|
|||
|
<svg><use href="#svg-arrow-right"></use></svg>
|
|||
|
<div class="page-info">
|
|||
|
<div class="context">
|
|||
|
<span>Previous</span>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="title">Pre- and Post-Conditions</div>
|
|||
|
|
|||
|
</div>
|
|||
|
</a>
|
|||
|
</div>
|
|||
|
|
|||
|
</footer>
|
|||
|
|
|||
|
</div>
|
|||
|
<aside class="toc-drawer">
|
|||
|
|
|||
|
|
|||
|
<div class="toc-sticky toc-scroll">
|
|||
|
<div class="toc-title-container">
|
|||
|
<span class="toc-title">
|
|||
|
Contents
|
|||
|
</span>
|
|||
|
</div>
|
|||
|
<div class="toc-tree-container">
|
|||
|
<div class="toc-tree">
|
|||
|
<ul>
|
|||
|
<li><a class="reference internal" href="#">Main Contexts</a><ul>
|
|||
|
<li><a class="reference internal" href="#summary">Summary</a></li>
|
|||
|
<li><a class="reference internal" href="#what-is-gmaincontext">What is GMainContext?</a></li>
|
|||
|
<li><a class="reference internal" href="#what-is-gmainloop">What is GMainLoop?</a></li>
|
|||
|
<li><a class="reference internal" href="#default-contexts">Default Contexts</a></li>
|
|||
|
<li><a class="reference internal" href="#implicit-use-of-the-global-default-main-context">Implicit Use of the Global-Default Main Context</a></li>
|
|||
|
<li><a class="reference internal" href="#using-gmaincontext-in-a-library">Using GMainContext in a Library</a></li>
|
|||
|
<li><a class="reference internal" href="#ensuring-functions-are-called-in-the-right-context">Ensuring Functions are Called in the Right Context</a></li>
|
|||
|
<li><a class="reference internal" href="#principles-of-invocation">Principles of Invocation</a></li>
|
|||
|
<li><a class="reference internal" href="#convenience-method-g-main-context-invoke-full">Convenience Method: g_main_context_invoke_full()</a></li>
|
|||
|
<li><a class="reference internal" href="#checking-threading">Checking Threading</a></li>
|
|||
|
<li><a class="reference internal" href="#gtask">GTask</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
</aside>
|
|||
|
</main>
|
|||
|
</div><script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
|
|||
|
<script src="../_static/jquery.js"></script>
|
|||
|
<script src="../_static/underscore.js"></script>
|
|||
|
<script src="../_static/doctools.js"></script>
|
|||
|
<script src="../_static/scripts/furo.js"></script>
|
|||
|
</body>
|
|||
|
</html>
|